From 793bd850f658a9c160c80d6ee1ad29199d3b3f4c Mon Sep 17 00:00:00 2001 From: veclavtalica Date: Sat, 15 Feb 2025 22:19:14 +0300 Subject: [PATCH] /apps/twnlua: ctx.udata preservation in reload, no export in .so, ignore /data/scripts/twnapi.lua --- apps/twnlua/.gitignore | 1 + apps/twnlua/bindgen.py | 3 -- apps/twnlua/data/scripts/game.lua | 37 ++++++++++++++--- apps/twnlua/data/twn.toml | 1 + apps/twnlua/game.c | 66 +++++++++++++++++++++++++++++-- apps/twnlua/minilua.h | 3 +- 6 files changed, 99 insertions(+), 12 deletions(-) diff --git a/apps/twnlua/.gitignore b/apps/twnlua/.gitignore index 1e9650c..1acdc1e 100644 --- a/apps/twnlua/.gitignore +++ b/apps/twnlua/.gitignore @@ -1 +1,2 @@ luabind.c +data/scripts/twnapi.lua diff --git a/apps/twnlua/bindgen.py b/apps/twnlua/bindgen.py index 4c5b16b..88edc57 100755 --- a/apps/twnlua/bindgen.py +++ b/apps/twnlua/bindgen.py @@ -151,15 +151,12 @@ for typename, typedesc in used_converters.items(): print('\n'.join(storages)) -print("extern void bindgen_init(void);\n") -print("void bindgen_init(void) {\n" + '\n'.join(initializers) + "\n}\n") print('\n'.join(converters)) print('\n'.join(bindings)) loader = "extern void bindgen_load_%s(lua_State *L);\n" % api["name"] loader += "void bindgen_load_%s(lua_State *L) {\n" % api["name"] -loader += " bindgen_init();\n" for procedure, procedure_desc in api["procedures"].items(): loader += " lua_pushcfunction(L, binding_%s);\n" % procedure loader += " lua_setglobal(L, \"%s\");\n" % procedure diff --git a/apps/twnlua/data/scripts/game.lua b/apps/twnlua/data/scripts/game.lua index 4b8e8a3..7866afe 100644 --- a/apps/twnlua/data/scripts/game.lua +++ b/apps/twnlua/data/scripts/game.lua @@ -4,16 +4,39 @@ offset = { x = 0, y = 0 } angle = 0 function game_tick() + if ctx.udata == nil then + ctx.udata = { + frame_count = 0, + nest = { + frame_count = 0, + }, + arr = { [0] = 0 }, + } + end + + draw_text { + string = tostring(ctx.udata.frame_count), + position = { x = 0, y = 0 }, + font = "/fonts/kenney-pixel.ttf", + } + + draw_text { + string = tostring(ctx.udata.nest.frame_count), + position = { x = 0, y = 14 }, + font = "/fonts/kenney-pixel.ttf", + } + + draw_text { + string = tostring(ctx.udata.arr[0]), + position = { x = 0, y = 28 }, + font = "/fonts/kenney-pixel.ttf", + } + input_action { name = "press", control = "A" } - draw_rectangle { - rect = { x = 0, y = 0, w = 640, h = 360 }, - color = { r = 127, g = 0, b = 127, a = 255 }, - } - draw_sprite { texture = "/assets/title.png", rect = { @@ -32,6 +55,10 @@ function game_tick() } end + ctx.udata.frame_count = ctx.udata.frame_count + 1 + ctx.udata.nest.frame_count = ctx.udata.nest.frame_count + 1 + ctx.udata.arr[0] = ctx.udata.arr[0] + 1 + offset.x = ORIGIN.x + (math.cos(angle) * RADIUS) offset.y = ORIGIN.y + (math.sin(angle) * RADIUS) angle = angle + 0.1 diff --git a/apps/twnlua/data/twn.toml b/apps/twnlua/data/twn.toml index a822d96..115a538 100644 --- a/apps/twnlua/data/twn.toml +++ b/apps/twnlua/data/twn.toml @@ -6,5 +6,6 @@ dev_id = "somebody" [game] resolution = [ 640, 360 ] +background_color = [ 127, 0, 127, 255 ] [engine] diff --git a/apps/twnlua/game.c b/apps/twnlua/game.c index f578a39..e41a12c 100644 --- a/apps/twnlua/game.c +++ b/apps/twnlua/game.c @@ -1,5 +1,6 @@ #include "twn_game_api.h" +/* TODO: actually move it back it its own file, it doesn't give any compilation benefits */ #define LUA_IMPL #include "minilua.h" @@ -8,6 +9,8 @@ #include +#define UDATA_NESTING_LIMIT 128 + /* generated by bindgen.py */ void bindgen_load_twn(lua_State *L); @@ -100,6 +103,47 @@ static void *custom_alloc(void *ud, void *ptr, size_t osize, size_t nsize) { } +static void exchange_lua_states(lua_State *from, lua_State *to, int level, int index) { + if (level >= UDATA_NESTING_LIMIT) { + log_critical("ctx.udata nesting limit is reached (%u)", UDATA_NESTING_LIMIT); + return; + } + + /* TODO: use arrays for optimized paths */ + /* TODO: preallocate table records */ + switch (lua_type(from, index)) { + case LUA_TTABLE: + lua_newtable(to); + lua_pushnil(from); /* first key */ + while (lua_next(from, index - 1) != 0) { + /* 'key' at index -2 and 'value' at index -1 */ + exchange_lua_states(from, to, level + 1, -2); + exchange_lua_states(from, to, level + 1, -1); + lua_settable(to, index - 2); + /* removes 'value'; keeps 'key' for next iteration */ + lua_pop(from, 1); + } + break; + case LUA_TNUMBER: + lua_pushnumber(to, lua_tonumber(from, index)); + break; + case LUA_TBOOLEAN: + lua_pushboolean(to, lua_toboolean(from, index)); + break; + case LUA_TSTRING: + lua_pushstring(to, lua_tostring(from, index)); + break; + case LUA_TNIL: + lua_pushnil(to); + break; + default: + /* TODO: provide a path and type of it for better diagnostic */ + log_warn("Unserializable udata found and is ignored"); + break; + } +} + + void game_tick(void) { if (ctx.initialization_needed) { if (!ctx.udata) @@ -108,13 +152,29 @@ void game_tick(void) { State *state = ctx.udata; /* let's init lua */ - /* state existed already */ + lua_State *new_state = luaL_newstate(); + lua_setallocf(new_state, custom_alloc, NULL); + + /* state existed already, copy its udata over */ if (state->L != NULL) { + lua_getglobal(state->L, "ctx"); + lua_getfield(state->L, -1, "udata"); + SDL_assert(!lua_isnoneornil(state->L, -1)); + SDL_assert(!lua_isnoneornil(state->L, -2)); + // SDL_TriggerBreakpoint(); + if (!lua_isnoneornil(state->L, -1)) { + log_info("Exchanging lua states..."); + lua_newtable(new_state); + exchange_lua_states(state->L, new_state, 0, -1); + lua_setfield(new_state, -2, "udata"); + lua_setglobal(new_state, "ctx"); + } + + /* bye :) */ lua_close(state->L); } - state->L = luaL_newstate(); - lua_setallocf(state->L, custom_alloc, NULL); + state->L = new_state; /* fakey version of luaL_openlibs() that excludes file i/o and os stuff */ { diff --git a/apps/twnlua/minilua.h b/apps/twnlua/minilua.h index e6288c6..53d6550 100644 --- a/apps/twnlua/minilua.h +++ b/apps/twnlua/minilua.h @@ -383,7 +383,8 @@ extern "C" { #else /* }{ */ -#define LUA_API extern +/* TWN: Don't export anything, there's no need. */ +#define LUA_API LUAI_FUNC #endif /* } */