From 5db5a8b06fc9d988207fac350e22979ac2b516c2 Mon Sep 17 00:00:00 2001 From: Josh Yelon Date: Tue, 30 Nov 2021 12:39:09 -0500 Subject: [PATCH] When you 'invoke' lua code, it creates coroutine --- luprex/core/cpp/lpxclient.cpp | 4 +- luprex/core/cpp/util.cpp | 7 ++ luprex/core/cpp/util.hpp | 3 + luprex/core/cpp/world-core.cpp | 109 +++++++++++++++++------------ luprex/core/cpp/world-diffxmit.cpp | 4 +- luprex/core/cpp/world.hpp | 2 +- 6 files changed, 81 insertions(+), 48 deletions(-) diff --git a/luprex/core/cpp/lpxclient.cpp b/luprex/core/cpp/lpxclient.cpp index 36563df1..7483b7d1 100644 --- a/luprex/core/cpp/lpxclient.cpp +++ b/luprex/core/cpp/lpxclient.cpp @@ -239,7 +239,9 @@ public: // Channel print statements. if (print_channeler_.channel(world_->get_printbuffer(actor_id_), stdostream())) { - send_invocation(print_channeler_.invocation(actor_id_)); + if (channel_ != nullptr) { + send_invocation(print_channeler_.invocation(actor_id_)); + } } } }; diff --git a/luprex/core/cpp/util.cpp b/luprex/core/cpp/util.cpp index 384dd7fa..7cf9be6a 100644 --- a/luprex/core/cpp/util.cpp +++ b/luprex/core/cpp/util.cpp @@ -264,6 +264,13 @@ bool world_type_authoritative(util::WorldType wt) { return (wt == WORLD_TYPE_MASTER) || (wt == WORLD_TYPE_STANDALONE); } +LuaSourcePtr make_lua_source(const std::string &code) { + LuaSourcePtr result(new LuaSourceVec); + std::string fn = "file.lua"; + result->push_back(std::make_pair(fn, code)); + return result; +} + static std::string get_file_contents(const std::string &fn) { std::ifstream fs(fn); std::stringstream buffer; diff --git a/luprex/core/cpp/util.hpp b/luprex/core/cpp/util.hpp index 5c6f78b3..fd0c36f0 100644 --- a/luprex/core/cpp/util.hpp +++ b/luprex/core/cpp/util.hpp @@ -105,6 +105,9 @@ double distance_squared(double x1, double y1, double x2, double y2); // Return true if a world type is authoritative. bool world_type_authoritative(util::WorldType wt); +// Make a LuaSourceVec with one element, for unit testing. +LuaSourcePtr make_lua_source(const std::string &code); + // Remove nullptrs from a vector of unique pointers. template void remove_nullptrs(std::vector> &vec) { diff --git a/luprex/core/cpp/world-core.cpp b/luprex/core/cpp/world-core.cpp index 9cfbd42d..317ccd85 100644 --- a/luprex/core/cpp/world-core.cpp +++ b/luprex/core/cpp/world-core.cpp @@ -374,7 +374,7 @@ void World::update_gui(int64_t actor_id, int64_t place_id, Gui *gui) { assert(stack_is_clear()); } -void World::update_source(util::LuaSourcePtr source) { +void World::update_source(const util::LuaSourcePtr &source) { assert(stack_is_clear()); if (source != nullptr) { source_db_.update(*source); @@ -436,23 +436,28 @@ void World::invoke_flush_prints(int64_t actor_id, int64_t place_id, const std::s } + void World::invoke_lua(int64_t actor_id, int64_t place_id, const std::string &action, const InvocationData &data) { assert(stack_is_clear()); - // Get the actor and place, which must be the same. - if (actor_id != place_id) return; + // Make sure that actor and place exist. Tangible *tactor = tangible_get(actor_id); - if (tactor == nullptr) { + Tangible *tplace = tangible_get(place_id); + if ((tactor == nullptr) || (tplace == nullptr)) { return; } + // Get a thread ID for the new thread. + int64_t tid = tplace->id_player_pool_.get_one(); + + // Set up for lua manipulation. lua_State *L = state(); - LuaVar closure; - LuaStack LS(L, closure); + LuaVar func, tangibles, place, mt, thread, thinfo, threads; + LuaStack LS(L, func, tangibles, place, mt, thread, thinfo, threads); // create the compiled closure. int status = luaL_loadbuffer(L, action.c_str(), action.size(), "=invoke"); - lua_replace(L, closure.index()); + lua_replace(L, func.index()); if (status != LUA_OK) { // The closure is actually an error message. Do nothing. // This should normally not happen: LuaConsole should filter @@ -461,31 +466,44 @@ void World::invoke_lua(int64_t actor_id, int64_t place_id, const std::string &ac return; } - // Call the closure. - int top = lua_gettop(L); - lua_pushvalue(L, closure.index()); - open_lthread_state(actor_id, place_id, false, true); - status = traceback_pcall(L, 0, LUA_MULTRET); - - // If there's an error message, print it. - // Otherwise, pretty-print the results. - std::ostream *ostream = lthread_print_stream(); - if (status == LUA_OK) { - for (int i = top + 1; i <= lua_gettop(L); i++) { - LuaSpecial root(i); - pprint(LS, root, true, ostream); - (*ostream) << std::endl; - } - } else { - const char *msg = lua_tostring(L, -1); - if (msg == NULL) { - msg = "(error object is not a string)"; - } - (*ostream) << msg << std::endl; + // Get the place. + LS.rawget(tangibles, LuaRegistry, "tangibles"); + LS.rawget(place, tangibles, place_id); + if (!LS.istable(place)) { + LS.result(); + return; } - close_lthread_state(); + // Get the place's metatable. + LS.getmetatable(mt, place); + if (!LS.istable(mt)) { + LS.result(); + return; + } + + // Create a new thread, set up function and parameters. + lua_State *CO = LS.newthread(thread); + lua_pushvalue(L, func.index()); + lua_xmove(L, CO, 1); + + // Create the thread info table. + LS.newtable(thinfo); + LS.rawset(thinfo, "thread", thread); + LS.rawset(thinfo, "actorid", actor_id); + LS.rawset(thinfo, "nargs", 0); + LS.rawset(thinfo, "useppool", true); + + // Store the thread into place's thread table. + LS.rawget(threads, mt, "threads"); + if (!LS.istable(threads)) { + LS.result(); + return; + } + LS.rawset(threads, tid, thinfo); LS.result(); + + thread_sched_.add(0, tid, place_id); + run_scheduled_threads(); assert(stack_is_clear()); } @@ -554,8 +572,6 @@ void World::invoke_plan(int64_t actor_id, int64_t place_id, const std::string &a // Create a new thread, set up function and parameters. lua_State *CO = LS.newthread(thread); - - // Move function and args to new thread. lua_pushvalue(L, func.index()); lua_pushvalue(L, actor.index()); lua_pushvalue(L, place.index()); @@ -566,7 +582,7 @@ void World::invoke_plan(int64_t actor_id, int64_t place_id, const std::string &a LS.newtable(thinfo); LS.rawset(thinfo, "thread", thread); LS.rawset(thinfo, "actorid", actor_id); - LS.rawset(thinfo, "isnew", true); + LS.rawset(thinfo, "nargs", 3); // actor, place, invdata LS.rawset(thinfo, "useppool", true); // Store the thread into place's thread table. @@ -595,10 +611,10 @@ void World::invoke_tick(int64_t actor_id, int64_t place_id, const std::string &a void World::run_scheduled_threads() { assert(stack_is_clear()); lua_State *L = state(); - LuaVar tangibles, place, mt, threads, thinfo, actorid, isnew, useppool, thread; - LuaStack LS(L, tangibles, place, mt, threads, thinfo, actorid, isnew, useppool, thread); - LS.rawget(tangibles, LuaRegistry, "tangibles"); + LuaVar tangibles, place, mt, threads, thinfo, actorid, nargs, useppool, thread; + LuaStack LS(L, tangibles, place, mt, threads, thinfo, actorid, nargs, useppool, thread); + LS.rawget(tangibles, LuaRegistry, "tangibles"); while (thread_sched_.ready(clock_)) { SchedEntry sched = thread_sched_.pop(); LS.rawget(place, tangibles, sched.place_id()); @@ -621,8 +637,8 @@ void World::run_scheduled_threads() { if (!LS.isnumber(actorid)) { continue; } - LS.rawget(isnew, thinfo, "isnew"); - if (!LS.isboolean(isnew)) { + LS.rawget(nargs, thinfo, "nargs"); + if (!LS.isnumber(nargs)) { continue; } LS.rawget(useppool, thinfo, "useppool"); @@ -637,11 +653,13 @@ void World::run_scheduled_threads() { // Resume the coroutine. lua_State *CO = LS.ckthread(thread); open_lthread_state(LS.ckinteger(actorid), sched.place_id(), LS.ckboolean(useppool), true); - int status = lua_resume(CO, nullptr, LS.ckboolean(isnew) ? 3 : 0); - close_lthread_state(); - + int status = lua_resume(CO, nullptr, LS.ckint(nargs)); + std::ostream *ostream = lthread_print_stream(); + // Three possible outcomes: finished, yielded, or errored. - if (status == LUA_YIELD) { + if (!util::world_type_authoritative(world_type_)) { + LS.rawset(threads, sched.thread_id(), LuaNil); + } else if (status == LUA_YIELD) { // If there's nothing on the stack, infer that tangible.nopredict yielded. if (lua_gettop(CO) == 0) { // std::cerr << "Thread killed self using tangible.nopredict" << std::endl; @@ -651,10 +669,10 @@ void World::run_scheduled_threads() { else if ((lua_gettop(CO) == 1) && (lua_isnumber(CO, 1))) { lua_Number delay = lua_tonumber(CO, 1); lua_settop(CO, 0); - LS.rawset(thinfo, "isnew", false); + LS.rawset(thinfo, "nargs", 0); LS.rawset(thinfo, "useppool", false); // std::cerr << "Thread wait = " << delay << std::endl; - thread_sched_.add(sched.clock() + int64_t(delay), sched.thread_id(), sched.place_id()); + thread_sched_.add(clock_ + int64_t(delay), sched.thread_id(), sched.place_id()); // std::cerr << "Added to schedule." << std::endl; } // In any other case, generate an error and kill the coroutine. @@ -668,11 +686,14 @@ void World::run_scheduled_threads() { LS.rawset(threads, sched.thread_id(), LuaNil); } else { // Generated an error. Add a traceback, print, and kill the coroutine. + // Currently, the error is sent to the actor. That seems... not right in the long run. traceback_coroutine(CO); - // std::cerr << lua_tostring(CO, -1); + (*ostream) << lua_tostring(CO, -1); LS.rawset(threads, sched.thread_id(), LuaNil); } + close_lthread_state(); } + LS.result(); assert(stack_is_clear()); } diff --git a/luprex/core/cpp/world-diffxmit.cpp b/luprex/core/cpp/world-diffxmit.cpp index fbfc3054..3057282b 100644 --- a/luprex/core/cpp/world-diffxmit.cpp +++ b/luprex/core/cpp/world-diffxmit.cpp @@ -170,9 +170,9 @@ void World::patch_luatabs(StreamBuffer *sb, DebugCollector *dbc) { int ncreate = sb->read_int32(); util::IdVector closetans = get_near(actor_id, RadiusClose, true, false); assert(closehash == util::hash_id_vector(closetans)); - int nt = number_lua_tables(closetans); + number_lua_tables(closetans); create_new_tables(ncreate); - DebugLine(dbc) << "lua tables: " << nt << " existing " << ncreate << " new"; + // DebugLine(dbc) << "lua tables: " << nt << " existing " << ncreate << " new"; patch_tangible_databases(sb, dbc); patch_numbered_tables(sb, dbc); unnumber_lua_tables(); diff --git a/luprex/core/cpp/world.hpp b/luprex/core/cpp/world.hpp index 4839f847..90476fe8 100644 --- a/luprex/core/cpp/world.hpp +++ b/luprex/core/cpp/world.hpp @@ -195,7 +195,7 @@ public: // // Special case: if the source pointer is nullptr, does not update. // - void update_source(util::LuaSourcePtr source); + void update_source(const util::LuaSourcePtr &source); // Run all unit tests. //