diff --git a/luprex/cpp/core/drivenengine.cpp b/luprex/cpp/core/drivenengine.cpp index f54a085e..acc383d6 100644 --- a/luprex/cpp/core/drivenengine.cpp +++ b/luprex/cpp/core/drivenengine.cpp @@ -220,8 +220,10 @@ enum DrvAction { PLAY_RECV_INCOMING, PLAY_NOTIFY_CLOSE, PLAY_NOTIFY_ACCEPT, - PLAY_INVOKE_EVENT_UPDATE, - PLAY_SET_LUA_SOURCE_PACK, + PLAY_CALL_EVENT_UPDATE, + PLAY_INVOKE_PLAYER, + PLAY_INVOKE_CHOOSE, + PLAY_INVOKE_LUA_SOURCE, PLAY_RELEASE, }; @@ -233,8 +235,10 @@ inline static const char *action_string(DrvAction act) { case PLAY_RECV_INCOMING: return "PLAY_RECV_INCOMING"; case PLAY_NOTIFY_CLOSE: return "PLAY_NOTIFY_CLOSE"; case PLAY_NOTIFY_ACCEPT: return "PLAY_NOTIFY_ACCEPT"; - case PLAY_INVOKE_EVENT_UPDATE: return "PLAY_INVOKE_EVENT_UPDATE"; - case PLAY_SET_LUA_SOURCE_PACK: return "PLAY_SET_LUA_SOURCE_PACK"; + case PLAY_CALL_EVENT_UPDATE: return "PLAY_CALL_EVENT_UPDATE"; + case PLAY_INVOKE_PLAYER: return "PLAY_INVOKE_PLAYER"; + case PLAY_INVOKE_CHOOSE: return "PLAY_INVOKE_CHOOSE"; + case PLAY_INVOKE_LUA_SOURCE: return "PLAY_INVOKE_LUA_SOURCE"; case PLAY_RELEASE: return "PLAY_RELEASE"; default: return "unknown"; } @@ -477,12 +481,22 @@ uint32_t DrivenEngine::drv_notify_accept(uint32_t port) { return chid; } -void DrivenEngine::drv_invoke_event_update(double clock) { +void DrivenEngine::drv_call_event_update(double clock) { clock_ = clock; event_update(); } -void DrivenEngine::drv_set_lua_source_pack(uint32_t srcpklen, const char *srcpk) { +void DrivenEngine::drv_invoke_player(int64_t place, uint32_t datapklen, const char *datapk) { + Invocation *inv = new Invocation(Invocation::KIND_PLAYER, visible_actor_id_, place, std::string_view(datapk, datapklen)); + queued_invocations_.emplace_back(inv); +} + +void DrivenEngine::drv_invoke_choose(int64_t place, uint32_t datapklen, const char *datapk) { + Invocation *inv = new Invocation(Invocation::KIND_CHOOSE, visible_actor_id_, place, std::string_view(datapk, datapklen)); + queued_invocations_.emplace_back(inv); +} + +void DrivenEngine::drv_invoke_lua_source(uint32_t srcpklen, const char *srcpk) { Invocation *inv = new Invocation(Invocation::KIND_LUA_SOURCE, visible_actor_id_, visible_actor_id_, std::string_view(srcpk, srcpklen)); queued_invocations_.emplace_back(inv); rescan_lua_source_ = false; @@ -791,12 +805,12 @@ static void replay_notify_accept(EngineWrapper *w) { static void play_invoke_event_update(EngineWrapper *w, double clock) { assert(w->rlog == nullptr); if (w->wlog != nullptr) { - w->wlog->write_cmd_hash(PLAY_INVOKE_EVENT_UPDATE, eng::memhash()); + w->wlog->write_cmd_hash(PLAY_CALL_EVENT_UPDATE, eng::memhash()); w->wlog->write_double(clock); w->wlog->flush(); } - w->engine->drv_invoke_event_update(clock); + w->engine->drv_call_event_update(clock); } static void replay_invoke_event_update(EngineWrapper *w) { @@ -805,32 +819,83 @@ static void replay_invoke_event_update(EngineWrapper *w) { return reset_wrapper(w, "replay log corrupt in replay_event_update"); } - w->engine->drv_invoke_event_update(clock); + w->engine->drv_call_event_update(clock); } //////////////////////// -void play_set_lua_source_pack(EngineWrapper *w, uint32_t srcpklen, const char *srcpk) { +void play_invoke_player(EngineWrapper *w, int64_t place, uint32_t datapklen, const char *datapk) { assert(w->rlog == nullptr); if (w->wlog != nullptr) { - w->wlog->write_cmd_hash(PLAY_SET_LUA_SOURCE_PACK, eng::memhash()); + w->wlog->write_cmd_hash(PLAY_INVOKE_PLAYER, eng::memhash()); + w->wlog->write_int64(place); + w->wlog->write_string(std::string_view(datapk, datapklen)); + w->wlog->flush(); + } + + w->engine->drv_invoke_player(place, datapklen, datapk); +} + +void replay_invoke_player(EngineWrapper *w) { + int64_t place = w->rlog->read_int64(); + std::string srcpack = w->rlog->read_string(); + if (!w->rlog->good()) { + return reset_wrapper(w, "replay log corrupt in replay_invoke_player"); + } + + w->engine->drv_invoke_player(place, srcpack.size(), srcpack.c_str()); +} + + +//////////////////////// + + +void play_invoke_choose(EngineWrapper *w, int64_t place, uint32_t datapklen, const char *datapk) { + assert(w->rlog == nullptr); + if (w->wlog != nullptr) { + w->wlog->write_cmd_hash(PLAY_INVOKE_CHOOSE, eng::memhash()); + w->wlog->write_int64(place); + w->wlog->write_string(std::string_view(datapk, datapklen)); + w->wlog->flush(); + } + + w->engine->drv_invoke_choose(place, datapklen, datapk); +} + +void replay_invoke_choose(EngineWrapper *w) { + int64_t place = w->rlog->read_int64(); + std::string srcpack = w->rlog->read_string(); + if (!w->rlog->good()) { + return reset_wrapper(w, "replay log corrupt in replay_invoke_choose"); + } + + w->engine->drv_invoke_choose(place, srcpack.size(), srcpack.c_str()); +} + + +//////////////////////// + + +void play_invoke_lua_source(EngineWrapper *w, uint32_t srcpklen, const char *srcpk) { + assert(w->rlog == nullptr); + if (w->wlog != nullptr) { + w->wlog->write_cmd_hash(PLAY_INVOKE_LUA_SOURCE, eng::memhash()); w->wlog->write_string(std::string_view(srcpk, srcpklen)); w->wlog->flush(); } - w->engine->drv_set_lua_source_pack(srcpklen, srcpk); + w->engine->drv_invoke_lua_source(srcpklen, srcpk); } -void replay_set_lua_source_pack(EngineWrapper *w) { +void replay_invoke_lua_source(EngineWrapper *w) { std::string srcpack = w->rlog->read_string(); - if (!w->rlog->good()) { - return reset_wrapper(w, "replay log corrupt in replay_set_lua_source_pack"); + return reset_wrapper(w, "replay log corrupt in replay_invoke_lua_source"); } - w->engine->drv_set_lua_source_pack(srcpack.size(), srcpack.c_str()); + w->engine->drv_invoke_lua_source(srcpack.size(), srcpack.c_str()); } @@ -900,8 +965,10 @@ static void replaycore_step(EngineWrapper *w) { case PLAY_RECV_INCOMING: replay_recv_incoming(w); return; case PLAY_NOTIFY_CLOSE: replay_notify_close(w); return; case PLAY_NOTIFY_ACCEPT: replay_notify_accept(w); return; - case PLAY_INVOKE_EVENT_UPDATE: replay_invoke_event_update(w); return; - case PLAY_SET_LUA_SOURCE_PACK: replay_set_lua_source_pack(w); return; + case PLAY_CALL_EVENT_UPDATE: replay_invoke_event_update(w); return; + case PLAY_INVOKE_PLAYER: replay_invoke_lua_source(w); return; + case PLAY_INVOKE_CHOOSE: replay_invoke_lua_source(w); return; + case PLAY_INVOKE_LUA_SOURCE: replay_invoke_lua_source(w); return; case PLAY_RELEASE: release(w); return; default: return reset_wrapper(w, "Replay log corrupt in command dispatcher"); } @@ -962,7 +1029,9 @@ static void init_engine_wrapper_helper(EngineWrapper *w) { w->play_notify_close = play_notify_close; w->play_notify_accept = play_notify_accept; w->play_invoke_event_update = play_invoke_event_update; - w->play_set_lua_source_pack = play_set_lua_source_pack; + w->play_invoke_player = play_invoke_player; + w->play_invoke_choose = play_invoke_choose; + w->play_invoke_lua_source = play_invoke_lua_source; w->replay_initialize = replaycore_initialize; w->replay_step = replaycore_step; diff --git a/luprex/cpp/core/drivenengine.hpp b/luprex/cpp/core/drivenengine.hpp index 63c0dfac..4cf13cfc 100644 --- a/luprex/cpp/core/drivenengine.hpp +++ b/luprex/cpp/core/drivenengine.hpp @@ -292,9 +292,11 @@ public: void drv_recv_incoming(uint32_t chid, uint32_t nbytes, const char *bytes); void drv_notify_close(uint32_t chid, uint32_t len, const char *data); uint32_t drv_notify_accept(uint32_t port); - void drv_invoke_event_update(double clock); - void drv_set_lua_source_pack(uint32_t srcpklen, const char *srcpk); - + void drv_call_event_update(double clock); + void drv_invoke_player(int64_t place, uint32_t datapklen, const char *datapk); + void drv_invoke_choose(int64_t place, uint32_t datapklen, const char *datapk); + void drv_invoke_lua_source(uint32_t srcpklen, const char *srcpk); + private: // Find a currently-unused channel ID. Channel IDs // are small integers that are reused. diff --git a/luprex/cpp/core/enginewrapper.hpp b/luprex/cpp/core/enginewrapper.hpp index 438b7476..42b42398 100644 --- a/luprex/cpp/core/enginewrapper.hpp +++ b/luprex/cpp/core/enginewrapper.hpp @@ -197,9 +197,27 @@ struct EngineWrapper { // void (*play_invoke_event_update)(EngineWrapper *w, double clock); - // Store the lua source code. + // Send an invoke/KIND_PLAYER // - void (*play_set_lua_source_pack)(EngineWrapper *w, uint32_t srcpklen, const char *srcpk); + // This is the main pathway for the graphics engine to control the player + // character. + // + void (*play_invoke_player)(EngineWrapper *w, int64_t place, uint32_t datapklen, const char *datapk); + + // Send an invoke/KIND_CHOOSE + // + // This is the main pathway for the graphics engine to choose a menu + // item or click a user interface button. + // + void (*play_invoke_choose)(EngineWrapper *w, int64_t place, uint32_t datapklen, const char *datapk); + + // Send an invoke/KIND_LUA_SOURCE + // + // This is used to update the lua source at runtime, without stopping + // the game. In addition to sending the invoke/KIND_LUA_SOURCE, it also + // clears the rescan_lua_source flag. + // + void (*play_invoke_lua_source)(EngineWrapper *w, uint32_t srcpklen, const char *srcpk); ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// diff --git a/luprex/cpp/core/invocation.cpp b/luprex/cpp/core/invocation.cpp index 1a9eedf7..9a5faba4 100644 --- a/luprex/cpp/core/invocation.cpp +++ b/luprex/cpp/core/invocation.cpp @@ -31,7 +31,7 @@ eng::string Invocation::debug_string() const { oss << "inv["; switch (kind_) { case KIND_INVALID: oss << "invalid"; break; - case KIND_PLAN: oss << "plan"; break; + case KIND_CHOOSE: oss << "choose"; break; case KIND_LUA: oss << "lua"; break; case KIND_FLUSH_PRINTS: oss << "flush_prints"; break; case KIND_TICK: oss << "tick"; break; diff --git a/luprex/cpp/core/invocation.hpp b/luprex/cpp/core/invocation.hpp index b7dff90b..e372610b 100644 --- a/luprex/cpp/core/invocation.hpp +++ b/luprex/cpp/core/invocation.hpp @@ -47,7 +47,8 @@ class Invocation : public eng::opnew { public: enum Kind { KIND_INVALID, - KIND_PLAN, + KIND_CHOOSE, + KIND_PLAYER, KIND_LUA, KIND_FLUSH_PRINTS, KIND_TICK, diff --git a/luprex/cpp/core/lpxclient.cpp b/luprex/cpp/core/lpxclient.cpp index 106a7668..3683fff3 100644 --- a/luprex/cpp/core/lpxclient.cpp +++ b/luprex/cpp/core/lpxclient.cpp @@ -128,7 +128,7 @@ public: return; } stdostream() << "Invoking plan: " << action << std::endl; - Invocation inv(Invocation::KIND_PLAN, actor_id_, gui_.place(), action); + Invocation inv(Invocation::KIND_CHOOSE, actor_id_, gui_.place(), action); send_invocation(inv); } diff --git a/luprex/cpp/core/lpxserver.cpp b/luprex/cpp/core/lpxserver.cpp index f051a4ee..568ee887 100644 --- a/luprex/cpp/core/lpxserver.cpp +++ b/luprex/cpp/core/lpxserver.cpp @@ -84,7 +84,7 @@ public: return; } stdostream() << "Invoking plan: " << action << std::endl; - master_->invoke(Invocation(Invocation::KIND_PLAN, admin_id_, gui_.place(), action)); + master_->invoke(Invocation(Invocation::KIND_CHOOSE, admin_id_, gui_.place(), action)); } void do_tick_command(const util::StringVec &words) { diff --git a/luprex/cpp/core/world-core.cpp b/luprex/cpp/core/world-core.cpp index ea91edd3..9b96d14e 100644 --- a/luprex/cpp/core/world-core.cpp +++ b/luprex/cpp/core/world-core.cpp @@ -566,8 +566,11 @@ void World::run_unittests() { void World::invoke(const Invocation &inv) { switch (inv.kind()) { - case Invocation::KIND_PLAN: - invoke_plan(inv.actor(), inv.place(), inv.datapack()); + case Invocation::KIND_CHOOSE: + invoke_choose(inv.actor(), inv.place(), inv.datapack()); + break; + case Invocation::KIND_PLAYER: + invoke_player(inv.actor(), inv.place(), inv.datapack()); break; case Invocation::KIND_LUA: invoke_lua(inv.actor(), inv.place(), inv.datapack()); @@ -609,8 +612,6 @@ void World::invoke_flush_prints(int64_t actor_id, int64_t place_id, std::string_ assert(stack_is_clear()); } - - void World::invoke_lua(int64_t actor_id, int64_t place_id, std::string_view datapack) { assert(stack_is_clear()); @@ -679,7 +680,7 @@ void World::invoke_lua(int64_t actor_id, int64_t place_id, std::string_view data assert(stack_is_clear()); } -void World::invoke_plan(int64_t actor_id, int64_t place_id, std::string_view datapack) { +void World::invoke_choose(int64_t actor_id, int64_t place_id, std::string_view datapack) { assert(stack_is_clear()); // Validate that the action is legal. @@ -768,6 +769,98 @@ void World::invoke_plan(int64_t actor_id, int64_t place_id, std::string_view dat assert(stack_is_clear()); } +void World::invoke_player(int64_t actor_id, int64_t place_id, std::string_view datapack) { + assert(stack_is_clear()); + + // Get the actor and place. Make sure both exist. + Tangible *tactor = tangible_get(actor_id); + Tangible *tplace = tangible_get(place_id); + if ((tactor == nullptr) || (tplace == nullptr)) { + return; + } + + // Get an ID for the thread. We always use the player + // pool in this case. + int64_t tid = tactor->id_player_pool_.get_one(); + + // Use a streambuffer to parse the datapack. + StreamBuffer datasb(datapack); + + // Extract the function name from the datapack. + eng::string funcname; + try { + funcname = datasb.read_string_limit(100); + } catch (const StreamException &ex) { + return; + } + if (sv::is_lua_id(funcname)) { + return; + } + + { + // Set up for Lua manipulation. + lua_State *L = state(); + LuaVar actor, place, func, mt, tangibles, playercb, thread, threads, thinfo, message; + LuaExtStack LS(L, actor, place, func, mt, tangibles, playercb, thread, threads, thinfo, message); + + // Get the actor and place. + LS.rawget(tangibles, LuaRegistry, "tangibles"); + LS.rawget(actor, tangibles, actor_id); + LS.rawget(place, tangibles, place_id); + if (!LS.istable(actor) || !LS.istable(place)) { + return; + } + + // Get the closure (playerinvoke.funcname). + eng::string err = LS.getclass(playercb, "playerinvoke"); + if ((!err.empty()) || (!LS.istable(playercb))) { + return; + } + LS.rawget(func, playercb, funcname); + if (!LS.isfunction(func)) { + return; + } + + // Create a new thread, push func, actor, place. + int nargs = 3; + lua_State *CO = LS.newthread(thread); + lua_pushvalue(L, func.index()); + lua_pushvalue(L, actor.index()); + lua_pushvalue(L, place.index()); + + // Push any additional args from the datapack. + // TODO: IMPLEMENT ME. + + // Transfer all arguments to the new thread. + lua_xmove(L, CO, nargs); + + // Create the thread info table. + LS.newtable(thinfo); + LS.rawset(thinfo, "thread", thread); + LS.rawset(thinfo, "actorid", actor_id); + LS.rawset(thinfo, "isnew", true); + LS.rawset(thinfo, "useppool", true); + LS.rawset(thinfo, "print", false); + + // Store the thread into place's thread table. + LS.getmetatable(mt, place); + if (!LS.istable(mt)) { + return; + } + LS.rawget(threads, mt, "threads"); + if (!LS.istable(threads)) { + return; + } + LS.rawset(threads, tid, thinfo); + } + + // Push the thread's ID into the runnable thread queue, + // then run the thread queue. + schedule(0, tid, place_id); + run_scheduled_threads(); + assert(stack_is_clear()); +} + void World::invoke_tick(int64_t actor_id, int64_t place_id, std::string_view datapack) { if (!is_authoritative()) { return; diff --git a/luprex/cpp/core/world.hpp b/luprex/cpp/core/world.hpp index 02ded864..3d077158 100644 --- a/luprex/cpp/core/world.hpp +++ b/luprex/cpp/core/world.hpp @@ -349,9 +349,13 @@ private: // static void store_global_pointer(lua_State *L, World *w); - // Invoke a plan. + // Invoke choosing an item from a plan. // - void invoke_plan(int64_t actor_id, int64_t place_id, std::string_view datapack); + void invoke_choose(int64_t actor_id, int64_t place_id, std::string_view datapack); + + // Invoke a player method, used by the graphics engine to control the player. + // + void invoke_player(int64_t actor_id, int64_t place_id, std::string_view datapack); // Invoke a lua string. // diff --git a/luprex/cpp/drv/driver.cpp b/luprex/cpp/drv/driver.cpp index 1c0f4707..16778a2c 100644 --- a/luprex/cpp/drv/driver.cpp +++ b/luprex/cpp/drv/driver.cpp @@ -193,7 +193,7 @@ class Driver { std::string err = drvutil::package_lua_source(".", &oss); if_error_print_and_exit(err); std::string_view ossv = oss.view(); - engw.play_set_lua_source_pack(&engw, ossv.size(), ossv.data()); + engw.play_invoke_lua_source(&engw, ossv.size(), ossv.data()); } }