Implemented World::probe_lua_call and tested

This commit is contained in:
2024-09-01 19:43:00 -04:00
parent d4f46eef45
commit 47a570064c
9 changed files with 196 additions and 47 deletions

View File

@@ -102,7 +102,7 @@
#include <cassert> #include <cassert>
#include <ostream> #include <ostream>
struct AnimValue : public SimpleDynamic<eng::string> { struct AnimValue : public SimpleDynamicValue {
bool persistent; bool persistent;
AnimValue() { persistent = false; } AnimValue() { persistent = false; }

View File

@@ -183,7 +183,7 @@ public:
virtual void do_luaprobe_command(std::string_view cmd) override { virtual void do_luaprobe_command(std::string_view cmd) override {
world_to_asynchronous(); world_to_asynchronous();
stdostream() << world_->probe_lua(actor_id_, cmd); stdostream() << world_->probe_lua_expr(actor_id_, cmd);
world_to_synchronous(); world_to_synchronous();
} }

View File

@@ -90,7 +90,35 @@ public:
} }
virtual void do_work_command() override { 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 { virtual void do_display_command() override {
@@ -111,7 +139,7 @@ public:
virtual void do_luaprobe_command(std::string_view cmd) override { virtual void do_luaprobe_command(std::string_view cmd) override {
master_->snapshot(); master_->snapshot();
stdostream() << master_->probe_lua(admin_id_, cmd); stdostream() << master_->probe_lua_expr(admin_id_, cmd);
master_->rollback(); master_->rollback();
} }

View File

@@ -171,7 +171,7 @@ std::optional<util::DXYZ> LuaCoreStack::tryxyz(LuaSlot s) const {
lua_rawgeti(L_, s, 3); lua_rawgeti(L_, s, 3);
lua_rawgeti(L_, s, 2); lua_rawgeti(L_, s, 2);
lua_rawgeti(L_, s, 1); 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; util::DXYZ result;
result.x = lua_tonumber(L_, -1); result.x = lua_tonumber(L_, -1);
result.y = lua_tonumber(L_, -2); result.y = lua_tonumber(L_, -2);

View File

@@ -243,6 +243,8 @@ public:
virtual char const *what() const { return "Stream Corruption"; } virtual char const *what() const { return "Stream Corruption"; }
}; };
using SimpleDynamicValue = SimpleDynamic<eng::string>;
class StreamBufferCore { class StreamBufferCore {
protected: protected:
void *basebuffer_malloc(size_t size) { return eng::malloc(size); } void *basebuffer_malloc(size_t size) { return eng::malloc(size); }

View File

@@ -9,6 +9,68 @@
#include <iostream> #include <iostream>
// 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<util::DXYZ> 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) { void World::store_global_pointer(lua_State *L, World *v) {
lua_pushstring(L, "world"); lua_pushstring(L, "world");
lua_pushlightuserdata(L, v); 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()); assert(stack_is_clear());
lua_State *L = state(); lua_State *L = state();
@@ -343,6 +405,93 @@ eng::string World::probe_lua(int64_t actor_id, std::string_view lua) {
return result; 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 // This is called from World::update_source, and also
// from World::patch_source in the difference transmitter. // 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) { void World::invoke_lua_call(int64_t actor_id, int64_t place_id, std::string_view datapack) {
assert(stack_is_clear()); 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() { void World::close_lthread_state() {
// Copy prints from lthread_prints_ stringstream into // Copy prints from lthread_prints_ stringstream into
// the appropriate actor's PrintBuffer. If for some reason // the appropriate actor's PrintBuffer.
// there isn't an actor, or if the actor doesn't have a PrintBuffer,
// send the output to std::cerr.
if (lthread_prints_ != nullptr) { if (lthread_prints_ != nullptr) {
const eng::string &output = lthread_prints_->str(); const eng::string &output = lthread_prints_->str();
if (output.size() > 0) { if (output.size() > 0) {

View File

@@ -242,7 +242,14 @@ public:
// from the stringstream. If the lua expression returns a // from the stringstream. If the lua expression returns a
// value, that is also printed to the stringstream. // 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. // Invoke an Invocation object.
// //

View File

@@ -628,7 +628,7 @@ public:
break; break;
} }
case SimpleDynamicTag::STRING: result->set_string(read_string()); break; case SimpleDynamicTag::STRING: result->set_string(read_string()); break;
default: assert(false); default: result->set_uninitialized(); break;
} }
} }

View File

@@ -50,6 +50,7 @@ function engio.myfunction(actor, place, a, b, c)
pprint("A:", a) pprint("A:", a)
pprint("B:", b) pprint("B:", b)
pprint("C:", c) pprint("C:", c)
return {13, "chicken", {2,3,4}, false}
end end
function engio.move(actor, place, action, xyz, facing) function engio.move(actor, place, action, xyz, facing)