#include "twn_game_api.h" #include "state.h" #include #include #include #include #include /* generated by bindgen.py */ void bindgen_load_twn(lua_State *L); void bindgen_unload_twn(lua_State *L); void bindgen_build_context(lua_State *L); void bindgen_upload_context(lua_State *L); /* require will go through physicsfs exclusively so that scripts can be in the data dir */ static int physfs_loader(lua_State *L) { const char *name = luaL_checkstring(L, 1); char *final_path = NULL; SDL_asprintf(&final_path, "%s.lua", name); if (!file_exists(final_path)) { char *error_message = NULL; SDL_asprintf(&error_message, "could not find module %s in filesystem", name); lua_pushstring(L, error_message); free(error_message); free(final_path); return 1; } unsigned char *buf = NULL; int64_t buf_size = file_to_bytes(final_path, &buf); free(final_path); luaL_loadbuffer(L, (char *)buf, buf_size, name); free(buf); return 1; } /* WARN! experimental and will probably be removed */ /* it is an attempt to ease memory usage problems posed by using lua, partially successful */ static void *custom_alloc(void *ud, void *ptr, size_t osize, size_t nsize) { (void)ud; /* small allocations are placed in slots, as there's a big chance they will not need to be resized */ static char slots[1024][128]; static int16_t free_slots[1024] = { [0] = -1 }; static size_t free_slot_count = 1024; if (free_slots[0] == -1) for (int i = 0; i < 1024; i++) free_slots[i] = (int16_t)i; if (nsize == 0) { if (ptr && (char *)ptr >= &slots[0][0] && (char *)ptr <= &slots[1024-1][128-1]) free_slots[free_slot_count++] = (int16_t)(((uintptr_t)ptr - (uintptr_t)slots) / 128); else if (osize) SDL_free(ptr); return NULL; } else { if (!ptr && nsize <= 128 && free_slot_count > 0) { /* use a slot */ return slots[free_slots[--free_slot_count]]; } if ((char *)ptr >= &slots[0][0] && (char *)ptr <= &slots[1024-1][128-1]) { /* still fits */ if (nsize <= 128) return ptr; /* move from slot to dynamic memory */ void *mem = SDL_malloc(nsize); SDL_memcpy(mem, ptr, osize); free_slots[free_slot_count++] = (int16_t)(((uintptr_t)ptr - (uintptr_t)slots) / 128); return mem; } return SDL_realloc(ptr, nsize); } } void game_tick(void) { if (ctx.initialization_needed) { if (!ctx.udata) ctx.udata = ccalloc(1, sizeof (State)); State *state = ctx.udata; /* let's init lua */ /* state existed already */ if (state->L != NULL) { lua_close(state->L); } state->L = luaL_newstate(); lua_setallocf(state->L, custom_alloc, NULL); /* fakey version of luaL_openlibs() that excludes file i/o and os stuff */ { static const luaL_Reg loaded_libs[] = { { LUA_GNAME, luaopen_base }, { LUA_LOADLIBNAME, luaopen_package }, { LUA_COLIBNAME, luaopen_coroutine }, { LUA_TABLIBNAME, luaopen_table }, { LUA_STRLIBNAME, luaopen_string }, { LUA_MATHLIBNAME, luaopen_math }, { LUA_UTF8LIBNAME, luaopen_utf8 }, { LUA_DBLIBNAME, luaopen_debug }, { NULL, NULL }, }; for (const luaL_Reg *lib = loaded_libs; lib->func; ++lib) { luaL_requiref(state->L, lib->name, lib->func, true); lua_pop(state->L, 1); /* already stored, don't need the copy */ } } /* package.searchers = { physfs_loader } */ lua_getglobal(state->L, "package"); lua_createtable(state->L, 0, 1); lua_setfield(state->L, -2, "searchers"); lua_getfield(state->L, -1, "searchers"); lua_pushcfunction(state->L, physfs_loader); lua_seti(state->L, -2, 1); /* pop package, package.searchers */ lua_pop(state->L, 2); /* binding */ // lua_register(state->L, "sprite", b_sprite); // lua_register(state->L, "rectangle", b_rectangle); // lua_register(state->L, "text", b_text); bindgen_load_twn(state->L); /* now finally get to running the code */ unsigned char *game_buf = NULL; size_t game_buf_size = file_to_bytes("/scripts/game.lua", &game_buf); if (luaL_loadbuffer(state->L, (char *)game_buf, game_buf_size, "game.lua") == LUA_OK) { if (lua_pcall(state->L, 0, 0, 0) != LUA_OK) { log_critical("%s", lua_tostring(state->L, -1)); lua_pop(state->L, 1); } } free(game_buf); /* from this point we have access to everything defined in lua */ } State *state = ctx.udata; bindgen_build_context(state->L); lua_getglobal(state->L, "ctx"); if (!lua_isnoneornil(state->L, -1)) { lua_getfield(state->L, -1, "udata"); lua_setfield(state->L, -3, "udata"); } lua_pop(state->L, 1); lua_setglobal(state->L, "ctx"); lua_getglobal(state->L, "game_tick"); if (lua_pcall(state->L, 0, 0, 0) != LUA_OK) { log_critical("%s", lua_tostring(state->L, -1)); lua_pop(state->L, 1); } lua_getglobal(state->L, "ctx"); bindgen_upload_context(state->L); } void game_end(void) { State *state = ctx.udata; bindgen_unload_twn(state->L); lua_close(state->L); free(state); }