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 "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<Gui>(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<Gui>(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);

View File

@@ -2,6 +2,7 @@
#define GUI_HPP
#include <string>
#include <map>
#include <vector>
#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<std::string, std::string>;
#endif // GUI_HPP

View File

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

View File

@@ -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();
}

View File

@@ -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<typename T>
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 <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;
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<name>)
#define LuaStringify(x) #x
#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::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) {

View File

@@ -6,18 +6,13 @@
#include "traceback.hpp"
#include <iostream>
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>(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<Gui>(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;
}

View File

@@ -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.
//

View File

@@ -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")