192 lines
		
	
	
		
			5.9 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			192 lines
		
	
	
		
			5.9 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);
 | |
| 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 */
 | |
|         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);
 | |
|             } else
 | |
|                 state->loaded_successfully = true;
 | |
|         } else {
 | |
|             /* got some sort of error, it should be pushed on top of the stack */
 | |
|             SDL_assert(lua_isstring(state->L, -1));
 | |
|             log_critical("Error loading /scripts/game.lua entry: %s", luaL_tolstring(state->L, -1, NULL));
 | |
|             lua_pop(state->L, 1);
 | |
|         }
 | |
| 
 | |
|         free(game_buf);
 | |
| 
 | |
|         /* from this point we have access to everything defined in lua */
 | |
|     }
 | |
| 
 | |
|     State *state = ctx.udata;
 | |
| 
 | |
|     if (state->loaded_successfully) {
 | |
|         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);
 | |
| }
 |