diff --git a/luprex/core/cpp/textgame.cpp b/luprex/core/cpp/textgame.cpp index f7d14e9f..7546e752 100644 --- a/luprex/core/cpp/textgame.cpp +++ b/luprex/core/cpp/textgame.cpp @@ -122,7 +122,7 @@ void TextGame::do_choose_command(const StringVec &cmd) { } std::string action = elts[index].action(); std::cerr << "Invoking plan: " << action << std::endl; - world_->invoke_plan(1, gui_place_, action, &gui_); + world_->invoke_plan(1, gui_place_, action); } void TextGame::do_snapshot_command(const StringVec &cmd) { diff --git a/luprex/core/cpp/world.cpp b/luprex/core/cpp/world.cpp index ea40d699..9a33a904 100644 --- a/luprex/core/cpp/world.cpp +++ b/luprex/core/cpp/world.cpp @@ -193,7 +193,7 @@ void World::update_gui(int64_t actor_id, int64_t place_id, Gui *gui) { LS.result(); } -void World::invoke_plan(int64_t actor_id, int64_t place_id, const std::string &action, Gui *gui) { +void World::invoke_plan(int64_t actor_id, int64_t place_id, const std::string &action) { // Validate that the action is legal. Gui validation_gui; update_gui(actor_id, place_id, &validation_gui); @@ -201,10 +201,15 @@ void World::invoke_plan(int64_t actor_id, int64_t place_id, const std::string &a return; } - lua_State *L = state(); + // Get an ID batch for the thread, and take one for the thread itself. + Tangible *tactor = tangible_get(actor_id); + int64_t id_batch = tactor->id_player_pool_->get_batch(); + int64_t tid = id_batch++; - LuaVar actor, place, ugui, func, tangibles, mt, index, actions, thread, message; - LuaStack LS(L, actor, place, ugui, func, tangibles, mt, index, actions, thread, message); + // Set up for Lua manipulation. + lua_State *L = state(); + LuaVar actor, place, func, tangibles, mt, index, actions, threadtab, thread, threadinfo, message; + LuaStack LS(L, actor, place, func, tangibles, mt, index, actions, threadtab, thread, threadinfo, message); // Get the actor and place. LS.rawget(tangibles, LuaRegistry, "tangibles"); @@ -237,40 +242,100 @@ void World::invoke_plan(int64_t actor_id, int64_t place_id, const std::string &a return; } - // Construct the userdata with the GUI pointer. - LS.newpointer(ugui, gui, false); - // 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()); + lua_pushnil(L); // Gui state not implemented yet. lua_xmove(L, CO, 4); - // Resume the new coroutine. + // Store the thread into place's thread table. + LS.rawget(threadtab, mt, "threads"); + if (!LS.istable(threadtab)) { + LS.result(); + return; + } + LS.set(threadinfo, LuaNewTable); + LS.rawset(threadinfo, "thread", thread); + LS.rawset(threadtab, tid, threadinfo); + LS.result(); + + // Push the thread's ID into the runnable thread queue, + // then run the thread queue. + enqueue_thread(tid, place_id); + run_thread_queue(); +} + +void World::enqueue_thread(int64_t tid, int64_t place_id) { + thread_queue_.insert(std::make_pair(tid, place_id)); +} + +void World::run_thread(int64_t tid, int64_t place_id) { + lua_State *L = state(); + LuaVar tangibles, place, mt, threads, threadtab, thread; + LuaStack LS(L, tangibles, place, mt, threads, threadtab, thread); + + LS.rawget(tangibles, LuaRegistry, "tangibles"); + LS.rawget(place, tangibles, place_id); + if (!LS.istable(place)) { + LS.result(); + return; + } + LS.getmetatable(mt, place); + if (!LS.istable(mt)) { + LS.result(); + return; + } + LS.rawget(threads, mt, "threads"); + if (!LS.istable(threads)) { + LS.result(); + return; + } + LS.rawget(threadtab, threads, tid); + if (!LS.istable(threadtab)) { + LS.result(); + return; + } + LS.rawget(thread, threadtab, "thread"); + if (!LS.isthread(thread)) { + LS.result(); + return; + } + LS.rawset(threadtab, "wake", LuaNil); + + // Resume the coroutine. + lua_State *CO = LS.ckthread(thread); 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; + // When the wait statement yields, it yields the desired timestamp. + std::cerr << "Thread yield top = " << lua_gettop(CO) << std::endl; + LS.rawset(threads, tid, LuaNil); } else if (status == 0) { - // Successfully ran to completion. + // Successfully ran to completion. Remove from thread table. std::cerr << "Thread ran to completion." << std::endl; + LS.rawset(threads, tid, LuaNil); } else { // Transfer the error message from CO to L, and add a traceback. + LS.rawset(threads, tid, LuaNil); traceback_coroutine(L, CO); std::cerr << lua_tostring(L, -1); } - - // Nuke the userdata, in case somebody saved a pointer to it. - LS.clearuserdata(ugui); - - // And we're done. LS.result(); } +void World::run_thread_queue() { + while (!thread_queue_.empty()) { + auto iter = thread_queue_.begin(); + int64_t tid = iter->first; + int64_t place_id = iter->second; + run_thread(tid, place_id); + thread_queue_.erase(iter); + } +} + LuaDefine(tangible_get, "c") { LuaArg id; LuaRet database; diff --git a/luprex/core/cpp/world.hpp b/luprex/core/cpp/world.hpp index eb6bee96..b2e64f1d 100644 --- a/luprex/core/cpp/world.hpp +++ b/luprex/core/cpp/world.hpp @@ -9,6 +9,8 @@ #include "source.hpp" #include "gui.hpp" #include "luasnap.hpp" +#include +#include #include #include @@ -44,6 +46,10 @@ public: class World { public: + // A thread ID appended to a place ID. + // + using ThreadPair = std::pair; + // A lua intepreter with snapshot function. // LuaSnap lua_snap_; @@ -58,6 +64,14 @@ public: PlaneMap plane_map_; std::unordered_map tangibles_; + // Thread queue. + // + std::set thread_queue_; + + void enqueue_thread(int64_t tid, int64_t place_id); + void run_thread_queue(); + void run_thread(int64_t tid, int64_t place_id); + public: // Constructor. // @@ -114,7 +128,9 @@ public: // Invoke an action plan. // - void invoke_plan(int64_t actor_id, int64_t place_id, const std::string &action, Gui *gui); + // Eventually we'll add another parameter for gui-state. + // + void invoke_plan(int64_t actor_id, int64_t place_id, const std::string &action); // fetch // diff --git a/luprex/core/lua/player.lua b/luprex/core/lua/player.lua index f277744b..b5d8a044 100644 --- a/luprex/core/lua/player.lua +++ b/luprex/core/lua/player.lua @@ -21,6 +21,4 @@ end function player.action.west(actor, place, gui) print("Moving west") - t = nil - t[3] = 5 end