From 6405c34938074f6d8dbeed5dff9cb3648d1a79be Mon Sep 17 00:00:00 2001 From: Josh Yelon Date: Thu, 25 Feb 2021 14:09:16 -0500 Subject: [PATCH] Changed how userdata is handled --- luprex/core/cpp/gui.cpp | 31 ++++++++++------ luprex/core/cpp/gui.hpp | 11 ++++++ luprex/core/cpp/idalloc.cpp | 2 -- luprex/core/cpp/luastack.cpp | 66 ---------------------------------- luprex/core/cpp/luastack.hpp | 46 +----------------------- luprex/core/cpp/textgame.cpp | 5 ++- luprex/core/cpp/world.cpp | 69 +++++++++++++++++++++--------------- luprex/core/cpp/world.hpp | 5 +-- luprex/core/lua/player.lua | 20 +++++------ 9 files changed, 88 insertions(+), 167 deletions(-) diff --git a/luprex/core/cpp/gui.cpp b/luprex/core/cpp/gui.cpp index 92ccea56..472203e3 100644 --- a/luprex/core/cpp/gui.cpp +++ b/luprex/core/cpp/gui.cpp @@ -1,6 +1,22 @@ #include "gui.hpp" +#include "luastack.hpp" -LuaDefineType(Gui); +void Gui::store_global_pointer(lua_State *L, Gui *g) { + lua_pushstring(L, "gui"); + lua_pushlightuserdata(L, g); + lua_rawset(L, LUA_REGISTRYINDEX); +} + +Gui *Gui::fetch_global_pointer(lua_State *L) { + lua_pushstring(L, "gui"); + lua_rawget(L, LUA_REGISTRYINDEX); + Gui *result = (Gui *)lua_touserdata(L, -1); + if (result == nullptr) { + luaL_error(L, "Not currently building a GUI"); + } + lua_pop(L, 1); + return result; +} void Gui::menu_item(const std::string &action, const std::string &label) { GuiElt elt; @@ -19,17 +35,10 @@ bool Gui::has_action(const std::string &action) const { return false; } -LuaDefine(gui_create, "c") { - LuaRet lgui; - LuaStack LS(L, lgui); - LS.newpointer(lgui, new Gui, true); - return LS.result(); -} - LuaDefine(gui_menu_item, "c") { - LuaArg lgui, laction, llabel; - LuaStack LS(L, lgui, laction, llabel); - Gui *gui = LS.ckuserdata(lgui); + Gui *gui = Gui::fetch_global_pointer(L); + LuaArg laction, llabel; + LuaStack LS(L, laction, llabel); std::string action = LS.ckstring(laction); std::string label = LS.ckstring(llabel); gui->menu_item(action, label); diff --git a/luprex/core/cpp/gui.hpp b/luprex/core/cpp/gui.hpp index e1452a80..57613f3d 100644 --- a/luprex/core/cpp/gui.hpp +++ b/luprex/core/cpp/gui.hpp @@ -2,6 +2,7 @@ #define GUI_HPP #include +#include #include #include "luastack.hpp" @@ -34,7 +35,17 @@ public: void clear() { elts_.clear(); } bool has_action(const std::string &action) const; void menu_item(const std::string &action, const std::string &label); + + // Put a pointer to a gui into the lua registry. + // + // All lua commands that manipulate the GUI implicitly + // operate on this global gui pointer. + // + static void store_global_pointer(lua_State *L, Gui *g); + static Gui *fetch_global_pointer(lua_State *L); }; +using GuiResult = std::map; + #endif // GUI_HPP diff --git a/luprex/core/cpp/idalloc.cpp b/luprex/core/cpp/idalloc.cpp index 22dac065..1a91b33d 100644 --- a/luprex/core/cpp/idalloc.cpp +++ b/luprex/core/cpp/idalloc.cpp @@ -1,8 +1,6 @@ #include "idalloc.hpp" #include -LuaDefineType(IdGlobalPool); -LuaDefineType(IdPlayerPool); static int64_t nthbatch(int64_t n) { return int64_t(0x0001000000000000) + n*256; diff --git a/luprex/core/cpp/luastack.cpp b/luprex/core/cpp/luastack.cpp index 5e7b25fc..f97ae9b5 100644 --- a/luprex/core/cpp/luastack.cpp +++ b/luprex/core/cpp/luastack.cpp @@ -8,57 +8,6 @@ LuaNilMarker LuaNil; LuaNewTableMarker LuaNewTable; LuaDiscardMarker LuaDiscard; -void LuaStack::clear_tagged_pointer(LuaSlot target) { - TaggedPointer *tp = (TaggedPointer*)lua_touserdata(L_, target.index()); - tp->ptr = 0; -} - -void LuaStack::make_tagged_pointer(LuaSlot target, void *ptr, LuaTypeTag tag, LuaDeleterFn del) { - TaggedPointer *tp = (TaggedPointer*)lua_newuserdata(L_, sizeof(TaggedPointer)); - tp->ptr = ptr; - tp->tag = tag; - tp->del = del; - lua_pushlightuserdata(L_, (void*)tag); - lua_rawget(L_, LUA_REGISTRYINDEX); - if (lua_isnil(L_, -1)) luaL_error(L_, "type not registered with LuaDefineType"); - lua_setmetatable(L_, -2); - lua_replace(L_, target); -} - -int LuaStack::collect_tagged_pointer(lua_State *L) { - TaggedPointer *p = (TaggedPointer*)lua_touserdata(L, 1); - if (p==0) { - luaL_error(L, "lua deleter function received a non-userdata"); - } - if (p->ptr != 0) { - p->del(p->ptr); - p->ptr = 0; - } - return 0; -} - -void LuaStack::register_all_userdata(lua_State *L) { - LuaVar tab, lud, classtab, classname; - LuaStack LS(L, tab, lud, classtab, classname); - auto regs = LuaFunctionReg::all(); - for (const LuaFunctionReg *r : regs) { - const std::string &name = util::tolower(r->get_name()); - lua_CFunction tag = r->get_func(); - std::string mode = r->get_mode(); - LS.set(classname, name); - if (mode.find('t') != std::string::npos) { // Register type - LS.newtable(tab); - LS.rawset(tab, "type", name); - LS.rawset(tab, "__gc", collect_tagged_pointer); - LS.makeclass(classtab, classname); - LS.rawset(tab, "__index", classtab); - LS.setlightuserdata(lud, (void *)tag); - LS.rawset(LuaRegistry, lud, tab); - } - } - LS.result(); -} - LuaFunctionReg::LuaFunctionReg(const char *m, const char *n, lua_CFunction f) { mode_ = m; name_ = n; @@ -251,18 +200,3 @@ void LuaStack::check_nret(int xnret, int otop, int nret) const { luaL_error(L_, "expected %d return values", xnret); } } - -LuaDefine(system_type, "f") { - LuaArg obj; - LuaRet tname; - LuaVar mt; - LuaStack LS(L, obj, tname, mt); - int type = LS.type(obj); - if (type == LUA_TUSERDATA) { - LS.getmetatable(mt, obj); - LS.rawget(tname, mt, "type"); - } else { - LS.set(tname, lua_typename(L, type)); - } - return LS.result(); -} diff --git a/luprex/core/cpp/luastack.hpp b/luprex/core/cpp/luastack.hpp index e62a85c4..a016c6f5 100644 --- a/luprex/core/cpp/luastack.hpp +++ b/luprex/core/cpp/luastack.hpp @@ -360,20 +360,6 @@ private: // original stack top, and number of declared return values. void check_nret(int xnret, int otop, int nret) const; - // Tagged pointers: we expect all userdata to be tagged pointers. - // This starts with a void pointer, then a type tag that identifies the - // underlying C++ type, then a deleter function. In addition, there will - // be a metatable that contains the type name as a string and a collect - // function. - struct TaggedPointer { - void *ptr; - LuaTypeTag tag; - LuaDeleterFn del; - }; - static int collect_tagged_pointer(lua_State *L); - void make_tagged_pointer(LuaSlot target, void *ptr, LuaTypeTag tag, LuaDeleterFn del); - void clear_tagged_pointer(LuaSlot target); - template static void delete_pointer(void *p) { delete (T*)p; } static void do_not_delete(void *p) { } @@ -418,7 +404,7 @@ public: double cknumber(LuaSlot s) const { return luaL_cknumber(L_, s); } std::string ckstring(LuaSlot s) const; lua_State *ckthread(LuaSlot s) const { return luaL_ckthread(L_, s); } - + void clearmetatable(LuaSlot tab) const; void setmetatable(LuaSlot tab, LuaSlot mt) const; void getmetatable(LuaSlot mt, LuaSlot tab) const; @@ -431,31 +417,6 @@ public: void setlightuserdata(LuaSlot target, void *p) const; - template - void newpointer(LuaSlot target, T *ptr, bool autodel) { - make_tagged_pointer(target, ptr, LuaTypeTagValue, autodel ? delete_pointer : do_not_delete); - } - - template - T *touserdata(LuaSlot target) { - TaggedPointer *tp = (TaggedPointer*)lua_touserdata(L_, target); - LuaTypeTag tag = LuaTypeTagValue; - if ((tp == 0) || (tp->tag != tag)) return 0; - return (T*)(tp->ptr); - } - - template - T *ckuserdata(LuaSlot target) { - TaggedPointer *tp = (TaggedPointer*)lua_touserdata(L_, target); - LuaTypeTag tag = LuaTypeTagValue; - if ((tp == 0) || (tp->tag != tag) || (tp->ptr==0)) { - luaL_error(L_, "wrong userdata type"); - } - return (T*)(tp->ptr); - } - - void clearuserdata(LuaSlot target) { clear_tagged_pointer(target); } - int next(LuaSlot tab, LuaSlot key, LuaSlot value) const; void makeclass(LuaSlot tab, LuaSlot name) const; @@ -524,8 +485,6 @@ public: void call(T&... args) { call_cfunction<0>(lua_gettop(L_), args...); } - - static void register_all_userdata(lua_State *L); }; @@ -555,9 +514,6 @@ public: LuaFunctionReg reg_##name(mode, #name, name); \ int name(lua_State *L) -#define LuaDefineType(name) \ - LuaFunctionReg regt_##name("t", #name, LuaTypeTagValue) - #define LuaStringify(x) #x #define LuaAssert(L, x) if (!(x)) { luaL_error((L), "Assert failed: %s (file %s line %d)", LuaStringify(x), __FILE__, __LINE__); } diff --git a/luprex/core/cpp/textgame.cpp b/luprex/core/cpp/textgame.cpp index 7546e752..3ef81638 100644 --- a/luprex/core/cpp/textgame.cpp +++ b/luprex/core/cpp/textgame.cpp @@ -122,7 +122,10 @@ 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); + GuiResult dummyresult; + dummyresult["flavor"] = "chocolate"; + dummyresult["color"] = "blue"; + world_->invoke_plan(1, gui_place_, action, dummyresult); } void TextGame::do_snapshot_command(const StringVec &cmd) { diff --git a/luprex/core/cpp/world.cpp b/luprex/core/cpp/world.cpp index e364a608..d52ba0ac 100644 --- a/luprex/core/cpp/world.cpp +++ b/luprex/core/cpp/world.cpp @@ -6,18 +6,13 @@ #include "traceback.hpp" #include -LuaDefineType(World); - Tangible::Tangible() : world_(nullptr) { } World::~World() { } -World::World() { - // Initialize the userdata metatables. - LuaStack::register_all_userdata(state()); - +World::World() { // Initialize the ID allocator in master mode. id_global_pool_.init_master(10); @@ -26,8 +21,10 @@ World::World() { LuaStack LS(state(), world); // Put the world pointer into the lua registry. - LS.newpointer(world, this, false); - LS.rawset(LuaRegistry, "world", world); + World::store_global_pointer(state(), this); + + // Clear the global GUI pointer. + Gui::store_global_pointer(state(), nullptr); // Create the tangibles table in the registry. LS.rawset(LuaRegistry, "tangibles", LuaNewTable); @@ -127,13 +124,21 @@ Tangible *World::tangible_make(lua_State *L, int64_t id, bool pushdb) { return t; } -World *World::fetch(lua_State *L) { - LuaVar world; - LuaStack LS(L, world); - LS.rawget(world, LuaRegistry, "world"); - World *w = LS.ckuserdata(world); - LS.result(); - return w; +void World::store_global_pointer(lua_State *L, World *v) { + lua_pushstring(L, "world"); + lua_pushlightuserdata(L, v); + lua_rawset(L, LUA_REGISTRYINDEX); +} + +World *World::fetch_global_pointer(lua_State *L) { + lua_pushstring(L, "world"); + lua_rawget(L, LUA_REGISTRYINDEX); + World *result = (World *)lua_touserdata(L, -1); + if (result == nullptr) { + luaL_error(L, "No world pointer stored."); + } + lua_pop(L, 1); + return result; } void World::update_gui(int64_t actor_id, int64_t place_id, Gui *gui) { @@ -169,30 +174,25 @@ void World::update_gui(int64_t actor_id, int64_t place_id, Gui *gui) { return; } - // Construct the userdata with the GUI pointer. - LS.newpointer(ugui, gui, false); - // Call the interface function. lua_pushvalue(L, func.index()); lua_pushvalue(L, actor.index()); lua_pushvalue(L, place.index()); - lua_pushvalue(L, ugui.index()); - int status = traceback_pcall(L, 3, 0); + Gui::store_global_pointer(L, gui); + int status = traceback_pcall(L, 2, 0); + Gui::store_global_pointer(L, nullptr); if (status != 0) { gui->clear(); std::cerr << lua_tostring(L, -1); LS.result(); return; } - - // Nuke the userdata, in case somebody saved a pointer to it. - LS.clearuserdata(ugui); // And we're done. LS.result(); } -void World::invoke_plan(int64_t actor_id, int64_t place_id, const std::string &action) { +void World::invoke_plan(int64_t actor_id, int64_t place_id, const std::string &action, const GuiResult &gres) { // Validate that the action is legal. Gui validation_gui; update_gui(actor_id, place_id, &validation_gui); @@ -207,8 +207,8 @@ void World::invoke_plan(int64_t actor_id, int64_t place_id, const std::string &a // Set up for Lua manipulation. lua_State *L = state(); - LuaVar actor, place, func, tangibles, mt, index, actions, thread, threads, message; - LuaStack LS(L, actor, place, func, tangibles, mt, index, actions, thread, threads, message); + LuaVar actor, place, func, tangibles, mt, index, actions, thread, threads, message, guires; + LuaStack LS(L, actor, place, func, tangibles, mt, index, actions, thread, threads, message, guires); // Get the actor and place. LS.rawget(tangibles, LuaRegistry, "tangibles"); @@ -241,12 +241,18 @@ void World::invoke_plan(int64_t actor_id, int64_t place_id, const std::string &a return; } + // Convert the GuiResult into a lua table. + LS.newtable(guires); + for (const auto &p : gres) { + LS.rawset(guires, p.first, p.second); + } + // 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_pushnil(L); // Gui state not implemented yet. + lua_pushvalue(L, guires.index()); lua_xmove(L, CO, 4); // Store the thread into place's thread table. @@ -333,13 +339,18 @@ LuaDefine(tangible_get, "c") { } LuaDefine(tangible_make, "c") { - World::fetch(L)->tangible_make(L, 0, true); + World::fetch_global_pointer(L)->tangible_make(L, 0, true); return 1; } LuaDefine(world_wait, "f") { - if (lua_type(L, -1) != LUA_TNUMBER) { + if ((lua_gettop(L) != 1) || (lua_type(L, -1) != LUA_TNUMBER)) { luaL_error(L, "Argument to wait must be a number."); } return lua_yield(L, 1); } + +LuaDefine(world_getregistry, "f") { + lua_pushvalue(L, LUA_REGISTRYINDEX); + return 1; +} diff --git a/luprex/core/cpp/world.hpp b/luprex/core/cpp/world.hpp index 29b68e1a..d8b94bda 100644 --- a/luprex/core/cpp/world.hpp +++ b/luprex/core/cpp/world.hpp @@ -125,14 +125,15 @@ public: // // Eventually we'll add another parameter for gui-state. // - void invoke_plan(int64_t actor_id, int64_t place_id, const std::string &action); + void invoke_plan(int64_t actor_id, int64_t place_id, const std::string &action, const GuiResult &gres); // fetch // // Given a lua state, fetch the world model associated with // that lua state. // - static World *fetch(lua_State *L); + static void store_global_pointer(lua_State *L, World *w); + static World *fetch_global_pointer(lua_State *L); // Snapshot and rollback - temporary. // diff --git a/luprex/core/lua/player.lua b/luprex/core/lua/player.lua index 26089723..74f293e2 100644 --- a/luprex/core/lua/player.lua +++ b/luprex/core/lua/player.lua @@ -1,27 +1,25 @@ maketangible('player') -function player.interface(actor, place, gui) - gui:menu_item("north", "Go North") - gui:menu_item("south", "Go South") - gui:menu_item("east", "Go East") - gui:menu_item("west", "Go West") +function player.interface(actor, place) + gui.menu_item("north", "Go North") + gui.menu_item("south", "Go South") + gui.menu_item("east", "Go East") + gui.menu_item("west", "Go West") end -function player.action.north(actor, place, gui) +function player.action.north(actor, place, dialog) print("Moving north") end -function player.action.south(actor, place, gui) +function player.action.south(actor, place, dialog) print("Moving south") end -function player.action.east(actor, place, gui) +function player.action.east(actor, place, dialog) print("Moving east") - t = nil - t[3] = 4 end -function player.action.west(actor, place, gui) +function player.action.west(actor, place, dialog) print("Moving west") wait(0) print("Moving west again")