From 47a570064c0defc0930f50eded5a7bd1c39be870 Mon Sep 17 00:00:00 2001 From: jyelon Date: Sun, 1 Sep 2024 19:43:00 -0400 Subject: [PATCH] Implemented World::probe_lua_call and tested --- luprex/cpp/core/animqueue.hpp | 2 +- luprex/cpp/core/lpxclient.cpp | 2 +- luprex/cpp/core/lpxserver.cpp | 32 +++++- luprex/cpp/core/luastack.cpp | 2 +- luprex/cpp/core/streambuffer.hpp | 2 + luprex/cpp/core/world-core.cpp | 191 ++++++++++++++++++++++++------- luprex/cpp/core/world.hpp | 9 +- luprex/ext/base-buffer.hpp | 2 +- luprex/lua/login.lua | 1 + 9 files changed, 196 insertions(+), 47 deletions(-) diff --git a/luprex/cpp/core/animqueue.hpp b/luprex/cpp/core/animqueue.hpp index 6f08d677..13910233 100644 --- a/luprex/cpp/core/animqueue.hpp +++ b/luprex/cpp/core/animqueue.hpp @@ -102,7 +102,7 @@ #include #include -struct AnimValue : public SimpleDynamic { +struct AnimValue : public SimpleDynamicValue { bool persistent; AnimValue() { persistent = false; } diff --git a/luprex/cpp/core/lpxclient.cpp b/luprex/cpp/core/lpxclient.cpp index 148300e0..68b451a7 100644 --- a/luprex/cpp/core/lpxclient.cpp +++ b/luprex/cpp/core/lpxclient.cpp @@ -183,7 +183,7 @@ public: virtual void do_luaprobe_command(std::string_view cmd) override { world_to_asynchronous(); - stdostream() << world_->probe_lua(actor_id_, cmd); + stdostream() << world_->probe_lua_expr(actor_id_, cmd); world_to_synchronous(); } diff --git a/luprex/cpp/core/lpxserver.cpp b/luprex/cpp/core/lpxserver.cpp index 6669ad6d..47267c21 100644 --- a/luprex/cpp/core/lpxserver.cpp +++ b/luprex/cpp/core/lpxserver.cpp @@ -90,7 +90,35 @@ public: } virtual void do_work_command() override { - do_unknown_command("work"); + master_->snapshot(); + StreamBuffer datapack; + StreamBuffer retvals; + datapack.write_string("engio"); + datapack.write_string("myfunction"); + datapack.write_simple_dynamic_tag(SimpleDynamicTag::NUMBER); + datapack.write_double(3.7); + datapack.write_simple_dynamic_tag(SimpleDynamicTag::STRING); + datapack.write_string("banana"); + datapack.write_simple_dynamic_tag(SimpleDynamicTag::BOOLEAN); + datapack.write_bool(true); + master_->probe_lua_call(admin_id_, admin_id_, datapack.view(), &retvals); + while (!retvals.empty()) { + SimpleDynamicValue value; + retvals.read_simple_dynamic(&value); + if (value.type == SimpleDynamicTag::NUMBER) { + util::dprint("retval: ", value.x); + } else if (value.type == SimpleDynamicTag::STRING) { + util::dprint("retval: ", value.s); + } else if (value.type == SimpleDynamicTag::BOOLEAN) { + util::dprint(value.x ? "retval: true" : "retval: false"); + } else if (value.type == SimpleDynamicTag::VECTOR) { + util::dprint("retval: ", value.x, " ", value.y, " ", value.z); + } else { + util::dprint("retval: invalid"); + break; + } + } + master_->rollback(); } virtual void do_display_command() override { @@ -111,7 +139,7 @@ public: virtual void do_luaprobe_command(std::string_view cmd) override { master_->snapshot(); - stdostream() << master_->probe_lua(admin_id_, cmd); + stdostream() << master_->probe_lua_expr(admin_id_, cmd); master_->rollback(); } diff --git a/luprex/cpp/core/luastack.cpp b/luprex/cpp/core/luastack.cpp index 325d9b3a..84c22d26 100644 --- a/luprex/cpp/core/luastack.cpp +++ b/luprex/cpp/core/luastack.cpp @@ -171,7 +171,7 @@ std::optional LuaCoreStack::tryxyz(LuaSlot s) const { lua_rawgeti(L_, s, 3); lua_rawgeti(L_, s, 2); lua_rawgeti(L_, s, 1); - if (lua_isnumber(L_, -1) && lua_isnumber(L_, -2) && lua_isnumber(L_, -3)) { + if ((lua_type(L_, -1)==LUA_TNUMBER) && (lua_type(L_, -2)==LUA_TNUMBER) && (lua_type(L_, -3)==LUA_TNUMBER)) { util::DXYZ result; result.x = lua_tonumber(L_, -1); result.y = lua_tonumber(L_, -2); diff --git a/luprex/cpp/core/streambuffer.hpp b/luprex/cpp/core/streambuffer.hpp index 926bb0a4..8c6ab1e2 100644 --- a/luprex/cpp/core/streambuffer.hpp +++ b/luprex/cpp/core/streambuffer.hpp @@ -243,6 +243,8 @@ public: virtual char const *what() const { return "Stream Corruption"; } }; +using SimpleDynamicValue = SimpleDynamic; + class StreamBufferCore { protected: void *basebuffer_malloc(size_t size) { return eng::malloc(size); } diff --git a/luprex/cpp/core/world-core.cpp b/luprex/cpp/core/world-core.cpp index 41d1702a..e4c5ed0b 100644 --- a/luprex/cpp/core/world-core.cpp +++ b/luprex/cpp/core/world-core.cpp @@ -9,6 +9,68 @@ #include +// Read a SimpleDynamic value from the streambuffer and push +// it onto the lua stack. +void push_simple_dynamic(lua_State *L, StreamBuffer *sb) { + SimpleDynamicTag type = sb->read_simple_dynamic_tag(); + switch (type) { + case SimpleDynamicTag::NUMBER: { + lua_pushnumber(L, sb->read_double()); + break; + } + case SimpleDynamicTag::BOOLEAN: { + lua_pushboolean(L, sb->read_bool() ? 1:0); + break; + } + case SimpleDynamicTag::STRING: { + std::string_view s = sb->read_string_view(); + lua_pushlstring(L, s.data(), s.size()); + break; + } + case SimpleDynamicTag::VECTOR: { + double x = sb->read_double(); + double y = sb->read_double(); + double z = sb->read_double(); + lua_newtable(L); + lua_pushnumber(L, x); + lua_rawseti(L, -2, 1); + lua_pushnumber(L, y); + lua_rawseti(L, -2, 2); + lua_pushnumber(L, z); + lua_rawseti(L, -2, 3); + break; + } + default: throw StreamCorruption(); + } +} + +// Given a lua value, try to pack it into the stream buffer as a simple dynamic. +bool encode_simple_dynamic(LuaCoreStack &LS, LuaSlot &slot, StreamBuffer *sb) { + switch (LS.type(slot)) { + case LUA_TNUMBER: + sb->write_simple_dynamic_tag(SimpleDynamicTag::NUMBER); + sb->write_double(LS.cknumber(slot)); + return true; + case LUA_TSTRING: + sb->write_simple_dynamic_tag(SimpleDynamicTag::STRING); + sb->write_string(LS.ckstringview(slot)); + return true; + case LUA_TBOOLEAN: + sb->write_simple_dynamic_tag(SimpleDynamicTag::BOOLEAN); + sb->write_bool(LS.ckboolean(slot)); + return true; + case LUA_TTABLE: { + std::optional xyz = LS.tryxyz(slot); + if (!xyz.has_value()) return false; + sb->write_simple_dynamic_tag(SimpleDynamicTag::VECTOR); + sb->write_dxyz(xyz.value()); + return true; + } + default: return false; + } +} + + void World::store_global_pointer(lua_State *L, World *v) { lua_pushstring(L, "world"); lua_pushlightuserdata(L, v); @@ -292,7 +354,7 @@ void World::disconnected(int64_t actor_id) { } } -eng::string World::probe_lua(int64_t actor_id, std::string_view lua) { +eng::string World::probe_lua_expr(int64_t actor_id, std::string_view lua) { assert(stack_is_clear()); lua_State *L = state(); @@ -343,6 +405,93 @@ eng::string World::probe_lua(int64_t actor_id, std::string_view lua) { return result; } +void World::probe_lua_call(int64_t place_id, int64_t actor_id, std::string_view datapack, StreamBuffer *retvals) { + assert(stack_is_clear()); + lua_State *L = state(); + + // Get the actor and place, C++ version. Make sure both exist. + Tangible *tactor = tangible_get(actor_id); + Tangible *tplace = tangible_get(place_id); + if ((tactor == nullptr) || (tplace == nullptr)) { + return; + } + + // Use a streambuffer to parse the datapack. + StreamBuffer datasb(datapack); + + // Extract the class and function name from the datapack. + eng::string classname; + eng::string funcname; + try { + classname = datasb.read_string_limit(100); + funcname = datasb.read_string_limit(100); + } catch (const StreamException &ex) { + return; + } + if ((!sv::is_lua_id(classname)) || (!sv::is_lua_id(funcname))) { + return; + } + + LuaVar lclass, lfunc, actor, place, tangibles, retvec, retval; + LuaExtStack LS(L, lclass, lfunc, actor, place, tangibles, retvec, retval); + + // Get the actor and place, lua version. Make sure both exist. + 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 class table and closure + eng::string err = LS.getclass(lclass, classname); + if ((!err.empty()) || (!LS.istable(lclass))) { + return; + } + LS.rawget(lfunc, lclass, funcname); + if (!LS.isfunction(lfunc)) { + return; + } + + // Call the closure. + lua_pushvalue(L, lfunc.index()); + lua_pushvalue(L, actor.index()); + lua_pushvalue(L, place.index()); + int nargs = 2; + try { + while (!datasb.empty()) { + push_simple_dynamic(L, &datasb); + nargs++; + } + } catch (const StreamException &exc) { + return; + } + + open_lthread_state(actor_id, place_id, 0, false, true); + eng::string msg = traceback_pcall(L, nargs, 1); + + // Send any prints to the console. + eng::string prints = lthread_prints_->str(); + lthread_prints_.reset(); + util::dprint(prints); + + if (msg.empty()) { + lua_replace(L, retvec.index()); + if (LS.istable(retvec)) { + for (int i = 1 ; ; i++) { + LS.rawget(retval, retvec, i); + bool ok = encode_simple_dynamic(LS, retval, retvals); + if (!ok) break; + } + } + } else { + util::dprint(msg); + } + + close_lthread_state(); +} + + // This is called from World::update_source, and also // from World::patch_source in the difference transmitter. // @@ -704,42 +853,6 @@ void World::invoke_lua(int64_t actor_id, int64_t place_id, std::string_view data } -// Read a SimpleDynamic value from the streambuffer and push -// it onto the lua stack. -void push_simple_dynamic(lua_State *L, StreamBuffer *sb) { - SimpleDynamicTag type = sb->read_simple_dynamic_tag(); - switch (type) { - case SimpleDynamicTag::NUMBER: { - lua_pushnumber(L, sb->read_double()); - break; - } - case SimpleDynamicTag::BOOLEAN: { - lua_pushboolean(L, sb->read_bool() ? 1:0); - break; - } - case SimpleDynamicTag::STRING: { - std::string_view s = sb->read_string_view(); - lua_pushlstring(L, s.data(), s.size()); - break; - } - case SimpleDynamicTag::VECTOR: { - double x = sb->read_double(); - double y = sb->read_double(); - double z = sb->read_double(); - lua_newtable(L); - lua_pushnumber(L, x); - lua_rawseti(L, -2, 1); - lua_pushnumber(L, y); - lua_rawseti(L, -2, 2); - lua_pushnumber(L, z); - lua_rawseti(L, -2, 3); - break; - } - default: throw StreamCorruption(); - } -} - - void World::invoke_lua_call(int64_t actor_id, int64_t place_id, std::string_view datapack) { assert(stack_is_clear()); @@ -966,9 +1079,7 @@ void World::open_lthread_state(int64_t actor, int64_t place, int64_t thread, boo void World::close_lthread_state() { // Copy prints from lthread_prints_ stringstream into - // the appropriate actor's PrintBuffer. If for some reason - // there isn't an actor, or if the actor doesn't have a PrintBuffer, - // send the output to std::cerr. + // the appropriate actor's PrintBuffer. if (lthread_prints_ != nullptr) { const eng::string &output = lthread_prints_->str(); if (output.size() > 0) { diff --git a/luprex/cpp/core/world.hpp b/luprex/cpp/core/world.hpp index 960007dc..731e92f3 100644 --- a/luprex/cpp/core/world.hpp +++ b/luprex/cpp/core/world.hpp @@ -242,7 +242,14 @@ public: // from the stringstream. If the lua expression returns a // value, that is also printed to the stringstream. // - eng::string probe_lua(int64_t actor_id, std::string_view lua); + eng::string probe_lua_expr(int64_t actor_id, std::string_view lua); + + // Probe that calls lua function, passing arguments. + // + // Print statements are discarded. The lua function may return a vector + // of values. If so, the values are packed into a StreamBuffer. + // + void probe_lua_call(int64_t place_id, int64_t actor_id, std::string_view datapack, StreamBuffer *retvals); // Invoke an Invocation object. // diff --git a/luprex/ext/base-buffer.hpp b/luprex/ext/base-buffer.hpp index 38cf76b4..5655205d 100644 --- a/luprex/ext/base-buffer.hpp +++ b/luprex/ext/base-buffer.hpp @@ -628,7 +628,7 @@ public: break; } case SimpleDynamicTag::STRING: result->set_string(read_string()); break; - default: assert(false); + default: result->set_uninitialized(); break; } } diff --git a/luprex/lua/login.lua b/luprex/lua/login.lua index cc7fdda4..e0b45bb9 100644 --- a/luprex/lua/login.lua +++ b/luprex/lua/login.lua @@ -50,6 +50,7 @@ function engio.myfunction(actor, place, a, b, c) pprint("A:", a) pprint("B:", b) pprint("C:", c) + return {13, "chicken", {2,3,4}, false} end function engio.move(actor, place, action, xyz, facing)