diff --git a/luprex/core/cpp/luastack.cpp b/luprex/core/cpp/luastack.cpp index e2f0ae0f..5e7b25fc 100644 --- a/luprex/core/cpp/luastack.cpp +++ b/luprex/core/cpp/luastack.cpp @@ -194,6 +194,12 @@ void LuaStack::newtable(LuaSlot target) const { lua_replace(L_, target); } +lua_State *LuaStack::newthread(LuaSlot target) const { + lua_State *result = lua_newthread(L_); + lua_replace(L_, target); + return result; +} + void LuaStack::makeclass(LuaSlot classtab, LuaSlot classname) const { checkstring(classname); diff --git a/luprex/core/cpp/luastack.hpp b/luprex/core/cpp/luastack.hpp index 4d511706..cc9efd0c 100644 --- a/luprex/core/cpp/luastack.hpp +++ b/luprex/core/cpp/luastack.hpp @@ -425,6 +425,7 @@ public: void checknometa(LuaSlot index) const; void newtable(LuaSlot target) const; + lua_State *newthread(LuaSlot target) const; void makesubtable(LuaSlot sub, LuaSlot tab, const char *name) const; diff --git a/luprex/core/cpp/traceback.cpp b/luprex/core/cpp/traceback.cpp index 74279c94..ae00d732 100644 --- a/luprex/core/cpp/traceback.cpp +++ b/luprex/core/cpp/traceback.cpp @@ -4,26 +4,26 @@ #define TRACEBACK_LEVELS2 10 -static int traceback_general(lua_State *L, lua_State *L1, int msgindex) { +// Call this with the error message on top of the stack. +static void traceback_general(lua_State *L, lua_State *L1, int baselevel) { int top = lua_gettop(L); - // Convert message to a string and push the string. - if (lua_tostring(L, msgindex)) { - lua_pushvalue(L, msgindex); - } else { - luaL_callmeta(L, msgindex, "__tostring"); - } - - // If we didn't end up with exactly one string on - // the stack, then clear the stack and push 'unknown error'. - if ((lua_gettop(L) != top + 1) || (!lua_tostring(L, -1))) { - lua_settop(L, top); - lua_pushstring(L, "unknown error"); + // Convert message to a string + if (!lua_tostring(L, top)) { + luaL_callmeta(L, top, "__tostring"); + // If callmeta didn't produce exactly one string, clear the stack + // and push "unknown error" + if ((lua_gettop(L) == top + 1) && (lua_tostring(L, -1))) { + lua_remove(L, top); + } else { + lua_settop(L, top - 1); + lua_pushstring(L, "unknown error"); + } } // Append the traceback. lua_Debug ar; - int level = 1; + int level = baselevel; int firstpart = 1; while (lua_getstack(L1, level++, &ar)) { if (level > TRACEBACK_LEVELS1 && firstpart) { @@ -55,27 +55,25 @@ static int traceback_general(lua_State *L, lua_State *L1, int msgindex) { lua_pushfstring(L, " in function <%s:%d>", ar.short_src, ar.linedefined); } - if (lua_gettop(L) - top > 5) { - lua_concat(L, lua_gettop(L) - top); + if (1 + lua_gettop(L) - top > 5) { + lua_concat(L, 1 + lua_gettop(L) - top); } } } lua_pushstring(L, "\n"); - if (lua_gettop(L) - top > 1) { - lua_concat(L, lua_gettop(L) - top); + if (1 + lua_gettop(L) - top > 1) { + lua_concat(L, 1 + lua_gettop(L) - top); } +} + +int traceback_handler(lua_State *L) { + traceback_general(L, L, 1); return 1; } -LuaDefine(traceback_handler, "c") { - return traceback_general(L, L, lua_gettop(L)); -} - -LuaDefine(traceback_coroutine, "c") { - LuaArg thread, message; - LuaStack LS(L, thread, message); - lua_State *L1 = LS.ckthread(thread); - return traceback_general(L, L1, message.index()); +void traceback_coroutine(lua_State *L, lua_State *CO) { + lua_xmove(CO, L, 1); + traceback_general(L, CO, 0); } int traceback_pcall(lua_State *L, int narg, int nret) { diff --git a/luprex/core/cpp/traceback.hpp b/luprex/core/cpp/traceback.hpp index 398acb43..05407005 100644 --- a/luprex/core/cpp/traceback.hpp +++ b/luprex/core/cpp/traceback.hpp @@ -15,17 +15,10 @@ // traceback_coroutine // -// Given a coroutine and an error message, returns a traceback -// of the coroutine. +// Given a coroutine which contains an error message, pop the error +// message from the coroutine, and push a traceback onto L. // -int traceback_coroutine(lua_State *L); - -// traceback_handler -// -// The function 'pcall' expects you to pass in a message handler routine. -// 'traceback_handler' is designed to be used as this argument to pcall. -// -int traceback_handler(lua_State *L); +void traceback_coroutine(lua_State *L, lua_State *CO); // traceback_pcall // diff --git a/luprex/core/cpp/viewer.cpp b/luprex/core/cpp/viewer.cpp new file mode 100644 index 00000000..0be28186 --- /dev/null +++ b/luprex/core/cpp/viewer.cpp @@ -0,0 +1,19 @@ + +#include "viewer.hpp" + +Viewer::Viewer() { + world_.reset(new World); + world_->init_standalone(); +} + +Viewer::~Viewer() { +} + +int64_t Viewer::get_player_id() { + return 1; +} + +void Viewer::update_gui(int64_t place, Gui *gui) { + world_->update_gui(get_player_id(), place, gui); +} + diff --git a/luprex/core/cpp/viewer.hpp b/luprex/core/cpp/viewer.hpp new file mode 100644 index 00000000..85f7c5fa --- /dev/null +++ b/luprex/core/cpp/viewer.hpp @@ -0,0 +1,46 @@ +#ifndef VIEWER_HPP +#define VIEWER_HPP + +#include "world.hpp" +#include "animqueue.hpp" +#include "gui.hpp" +#include +#include +#include + +class Viewer { +private: + std::unique_ptr world_; + +public: + Viewer(); + ~Viewer(); + + // Snapshot/rollback the lua state (temporary hack) + void snapshot() { world_->lua_snap_.snapshot(); } + void rollback() { world_->lua_snap_.rollback(); } + + // Get the lua state for interaction. + // + lua_State *state() { return world_->state(); } + + // Get the player ID of the current player. + // + int64_t get_player_id(); + + // Get the list of tangibles near the player. + // + std::vector get_near() { return world_->get_near(1, 100); } + + // Get the specified tangible. + // + const Tangible *tangible_get(int64_t id) { return world_->tangible_get(id); } + + // Update the GUI for the specified sprite. + // + // The gui passed in will be overwritten. + // + void update_gui(int64_t id, Gui *g); +}; + +#endif // VIEWER_HPP diff --git a/luprex/core/cpp/world.cpp b/luprex/core/cpp/world.cpp index 2de05651..ea40d699 100644 --- a/luprex/core/cpp/world.cpp +++ b/luprex/core/cpp/world.cpp @@ -120,6 +120,7 @@ Tangible *World::tangible_make(lua_State *L, int64_t id, bool pushdb) { LS.rawset(database, "inventory", LuaNewTable); LS.rawset(database, "id", id); LS.rawset(metatab, "id", id); + LS.rawset(metatab, "threads", LuaNewTable); // LS.rawset(metatab, "__metatable", LuaNil); LS.result(); @@ -202,8 +203,8 @@ void World::invoke_plan(int64_t actor_id, int64_t place_id, const std::string &a lua_State *L = state(); - LuaVar actor, place, ugui, func, tangibles, mt, index, actions; - LuaStack LS(L, actor, place, ugui, func, tangibles, mt, index, actions); + LuaVar actor, place, ugui, func, tangibles, mt, index, actions, thread, message; + LuaStack LS(L, actor, place, ugui, func, tangibles, mt, index, actions, thread, message); // Get the actor and place. LS.rawget(tangibles, LuaRegistry, "tangibles"); @@ -239,17 +240,28 @@ void World::invoke_plan(int64_t actor_id, int64_t place_id, const std::string &a // Construct the userdata with the GUI pointer. LS.newpointer(ugui, gui, false); - // Call the action function. + // Create a new thread, set up function and parameters. + lua_State *CO = LS.newthread(thread); lua_pushvalue(L, func.index()); lua_pushvalue(L, actor.index()); lua_pushvalue(L, place.index()); lua_pushvalue(L, ugui.index()); - int status = traceback_pcall(L, 3, 0); - if (status != 0) { - gui->clear(); + lua_xmove(L, CO, 4); + + // Resume the new coroutine. + int status = lua_resume(CO, 3); + + // Three possible outcomes: finished, yielded, or errored. + if (status == LUA_YIELD) { + // Yield not implemented yet. The coroutine is simply dropped. + std::cerr << "Thread yield not implemented yet." << std::endl; + } else if (status == 0) { + // Successfully ran to completion. + std::cerr << "Thread ran to completion." << std::endl; + } else { + // Transfer the error message from CO to L, and add a traceback. + traceback_coroutine(L, CO); std::cerr << lua_tostring(L, -1); - LS.result(); - return; } // Nuke the userdata, in case somebody saved a pointer to it. diff --git a/luprex/core/lua/player.lua b/luprex/core/lua/player.lua index b5d8a044..f277744b 100644 --- a/luprex/core/lua/player.lua +++ b/luprex/core/lua/player.lua @@ -21,4 +21,6 @@ end function player.action.west(actor, place, gui) print("Moving west") + t = nil + t[3] = 5 end