diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 3e992f0a3..50f3f813a 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -242,6 +242,7 @@ if(${SRB2_CONFIG_HAVE_BLUA}) lua_skinlib.c lua_thinkerlib.c lua_udatalib.c + lua_glib.c ) set(SRB2_LUA_HEADERS lua_hook.h @@ -250,6 +251,7 @@ if(${SRB2_CONFIG_HAVE_BLUA}) lua_libs.h lua_script.h lua_udatalib.h + lua_glib.h ) prepend_sources(SRB2_LUA_SOURCES) diff --git a/src/blua/Makefile.cfg b/src/blua/Makefile.cfg index fb3662592..a7146c79e 100644 --- a/src/blua/Makefile.cfg +++ b/src/blua/Makefile.cfg @@ -52,4 +52,5 @@ OBJS:=$(OBJS) \ $(OBJDIR)/lua_maplib.o \ $(OBJDIR)/lua_hudlib.o \ $(OBJDIR)/lua_hudlib_drawlist.o \ - $(OBJDIR)/lua_udatalib.o + $(OBJDIR)/lua_udatalib.o \ + $(OBJDIR)/lua_glib.o diff --git a/src/dehacked.c b/src/dehacked.c index 349b66e7c..16fd9f3da 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -33,6 +33,7 @@ #include "r_sky.h" #include "fastcmp.h" #include "lua_script.h" +#include "lua_glib.h" #include "lua_hook.h" #include "d_clisrv.h" @@ -43,6 +44,8 @@ #include "r_draw.h" // translation colormap consts (for lua) #endif +#define REQUIRE_MATHLIB_GUID "{748fcbc8-6480-4013-ac4e-7afce6cab766}" + // Free slot names // The crazy word-reading stuff uses these. static char *FREE_STATES[NUMSTATEFREESLOTS]; @@ -891,7 +894,7 @@ static void readlevelheader(MYFILE *f, INT32 num, INT32 wadnum) // This call automatically saves all previous information when DELFILE is defined. // We don't need to do it ourselves. P_AllocMapHeader((INT16)(num-1)); - + if (!strstr(wadfiles[wadnum]->filename, ".soc")) mapwads[num-1] = wadnum; @@ -9266,243 +9269,18 @@ static int lib_dummysuper(lua_State *L) static inline int lib_getenum(lua_State *L) { + const int UV_MATHLIB = lua_upvalueindex(1); + const int GLIB_PROXY = lua_upvalueindex(2); + const char *word, *p; fixed_t i; - boolean mathlib = lua_toboolean(L, lua_upvalueindex(1)); + boolean mathlib = lua_toboolean(L, UV_MATHLIB); if (lua_type(L,2) != LUA_TSTRING) return 0; word = lua_tostring(L,2); - if (strlen(word) == 1) { // Assume sprite frame if length 1. - if (*word >= 'A' && *word <= '~') - { - lua_pushinteger(L, *word-'A'); - return 1; - } - if (mathlib) return luaL_error(L, "constant '%s' could not be parsed.\n", word); - return 0; - } - else if (fastncmp("MF_", word, 3)) { - p = word+3; - for (i = 0; MOBJFLAG_LIST[i]; i++) - if (fastcmp(p, MOBJFLAG_LIST[i])) { - lua_pushinteger(L, ((lua_Integer)1< *size) + { + void *ptr = realloc(*buf, length+1); + if (!ptr) return NULL; + *buf = ptr; + *size = length+1; + + va_start(va, format); + vsnprintf(*buf, *size, format, va); + } + va_end(va); + + for (int i = 0; i < length; i++) + { + (*buf)[i] = toupper((*buf)[i]); + } + + return *buf; +} + +static int lua_enumlib_super_get(lua_State *L) +{ + /** + * TODO: I think there is still some improvement to be made here + * Not exactly sure how + */ + + if (!superstack) + { + lua_pushcfunction(L, lib_dummysuper); + return 1; + } + + for (int i = 0; actionpointers[i].name; i++) + if (fasticmp(superactions[superstack-1], actionpointers[i].name)) { + lua_pushlightuserdata(L, &actionpointers[i].action); + lua_pushcclosure(L, lib_action, 1); + return 1; + } + + return 0; +} + +#define ARRAYLEN(arr) (sizeof(arr)/sizeof(*arr)) + int LUA_EnumLib(lua_State *L) { if (lua_gettop(L) == 0) lua_pushboolean(L, 0); - // Set the global metatable - lua_createtable(L, 0, 1); - lua_pushvalue(L, 1); // boolean passed to LUA_EnumLib as first argument. - lua_pushcclosure(L, lib_getenum, 1); - lua_setfield(L, -2, "__index"); + int mathlib = lua_toboolean(L, 1); + + /* Store mathlib requirement to the registry. */ + lua_pushliteral(L, REQUIRE_MATHLIB_GUID); + lua_pushvalue(L, 1); + lua_settable(L, LUA_REGISTRYINDEX); + + lua_pushcfunction(L, lua_glib_require); + lua_pushboolean(L, 0); /* Inhibit setting the globals table. */ + lua_call(L, 1, 0); + + /* Although lua_glib_require already adds a metatable, make a new one here. */ + lua_newtable(L); + lua_pushliteral(L, "__index"); + lua_pushvalue(L ,1); + lua_glib_get_proxy(L); + lua_pushcclosure(L, lib_getenum, 2); + lua_settable(L, -3); lua_setmetatable(L, LUA_GLOBALSINDEX); + + lua_pushcfunction(L, lua_glib_append_cache); + lua_createtable(L, 0, ('~'-'A'+1) + ARRAYLEN(INT_CONST)); + + /* Push frame constants. */ + for (char chr = 'A'; chr <= '~'; chr++) + { + lua_pushfstring(L, "%c", chr); + lua_pushinteger(L, chr-'A'); + lua_settable(L, -3); + } + + /* Push other constants. */ + for (int i = 0; INT_CONST[i].n; i++) + { + lua_pushstring(L, INT_CONST[i].n); + lua_pushinteger(L, INT_CONST[i].v); + lua_settable(L, -3); + } + + lua_pushliteral(L, "VERSIONSTRING"); + lua_pushliteral(L, VERSIONSTRING); + lua_settable(L, -3); + + // TODO: ADD constant errors. + lua_call(L, 1, 0); + + /* MOBJFLAG (MF_) */ + lua_pushcfunction(L, lua_glib_new_enum); + lua_glib_push_bitfield_cache(L, "MF_", MOBJFLAG_LIST, ARRAYLEN(MOBJFLAG_LIST)); + lua_pushliteral(L, "MF_"); + lua_pushliteral(L, "mobjflag"); + lua_pushcclosure(L, lua_enumlib_basic_fallback, 1); + lua_call(L, 3, 0); + + /* MOBJFLAG2 (MF2_) */ + lua_pushcfunction(L, lua_glib_new_enum); + lua_glib_push_bitfield_cache(L, "MF2_", MOBJFLAG2_LIST, ARRAYLEN(MOBJFLAG2_LIST)); + lua_pushliteral(L, "MF2_"); + lua_pushliteral(L, "mobjflag2"); + lua_pushcclosure(L, lua_enumlib_basic_fallback, 1); + lua_call(L, 3, 0); + + /* MOBJEFLAG (MFE_) */ + lua_pushcfunction(L, lua_glib_new_enum); + lua_glib_push_bitfield_cache(L, "MFE_", MOBJEFLAG_LIST, ARRAYLEN(MOBJEFLAG_LIST)); + lua_pushliteral(L, "MFE_"); + lua_pushliteral(L, "mobjeflag"); + lua_pushcclosure(L, lua_enumlib_basic_fallback, 1); + lua_call(L, 3, 0); + + /* mapthingflag (MTF_) */ + /* Because MTF_ flags array is stinky you have to do it manually :(*/ + lua_pushcfunction(L, lua_glib_new_enum); + lua_createtable(L, 0, 3); + for (int i = 1; i < 4; i++) + { + lua_pushfstring(L, "MTF_%s", MAPTHINGFLAG_LIST[i]); + lua_pushinteger(L, (lua_Integer)1 << i); + lua_settable(L, -3); + } + lua_pushliteral(L, "MTF_"); + lua_pushliteral(L, "mapthingflag"); + lua_pushcclosure(L, lua_enumlib_basic_fallback, 1); + lua_call(L, 3, 0); + + /* playerflag (PF_) */ + lua_pushcfunction(L, lua_glib_new_enum); + lua_glib_push_bitfield_cache(L, "PF_", PLAYERFLAG_LIST, ARRAYLEN(PLAYERFLAG_LIST)); + lua_pushliteral(L, "PF_FULLSTASIS"); + lua_pushinteger(L, PF_FULLSTASIS); + lua_settable(L, -3); + lua_pushliteral(L, "PF_"); + lua_pushliteral(L, "playerflag"); + lua_pushcclosure(L, lua_enumlib_basic_fallback, 1); + lua_call(L, 3, 0); + + /* linedef flag (ML_) */ + lua_pushcfunction(L, lua_glib_new_enum); + lua_glib_push_bitfield_cache(L, "ML_", ML_LIST, 16); + lua_pushliteral(L, "ML_NETONLY"); + lua_pushinteger(L, ML_NETONLY); + lua_settable(L, -3); + lua_pushliteral(L, "ML_"); + lua_pushliteral(L, "linedef flag"); + lua_pushcclosure(L, lua_enumlib_basic_fallback, 1); + lua_call(L, 3, 0); + + /* States (S_) */ + lua_pushcfunction(L, lua_glib_new_enum); + lua_glib_push_linear_cache(L, "", STATE_LIST, S_FIRSTFREESLOT); + lua_pushliteral(L, "S_"); + lua_pushcfunction(L, lua_enumlib_state_get); + lua_call(L, 3, 0); + + /* Mobj Types (MT_) */ + lua_pushcfunction(L, lua_glib_new_enum); + lua_glib_push_linear_cache(L, "", MOBJTYPE_LIST, MT_FIRSTFREESLOT); + lua_pushliteral(L, "MT_"); + lua_pushcfunction(L, lua_enumlib_mobjtype_get); + lua_call(L, 3, 0); + + /* Sprites (SPR_) */ + lua_pushcfunction(L, lua_glib_new_enum); + lua_newtable(L); + for (int i = 0; i < SPR_FIRSTFREESLOT; i++) + { + lua_pushfstring(L,"SPR_%4s", &sprnames[i][0]); + lua_pushinteger(L, i); + lua_settable(L, -3); + } + lua_pushliteral(L, "SPR_"); + lua_pushcfunction(L, lua_enumlib_sprite_get); + lua_call(L, 3, 0); + + if (mathlib) + { + char *str = NULL; + int size = 0; + + /* SFX (SFX_) */ + lua_pushcfunction(L, lua_glib_new_enum); + lua_createtable(L, 0, NUMSFX); + for (int i = 0; i < NUMSFX; i++) + { + if (!S_sfx[i].name) continue; + + if (lua_enumlib_sprintf_upper(&str, &size, "SFX_%s", S_sfx[i].name) == NULL) + return luaL_error(L, "Ran out of memory."); + + lua_pushstring(L, str); + lua_pushinteger(L, i); + lua_settable(L, -3); + } + lua_pushliteral(L, "SFX_"); + lua_pushcfunction(L, lua_enumlib_sfx_get_uppercase); + lua_call(L, 3, 0); + + /* SFX (DS) */ + lua_pushcfunction(L, lua_glib_new_enum); + lua_createtable(L, 0, NUMSFX); + for (int i = 0; i < NUMSFX; i++) + { + if (!S_sfx[i].name) continue; + + if (lua_enumlib_sprintf_upper(&str, &size, "DS%s", S_sfx[i].name) == NULL) + return luaL_error(L, "Ran out of memory."); + + lua_pushstring(L, str); + lua_pushinteger(L, i); + lua_settable(L, -3); + } + lua_pushliteral(L, "DS"); + lua_pushcfunction(L, lua_enumlib_sfx_get_ds); + lua_call(L, 3, 0); + + #ifdef MUSICSLOT_COMPATIBILITY + /* Music (MUS_) */ + lua_pushcfunction(L, lua_glib_new_enum); + lua_pushnil(L); + lua_pushliteral(L, "MUS_"); + lua_pushcfunction(L, lua_enumlib_mus_get_mathlib); + lua_call(L, 3, 0); + + /* Music (O_) */ + lua_pushcfunction(L, lua_glib_new_enum); + lua_pushnil(L); + lua_pushliteral(L, "O_"); + lua_pushcfunction(L, lua_enumlib_mus_get_mathlib); + lua_call(L, 3, 0); + #endif + + free(str); + } + else + { + /* SFX (sfx_) */ + lua_pushcfunction(L, lua_glib_new_enum); + lua_createtable(L, 0, NUMSFX); + for (int i = 0; i < NUMSFX; i++) + { + if (!S_sfx[i].name) continue; + + lua_pushfstring(L, "sfx_%s", S_sfx[i].name); + lua_pushinteger(L, i); + lua_settable(L, -3); + } + lua_pushliteral(L, "sfx_"); + lua_pushcfunction(L, lua_enumlib_sfx_get); + lua_call(L, 3, 0); + + #ifdef MUSICSLOT_COMPATIBILITY + /* SFX (mus_) */ + lua_pushcfunction(L, lua_glib_new_enum); + lua_pushnil(L); + lua_pushliteral(L, "mus_"); + lua_pushcfunction(L, lua_enumlib_mus_get); + lua_call(L, 3, 0); + #endif + } + + /* Powers (pw_/PW_)*/ + lua_pushcfunction(L, lua_glib_new_enum); + lua_createtable(L, 0, NUMPOWERS); + for (int i = 0; i < NUMPOWERS; i++) + { + lua_pushfstring(L, mathlib ? "PW_%s": "pw_%s", POWERS_LIST[i]); + lua_pushinteger(L, i); + lua_settable(L, -3); + } + lua_pushstring(L, mathlib ? "PW_" : "pw_"); + lua_pushcfunction(L, lua_enumlib_power_get); + lua_call(L, 3, 0); + + /* Kart Stuff (K_/k_)*/ + lua_pushcfunction(L, lua_glib_new_enum); + lua_createtable(L, 0, NUMKARTSTUFF); + for (int i = 0; i < NUMKARTSTUFF; i++) + { + lua_pushfstring(L, mathlib ? "K_%s": "k_%s", KARTSTUFF_LIST[i]); + lua_pushinteger(L, i); + lua_settable(L, -3); + } + lua_pushstring(L, mathlib ? "K_" : "k_"); + lua_pushcfunction(L, lua_enumlib_kartstuff_get); + lua_call(L, 3, 0); + + /* Head up display (HUD_) */ + lua_pushcfunction(L, lua_glib_new_enum); + lua_glib_push_linear_cache(L, "HUD_", HUDITEMS_LIST, NUMHUDITEMS); + lua_pushliteral(L, "HUD_"); + lua_pushliteral(L, "huditem"); + lua_pushcclosure(L, lua_enumlib_basic_fallback, 1); + lua_call(L, 3, 0); + + /* Skin Color (SKINCOLOR_) */ + lua_pushcfunction(L, lua_glib_new_enum); + lua_glib_push_linear_cache(L, "SKINCOLOR_", COLOR_ENUMS, MAXTRANSLATIONS); + lua_pushliteral(L, "SKINCOLOR_"); + lua_pushliteral(L, "skincolor"); + lua_pushcclosure(L, lua_enumlib_basic_fallback, 1); + lua_call(L, 3, 0); + + /* Actions (A_) + * These are only the hard coded actions are intended as a fast lookup path + * when possible. + */ + lua_pushcfunction(L, lua_glib_new_enum); + lua_createtable(L, 0, ARRAYLEN(actionpointers)); + for (int i = 0; actionpointers[i].name; i++) + { + lua_pushfstring(L, "A_%s", actionpointers[i].name); + lua_pushlightuserdata(L, &actionpointers[i].action); + lua_pushcclosure(L, lib_action, 1); + lua_settable(L, -3); + } + lua_pushliteral(L, "A_"); + lua_pushcfunction(L, lua_enumlib_action_get); + lua_call(L, 3, 0); + + /* Getters :) */ + +#define PUSHGETTER(what, t) do {\ + lua_pushcfunction(L, lua_glib_new_getter); \ + lua_pushliteral(L, #what); \ + lua_glib_push_##t##_getter(L, &what); \ + lua_call(L, 2, 0); \ +} while(0) + + PUSHGETTER(gamemap, i16); + PUSHGETTER(maptol, i16); + PUSHGETTER(ultimatemode, b8); + PUSHGETTER(circuitmap, b32); + PUSHGETTER(netgame, b32); + PUSHGETTER(multiplayer, b32); + PUSHGETTER(modeattacking, b8); + PUSHGETTER(splitscreen, u8); + PUSHGETTER(gamecomplete, b32); + PUSHGETTER(devparm, b32); + PUSHGETTER(majormods, b32); + PUSHGETTER(menuactive, b32); + PUSHGETTER(paused, b8); + PUSHGETTER(gametype, i16); + PUSHGETTER(leveltime, u32); + PUSHGETTER(curWeather, i32); + PUSHGETTER(globalweather, i8); + PUSHGETTER(levelskynum, i32); + PUSHGETTER(globallevelskynum, i32); + PUSHGETTER(mapmusname, str); + PUSHGETTER(mapmusflags, u16); + PUSHGETTER(mapmusposition, u32); + PUSHGETTER(gravity, fxp); + PUSHGETTER(gamespeed, u8); + PUSHGETTER(encoremode, b32); + PUSHGETTER(franticitems, b32); + PUSHGETTER(comeback, b32); + PUSHGETTER(wantedcalcdelay, u32); + PUSHGETTER(indirectitemcooldown, u32); + PUSHGETTER(hyubgone, u32); + PUSHGETTER(thwompsactive, b32); + PUSHGETTER(spbplace, i8); + PUSHGETTER(mapobjectscale, fxp); + PUSHGETTER(racecountdown, u32); + PUSHGETTER(exitcountdown, u32); + + lua_pushcfunction(L, lua_glib_new_getter); + lua_pushliteral(L, "mariomode"); + lua_pushcfunction(L, lua_enumlib_mariomode_get); + lua_call(L, 2, 0); + + lua_pushcfunction(L, lua_glib_new_getter); + lua_pushliteral(L, "twodlevel"); + lua_pushcfunction(L, lua_enumlib_twodlevel_get); + lua_call(L, 2, 0); + + lua_pushcfunction(L, lua_glib_new_getter); + lua_pushliteral(L, "modifiedgame"); + lua_pushcfunction(L, lua_enumlib_modifiedgame_get); + lua_call(L, 2, 0); + + lua_pushcfunction(L, lua_glib_new_getter); + lua_pushliteral(L, "defrosting"); + lua_glib_push_i32_getter(L, &hook_defrosting); + lua_call(L, 2, 0); + + lua_pushcfunction(L, lua_glib_new_getter); + lua_pushliteral(L, "server"); + lua_pushcfunction(L, lua_enumlib_server_get); + lua_call(L, 2, 0); + + lua_pushcfunction(L, lua_glib_new_getter); + lua_pushliteral(L, "consoleplayer"); + lua_pushcfunction(L, lua_enumlib_consoleplayer_get); + lua_call(L, 2, 0); + + lua_pushcfunction(L, lua_glib_new_getter); + lua_pushliteral(L, "isserver"); + lua_glib_push_b32_getter(L, &server); + lua_call(L, 2, 0); + + lua_pushcfunction(L, lua_glib_new_getter); + lua_pushliteral(L, "isdedicatedserver"); + lua_glib_push_b32_getter(L, &dedicated); + lua_call(L, 2, 0); + + lua_pushcfunction(L, lua_glib_new_getter); + lua_pushliteral(L, "numlaps"); + lua_glib_push_i32_getter(L, &cv_numlaps.value); + lua_call(L, 2, 0); + + lua_pushcfunction(L, lua_glib_new_getter); + lua_pushliteral(L, "replayplayback"); + lua_glib_push_b32_getter(L, &demo.playback); + lua_call(L, 2, 0); + + if (!mathlib) + { + lua_pushcfunction(L, lua_glib_new_getter); + lua_pushliteral(L, "super"); + lua_pushcfunction(L, lua_enumlib_super_get); + lua_call(L, 2, 0); + } + +#undef PUSHGETTER + + // lua_pushliteral(L, "{cf75e032-00c8-4887-b15f-87b755784ad3}"); + // lua_gettable(L, LUA_REGISTRYINDEX); + // lua_pushliteral(L, "cache"); + // lua_gettable(L, -2); + + // lua_pushnil(L); + // while (lua_next(L, -2)) + // { + // lua_pushvalue(L, -2); + // CONS_Printf("%s: %s\n", lua_tostring(L, -1), lua_tostring(L, -2)); + // lua_pop(L, 2); + // } return 0; } diff --git a/src/lua_glib.c b/src/lua_glib.c new file mode 100644 index 000000000..c213230ec --- /dev/null +++ b/src/lua_glib.c @@ -0,0 +1,381 @@ +#include "lua_glib.h" + +#define GLIB_DATABASE_GUID "{cf75e032-00c8-4887-b15f-87b755784ad3}" + #define GLIB_DATABASE_GETTERS "getters" /* Table of getter functions. */ + #define GLIB_DATABASE_SETTERS "setters" /* Table of setter functions. */ + #define GLIB_DATABASE_ENUMS "enums" /* Table of enum fallback functions. */ + #define GLIB_DATABASE_CACHE "cache" /* Table of cached enum values. */ + #define GLIB_DATABASE_MTABLE "metatable" /* Metatable.*/ + #define GLIB_DATABASE_PROXY "proxy" /* Proxy table for the library that isn't the global table. */ + +static inline void lua_glib_push_db(lua_State *L) +{ + lua_pushliteral(L, GLIB_DATABASE_GUID); + lua_gettable(L, LUA_REGISTRYINDEX); +} + +void lua_glib_get_proxy(lua_State *L) +{ + lua_pushliteral(L, GLIB_DATABASE_GUID); + lua_gettable(L, LUA_REGISTRYINDEX); + lua_pushliteral(L, GLIB_DATABASE_PROXY); + lua_gettable(L, -2); + lua_remove(L, -2); +} + +/** + * Copy table contents to another. + * @param L[1] Destination table. + * @param L[2] Source table. + */ +static int lua_glib_copy_table(lua_State *L) +{ + luaL_checktype(L, 1, LUA_TTABLE); + luaL_checktype(L, 2, LUA_TTABLE); + + lua_pushnil(L); + while (lua_next(L, 2)) + { + lua_pushvalue(L, -2); /* Push key. */ + lua_pushvalue(L, -2); /* Push value. */ + lua_settable(L, 1); /* Store at dst. */ + + lua_pop(L, 1); /* Pop the value. */ + } + + return 0; +} + +static int lua_glib___index(lua_State *L) +{ + /* function mt:__index(key) */ + + const int CACHE = lua_upvalueindex(1); /* Enum cache. */ + const int GETTERS = lua_upvalueindex(2); /* Storage for getters. */ + const int ENUMS = lua_upvalueindex(3); /* Enum fallback cache. */ + + if (!lua_isstring(L, 2)) + { + /* A non string key should not happen. But somebody might really want it. */ + return 0; + } + + /* Attempt cache lookup. */ + + lua_pushvalue(L, 2); + lua_gettable(L, CACHE); + if (!lua_isnil(L, -1)) + { + return 1; + } + + lua_pop(L, 1); + + /* Attempt a get property. */ + + lua_pushvalue(L, 2); + lua_gettable(L, GETTERS); + if (lua_isfunction(L, -1)) + { + lua_call(L, 0, 1); + return 1; + } + + /* Try finding a the relevant enum fallback function. */ + const char *str = lua_tostring(L, 2); + int sz = lua_strlen(L, 2); + for (int i = 1; i < sz; i++) + { + lua_pushlstring(L, str, i); + lua_gettable(L, ENUMS); + + if (lua_isfunction(L, -1)) + { + lua_pushvalue(L, 2); + lua_call(L, 1, 1); + + /* Store value in cache. */ + lua_pushvalue(L, 2); + lua_pushvalue(L, -2); + lua_settable(L, CACHE); + + /* Return it. */ + return 1; + } + else + { + lua_pop(L, 1); + } + } + + /* This really does not exist. Returns nil. */ + return 0; +} + +static int lua_glib___newindex(lua_State *L) +{ + /* function mt:__newindex(key, value) */ + + const int SETTERS = lua_upvalueindex(1); + + lua_pushvalue(L, 2); + lua_gettable(L, SETTERS); + if (!lua_isfunction(L, -1)) + { + return luaL_error(L, "You cannot set the global table except known writeable properties. If this isn't a mistake use `rawset(_G, k, v)`."); + } + + /* Set the property. */ + lua_pushvalue(L, 3); + lua_call(L, 1, 0); + return 0; +} + +int lua_glib_append_cache(lua_State *L) +{ + luaL_checktype(L, 1, LUA_TTABLE); + + lua_glib_push_db(L); + + lua_pushcfunction(L, lua_glib_copy_table); + lua_pushliteral(L, GLIB_DATABASE_CACHE); + lua_gettable(L, -3); + lua_pushvalue(L, 1); + lua_call(L, 2, 0); + + return 0; +} + +int lua_glib_require(lua_State *L) +{ + if (!lua_isboolean(L, 1)) + { + /* Request loading the metatable by default. */ + lua_pushboolean(L, 1); + } + + lua_pushliteral(L, GLIB_DATABASE_GUID); + lua_newtable(L); + /* Standard functions. */ + lua_pushliteral(L, GLIB_DATABASE_GETTERS); + lua_newtable(L); + lua_settable(L, -3); + + lua_pushliteral(L, GLIB_DATABASE_SETTERS); + lua_newtable(L); + lua_settable(L, -3); + + lua_pushliteral(L, GLIB_DATABASE_ENUMS); + lua_newtable(L); + lua_settable(L, -3); + + lua_pushliteral(L, GLIB_DATABASE_CACHE); + lua_newtable(L); + lua_settable(L, -3); + + lua_pushliteral(L, GLIB_DATABASE_MTABLE); + lua_newtable(L); + lua_pushliteral(L, "__index"); + lua_pushliteral(L, GLIB_DATABASE_CACHE); + lua_gettable(L, -5); + + lua_pushliteral(L, GLIB_DATABASE_GETTERS); + lua_gettable(L, -6); + + lua_pushliteral(L, GLIB_DATABASE_ENUMS); + lua_gettable(L, -7); + + lua_pushcclosure(L, lua_glib___index, 3); + lua_settable(L, -3); /* __index */ + + lua_pushliteral(L, "__newindex"); + lua_pushliteral(L, GLIB_DATABASE_SETTERS); + lua_gettable(L, -5); + + lua_pushcclosure(L, lua_glib___newindex, 1); + lua_settable(L, -3); /* __newindex */ + lua_settable(L, -3); /* GLIB_DATABASE_MTABLE. */ + + lua_pushliteral(L, GLIB_DATABASE_PROXY); + lua_newtable(L); + lua_pushliteral(L, GLIB_DATABASE_MTABLE); + lua_gettable(L, -4); + lua_setmetatable(L, -2); /* get GLIB_DATABASE_MTABLE. */ + lua_settable(L, -3); /* GLIB_DATABASE_PROXY. */ + + lua_settable(L, LUA_REGISTRYINDEX); /* GLIB_DATABASE_GUID. */ + + /* Set global metatable. */ + if (lua_toboolean(L, 1)) + { + lua_glib_push_db(L); + lua_pushliteral(L, GLIB_DATABASE_MTABLE); + lua_gettable(L, -2); + lua_setmetatable(L, LUA_GLOBALSINDEX); + } + + return 0; +} + +int lua_glib_new_enum(lua_State *L) +{ + lua_glib_push_db(L); + + if (lua_istable(L, 1)) + { + /* copy_table(dst, src) */ + + lua_pushcfunction(L, lua_glib_copy_table); + + lua_pushliteral(L, GLIB_DATABASE_CACHE); + lua_gettable(L, -3); /* dst */ + + lua_pushvalue(L, 1); /* src */ + lua_call(L, 2, 0); + } + + if (lua_isstring(L, 2) && lua_isfunction(L, 3)) + { + lua_pushliteral(L, GLIB_DATABASE_ENUMS); + lua_gettable(L, -2); + + lua_pushvalue(L, 2); /* prefix */ + lua_pushvalue(L, 3); /* fallback */ + lua_settable(L, -3); + } + + return 0; +} + +int lua_glib_new_getter(lua_State *L) +{ + luaL_checktype(L, 1, LUA_TSTRING); + luaL_checktype(L, 2, LUA_TFUNCTION); + + lua_glib_push_db(L); + lua_pushliteral(L, GLIB_DATABASE_GETTERS); + lua_gettable(L, -2); + + lua_pushvalue(L, 1); + lua_pushvalue(L, 2); + lua_settable(L, -3); + + return 0; +} + +int lua_glib_new_setter(lua_State *L) +{ + luaL_checktype(L, 1, LUA_TSTRING); + luaL_checktype(L, 2, LUA_TFUNCTION); + + lua_glib_push_db(L); + lua_pushliteral(L, GLIB_DATABASE_SETTERS); + lua_gettable(L, -2); + + lua_pushvalue(L, 1); + lua_pushvalue(L, 2); + lua_settable(L, -3); + + return 0; +} + +#define _GLIB_DECL_INT_GETTER(T, name) \ + int lua_glib_getter_##name(lua_State *L) { \ + const T* ptr = (const T*)lua_topointer(L, lua_upvalueindex(1)); \ + lua_pushinteger(L, (lua_Integer)*ptr); \ + return 1; \ + } + +#define _GLIB_DECL_INT_SETTER(T, name) \ + int lua_glib_setter_##name(lua_State *L) { \ + luaL_checktype(L, 1, LUA_TNUMBER); \ + T* ptr = (T*)lua_topointer(L, lua_upvalueindex(1)); \ + *ptr = (T)lua_tointeger(L, 1); \ + return 0; \ + } + +#define _GLIB_DECL_BOOL_GETTER(T, name) \ + int lua_glib_getter_##name(lua_State *L) { \ + const T* ptr = (const T*)lua_topointer(L, lua_upvalueindex(1)); \ + lua_pushboolean(L, *ptr != 0); \ + return 1; \ + } + +#define _GLIB_DECL_FLOAT_GETTER(T, name) \ + int lua_glib_getter_##name(lua_State *L) { \ + const T* ptr = (const T*)lua_topointer(L, lua_upvalueindex(1)); \ + lua_pushnumber(L, (lua_Number)*ptr); \ + return 1; \ + } + +#define _GLIB_DECL_FLOAT_SETTER(T, name) \ + int lua_glib_setter_##name(lua_State *L) { \ + luaL_checktype(L, 1, LUA_TNUMBER); \ + T* ptr = (T*)lua_topointer(L, lua_upvalueindex(1)); \ + *ptr = (T)lua_tonumber(L, 1); \ + return 0; \ + } + +#define _GLIB_DECL_BOOL_SETTER(T, name) \ + int lua_glib_setter_##name(lua_State *L) { \ + T* ptr = (T*)lua_topointer(L, lua_upvalueindex(1)); \ + *ptr = (T)lua_toboolean(L, 1); \ + return 0; \ + } + +_GLIB_DECL_INT_GETTER(int8_t, i8); +_GLIB_DECL_INT_GETTER(int16_t, i16); +_GLIB_DECL_INT_GETTER(int32_t, i32); +_GLIB_DECL_INT_GETTER(int64_t, i64); +_GLIB_DECL_INT_GETTER(uint8_t, u8); +_GLIB_DECL_INT_GETTER(uint16_t, u16); +_GLIB_DECL_INT_GETTER(uint32_t, u32); +_GLIB_DECL_INT_GETTER(uint64_t, u64); +_GLIB_DECL_FLOAT_GETTER(float, f32); +_GLIB_DECL_FLOAT_GETTER(double, f64); +_GLIB_DECL_BOOL_GETTER(uint8_t, b8); +_GLIB_DECL_BOOL_GETTER(uint16_t, b16); +_GLIB_DECL_BOOL_GETTER(uint32_t, b32); +_GLIB_DECL_BOOL_GETTER(uint64_t, b64); + +_GLIB_DECL_INT_SETTER(int8_t, i8); +_GLIB_DECL_INT_SETTER(int16_t, i16); +_GLIB_DECL_INT_SETTER(int32_t, i32); +_GLIB_DECL_INT_SETTER(int64_t, i64); +_GLIB_DECL_INT_SETTER(uint8_t, u8); +_GLIB_DECL_INT_SETTER(uint16_t, u16); +_GLIB_DECL_INT_SETTER(uint32_t, u32); +_GLIB_DECL_INT_SETTER(uint64_t, u64); +_GLIB_DECL_FLOAT_SETTER(float, f32); +_GLIB_DECL_FLOAT_SETTER(double, f64); +_GLIB_DECL_BOOL_SETTER(uint8_t, b8); +_GLIB_DECL_BOOL_SETTER(uint16_t, b16); +_GLIB_DECL_BOOL_SETTER(uint32_t, b32); +_GLIB_DECL_BOOL_SETTER(uint64_t, b64); + +int lua_glib_setter_fxp(lua_State *L) +{ + fixed_t *fxp = (fixed_t*)lua_topointer(L, lua_upvalueindex(1)); + *fxp = lua_tointeger(L, 1); + return 0; +} + +int lua_glib_getter_fxp(lua_State *L) +{ + const fixed_t *fxp = (const fixed_t*)lua_topointer(L, lua_upvalueindex(1)); + lua_pushfixed(L, *fxp); + return 1; +} + +int lua_glib_getter_str(lua_State *L) +{ + const char *str = (const char*)lua_topointer(L, lua_upvalueindex(1)); + lua_pushstring(L, str); + return 1; +} + +int lua_glib_getter_ptr(lua_State *L) +{ + lua_pushvalue(L, lua_upvalueindex(1)); + return 1; +} diff --git a/src/lua_glib.h b/src/lua_glib.h new file mode 100644 index 000000000..1405f807a --- /dev/null +++ b/src/lua_glib.h @@ -0,0 +1,175 @@ +/** + * @file lua_glib.h Lua global table field management library. + */ +#ifndef _LUA_GLIB_H_ +#define _LUA_GLIB_H_ + +#include "doomdef.h" +#include "lua_script.h" + +/** + * Feed a list of items to append to the enum cache. + * @param L[1] The table of tiems to add to the enum cache. + * @returns Nothing. + */ +int lua_glib_append_cache(lua_State *L); + +/** + * Create a new enum. + * @param L[1] The cache table for the enum. + * @param L[2] The prefix for the enum. + * @param L[3] The fallback function for the enum. Can be nil. + * @returns Nothing. + */ +int lua_glib_new_enum(lua_State *L); + +/** + * Create a new getter. + * @param L[1] The name of the getter. + * @param L[2] The getter() function. + * @returns Nothing. + */ +int lua_glib_new_getter(lua_State *L); + +/** + * Create a new setter. + * @param L[1] The name of the setter. + * @param L[2] The setter(value) function. + */ +int lua_glib_new_setter(lua_State *L); + +/** + * Require the globals library. + * @param L[1] (optional) False to inhibit setting the global metatable. + * @remarks This is a standard Lua require function. + */ +int lua_glib_require(lua_State *L); + +/** + * Get the proxy table that can be used to get globals. + */ +void lua_glib_get_proxy(lua_State *L); + +/** + * Helper function to push a linear enum cache. + * @param L The Lua state. + * @param prefix enum prefix. + * @param list Enum list. + * @param count Number of elements in list. INT_MAX for NULL termination. + */ +inline static void lua_glib_push_linear_cache(lua_State *L, const char *prefix, const char * const *list, lua_Integer count) +{ + lua_newtable(L); + for (lua_Integer i = 0; i < count && list[i]; i++) + { + lua_pushfstring(L, "%s%s", prefix, list[i]); + lua_pushinteger(L, i); + lua_settable(L, -3); + } +} + +/** + * Helper function to push a bitfield enum cache. + * @param L The Lua state. + * @param prefix enum prefix. + * @param list Enum list. + * @param count Number of elements in list. INT_MAX for NULL termination. + */ +inline static void lua_glib_push_bitfield_cache(lua_State *L, const char *prefix, const char * const *list, lua_Integer count) +{ + lua_newtable(L); + for (lua_Integer i = 0; i < count && list[i]; i++) + { + lua_pushfstring(L, "%s%s", prefix, list[i]); + lua_pushinteger(L, (lua_Integer)1 << i); + lua_settable(L, -3); + } +} + +/* Base getters. */ + +int lua_glib_getter_i8(lua_State *L); +int lua_glib_getter_i16(lua_State *L); +int lua_glib_getter_i32(lua_State *L); +int lua_glib_getter_i64(lua_State *L); +int lua_glib_getter_u8(lua_State *L); +int lua_glib_getter_u16(lua_State *L); +int lua_glib_getter_u32(lua_State *L); +int lua_glib_getter_u64(lua_State *L); +int lua_glib_getter_f32(lua_State *L); +int lua_glib_getter_f64(lua_State *L); +int lua_glib_getter_b8(lua_State *L); +int lua_glib_getter_b16(lua_State *L); +int lua_glib_getter_b32(lua_State *L); +int lua_glib_getter_b64(lua_State *L); +int lua_glib_getter_fxp(lua_State *L); +int lua_glib_getter_str(lua_State *L); +int lua_glib_getter_ptr(lua_State *L); + +/* Base setters. */ + +int lua_glib_setter_i8(lua_State *L); +int lua_glib_setter_i16(lua_State *L); +int lua_glib_setter_i32(lua_State *L); +int lua_glib_setter_i64(lua_State *L); +int lua_glib_setter_u8(lua_State *L); +int lua_glib_setter_u16(lua_State *L); +int lua_glib_setter_u32(lua_State *L); +int lua_glib_setter_u64(lua_State *L); +int lua_glib_setter_f32(lua_State *L); +int lua_glib_setter_f64(lua_State *L); +int lua_glib_setter_b8(lua_State *L); +int lua_glib_setter_b16(lua_State *L); +int lua_glib_setter_b32(lua_State *L); +int lua_glib_setter_b64(lua_State *L); +int lua_glib_setter_fxp(lua_State *L); + +#define _LUA_GLIB_DECL_PGETTER(T, name) \ + static inline void lua_glib_push_##name##_getter(lua_State *L, const T* ptr) { \ + lua_pushlightuserdata(L, (void*)ptr); \ + lua_pushcclosure(L, lua_glib_getter_##name, 1); \ + } + + _LUA_GLIB_DECL_PGETTER(int8_t, i8); + _LUA_GLIB_DECL_PGETTER(int16_t, i16); + _LUA_GLIB_DECL_PGETTER(int32_t, i32); + _LUA_GLIB_DECL_PGETTER(int64_t, i64); + _LUA_GLIB_DECL_PGETTER(uint8_t, u8); + _LUA_GLIB_DECL_PGETTER(uint16_t, u16); + _LUA_GLIB_DECL_PGETTER(uint32_t, u32); + _LUA_GLIB_DECL_PGETTER(uint64_t, u64); + _LUA_GLIB_DECL_PGETTER(float, f32); + _LUA_GLIB_DECL_PGETTER(double, f64); + _LUA_GLIB_DECL_PGETTER(char, str); + _LUA_GLIB_DECL_PGETTER(void, ptr); + _LUA_GLIB_DECL_PGETTER(uint8_t, b8); + _LUA_GLIB_DECL_PGETTER(uint16_t, b16); + _LUA_GLIB_DECL_PGETTER(uint32_t, b32); + _LUA_GLIB_DECL_PGETTER(uint64_t, b64); + _LUA_GLIB_DECL_PGETTER(fixed_t, fxp); +#undef _LUA_GLIB_DECL_PGETTER + +#define _LUA_GLIB_DECL_PSETTER(T, name) \ + static inline void lua_glib_push_##name##_setter(lua_State *L, T* ptr) { \ + lua_pushlightuserdata(L, ptr); \ + lua_pushcclosure(L, lua_glib_setter_##name, 1); \ + } + + _LUA_GLIB_DECL_PSETTER(int8_t, i8); + _LUA_GLIB_DECL_PSETTER(int16_t, i16); + _LUA_GLIB_DECL_PSETTER(int32_t, i32); + _LUA_GLIB_DECL_PSETTER(int64_t, i64); + _LUA_GLIB_DECL_PSETTER(uint8_t, u8); + _LUA_GLIB_DECL_PSETTER(uint16_t, u16); + _LUA_GLIB_DECL_PSETTER(uint32_t, u32); + _LUA_GLIB_DECL_PSETTER(uint64_t, u64); + _LUA_GLIB_DECL_PSETTER(float, f32); + _LUA_GLIB_DECL_PSETTER(double, f64); + _LUA_GLIB_DECL_PSETTER(uint8_t, b8); + _LUA_GLIB_DECL_PSETTER(uint16_t, b16); + _LUA_GLIB_DECL_PSETTER(uint32_t, b32); + _LUA_GLIB_DECL_PSETTER(uint64_t, b64); + _LUA_GLIB_DECL_PSETTER(fixed_t, fxp); +#undef _LUA_GLIB_DECL_PSETTER + +#endif diff --git a/src/lua_script.c b/src/lua_script.c index fe61e6e50..d809de33d 100644 --- a/src/lua_script.c +++ b/src/lua_script.c @@ -358,20 +358,23 @@ void LUA_DumpFile(const char *filename) fixed_t LUA_EvalMath(const char *word) { - lua_State *L = NULL; + static lua_State *L = NULL; char buf[1024], *b; const char *p; fixed_t res = 0; // make a new state so SOC can't interefere with scripts // allocate state - L = lua_newstate(LUA_Alloc, NULL); - lua_atpanic(L, LUA_Panic); + if (L == NULL) + { + L = lua_newstate(LUA_Alloc, NULL); + lua_atpanic(L, LUA_Panic); - // open only enum lib - lua_pushcfunction(L, LUA_EnumLib); - lua_pushboolean(L, true); - lua_call(L, 1, 0); + // open only enum lib + lua_pushcfunction(L, LUA_EnumLib); + lua_pushboolean(L, true); + lua_call(L, 1, 0); + } // change ^ into ^^ for Lua. strcpy(buf, "return "); @@ -396,8 +399,6 @@ fixed_t LUA_EvalMath(const char *word) else res = lua_tointeger(L, -1); - // clean up and return. - lua_close(L); return res; }