From 621efbfa158e49a33f6ba8a55c267cb10f85b602 Mon Sep 17 00:00:00 2001 From: James R Date: Fri, 4 Dec 2020 00:30:08 -0800 Subject: [PATCH 01/12] Lua taglib for accessing taggroups The global "tags" can be iterated upon for every unique tag which is set in the level. If a tag is set on a sector/line/thing, it will be included. Taking the length of "tags" will give you the number of these unique tags. (If a tag is set on multiple sectors/lines/things, it will only be counted once though.) For sectors, lines and mapthings, call the field "tagged". This function takes one argument, which is the tag. The return value can be iterated over for all the sectors/lines/things with that tag. The length can also be taken for the number of such objects. If no argument is given, the global tag is default. --- src/CMakeLists.txt | 1 + src/blua/Makefile.cfg | 1 + src/doomtype.h | 22 +++++++++++ src/lua_libs.h | 1 + src/lua_maplib.c | 60 +++++++++-------------------- src/lua_mobjlib.c | 31 +++++---------- src/lua_script.c | 59 ++++++++++++++++++++++++++-- src/lua_script.h | 48 ++++++++++++++++++++--- src/taglist.c | 90 +++++++++++++++++++++++-------------------- src/taglist.h | 10 +++++ 10 files changed, 208 insertions(+), 115 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index d35e774e9..87a0499b6 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -276,6 +276,7 @@ set(SRB2_LUA_SOURCES lua_hudlib.c lua_infolib.c lua_maplib.c + lua_taglib.c lua_mathlib.c lua_mobjlib.c lua_playerlib.c diff --git a/src/blua/Makefile.cfg b/src/blua/Makefile.cfg index eae95ba3a..3a2962e65 100644 --- a/src/blua/Makefile.cfg +++ b/src/blua/Makefile.cfg @@ -47,6 +47,7 @@ OBJS:=$(OBJS) \ $(OBJDIR)/lua_skinlib.o \ $(OBJDIR)/lua_thinkerlib.o \ $(OBJDIR)/lua_maplib.o \ + $(OBJDIR)/lua_taglib.o \ $(OBJDIR)/lua_polyobjlib.o \ $(OBJDIR)/lua_blockmaplib.o \ $(OBJDIR)/lua_hudlib.o diff --git a/src/doomtype.h b/src/doomtype.h index 4e13ba96d..08317c65a 100644 --- a/src/doomtype.h +++ b/src/doomtype.h @@ -379,4 +379,26 @@ Needed for some lua shenanigans. #define FIELDFROM( type, field, have, want ) \ (void *)((intptr_t)(field) - offsetof (type, have) + offsetof (type, want)) +typedef UINT8 bitarray_t; + +#define BIT_ARRAY_SIZE(n) (((n) + 7) >> 3) + +static inline int +in_bit_array (const bitarray_t * const array, const int value) +{ + return (array[value >> 3] & (1<<(value & 7))); +} + +static inline void +set_bit_array (bitarray_t * const array, const int value) +{ + array[value >> 3] |= (1<<(value & 7)); +} + +static inline void +unset_bit_array (bitarray_t * const array, const int value) +{ + array[value >> 3] &= ~(1<<(value & 7)); +} + #endif //__DOOMTYPE__ diff --git a/src/lua_libs.h b/src/lua_libs.h index 062a3fe50..aa0638683 100644 --- a/src/lua_libs.h +++ b/src/lua_libs.h @@ -93,6 +93,7 @@ int LUA_PlayerLib(lua_State *L); int LUA_SkinLib(lua_State *L); int LUA_ThinkerLib(lua_State *L); int LUA_MapLib(lua_State *L); +int LUA_TagLib(lua_State *L); int LUA_PolyObjLib(lua_State *L); int LUA_BlockmapLib(lua_State *L); int LUA_HudLib(lua_State *L); diff --git a/src/lua_maplib.c b/src/lua_maplib.c index 95cc8c101..a3df28cca 100644 --- a/src/lua_maplib.c +++ b/src/lua_maplib.c @@ -1385,25 +1385,15 @@ static int lib_iterateSectors(lua_State *L) static int lib_getSector(lua_State *L) { - int field; INLEVEL - lua_settop(L, 2); - lua_remove(L, 1); // dummy userdata table is unused. - if (lua_isnumber(L, 1)) + if (lua_isnumber(L, 2)) { - size_t i = lua_tointeger(L, 1); + size_t i = lua_tointeger(L, 2); if (i >= numsectors) return 0; LUA_PushUserdata(L, §ors[i], META_SECTOR); return 1; } - field = luaL_checkoption(L, 1, NULL, array_opt); - switch(field) - { - case 0: // iterate - lua_pushcfunction(L, lib_iterateSectors); - return 1; - } return 0; } @@ -1489,25 +1479,15 @@ static int lib_iterateLines(lua_State *L) static int lib_getLine(lua_State *L) { - int field; INLEVEL - lua_settop(L, 2); - lua_remove(L, 1); // dummy userdata table is unused. - if (lua_isnumber(L, 1)) + if (lua_isnumber(L, 2)) { - size_t i = lua_tointeger(L, 1); + size_t i = lua_tointeger(L, 2); if (i >= numlines) return 0; LUA_PushUserdata(L, &lines[i], META_LINE); return 1; } - field = luaL_checkoption(L, 1, NULL, array_opt); - switch(field) - { - case 0: // iterate - lua_pushcfunction(L, lib_iterateLines); - return 1; - } return 0; } @@ -2358,15 +2338,13 @@ int LUA_MapLib(lua_State *L) //lua_setfield(L, -2, "__len"); lua_pop(L, 1); - lua_newuserdata(L, 0); - lua_createtable(L, 0, 2); - lua_pushcfunction(L, lib_getSector); - lua_setfield(L, -2, "__index"); - - lua_pushcfunction(L, lib_numsectors); - lua_setfield(L, -2, "__len"); - lua_setmetatable(L, -2); - lua_setglobal(L, "sectors"); + LUA_PushTaggableObjectArray(L, "sectors", + lib_iterateSectors, + lib_getSector, + lib_numsectors, + tags_sectors, + &numsectors, §ors, + sizeof (sector_t), META_SECTOR); lua_newuserdata(L, 0); lua_createtable(L, 0, 2); @@ -2378,15 +2356,13 @@ int LUA_MapLib(lua_State *L) lua_setmetatable(L, -2); lua_setglobal(L, "subsectors"); - lua_newuserdata(L, 0); - lua_createtable(L, 0, 2); - lua_pushcfunction(L, lib_getLine); - lua_setfield(L, -2, "__index"); - - lua_pushcfunction(L, lib_numlines); - lua_setfield(L, -2, "__len"); - lua_setmetatable(L, -2); - lua_setglobal(L, "lines"); + LUA_PushTaggableObjectArray(L, "lines", + lib_iterateLines, + lib_getLine, + lib_numlines, + tags_lines, + &numlines, &lines, + sizeof (line_t), META_LINE); lua_newuserdata(L, 0); lua_createtable(L, 0, 2); diff --git a/src/lua_mobjlib.c b/src/lua_mobjlib.c index 7aae18c90..134f104ee 100644 --- a/src/lua_mobjlib.c +++ b/src/lua_mobjlib.c @@ -22,8 +22,6 @@ #include "lua_hud.h" // hud_running errors #include "lua_hook.h" // hook_cmd_running errors -static const char *const array_opt[] ={"iterate",NULL}; - enum mobj_e { mobj_valid = 0, mobj_x, @@ -1003,25 +1001,15 @@ static int lib_iterateMapthings(lua_State *L) static int lib_getMapthing(lua_State *L) { - int field; INLEVEL - lua_settop(L, 2); - lua_remove(L, 1); // dummy userdata table is unused. - if (lua_isnumber(L, 1)) + if (lua_isnumber(L, 2)) { - size_t i = lua_tointeger(L, 1); + size_t i = lua_tointeger(L, 2); if (i >= nummapthings) return 0; LUA_PushUserdata(L, &mapthings[i], META_MAPTHING); return 1; } - field = luaL_checkoption(L, 1, NULL, array_opt); - switch(field) - { - case 0: // iterate - lua_pushcfunction(L, lib_iterateMapthings); - return 1; - } return 0; } @@ -1068,14 +1056,13 @@ int LUA_MobjLib(lua_State *L) lua_setfield(L, -2, "__len"); lua_pop(L,1); - lua_newuserdata(L, 0); - lua_createtable(L, 0, 2); - lua_pushcfunction(L, lib_getMapthing); - lua_setfield(L, -2, "__index"); + LUA_PushTaggableObjectArray(L, "mapthings", + lib_iterateMapthings, + lib_getMapthing, + lib_nummapthings, + tags_mapthings, + &nummapthings, &mapthings, + sizeof (mapthing_t), META_MAPTHING); - lua_pushcfunction(L, lib_nummapthings); - lua_setfield(L, -2, "__len"); - lua_setmetatable(L, -2); - lua_setglobal(L, "mapthings"); return 0; } diff --git a/src/lua_script.c b/src/lua_script.c index eb4737f76..ee60a41c6 100644 --- a/src/lua_script.c +++ b/src/lua_script.c @@ -53,6 +53,7 @@ static lua_CFunction liblist[] = { LUA_SkinLib, // skin_t, skins[] LUA_ThinkerLib, // thinker_t LUA_MapLib, // line_t, side_t, sector_t, subsector_t + LUA_TagLib, // tags LUA_PolyObjLib, // polyobj_t LUA_BlockmapLib, // blockmap stuff LUA_HudLib, // HUD stuff @@ -739,25 +740,37 @@ void LUA_PushLightUserdata (lua_State *L, void *data, const char *meta) // Pushes it to the stack and stores it in the registry. void LUA_PushUserdata(lua_State *L, void *data, const char *meta) { + if (LUA_RawPushUserdata(L, data) == LPUSHED_NEW) + { + luaL_getmetatable(L, meta); + lua_setmetatable(L, -2); + } +} + +// Same as LUA_PushUserdata but don't set a metatable yet. +lpushed_t LUA_RawPushUserdata(lua_State *L, void *data) +{ + lpushed_t status = LPUSHED_NIL; + void **userdata; if (!data) { // push a NULL lua_pushnil(L); - return; + return status; } lua_getfield(L, LUA_REGISTRYINDEX, LREG_VALID); I_Assert(lua_istable(L, -1)); + lua_pushlightuserdata(L, data); lua_rawget(L, -2); + if (lua_isnil(L, -1)) { // no userdata? deary me, we'll have to make one. lua_pop(L, 1); // pop the nil // create the userdata userdata = lua_newuserdata(L, sizeof(void *)); *userdata = data; - luaL_getmetatable(L, meta); - lua_setmetatable(L, -2); // Set it in the registry so we can find it again lua_pushlightuserdata(L, data); // k (store the userdata via the data's pointer) @@ -765,8 +778,15 @@ void LUA_PushUserdata(lua_State *L, void *data, const char *meta) lua_rawset(L, -4); // stack is left with the userdata on top, as if getting it had originally succeeded. + + status = LPUSHED_NEW; } + else + status = LPUSHED_EXISTING; + lua_remove(L, -2); // remove LREG_VALID + + return status; } // When userdata is freed, use this function to remove it from Lua. @@ -1681,3 +1701,36 @@ int Lua_optoption(lua_State *L, int narg, return i; return -1; } + +void LUA_PushTaggableObjectArray +( lua_State *L, + const char *field, + lua_CFunction iterator, + lua_CFunction indexer, + lua_CFunction counter, + taggroup_t *garray[], + size_t * max_elements, + void * element_array, + size_t sizeof_element, + const char *meta) +{ + lua_newuserdata(L, 0); + lua_createtable(L, 0, 2); + lua_createtable(L, 0, 2); + lua_pushcfunction(L, iterator); + lua_setfield(L, -2, "iterate"); + + LUA_InsertTaggroupIterator(L, garray, + max_elements, element_array, sizeof_element, meta); + + lua_createtable(L, 0, 1); + lua_pushcfunction(L, indexer); + lua_setfield(L, -2, "__index"); + lua_setmetatable(L, -2); + lua_setfield(L, -2, "__index"); + + lua_pushcfunction(L, counter); + lua_setfield(L, -2, "__len"); + lua_setmetatable(L, -2); + lua_setglobal(L, field); +} diff --git a/src/lua_script.h b/src/lua_script.h index 79ba0bb38..2dc34446a 100644 --- a/src/lua_script.h +++ b/src/lua_script.h @@ -10,10 +10,14 @@ /// \file lua_script.h /// \brief Lua scripting basics +#ifndef LUA_SCRIPT_H +#define LUA_SCRIPT_H + #include "m_fixed.h" #include "doomtype.h" #include "d_player.h" #include "g_state.h" +#include "taglist.h" #include "blua/lua.h" #include "blua/lualib.h" @@ -46,12 +50,6 @@ void LUA_LoadLump(UINT16 wad, UINT16 lump, boolean noresults); void LUA_DumpFile(const char *filename); #endif fixed_t LUA_EvalMath(const char *word); -void LUA_PushLightUserdata(lua_State *L, void *data, const char *meta); -void LUA_PushUserdata(lua_State *L, void *data, const char *meta); -void LUA_InvalidateUserdata(void *data); -void LUA_InvalidateLevel(void); -void LUA_InvalidateMapthings(void); -void LUA_InvalidatePlayer(player_t *player); void LUA_Step(void); void LUA_Archive(void); void LUA_UnArchive(void); @@ -63,6 +61,42 @@ int Lua_optoption(lua_State *L, int narg, const char *def, const char *const lst[]); void LUAh_NetArchiveHook(lua_CFunction archFunc); +void LUA_PushTaggableObjectArray +( lua_State *L, + const char *field, + lua_CFunction iterator, + lua_CFunction indexer, + lua_CFunction counter, + taggroup_t *garray[], + size_t * max_elements, + void * element_array, + size_t sizeof_element, + const char *meta); + +void LUA_InsertTaggroupIterator +( lua_State *L, + taggroup_t *garray[], + size_t * max_elements, + void * element_array, + size_t sizeof_element, + const char * meta); + +typedef enum { + LPUSHED_NIL, + LPUSHED_NEW, + LPUSHED_EXISTING, +} lpushed_t; + +void LUA_PushLightUserdata(lua_State *L, void *data, const char *meta); +void LUA_PushUserdata(lua_State *L, void *data, const char *meta); +lpushed_t LUA_RawPushUserdata(lua_State *L, void *data); + +void LUA_InvalidateUserdata(void *data); + +void LUA_InvalidateLevel(void); +void LUA_InvalidateMapthings(void); +void LUA_InvalidatePlayer(player_t *player); + // Console wrapper void COM_Lua_f(void); @@ -98,3 +132,5 @@ void COM_Lua_f(void); #define INLEVEL if (! ISINLEVEL)\ return luaL_error(L, "This can only be used in a level!"); + +#endif/*LUA_SCRIPT_H*/ diff --git a/src/taglist.c b/src/taglist.c index b11216b6c..658605734 100644 --- a/src/taglist.c +++ b/src/taglist.c @@ -15,6 +15,11 @@ #include "z_zone.h" #include "r_data.h" +// Bit array of whether a tag exists for sectors/lines/things. +bitarray_t tags_available[BIT_ARRAY_SIZE (MAXTAGS)]; + +size_t num_tags; + // Taggroups are used to list elements of the same tag, for iteration. // Since elements can now have multiple tags, it means an element may appear // in several taggroups at the same time. These are built on level load. @@ -105,6 +110,33 @@ size_t Taggroup_Find (const taggroup_t *group, const size_t id) return -1; } +/// Iterate thru elements in a global taggroup. +INT32 Taggroup_Iterate +( taggroup_t *garray[], + const size_t max_elements, + const mtag_t tag, + const size_t p) +{ + const taggroup_t *group; + + if (tag == MTAG_GLOBAL) + { + if (p < max_elements) + return p; + return -1; + } + + group = garray[(UINT16)tag]; + + if (group) + { + if (p < group->count) + return group->elements[p]; + return -1; + } + return -1; +} + /// Add an element to a global taggroup. void Taggroup_Add (taggroup_t *garray[], const mtag_t tag, size_t id) { @@ -120,6 +152,11 @@ void Taggroup_Add (taggroup_t *garray[], const mtag_t tag, size_t id) if (Taggroup_Find(group, id) != (size_t)-1) return; + if (! in_bit_array(tags_available, tag)) + num_tags++; + + set_bit_array(tags_available, tag); + // Create group if empty. if (!group) { @@ -161,6 +198,11 @@ void Taggroup_Remove (taggroup_t *garray[], const mtag_t tag, size_t id) if ((rempos = Taggroup_Find(group, id)) == (size_t)-1) return; + if (in_bit_array(tags_available, tag)) + num_tags--; + + unset_bit_array(tags_available, tag); + // Strip away taggroup if no elements left. if (!(newcount = --group->count)) { @@ -209,6 +251,9 @@ void Taglist_InitGlobalTables(void) { size_t i, j; + memset(tags_available, 0, sizeof tags_available); + num_tags = 0; + for (i = 0; i < MAXTAGS; i++) { tags_sectors[i] = NULL; @@ -236,56 +281,17 @@ void Taglist_InitGlobalTables(void) INT32 Tag_Iterate_Sectors (const mtag_t tag, const size_t p) { - if (tag == MTAG_GLOBAL) - { - if (p < numsectors) - return p; - return -1; - } - - if (tags_sectors[(UINT16)tag]) - { - if (p < tags_sectors[(UINT16)tag]->count) - return tags_sectors[(UINT16)tag]->elements[p]; - return -1; - } - return -1; + return Taggroup_Iterate(tags_sectors, numsectors, tag, p); } INT32 Tag_Iterate_Lines (const mtag_t tag, const size_t p) { - if (tag == MTAG_GLOBAL) - { - if (p < numlines) - return p; - return -1; - } - - if (tags_lines[(UINT16)tag]) - { - if (p < tags_lines[(UINT16)tag]->count) - return tags_lines[(UINT16)tag]->elements[p]; - return -1; - } - return -1; + return Taggroup_Iterate(tags_lines, numlines, tag, p); } INT32 Tag_Iterate_Things (const mtag_t tag, const size_t p) { - if (tag == MTAG_GLOBAL) - { - if (p < nummapthings) - return p; - return -1; - } - - if (tags_mapthings[(UINT16)tag]) - { - if (p < tags_mapthings[(UINT16)tag]->count) - return tags_mapthings[(UINT16)tag]->elements[p]; - return -1; - } - return -1; + return Taggroup_Iterate(tags_mapthings, nummapthings, tag, p); } INT32 Tag_FindLineSpecial(const INT16 special, const mtag_t tag) diff --git a/src/taglist.h b/src/taglist.h index 0e6d9f842..e5db08806 100644 --- a/src/taglist.h +++ b/src/taglist.h @@ -43,6 +43,10 @@ typedef struct size_t count; } taggroup_t; +extern bitarray_t tags_available[]; + +extern size_t num_tags; + extern taggroup_t* tags_sectors[]; extern taggroup_t* tags_lines[]; extern taggroup_t* tags_mapthings[]; @@ -51,6 +55,12 @@ void Taggroup_Add (taggroup_t *garray[], const mtag_t tag, size_t id); void Taggroup_Remove (taggroup_t *garray[], const mtag_t tag, size_t id); size_t Taggroup_Find (const taggroup_t *group, const size_t id); +INT32 Taggroup_Iterate +( taggroup_t *garray[], + const size_t max_elements, + const mtag_t tag, + const size_t p); + void Taglist_InitGlobalTables(void); INT32 Tag_Iterate_Sectors (const mtag_t tag, const size_t p); From 96851e52a10bb87c02fcc579a8407a003d9f7be5 Mon Sep 17 00:00:00 2001 From: James R Date: Fri, 4 Dec 2020 02:27:14 -0800 Subject: [PATCH 02/12] hehehehehe what if I forgot to git add? --- src/lua_taglib.c | 186 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 186 insertions(+) create mode 100644 src/lua_taglib.c diff --git a/src/lua_taglib.c b/src/lua_taglib.c new file mode 100644 index 000000000..e6c82fea3 --- /dev/null +++ b/src/lua_taglib.c @@ -0,0 +1,186 @@ +// SONIC ROBO BLAST 2 +//----------------------------------------------------------------------------- +// Copyright (C) 2020 by James R. +// Copyright (C) 2020 by Sonic Team Junior. +// +// This program is free software distributed under the +// terms of the GNU General Public License, version 2. +// See the 'LICENSE' file for more details. +//----------------------------------------------------------------------------- +/// \file lua_taglib.c +/// \brief tag list iterator for Lua scripting + +#include "doomdef.h" +#include "taglist.h" +#include "r_state.h" + +#include "lua_script.h" +#include "lua_libs.h" + +static int tag_iterator(lua_State *L) +{ + INT32 tag = lua_isnil(L, 2) ? -1 : lua_tonumber(L, 2); + do + { + if (++tag >= MAXTAGS) + return 0; + } + while (! in_bit_array(tags_available, tag)) ; + lua_pushnumber(L, tag); + return 1; +} + +enum { +#define UPVALUE lua_upvalueindex + up_garray = UPVALUE(1), + up_max_elements = UPVALUE(2), + up_element_array = UPVALUE(3), + up_sizeof_element = UPVALUE(4), + up_meta = UPVALUE(5), +#undef UPVALUE +}; + +static INT32 next_element(lua_State *L, const mtag_t tag, const size_t p) +{ + taggroup_t ** garray = lua_touserdata(L, up_garray); + const size_t * max_elements = lua_touserdata(L, up_max_elements); + return Taggroup_Iterate(garray, *max_elements, tag, p); +} + +static void push_element(lua_State *L, void *element) +{ + if (LUA_RawPushUserdata(L, element) == LPUSHED_NEW) + { + lua_pushvalue(L, up_meta); + lua_setmetatable(L, -2); + } +} + +static void push_next_element(lua_State *L, const INT32 element) +{ + char * element_array = *(char **)lua_touserdata(L, up_element_array); + const size_t sizeof_element = lua_tonumber(L, up_sizeof_element); + push_element(L, &element_array[element * sizeof_element]); +} + +struct element_iterator_state { + mtag_t tag; + size_t p; +}; + +static int element_iterator(lua_State *L) +{ + struct element_iterator_state * state = lua_touserdata(L, 1); + const INT32 element = next_element(L, state->tag, state->p); + + if (element == -1) + return 0; + else + { + push_next_element(L, element); + state->p++; + return 1; + } +} + +static int lib_iterateTags(lua_State *L) +{ + if (lua_gettop(L) < 2) + { + lua_pushcfunction(L, tag_iterator); + return 1; + } + else + return tag_iterator(L); +} + +static int lib_numTags(lua_State *L) +{ + lua_pushnumber(L, num_tags); + return 1; +} + +static int lib_getTaggroup(lua_State *L) +{ + struct element_iterator_state *state; + + mtag_t tag; + + if (lua_gettop(L) > 1) + return luaL_error(L, "too many arguments"); + + if (lua_isnoneornil(L, 1)) + { + tag = MTAG_GLOBAL; + } + else + { + tag = lua_tonumber(L, 1); + luaL_argcheck(L, tag >= -1, 1, "tag out of range"); + } + + state = lua_newuserdata(L, sizeof *state); + state->tag = tag; + state->p = 0; + + lua_pushvalue(L, lua_upvalueindex(1)); + lua_setmetatable(L, -2); + + return 1; +} + +static int lib_numTaggroupElements(lua_State *L) +{ + const mtag_t tag = *(mtag_t *)lua_touserdata(L, 1); + if (tag == MTAG_GLOBAL) + lua_pushnumber(L, *(size_t *)lua_touserdata(L, up_max_elements)); + else + { + const taggroup_t ** garray = lua_touserdata(L, up_garray); + lua_pushnumber(L, garray[tag] ? garray[tag]->count : 0); + } + return 1; +} + +void LUA_InsertTaggroupIterator +( lua_State *L, + taggroup_t *garray[], + size_t * max_elements, + void * element_array, + size_t sizeof_element, + const char * meta) +{ + lua_createtable(L, 0, 2); + lua_pushlightuserdata(L, garray); + lua_pushlightuserdata(L, max_elements); + + lua_pushvalue(L, -2); + lua_pushvalue(L, -2); + lua_pushlightuserdata(L, element_array); + lua_pushnumber(L, sizeof_element); + luaL_getmetatable(L, meta); + lua_pushcclosure(L, element_iterator, 5); + lua_setfield(L, -4, "__call"); + + lua_pushcclosure(L, lib_numTaggroupElements, 2); + lua_setfield(L, -2, "__len"); + lua_pushcclosure(L, lib_getTaggroup, 1); + lua_setfield(L, -2, "tagged"); +} + +int LUA_TagLib(lua_State *L) +{ + lua_newuserdata(L, 0); + lua_createtable(L, 0, 2); + lua_createtable(L, 0, 1); + lua_pushcfunction(L, lib_iterateTags); + lua_setfield(L, -2, "iterate"); + lua_setfield(L, -2, "__index"); + + lua_pushcfunction(L, lib_numTags); + lua_setfield(L, -2, "__len"); + lua_setmetatable(L, -2); + lua_setglobal(L, "tags"); + + return 0; +} From c2217bb42689502a9511a752b6e6aae3fc60aa07 Mon Sep 17 00:00:00 2001 From: James R Date: Fri, 4 Dec 2020 04:54:12 -0800 Subject: [PATCH 03/12] Mkae Lua taggroups indexable They are 1-indexed. --- src/lua_taglib.c | 37 ++++++++++++++++++++++++------------- 1 file changed, 24 insertions(+), 13 deletions(-) diff --git a/src/lua_taglib.c b/src/lua_taglib.c index e6c82fea3..2e2a0d277 100644 --- a/src/lua_taglib.c +++ b/src/lua_taglib.c @@ -71,16 +71,9 @@ struct element_iterator_state { static int element_iterator(lua_State *L) { struct element_iterator_state * state = lua_touserdata(L, 1); - const INT32 element = next_element(L, state->tag, state->p); - - if (element == -1) - return 0; - else - { - push_next_element(L, element); - state->p++; - return 1; - } + lua_pushnumber(L, ++state->p); + lua_gettable(L, 1); + return 1; } static int lib_iterateTags(lua_State *L) @@ -129,6 +122,21 @@ static int lib_getTaggroup(lua_State *L) return 1; } +static int lib_getTaggroupElement(lua_State *L) +{ + const size_t p = luaL_checknumber(L, 2) - 1; + const mtag_t tag = *(mtag_t *)lua_touserdata(L, 1); + const INT32 element = next_element(L, tag, p); + + if (element == -1) + return 0; + else + { + push_next_element(L, element); + return 1; + } +} + static int lib_numTaggroupElements(lua_State *L) { const mtag_t tag = *(mtag_t *)lua_touserdata(L, 1); @@ -150,7 +158,7 @@ void LUA_InsertTaggroupIterator size_t sizeof_element, const char * meta) { - lua_createtable(L, 0, 2); + lua_createtable(L, 0, 3); lua_pushlightuserdata(L, garray); lua_pushlightuserdata(L, max_elements); @@ -159,11 +167,14 @@ void LUA_InsertTaggroupIterator lua_pushlightuserdata(L, element_array); lua_pushnumber(L, sizeof_element); luaL_getmetatable(L, meta); - lua_pushcclosure(L, element_iterator, 5); - lua_setfield(L, -4, "__call"); + lua_pushcclosure(L, lib_getTaggroupElement, 5); + lua_setfield(L, -4, "__index"); lua_pushcclosure(L, lib_numTaggroupElements, 2); lua_setfield(L, -2, "__len"); + + lua_pushcfunction(L, element_iterator); + lua_setfield(L, -2, "__call"); lua_pushcclosure(L, lib_getTaggroup, 1); lua_setfield(L, -2, "tagged"); } From 314fd2783a2b22c2803b9063994a2dd48ddd98a7 Mon Sep 17 00:00:00 2001 From: James R Date: Fri, 4 Dec 2020 13:47:22 -0800 Subject: [PATCH 04/12] Lua tag lists Index and take length of tag lists like a table, 1-indexed. There are three methods which may be used on tag lists: list:iterate() - returns an iterator over the tags in the list list:has(tag) - returns a boolean whether the tag is in the list list.shares(list2) - returns whether two lists share a tag "find" is also an alias to "has". Each method may be accessed from the global taglist library too, e.g. taglist.iterate(list) Tag lists may be compared with an equality operator too. This will tell you if the two lists are composed of identical tags. Accessible from sector.taglist, line.taglist and mapthing.taglist. --- src/lua_baselib.c | 3 + src/lua_libs.h | 3 + src/lua_maplib.c | 12 ++++ src/lua_mobjlib.c | 7 +++ src/lua_script.h | 2 + src/lua_taglib.c | 156 ++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 183 insertions(+) diff --git a/src/lua_baselib.c b/src/lua_baselib.c index 4667fdbf4..1324322a2 100644 --- a/src/lua_baselib.c +++ b/src/lua_baselib.c @@ -184,12 +184,15 @@ static const struct { {META_CVAR, "consvar_t"}, {META_SECTORLINES, "sector_t.lines"}, + {META_SECTORTAGLIST, "sector_t.taglist"}, {META_SIDENUM, "line_t.sidenum"}, {META_LINEARGS, "line_t.args"}, {META_LINESTRINGARGS, "line_t.stringargs"}, + {META_LINETAGLIST, "line_t.taglist"}, {META_THINGARGS, "mapthing.args"}, {META_THINGSTRINGARGS, "mapthing.stringargs"}, + {META_THINGTAGLIST, "mapthing_t.taglist"}, #ifdef HAVE_LUA_SEGS {META_NODEBBOX, "node_t.bbox"}, {META_NODECHILDREN, "node_t.children"}, diff --git a/src/lua_libs.h b/src/lua_libs.h index aa0638683..991fae3fd 100644 --- a/src/lua_libs.h +++ b/src/lua_libs.h @@ -56,11 +56,14 @@ extern lua_State *gL; #define META_CVAR "CONSVAR_T*" #define META_SECTORLINES "SECTOR_T*LINES" +#define META_SECTORTAGLIST "SECTOR_T*TAGLIST" #define META_SIDENUM "LINE_T*SIDENUM" #define META_LINEARGS "LINE_T*ARGS" #define META_LINESTRINGARGS "LINE_T*STRINGARGS" +#define META_LINETAGLIST "LINE_T*TAGLIST" #define META_THINGARGS "MAPTHING_T*ARGS" #define META_THINGSTRINGARGS "MAPTHING_T*STRINGARGS" +#define META_THINGTAGLIST "THING_T*TAGLIST" #define META_POLYOBJVERTICES "POLYOBJ_T*VERTICES" #define META_POLYOBJLINES "POLYOBJ_T*LINES" #ifdef HAVE_LUA_SEGS diff --git a/src/lua_maplib.c b/src/lua_maplib.c index a3df28cca..25edf83d8 100644 --- a/src/lua_maplib.c +++ b/src/lua_maplib.c @@ -37,6 +37,7 @@ enum sector_e { sector_lightlevel, sector_special, sector_tag, + sector_taglist, sector_thinglist, sector_heightsec, sector_camsec, @@ -55,6 +56,7 @@ static const char *const sector_opt[] = { "lightlevel", "special", "tag", + "taglist", "thinglist", "heightsec", "camsec", @@ -89,6 +91,7 @@ enum line_e { line_flags, line_special, line_tag, + line_taglist, line_args, line_stringargs, line_sidenum, @@ -113,6 +116,7 @@ static const char *const line_opt[] = { "flags", "special", "tag", + "taglist", "args", "stringargs", "sidenum", @@ -581,6 +585,9 @@ static int sector_get(lua_State *L) case sector_tag: lua_pushinteger(L, Tag_FGet(§or->tags)); return 1; + case sector_taglist: + LUA_PushUserdata(L, §or->tags, META_SECTORTAGLIST); + return 1; case sector_thinglist: // thinglist lua_pushcfunction(L, lib_iterateSectorThinglist); LUA_PushUserdata(L, sector->thinglist, META_MOBJ); @@ -682,6 +689,8 @@ static int sector_set(lua_State *L) case sector_tag: Tag_SectorFSet((UINT32)(sector - sectors), (INT16)luaL_checkinteger(L, 3)); break; + case sector_taglist: + return LUA_ErrSetDirectly(L, "sector_t", "taglist"); } return 0; } @@ -821,6 +830,9 @@ static int line_get(lua_State *L) case line_tag: lua_pushinteger(L, Tag_FGet(&line->tags)); return 1; + case line_taglist: + LUA_PushUserdata(L, &line->tags, META_LINETAGLIST); + return 1; case line_args: LUA_PushUserdata(L, line->args, META_LINEARGS); return 1; diff --git a/src/lua_mobjlib.c b/src/lua_mobjlib.c index 134f104ee..8d205780d 100644 --- a/src/lua_mobjlib.c +++ b/src/lua_mobjlib.c @@ -902,6 +902,11 @@ static int mapthing_get(lua_State *L) number = mt->extrainfo; else if(fastcmp(field,"tag")) number = Tag_FGet(&mt->tags); + else if(fastcmp(field,"taglist")) + { + LUA_PushUserdata(L, &mt->tags, META_THINGTAGLIST); + return 1; + } else if(fastcmp(field,"args")) { LUA_PushUserdata(L, mt->args, META_THINGARGS); @@ -964,6 +969,8 @@ static int mapthing_set(lua_State *L) } else if (fastcmp(field,"tag")) Tag_FSet(&mt->tags, (INT16)luaL_checkinteger(L, 3)); + else if (fastcmp(field,"taglist")) + return LUA_ErrSetDirectly(L, "mapthing_t", "taglist"); else if(fastcmp(field,"mobj")) mt->mobj = *((mobj_t **)luaL_checkudata(L, 3, META_MOBJ)); else diff --git a/src/lua_script.h b/src/lua_script.h index 2dc34446a..77fbb7c1d 100644 --- a/src/lua_script.h +++ b/src/lua_script.h @@ -102,6 +102,8 @@ void COM_Lua_f(void); #define LUA_ErrInvalid(L, type) luaL_error(L, "accessed " type " doesn't exist anymore, please check 'valid' before using " type "."); +#define LUA_ErrSetDirectly(L, type, field) luaL_error(L, type " field " LUA_QL(field) " cannot be set directly.") + // Deprecation warnings // Shows once upon use. Then doesn't show again. #define LUA_Deprecated(L,this_func,use_instead)\ diff --git a/src/lua_taglib.c b/src/lua_taglib.c index 2e2a0d277..73f033312 100644 --- a/src/lua_taglib.c +++ b/src/lua_taglib.c @@ -150,6 +150,137 @@ static int lib_numTaggroupElements(lua_State *L) return 1; } +static void push_taglist(lua_State *L, int idx) +{ + lua_getmetatable(L, idx); + lua_pushliteral(L, "taglist"); + lua_rawget(L, -2); + lua_remove(L, -2); +} + +static int has_valid_field(lua_State *L) +{ + int equal; + lua_pushliteral(L, "valid"); + equal = lua_rawequal(L, 2, -1); + lua_pop(L, 1); + return equal; +} + +static taglist_t * valid_taglist(lua_State *L, int idx, boolean getting) +{ + taglist_t *list = *(taglist_t **)lua_touserdata(L, idx); + + if (list == NULL) + { + if (getting && has_valid_field(L)) + lua_pushboolean(L, 0); + else + LUA_ErrInvalid(L, "taglist_t");/* doesn't actually return */ + return NULL; + } + else + return list; +} + +static taglist_t * check_taglist(lua_State *L, int idx) +{ + luaL_checktype(L, idx, LUA_TUSERDATA); + push_taglist(L, idx); + luaL_argcheck(L, lua_toboolean(L, -1), idx, "must be a tag list"); + return valid_taglist(L, idx, false); +} + +static int taglist_get(lua_State *L) +{ + const taglist_t *list = valid_taglist(L, 1, true); + + if (list == NULL)/* valid check */ + return 1; + + if (lua_isnumber(L, 2)) + { + const size_t i = lua_tonumber(L, 2); + + if (list && i <= list->count) + { + lua_pushnumber(L, list->tags[i - 1]); + return 1; + } + else + return 0; + } + else if (has_valid_field(L)) + { + lua_pushboolean(L, 1); + return 1; + } + else + { + push_taglist(L, 1); + lua_replace(L, 1); + lua_rawget(L, 1); + return 1; + } +} + +static int taglist_len(lua_State *L) +{ + const taglist_t *list = valid_taglist(L, 1, false); + lua_pushnumber(L, list->count); + return 1; +} + +static int taglist_equal(lua_State *L) +{ + const taglist_t *lhs = check_taglist(L, 1); + const taglist_t *rhs = check_taglist(L, 2); + lua_pushboolean(L, Tag_Compare(lhs, rhs)); + return 1; +} + +static int taglist_iterator(lua_State *L) +{ + const taglist_t *list = valid_taglist(L, 1, false); + const size_t i = 1 + lua_tonumber(L, lua_upvalueindex(1)); + if (i <= list->count) + { + lua_pushnumber(L, list->tags[i - 1]); + /* watch me exploit an upvalue as a control because + I want to use the control as the value */ + lua_pushnumber(L, i); + lua_replace(L, lua_upvalueindex(1)); + return 1; + } + else + return 0; +} + +static int taglist_iterate(lua_State *L) +{ + check_taglist(L, 1); + lua_pushnumber(L, 0); + lua_pushcclosure(L, taglist_iterator, 1); + lua_pushvalue(L, 1); + return 2; +} + +static int taglist_find(lua_State *L) +{ + const taglist_t *list = check_taglist(L, 1); + const mtag_t tag = luaL_checknumber(L, 2); + lua_pushboolean(L, Tag_Find(list, tag)); + return 1; +} + +static int taglist_shares(lua_State *L) +{ + const taglist_t *lhs = check_taglist(L, 1); + const taglist_t *rhs = check_taglist(L, 2); + lua_pushboolean(L, Tag_Share(lhs, rhs)); + return 1; +} + void LUA_InsertTaggroupIterator ( lua_State *L, taggroup_t *garray[], @@ -179,6 +310,13 @@ void LUA_InsertTaggroupIterator lua_setfield(L, -2, "tagged"); } +static luaL_Reg taglist_lib[] = { + {"iterate", taglist_iterate}, + {"find", taglist_find}, + {"shares", taglist_shares}, + {0} +}; + int LUA_TagLib(lua_State *L) { lua_newuserdata(L, 0); @@ -193,5 +331,23 @@ int LUA_TagLib(lua_State *L) lua_setmetatable(L, -2); lua_setglobal(L, "tags"); + luaL_newmetatable(L, META_THINGTAGLIST); + luaL_register(L, "taglist", taglist_lib); + lua_getfield(L, -1, "find"); + lua_setfield(L, -2, "has"); + lua_setfield(L, -2, "taglist"); + + lua_pushcfunction(L, taglist_get); + lua_setfield(L, -2, "__index"); + + lua_pushcfunction(L, taglist_len); + lua_setfield(L, -2, "__len"); + + lua_pushcfunction(L, taglist_equal); + lua_setfield(L, -2, "__eq"); + lua_pushvalue(L, -1); + lua_setfield(L, LUA_REGISTRYINDEX, META_LINETAGLIST); + lua_setfield(L, LUA_REGISTRYINDEX, META_SECTORTAGLIST); + return 0; } From 828d7e71ce62d1dae4ddcd7fe9319229005eac75 Mon Sep 17 00:00:00 2001 From: James R Date: Sat, 5 Dec 2020 00:36:54 -0800 Subject: [PATCH 05/12] Fix uninitialized last element when using Taggroup_Remove --- src/taglist.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/taglist.c b/src/taglist.c index 658605734..7dbad65c4 100644 --- a/src/taglist.c +++ b/src/taglist.c @@ -188,7 +188,7 @@ void Taggroup_Remove (taggroup_t *garray[], const mtag_t tag, size_t id) { taggroup_t *group; size_t rempos; - size_t newcount; + size_t oldcount; if (tag == MTAG_GLOBAL) return; @@ -204,7 +204,7 @@ void Taggroup_Remove (taggroup_t *garray[], const mtag_t tag, size_t id) unset_bit_array(tags_available, tag); // Strip away taggroup if no elements left. - if (!(newcount = --group->count)) + if (!(oldcount = group->count--)) { Z_Free(group->elements); Z_Free(group); @@ -212,19 +212,18 @@ void Taggroup_Remove (taggroup_t *garray[], const mtag_t tag, size_t id) } else { - size_t *newelements = Z_Malloc(newcount * sizeof(size_t), PU_LEVEL, NULL); + size_t *newelements = Z_Malloc(group->count * sizeof(size_t), PU_LEVEL, NULL); size_t i; // Copy the previous entries save for the one to remove. for (i = 0; i < rempos; i++) newelements[i] = group->elements[i]; - for (i = rempos + 1; i < group->count; i++) + for (i = rempos + 1; i < oldcount; i++) newelements[i - 1] = group->elements[i]; Z_Free(group->elements); group->elements = newelements; - group->count = newcount; } } From ae663e724774007e0242fc509c87402a3ff7b7c4 Mon Sep 17 00:00:00 2001 From: James R Date: Sat, 5 Dec 2020 00:46:51 -0800 Subject: [PATCH 06/12] Don't realloc twice when adding to the taggroup --- src/taglist.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/taglist.c b/src/taglist.c index 7dbad65c4..cfd9cbb9c 100644 --- a/src/taglist.c +++ b/src/taglist.c @@ -170,16 +170,15 @@ void Taggroup_Add (taggroup_t *garray[], const mtag_t tag, size_t id) for (i = 0; i < group->count; i++) if (group->elements[i] > id) break; - - group->elements = Z_Realloc(group->elements, (group->count + 1) * sizeof(size_t), PU_LEVEL, NULL); - - // Offset existing elements to make room for the new one. - if (i < group->count) - memmove(&group->elements[i + 1], &group->elements[i], group->count - i); } + group->elements = Z_Realloc(group->elements, (group->count + 1) * sizeof(size_t), PU_LEVEL, NULL); + + // Offset existing elements to make room for the new one. + if (i < group->count) + memmove(&group->elements[i + 1], &group->elements[i], group->count - i); + group->count++; - group->elements = Z_Realloc(group->elements, group->count * sizeof(size_t), PU_LEVEL, NULL); group->elements[i] = id; } From e5a3e6a845e105566dd6def263cd2d7326155d42 Mon Sep 17 00:00:00 2001 From: James R Date: Sat, 5 Dec 2020 01:14:52 -0800 Subject: [PATCH 07/12] Fix removing a tag unsetting the bit array even if more elements with that tag exist --- src/lua_taglib.c | 2 +- src/taglist.c | 28 +++++++++++++++++++++++----- src/taglist.h | 1 + 3 files changed, 25 insertions(+), 6 deletions(-) diff --git a/src/lua_taglib.c b/src/lua_taglib.c index 73f033312..07646af87 100644 --- a/src/lua_taglib.c +++ b/src/lua_taglib.c @@ -145,7 +145,7 @@ static int lib_numTaggroupElements(lua_State *L) else { const taggroup_t ** garray = lua_touserdata(L, up_garray); - lua_pushnumber(L, garray[tag] ? garray[tag]->count : 0); + lua_pushnumber(L, Taggroup_Count(garray[tag])); } return 1; } diff --git a/src/taglist.c b/src/taglist.c index cfd9cbb9c..a759f4d02 100644 --- a/src/taglist.c +++ b/src/taglist.c @@ -110,6 +110,12 @@ size_t Taggroup_Find (const taggroup_t *group, const size_t id) return -1; } +/// group->count, but also checks for NULL +size_t Taggroup_Count (const taggroup_t *group) +{ + return group ? group->count : 0; +} + /// Iterate thru elements in a global taggroup. INT32 Taggroup_Iterate ( taggroup_t *garray[], @@ -153,9 +159,10 @@ void Taggroup_Add (taggroup_t *garray[], const mtag_t tag, size_t id) return; if (! in_bit_array(tags_available, tag)) + { num_tags++; - - set_bit_array(tags_available, tag); + set_bit_array(tags_available, tag); + } // Create group if empty. if (!group) @@ -182,6 +189,16 @@ void Taggroup_Add (taggroup_t *garray[], const mtag_t tag, size_t id) group->elements[i] = id; } +static size_t total_elements_with_tag (const mtag_t tag) +{ + return + ( + Taggroup_Count(tags_sectors[tag]) + + Taggroup_Count(tags_lines[tag]) + + Taggroup_Count(tags_mapthings[tag]) + ); +} + /// Remove an element from a global taggroup. void Taggroup_Remove (taggroup_t *garray[], const mtag_t tag, size_t id) { @@ -197,10 +214,11 @@ void Taggroup_Remove (taggroup_t *garray[], const mtag_t tag, size_t id) if ((rempos = Taggroup_Find(group, id)) == (size_t)-1) return; - if (in_bit_array(tags_available, tag)) + if (group->count == 1 && total_elements_with_tag(tag) == 1) + { num_tags--; - - unset_bit_array(tags_available, tag); + unset_bit_array(tags_available, tag); + } // Strip away taggroup if no elements left. if (!(oldcount = group->count--)) diff --git a/src/taglist.h b/src/taglist.h index e5db08806..a0529ab6b 100644 --- a/src/taglist.h +++ b/src/taglist.h @@ -54,6 +54,7 @@ extern taggroup_t* tags_mapthings[]; void Taggroup_Add (taggroup_t *garray[], const mtag_t tag, size_t id); void Taggroup_Remove (taggroup_t *garray[], const mtag_t tag, size_t id); size_t Taggroup_Find (const taggroup_t *group, const size_t id); +size_t Taggroup_Count (const taggroup_t *group); INT32 Taggroup_Iterate ( taggroup_t *garray[], From 8dd964e3a726a30ad50e7cfc3a5d0356351f24bf Mon Sep 17 00:00:00 2001 From: James R Date: Sat, 5 Dec 2020 02:02:06 -0800 Subject: [PATCH 08/12] Lua: taglist.add and taglist.remove for sector tag lists --- src/lua_baselib.c | 6 ++- src/lua_libs.h | 10 +++-- src/lua_maplib.c | 2 +- src/lua_mobjlib.c | 2 +- src/lua_taglib.c | 110 +++++++++++++++++++++++++++++++++++++++------- 5 files changed, 108 insertions(+), 22 deletions(-) diff --git a/src/lua_baselib.c b/src/lua_baselib.c index 1324322a2..59c1d411b 100644 --- a/src/lua_baselib.c +++ b/src/lua_baselib.c @@ -155,6 +155,8 @@ static const struct { {META_PIVOTLIST, "spriteframepivot_t[]"}, {META_FRAMEPIVOT, "spriteframepivot_t"}, + {META_TAGLIST, "taglist"}, + {META_MOBJ, "mobj_t"}, {META_MAPTHING, "mapthing_t"}, @@ -184,15 +186,15 @@ static const struct { {META_CVAR, "consvar_t"}, {META_SECTORLINES, "sector_t.lines"}, +#ifdef MUTABLE_TAGS {META_SECTORTAGLIST, "sector_t.taglist"}, +#endif {META_SIDENUM, "line_t.sidenum"}, {META_LINEARGS, "line_t.args"}, {META_LINESTRINGARGS, "line_t.stringargs"}, - {META_LINETAGLIST, "line_t.taglist"}, {META_THINGARGS, "mapthing.args"}, {META_THINGSTRINGARGS, "mapthing.stringargs"}, - {META_THINGTAGLIST, "mapthing_t.taglist"}, #ifdef HAVE_LUA_SEGS {META_NODEBBOX, "node_t.bbox"}, {META_NODECHILDREN, "node_t.children"}, diff --git a/src/lua_libs.h b/src/lua_libs.h index 991fae3fd..e7f4ae253 100644 --- a/src/lua_libs.h +++ b/src/lua_libs.h @@ -12,6 +12,8 @@ extern lua_State *gL; +#define MUTABLE_TAGS + #define LREG_VALID "VALID_USERDATA" #define LREG_EXTVARS "LUA_VARS" #define LREG_STATEACTION "STATE_ACTION" @@ -27,6 +29,8 @@ extern lua_State *gL; #define META_PIVOTLIST "SPRITEFRAMEPIVOT_T[]" #define META_FRAMEPIVOT "SPRITEFRAMEPIVOT_T*" +#define META_TAGLIST "TAGLIST" + #define META_MOBJ "MOBJ_T*" #define META_MAPTHING "MAPTHING_T*" @@ -56,14 +60,14 @@ extern lua_State *gL; #define META_CVAR "CONSVAR_T*" #define META_SECTORLINES "SECTOR_T*LINES" -#define META_SECTORTAGLIST "SECTOR_T*TAGLIST" +#ifdef MUTABLE_TAGS +#define META_SECTORTAGLIST "sector_t.taglist" +#endif #define META_SIDENUM "LINE_T*SIDENUM" #define META_LINEARGS "LINE_T*ARGS" #define META_LINESTRINGARGS "LINE_T*STRINGARGS" -#define META_LINETAGLIST "LINE_T*TAGLIST" #define META_THINGARGS "MAPTHING_T*ARGS" #define META_THINGSTRINGARGS "MAPTHING_T*STRINGARGS" -#define META_THINGTAGLIST "THING_T*TAGLIST" #define META_POLYOBJVERTICES "POLYOBJ_T*VERTICES" #define META_POLYOBJLINES "POLYOBJ_T*LINES" #ifdef HAVE_LUA_SEGS diff --git a/src/lua_maplib.c b/src/lua_maplib.c index 25edf83d8..3520cdbda 100644 --- a/src/lua_maplib.c +++ b/src/lua_maplib.c @@ -831,7 +831,7 @@ static int line_get(lua_State *L) lua_pushinteger(L, Tag_FGet(&line->tags)); return 1; case line_taglist: - LUA_PushUserdata(L, &line->tags, META_LINETAGLIST); + LUA_PushUserdata(L, &line->tags, META_TAGLIST); return 1; case line_args: LUA_PushUserdata(L, line->args, META_LINEARGS); diff --git a/src/lua_mobjlib.c b/src/lua_mobjlib.c index 8d205780d..65adceb15 100644 --- a/src/lua_mobjlib.c +++ b/src/lua_mobjlib.c @@ -904,7 +904,7 @@ static int mapthing_get(lua_State *L) number = Tag_FGet(&mt->tags); else if(fastcmp(field,"taglist")) { - LUA_PushUserdata(L, &mt->tags, META_THINGTAGLIST); + LUA_PushUserdata(L, &mt->tags, META_TAGLIST); return 1; } else if(fastcmp(field,"args")) diff --git a/src/lua_taglib.c b/src/lua_taglib.c index 07646af87..7994b6625 100644 --- a/src/lua_taglib.c +++ b/src/lua_taglib.c @@ -17,6 +17,10 @@ #include "lua_script.h" #include "lua_libs.h" +#ifdef MUTABLE_TAGS +#include "z_zone.h" +#endif + static int tag_iterator(lua_State *L) { INT32 tag = lua_isnil(L, 2) ? -1 : lua_tonumber(L, 2); @@ -281,6 +285,63 @@ static int taglist_shares(lua_State *L) return 1; } +/* only sector tags are mutable... */ + +#ifdef MUTABLE_TAGS +static size_t sector_of_taglist(taglist_t *list) +{ + return (sector_t *)((char *)list - offsetof (sector_t, tags)) - sectors; +} + +static int this_taglist(lua_State *L) +{ + lua_settop(L, 1); + return 1; +} + +static int taglist_add(lua_State *L) +{ + taglist_t *list = *(taglist_t **)luaL_checkudata(L, 1, META_SECTORTAGLIST); + const mtag_t tag = luaL_checknumber(L, 2); + + if (! Tag_Find(list, tag)) + { + Taggroup_Add(tags_sectors, tag, sector_of_taglist(list)); + Tag_Add(list, tag); + } + + return this_taglist(L); +} + +static int taglist_remove(lua_State *L) +{ + taglist_t *list = *(taglist_t **)luaL_checkudata(L, 1, META_SECTORTAGLIST); + const mtag_t tag = luaL_checknumber(L, 2); + + size_t i; + + for (i = 0; i < list->count; ++i) + { + if (list->tags[i] == tag) + { + if (list->count > 1) + { + memmove(&list->tags[i], &list->tags[i + 1], + (list->count - 1 - i) * sizeof (mtag_t)); + list->tags = Z_Realloc(list->tags, + (--list->count) * sizeof (mtag_t), PU_LEVEL, NULL); + Taggroup_Remove(tags_sectors, tag, sector_of_taglist(list)); + } + else/* reset to default tag */ + Tag_SectorFSet(sector_of_taglist(list), 0); + break; + } + } + + return this_taglist(L); +} +#endif/*MUTABLE_TAGS*/ + void LUA_InsertTaggroupIterator ( lua_State *L, taggroup_t *garray[], @@ -314,9 +375,38 @@ static luaL_Reg taglist_lib[] = { {"iterate", taglist_iterate}, {"find", taglist_find}, {"shares", taglist_shares}, +#ifdef MUTABLE_TAGS + {"add", taglist_add}, + {"remove", taglist_remove}, +#endif {0} }; +static void open_taglist(lua_State *L) +{ + luaL_register(L, "taglist", taglist_lib); + + lua_getfield(L, -1, "find"); + lua_setfield(L, -2, "has"); +} + +static void set_taglist_metatable(lua_State *L, const char *meta) +{ + lua_createtable(L, 0, 4); + lua_getglobal(L, "taglist"); + lua_setfield(L, -2, "taglist"); + + lua_pushcfunction(L, taglist_get); + lua_setfield(L, -2, "__index"); + + lua_pushcfunction(L, taglist_len); + lua_setfield(L, -2, "__len"); + + lua_pushcfunction(L, taglist_equal); + lua_setfield(L, -2, "__eq"); + lua_setfield(L, LUA_REGISTRYINDEX, meta); +} + int LUA_TagLib(lua_State *L) { lua_newuserdata(L, 0); @@ -331,23 +421,13 @@ int LUA_TagLib(lua_State *L) lua_setmetatable(L, -2); lua_setglobal(L, "tags"); - luaL_newmetatable(L, META_THINGTAGLIST); - luaL_register(L, "taglist", taglist_lib); - lua_getfield(L, -1, "find"); - lua_setfield(L, -2, "has"); - lua_setfield(L, -2, "taglist"); + open_taglist(L); - lua_pushcfunction(L, taglist_get); - lua_setfield(L, -2, "__index"); + set_taglist_metatable(L, META_TAGLIST); - lua_pushcfunction(L, taglist_len); - lua_setfield(L, -2, "__len"); - - lua_pushcfunction(L, taglist_equal); - lua_setfield(L, -2, "__eq"); - lua_pushvalue(L, -1); - lua_setfield(L, LUA_REGISTRYINDEX, META_LINETAGLIST); - lua_setfield(L, LUA_REGISTRYINDEX, META_SECTORTAGLIST); +#ifdef MUTABLE_TAGS + set_taglist_metatable(L, META_SECTORTAGLIST); +#endif return 0; } From 5d1040c92441a2634410caf2da673eea335e92be Mon Sep 17 00:00:00 2001 From: James R Date: Sat, 5 Dec 2020 02:08:00 -0800 Subject: [PATCH 09/12] Reset taggroup iterator on successive calls --- src/lua_taglib.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/lua_taglib.c b/src/lua_taglib.c index 7994b6625..cfaf84872 100644 --- a/src/lua_taglib.c +++ b/src/lua_taglib.c @@ -75,6 +75,8 @@ struct element_iterator_state { static int element_iterator(lua_State *L) { struct element_iterator_state * state = lua_touserdata(L, 1); + if (lua_isnoneornil(L, 3)) + state->p = 0; lua_pushnumber(L, ++state->p); lua_gettable(L, 1); return 1; From 0b0f2e1e35aa822566986bcd6dc1526f3828f25e Mon Sep 17 00:00:00 2001 From: James R Date: Sat, 5 Dec 2020 02:26:00 -0800 Subject: [PATCH 10/12] Invalidate taglist userdata --- src/lua_script.c | 5 +++++ src/lua_taglib.c | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/lua_script.c b/src/lua_script.c index ee60a41c6..bc88928f3 100644 --- a/src/lua_script.c +++ b/src/lua_script.c @@ -846,6 +846,7 @@ void LUA_InvalidateLevel(void) { LUA_InvalidateUserdata(§ors[i]); LUA_InvalidateUserdata(§ors[i].lines); + LUA_InvalidateUserdata(§ors[i].tags); if (sectors[i].ffloors) { for (rover = sectors[i].ffloors; rover; rover = rover->next) @@ -855,6 +856,7 @@ void LUA_InvalidateLevel(void) for (i = 0; i < numlines; i++) { LUA_InvalidateUserdata(&lines[i]); + LUA_InvalidateUserdata(&lines[i].tags); LUA_InvalidateUserdata(lines[i].sidenum); } for (i = 0; i < numsides; i++) @@ -886,7 +888,10 @@ void LUA_InvalidateMapthings(void) return; for (i = 0; i < nummapthings; i++) + { LUA_InvalidateUserdata(&mapthings[i]); + LUA_InvalidateUserdata(&mapthings[i].tags); + } } void LUA_InvalidatePlayer(player_t *player) diff --git a/src/lua_taglib.c b/src/lua_taglib.c index cfaf84872..284b171a3 100644 --- a/src/lua_taglib.c +++ b/src/lua_taglib.c @@ -182,7 +182,7 @@ static taglist_t * valid_taglist(lua_State *L, int idx, boolean getting) if (getting && has_valid_field(L)) lua_pushboolean(L, 0); else - LUA_ErrInvalid(L, "taglist_t");/* doesn't actually return */ + LUA_ErrInvalid(L, "taglist");/* doesn't actually return */ return NULL; } else From 00dff6d2836890bf51803530626047bf40f7b2fa Mon Sep 17 00:00:00 2001 From: James R Date: Mon, 14 Dec 2020 05:53:57 -0800 Subject: [PATCH 11/12] Push "valid" only once --- src/lua_taglib.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/lua_taglib.c b/src/lua_taglib.c index 284b171a3..12d1a3c05 100644 --- a/src/lua_taglib.c +++ b/src/lua_taglib.c @@ -167,7 +167,7 @@ static void push_taglist(lua_State *L, int idx) static int has_valid_field(lua_State *L) { int equal; - lua_pushliteral(L, "valid"); + lua_rawgeti(L, LUA_ENVIRONINDEX, 1); equal = lua_rawequal(L, 2, -1); lua_pop(L, 1); return equal; @@ -392,6 +392,9 @@ static void open_taglist(lua_State *L) lua_setfield(L, -2, "has"); } +#define new_literal(L, s) \ + (lua_pushliteral(L, s), luaL_ref(L, -2)) + static void set_taglist_metatable(lua_State *L, const char *meta) { lua_createtable(L, 0, 4); @@ -399,6 +402,9 @@ static void set_taglist_metatable(lua_State *L, const char *meta) lua_setfield(L, -2, "taglist"); lua_pushcfunction(L, taglist_get); + lua_createtable(L, 0, 1); + new_literal(L, "valid"); + lua_setfenv(L, -2); lua_setfield(L, -2, "__index"); lua_pushcfunction(L, taglist_len); From d0f3a6d737137d2e68c4fb1506f8d7809f8801ae Mon Sep 17 00:00:00 2001 From: James R Date: Mon, 14 Dec 2020 08:08:01 -0800 Subject: [PATCH 12/12] Better check for tag list userdata --- src/lua_taglib.c | 52 +++++++++++++++++++++++++++++------------------- 1 file changed, 31 insertions(+), 21 deletions(-) diff --git a/src/lua_taglib.c b/src/lua_taglib.c index 12d1a3c05..c9f320fe8 100644 --- a/src/lua_taglib.c +++ b/src/lua_taglib.c @@ -156,13 +156,9 @@ static int lib_numTaggroupElements(lua_State *L) return 1; } -static void push_taglist(lua_State *L, int idx) -{ - lua_getmetatable(L, idx); - lua_pushliteral(L, "taglist"); - lua_rawget(L, -2); - lua_remove(L, -2); -} +#ifdef MUTABLE_TAGS +static int meta_ref[2]; +#endif static int has_valid_field(lua_State *L) { @@ -191,10 +187,19 @@ static taglist_t * valid_taglist(lua_State *L, int idx, boolean getting) static taglist_t * check_taglist(lua_State *L, int idx) { - luaL_checktype(L, idx, LUA_TUSERDATA); - push_taglist(L, idx); - luaL_argcheck(L, lua_toboolean(L, -1), idx, "must be a tag list"); - return valid_taglist(L, idx, false); + if (lua_isuserdata(L, idx) && lua_getmetatable(L, idx)) + { + lua_getref(L, meta_ref[0]); + lua_getref(L, meta_ref[1]); + + if (lua_rawequal(L, -3, -2) || lua_rawequal(L, -3, -1)) + { + lua_pop(L, 3); + return valid_taglist(L, idx, false); + } + } + + return luaL_argerror(L, idx, "must be a tag list"), NULL; } static int taglist_get(lua_State *L) @@ -223,7 +228,7 @@ static int taglist_get(lua_State *L) } else { - push_taglist(L, 1); + lua_getmetatable(L, 1); lua_replace(L, 1); lua_rawget(L, 1); return 1; @@ -395,12 +400,14 @@ static void open_taglist(lua_State *L) #define new_literal(L, s) \ (lua_pushliteral(L, s), luaL_ref(L, -2)) -static void set_taglist_metatable(lua_State *L, const char *meta) +#ifdef MUTABLE_TAGS +static int +#else +static void +#endif +set_taglist_metatable(lua_State *L, const char *meta) { - lua_createtable(L, 0, 4); - lua_getglobal(L, "taglist"); - lua_setfield(L, -2, "taglist"); - + luaL_newmetatable(L, meta); lua_pushcfunction(L, taglist_get); lua_createtable(L, 0, 1); new_literal(L, "valid"); @@ -412,7 +419,9 @@ static void set_taglist_metatable(lua_State *L, const char *meta) lua_pushcfunction(L, taglist_equal); lua_setfield(L, -2, "__eq"); - lua_setfield(L, LUA_REGISTRYINDEX, meta); +#ifdef MUTABLE_TAGS + return luaL_ref(L, LUA_REGISTRYINDEX); +#endif } int LUA_TagLib(lua_State *L) @@ -431,10 +440,11 @@ int LUA_TagLib(lua_State *L) open_taglist(L); - set_taglist_metatable(L, META_TAGLIST); - #ifdef MUTABLE_TAGS - set_taglist_metatable(L, META_SECTORTAGLIST); + meta_ref[0] = set_taglist_metatable(L, META_TAGLIST); + meta_ref[1] = set_taglist_metatable(L, META_SECTORTAGLIST); +#else + set_taglist_metatable(L, META_TAGLIST); #endif return 0;