townengine/apps/twnlua/game.c
2025-01-12 03:21:05 +03:00

182 lines
5.5 KiB
C

#include "twn_game_api.h"
#include "state.h"
#include <SDL2/SDL.h>
#include <lua.h>
#include <lualib.h>
#include <lauxlib.h>
#include <malloc.h>
/* 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);
/* 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);
}
}
void game_end(void) {
State *state = ctx.udata;
bindgen_unload_twn(state->L);
lua_close(state->L);
free(state);
}