diff --git a/src/lua_baselib.c b/src/lua_baselib.c index 132ebc1a8..3acbd3d0f 100644 --- a/src/lua_baselib.c +++ b/src/lua_baselib.c @@ -246,6 +246,56 @@ static int lib_userdataType(lua_State *L) return luaL_typerror(L, 1, "userdata"); } +// Takes a metatable as first and only argument +// Only callable during script loading +static int lib_registerMetatable(lua_State *L) +{ + static UINT16 nextid = 1; + + if (!lua_lumploading) + return luaL_error(L, "This function cannot be called from within a hook or coroutine!"); + luaL_checktype(L, 1, LUA_TTABLE); + + if (nextid == 0) + return luaL_error(L, "Too many metatables registered?! Please consider rewriting your script once you are sober again.\n"); + + lua_getfield(L, LUA_REGISTRYINDEX, LREG_METATABLES); // 2 + // registry.metatables[metatable] = nextid + lua_pushvalue(L, 1); // 3 + lua_pushnumber(L, nextid); // 4 + lua_settable(L, 2); + + // registry.metatables[nextid] = metatable + lua_pushnumber(L, nextid); // 3 + lua_pushvalue(L, 1); // 4 + lua_settable(L, 2); + lua_pop(L, 1); + + nextid++; + + return 0; +} + +// Takes a string as only argument and returns the metatable +// associated to the userdata type this string refers to +// Returns nil if the string does not refer to a valid userdata type +static int lib_userdataMetatable(lua_State *L) +{ + UINT32 i; + const char *udname = luaL_checkstring(L, 1); + + // Find internal metatable name + for (i = 0; meta2utype[i].meta; i++) + if (!strcmp(udname, meta2utype[i].utype)) + { + luaL_getmetatable(L, meta2utype[i].meta); + return 1; + } + + lua_pushnil(L); + return 1; +} + static int lib_isPlayerAdmin(lua_State *L) { player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER)); @@ -3494,6 +3544,8 @@ static luaL_Reg lib[] = { {"chatprint", lib_chatprint}, {"chatprintf", lib_chatprintf}, {"userdataType", lib_userdataType}, + {"registerMetatable", lib_registerMetatable}, + {"userdataMetatable", lib_userdataMetatable}, {"IsPlayerAdmin", lib_isPlayerAdmin}, {"reserveLuabanks", lib_reserveLuabanks}, diff --git a/src/lua_libs.h b/src/lua_libs.h index 03bd99cd2..062a3fe50 100644 --- a/src/lua_libs.h +++ b/src/lua_libs.h @@ -16,6 +16,7 @@ extern lua_State *gL; #define LREG_EXTVARS "LUA_VARS" #define LREG_STATEACTION "STATE_ACTION" #define LREG_ACTIONS "MOBJ_ACTION" +#define LREG_METATABLES "METATABLES" #define META_STATE "STATE_T*" #define META_MOBJINFO "MOBJINFO_T*" diff --git a/src/lua_script.c b/src/lua_script.c index 6e40cb785..bb022f9ce 100644 --- a/src/lua_script.c +++ b/src/lua_script.c @@ -443,6 +443,10 @@ static void LUA_ClearState(void) lua_newtable(L); lua_setfield(L, LUA_REGISTRYINDEX, LREG_VALID); + // make LREG_METATABLES table for all registered metatables + lua_newtable(L); + lua_setfield(L, LUA_REGISTRYINDEX, LREG_METATABLES); + // open srb2 libraries for(i = 0; liblist[i]; i++) { lua_pushcfunction(L, liblist[i]); @@ -980,8 +984,17 @@ static UINT8 ArchiveValue(int TABLESINDEX, int myindex) lua_pop(gL, 1); } if (!found) + { t++; + if (t == 0) + { + CONS_Alert(CONS_ERROR, "Too many tables to archive!\n"); + WRITEUINT8(save_p, ARCH_NULL); + return 0; + } + } + WRITEUINT8(save_p, ARCH_TABLE); WRITEUINT16(save_p, t); @@ -1294,8 +1307,22 @@ static void ArchiveTables(void) lua_pop(gL, 1); } - lua_pop(gL, 1); WRITEUINT8(save_p, ARCH_TEND); + + // Write metatable ID + if (lua_getmetatable(gL, -1)) + { + // registry.metatables[metatable] + lua_getfield(gL, LUA_REGISTRYINDEX, LREG_METATABLES); + lua_pushvalue(gL, -2); + lua_gettable(gL, -2); + WRITEUINT16(save_p, lua_isnil(gL, -1) ? 0 : lua_tointeger(gL, -1)); + lua_pop(gL, 3); + } + else + WRITEUINT16(save_p, 0); + + lua_pop(gL, 1); } } @@ -1466,6 +1493,7 @@ static void UnArchiveTables(void) { int TABLESINDEX; UINT16 i, n; + UINT16 metatableid; if (!gL) return; @@ -1490,6 +1518,19 @@ static void UnArchiveTables(void) else lua_rawset(gL, -3); } + + metatableid = READUINT16(save_p); + if (metatableid) + { + // setmetatable(table, registry.metatables[metatableid]) + lua_getfield(gL, LUA_REGISTRYINDEX, LREG_METATABLES); + lua_rawgeti(gL, -1, metatableid); + if (lua_isnil(gL, -1)) + I_Error("Unknown metatable ID %d\n", metatableid); + lua_setmetatable(gL, -3); + lua_pop(gL, 1); + } + lua_pop(gL, 1); } }