Skip to content
Snippets Groups Projects
Commit c44a935b authored by Yukita Mayako's avatar Yukita Mayako
Browse files

Rewrote `thinkers.iterate` to handle invalid pointers elegantly.

parent 11d57fba
No related branches found
No related tags found
No related merge requests found
...@@ -16,67 +16,87 @@ ...@@ -16,67 +16,87 @@
#include "lua_script.h" #include "lua_script.h"
#include "lua_libs.h" #include "lua_libs.h"
#define META_ITERATIONSTATE "iteration state"
static const char *const iter_opt[] = { static const char *const iter_opt[] = {
"all", "all",
"mobj", "mobj",
NULL}; NULL};
static int lib_iterateThinkers(lua_State *L) static const actionf_p1 iter_funcs[] = {
{ NULL,
int state = luaL_checkoption(L, 1, "mobj", iter_opt); (actionf_p1)P_MobjThinker
};
thinker_t *th = NULL; struct iterationState {
actionf_p1 searchFunc; actionf_p1 filter;
const char *searchMeta; int next;
};
lua_settop(L, 2);
lua_remove(L, 1); // remove state now.
switch(state) static int iterationState_gc(lua_State *L)
{ {
case 0: struct iterationState *it = luaL_checkudata(L, -1, META_ITERATIONSTATE);
searchFunc = NULL; if (it->next != LUA_REFNIL)
searchMeta = NULL; {
break; luaL_unref(L, LUA_REGISTRYINDEX, it->next);
case 1: it->next = LUA_REFNIL;
default: }
searchFunc = (actionf_p1)P_MobjThinker; return 0;
searchMeta = META_MOBJ;
break;
} }
if (!lua_isnil(L, 1)) { #define push_thinker(th) {\
if (lua_islightuserdata(L, 1)) if ((th)->function.acp1 == (actionf_p1)P_MobjThinker) \
th = (thinker_t *)lua_touserdata(L, 1); LUA_PushUserdata(L, (th), META_MOBJ); \
else if (searchMeta) else \
th = *((thinker_t **)luaL_checkudata(L, 1, searchMeta)); lua_pushlightuserdata(L, (th)); \
else }
th = *((thinker_t **)lua_touserdata(L, 1));
} else
th = &thinkercap;
if (!th) // something got our userdata invalidated! static int lib_iterateThinkers(lua_State *L)
return 0; {
thinker_t *th = NULL, *next = NULL;
struct iterationState *it = luaL_checkudata(L, 1, META_ITERATIONSTATE);
lua_settop(L, 2);
if (searchFunc == NULL) if (lua_isnil(L, 2))
th = &thinkercap;
else if (lua_isuserdata(L, 2))
{
if (lua_islightuserdata(L, 2))
th = lua_touserdata(L, 2);
else
{ {
if ((th = th->next) != &thinkercap) th = *(thinker_t **)lua_touserdata(L, -1);
if (!th)
{ {
if (th->function.acp1 == (actionf_p1)P_MobjThinker) if (it->next == LUA_REFNIL)
LUA_PushUserdata(L, th, META_MOBJ); return 0;
lua_rawgeti(L, LUA_REGISTRYINDEX, it->next);
if (lua_islightuserdata(L, -1))
next = lua_touserdata(L, -1);
else else
lua_pushlightuserdata(L, th); next = *(thinker_t **)lua_touserdata(L, -1);
return 1; }
} }
return 0;
} }
for (th = th->next; th != &thinkercap; th = th->next) luaL_unref(L, LUA_REGISTRYINDEX, it->next);
{ it->next = LUA_REFNIL;
if (th->function.acp1 != searchFunc)
continue;
LUA_PushUserdata(L, th, searchMeta); if (th && !next)
next = th->next;
if (!next)
return luaL_error(L, "next thinker invalidated during iteration");
for (; next != &thinkercap; next = next->next)
if (!it->filter || next->function.acp1 == it->filter)
{
push_thinker(next);
if (next->next != &thinkercap)
{
push_thinker(next->next);
it->next = luaL_ref(L, LUA_REGISTRYINDEX);
}
return 1; return 1;
} }
return 0; return 0;
...@@ -84,16 +104,30 @@ static int lib_iterateThinkers(lua_State *L) ...@@ -84,16 +104,30 @@ static int lib_iterateThinkers(lua_State *L)
static int lib_startIterate(lua_State *L) static int lib_startIterate(lua_State *L)
{ {
luaL_checkoption(L, 1, iter_opt[0], iter_opt); struct iterationState *it;
lua_pushcfunction(L, lib_iterateThinkers);
lua_pushvalue(L, 1); lua_pushvalue(L, lua_upvalueindex(1));
it = lua_newuserdata(L, sizeof(struct iterationState));
luaL_getmetatable(L, META_ITERATIONSTATE);
lua_setmetatable(L, -2);
it->filter = iter_funcs[luaL_checkoption(L, 1, "mobj", iter_opt)];
it->next = LUA_REFNIL;
return 2; return 2;
} }
#undef push_thinker
int LUA_ThinkerLib(lua_State *L) int LUA_ThinkerLib(lua_State *L)
{ {
luaL_newmetatable(L, META_ITERATIONSTATE);
lua_pushcfunction(L, iterationState_gc);
lua_setfield(L, -2, "__gc");
lua_pop(L, 1);
lua_createtable(L, 0, 1); lua_createtable(L, 0, 1);
lua_pushcfunction(L, lib_startIterate); lua_pushcfunction(L, lib_iterateThinkers);
lua_pushcclosure(L, lib_startIterate, 1);
lua_setfield(L, -2, "iterate"); lua_setfield(L, -2, "iterate");
lua_setglobal(L, "thinkers"); lua_setglobal(L, "thinkers");
return 0; return 0;
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment