Changed how userdata is handled

This commit is contained in:
2021-02-25 14:09:16 -05:00
parent 5f783e643b
commit 6405c34938
9 changed files with 88 additions and 167 deletions

View File

@@ -1,6 +1,22 @@
#include "gui.hpp" #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) { void Gui::menu_item(const std::string &action, const std::string &label) {
GuiElt elt; GuiElt elt;
@@ -19,17 +35,10 @@ bool Gui::has_action(const std::string &action) const {
return false; return false;
} }
LuaDefine(gui_create, "c") {
LuaRet lgui;
LuaStack LS(L, lgui);
LS.newpointer<Gui>(lgui, new Gui, true);
return LS.result();
}
LuaDefine(gui_menu_item, "c") { LuaDefine(gui_menu_item, "c") {
LuaArg lgui, laction, llabel; Gui *gui = Gui::fetch_global_pointer(L);
LuaStack LS(L, lgui, laction, llabel); LuaArg laction, llabel;
Gui *gui = LS.ckuserdata<Gui>(lgui); LuaStack LS(L, laction, llabel);
std::string action = LS.ckstring(laction); std::string action = LS.ckstring(laction);
std::string label = LS.ckstring(llabel); std::string label = LS.ckstring(llabel);
gui->menu_item(action, label); gui->menu_item(action, label);

View File

@@ -2,6 +2,7 @@
#define GUI_HPP #define GUI_HPP
#include <string> #include <string>
#include <map>
#include <vector> #include <vector>
#include "luastack.hpp" #include "luastack.hpp"
@@ -34,7 +35,17 @@ public:
void clear() { elts_.clear(); } void clear() { elts_.clear(); }
bool has_action(const std::string &action) const; bool has_action(const std::string &action) const;
void menu_item(const std::string &action, const std::string &label); 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<std::string, std::string>;
#endif // GUI_HPP #endif // GUI_HPP

View File

@@ -1,8 +1,6 @@
#include "idalloc.hpp" #include "idalloc.hpp"
#include <iostream> #include <iostream>
LuaDefineType(IdGlobalPool);
LuaDefineType(IdPlayerPool);
static int64_t nthbatch(int64_t n) { static int64_t nthbatch(int64_t n) {
return int64_t(0x0001000000000000) + n*256; return int64_t(0x0001000000000000) + n*256;

View File

@@ -8,57 +8,6 @@ LuaNilMarker LuaNil;
LuaNewTableMarker LuaNewTable; LuaNewTableMarker LuaNewTable;
LuaDiscardMarker LuaDiscard; 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) { LuaFunctionReg::LuaFunctionReg(const char *m, const char *n, lua_CFunction f) {
mode_ = m; mode_ = m;
name_ = n; 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); 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();
}

View File

@@ -360,20 +360,6 @@ private:
// original stack top, and number of declared return values. // original stack top, and number of declared return values.
void check_nret(int xnret, int otop, int nret) const; 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<typename T> template<typename T>
static void delete_pointer(void *p) { delete (T*)p; } static void delete_pointer(void *p) { delete (T*)p; }
static void do_not_delete(void *p) { } static void do_not_delete(void *p) { }
@@ -418,7 +404,7 @@ public:
double cknumber(LuaSlot s) const { return luaL_cknumber(L_, s); } double cknumber(LuaSlot s) const { return luaL_cknumber(L_, s); }
std::string ckstring(LuaSlot s) const; std::string ckstring(LuaSlot s) const;
lua_State *ckthread(LuaSlot s) const { return luaL_ckthread(L_, s); } lua_State *ckthread(LuaSlot s) const { return luaL_ckthread(L_, s); }
void clearmetatable(LuaSlot tab) const; void clearmetatable(LuaSlot tab) const;
void setmetatable(LuaSlot tab, LuaSlot mt) const; void setmetatable(LuaSlot tab, LuaSlot mt) const;
void getmetatable(LuaSlot mt, LuaSlot tab) const; void getmetatable(LuaSlot mt, LuaSlot tab) const;
@@ -431,31 +417,6 @@ public:
void setlightuserdata(LuaSlot target, void *p) const; void setlightuserdata(LuaSlot target, void *p) const;
template <typename T>
void newpointer(LuaSlot target, T *ptr, bool autodel) {
make_tagged_pointer(target, ptr, LuaTypeTagValue<T>, autodel ? delete_pointer<T> : do_not_delete);
}
template <typename T>
T *touserdata(LuaSlot target) {
TaggedPointer *tp = (TaggedPointer*)lua_touserdata(L_, target);
LuaTypeTag tag = LuaTypeTagValue<T>;
if ((tp == 0) || (tp->tag != tag)) return 0;
return (T*)(tp->ptr);
}
template <typename T>
T *ckuserdata(LuaSlot target) {
TaggedPointer *tp = (TaggedPointer*)lua_touserdata(L_, target);
LuaTypeTag tag = LuaTypeTagValue<T>;
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; int next(LuaSlot tab, LuaSlot key, LuaSlot value) const;
void makeclass(LuaSlot tab, LuaSlot name) const; void makeclass(LuaSlot tab, LuaSlot name) const;
@@ -524,8 +485,6 @@ public:
void call(T&... args) { void call(T&... args) {
call_cfunction<0>(lua_gettop(L_), 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); \ LuaFunctionReg reg_##name(mode, #name, name); \
int name(lua_State *L) int name(lua_State *L)
#define LuaDefineType(name) \
LuaFunctionReg regt_##name("t", #name, LuaTypeTagValue<name>)
#define LuaStringify(x) #x #define LuaStringify(x) #x
#define LuaAssert(L, x) if (!(x)) { luaL_error((L), "Assert failed: %s (file %s line %d)", LuaStringify(x), __FILE__, __LINE__); } #define LuaAssert(L, x) if (!(x)) { luaL_error((L), "Assert failed: %s (file %s line %d)", LuaStringify(x), __FILE__, __LINE__); }

View File

@@ -122,7 +122,10 @@ void TextGame::do_choose_command(const StringVec &cmd) {
} }
std::string action = elts[index].action(); std::string action = elts[index].action();
std::cerr << "Invoking plan: " << action << std::endl; 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) { void TextGame::do_snapshot_command(const StringVec &cmd) {

View File

@@ -6,18 +6,13 @@
#include "traceback.hpp" #include "traceback.hpp"
#include <iostream> #include <iostream>
LuaDefineType(World);
Tangible::Tangible() : world_(nullptr) { Tangible::Tangible() : world_(nullptr) {
} }
World::~World() { World::~World() {
} }
World::World() { World::World() {
// Initialize the userdata metatables.
LuaStack::register_all_userdata(state());
// Initialize the ID allocator in master mode. // Initialize the ID allocator in master mode.
id_global_pool_.init_master(10); id_global_pool_.init_master(10);
@@ -26,8 +21,10 @@ World::World() {
LuaStack LS(state(), world); LuaStack LS(state(), world);
// Put the world pointer into the lua registry. // Put the world pointer into the lua registry.
LS.newpointer(world, this, false); World::store_global_pointer(state(), this);
LS.rawset(LuaRegistry, "world", world);
// Clear the global GUI pointer.
Gui::store_global_pointer(state(), nullptr);
// Create the tangibles table in the registry. // Create the tangibles table in the registry.
LS.rawset(LuaRegistry, "tangibles", LuaNewTable); LS.rawset(LuaRegistry, "tangibles", LuaNewTable);
@@ -127,13 +124,21 @@ Tangible *World::tangible_make(lua_State *L, int64_t id, bool pushdb) {
return t; return t;
} }
World *World::fetch(lua_State *L) { void World::store_global_pointer(lua_State *L, World *v) {
LuaVar world; lua_pushstring(L, "world");
LuaStack LS(L, world); lua_pushlightuserdata(L, v);
LS.rawget(world, LuaRegistry, "world"); lua_rawset(L, LUA_REGISTRYINDEX);
World *w = LS.ckuserdata<World>(world); }
LS.result();
return w; 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) { 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; return;
} }
// Construct the userdata with the GUI pointer.
LS.newpointer<Gui>(ugui, gui, false);
// Call the interface function. // Call the interface function.
lua_pushvalue(L, func.index()); lua_pushvalue(L, func.index());
lua_pushvalue(L, actor.index()); lua_pushvalue(L, actor.index());
lua_pushvalue(L, place.index()); lua_pushvalue(L, place.index());
lua_pushvalue(L, ugui.index()); Gui::store_global_pointer(L, gui);
int status = traceback_pcall(L, 3, 0); int status = traceback_pcall(L, 2, 0);
Gui::store_global_pointer(L, nullptr);
if (status != 0) { if (status != 0) {
gui->clear(); gui->clear();
std::cerr << lua_tostring(L, -1); std::cerr << lua_tostring(L, -1);
LS.result(); LS.result();
return; return;
} }
// Nuke the userdata, in case somebody saved a pointer to it.
LS.clearuserdata(ugui);
// And we're done. // And we're done.
LS.result(); 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. // Validate that the action is legal.
Gui validation_gui; Gui validation_gui;
update_gui(actor_id, place_id, &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. // Set up for Lua manipulation.
lua_State *L = state(); lua_State *L = state();
LuaVar 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); LuaStack LS(L, actor, place, func, tangibles, mt, index, actions, thread, threads, message, guires);
// Get the actor and place. // Get the actor and place.
LS.rawget(tangibles, LuaRegistry, "tangibles"); 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; 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. // Create a new thread, set up function and parameters.
lua_State *CO = LS.newthread(thread); lua_State *CO = LS.newthread(thread);
lua_pushvalue(L, func.index()); lua_pushvalue(L, func.index());
lua_pushvalue(L, actor.index()); lua_pushvalue(L, actor.index());
lua_pushvalue(L, place.index()); lua_pushvalue(L, place.index());
lua_pushnil(L); // Gui state not implemented yet. lua_pushvalue(L, guires.index());
lua_xmove(L, CO, 4); lua_xmove(L, CO, 4);
// Store the thread into place's thread table. // Store the thread into place's thread table.
@@ -333,13 +339,18 @@ LuaDefine(tangible_get, "c") {
} }
LuaDefine(tangible_make, "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; return 1;
} }
LuaDefine(world_wait, "f") { 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."); luaL_error(L, "Argument to wait must be a number.");
} }
return lua_yield(L, 1); return lua_yield(L, 1);
} }
LuaDefine(world_getregistry, "f") {
lua_pushvalue(L, LUA_REGISTRYINDEX);
return 1;
}

View File

@@ -125,14 +125,15 @@ public:
// //
// Eventually we'll add another parameter for gui-state. // 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 // fetch
// //
// Given a lua state, fetch the world model associated with // Given a lua state, fetch the world model associated with
// that lua state. // 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. // Snapshot and rollback - temporary.
// //

View File

@@ -1,27 +1,25 @@
maketangible('player') maketangible('player')
function player.interface(actor, place, gui) function player.interface(actor, place)
gui:menu_item("north", "Go North") gui.menu_item("north", "Go North")
gui:menu_item("south", "Go South") gui.menu_item("south", "Go South")
gui:menu_item("east", "Go East") gui.menu_item("east", "Go East")
gui:menu_item("west", "Go West") gui.menu_item("west", "Go West")
end end
function player.action.north(actor, place, gui) function player.action.north(actor, place, dialog)
print("Moving north") print("Moving north")
end end
function player.action.south(actor, place, gui) function player.action.south(actor, place, dialog)
print("Moving south") print("Moving south")
end end
function player.action.east(actor, place, gui) function player.action.east(actor, place, dialog)
print("Moving east") print("Moving east")
t = nil
t[3] = 4
end end
function player.action.west(actor, place, gui) function player.action.west(actor, place, dialog)
print("Moving west") print("Moving west")
wait(0) wait(0)
print("Moving west again") print("Moving west again")