#include "plugin.h" #include "../ipc.h" #include "plugin_api.h" #include #include #include #include #include #include #define API_VERSION_MAJOR 1 #define API_VERSION_MINOR 0 #define API_ADD(FUNC) lua_pushstring(state, #FUNC);lua_pushcfunction(state, api_##FUNC);lua_settable(state, -3); #define API_ADD_SUB(FUNC, SUB) lua_pushstring(state, #FUNC);lua_pushcfunction(state, api_##SUB##_##FUNC);lua_settable(state, -3); #define API_ADD_SUB_ALIAS(FUNC, ALIAS, SUB) lua_pushstring(state, #ALIAS);lua_pushcfunction(state, api_##SUB##_##FUNC);lua_settable(state, -3); const char* BOLT_REGISTRYNAME = "bolt"; const char* BATCH2D_META_REGISTRYNAME = "batch2dindex"; const char* RENDER3D_META_REGISTRYNAME = "batch3dindex"; const char* MINIMAP_META_REGISTRYNAME = "minimapindex"; const char* SWAPBUFFERS_META_REGISTRYNAME = "swapbuffersindex"; const char* SURFACE_META_REGISTRYNAME = "surfaceindex"; uint64_t next_plugin_id = 1; void (*surface_init)(struct SurfaceFunctions*, unsigned int, unsigned int); void (*surface_destroy)(void*); enum { ENV_CALLBACK_SWAPBUFFERS, ENV_CALLBACK_2D, ENV_CALLBACK_3D, ENV_CALLBACK_MINIMAP, }; static int _bolt_api_init(lua_State* state); int fd = 0; lua_State* state = NULL; // macro for defining callback functions "_bolt_plugin_handle_*" and "api_setcallback*" // e.g. DEFINE_CALLBACK(swapbuffers, SWAPBUFFERS_META_REGISTRYNAME, SwapBuffersEvent, ENV_CALLBACK_SWAPBUFFERS) #define DEFINE_CALLBACK(APINAME, META_REGNAME, STRUCTNAME, ENUMNAME) \ void _bolt_plugin_handle_##APINAME(struct STRUCTNAME* e) { \ lua_pushlightuserdata(state, e); \ lua_getfield(state, LUA_REGISTRYINDEX, META_REGNAME); \ lua_setmetatable(state, -2); \ int userdata_index = lua_gettop(state); \ lua_getfield(state, LUA_REGISTRYINDEX, BOLT_REGISTRYNAME); \ int key_index = lua_gettop(state); \ lua_pushnil(state); \ while (lua_next(state, key_index) != 0) { \ if (!lua_isnumber(state, -2)) { \ lua_pop(state, 1); \ continue; \ } \ lua_pushnumber(state, ENUMNAME); \ lua_gettable(state, -2); \ if (!lua_isfunction(state, -1)) { \ lua_pop(state, 2); \ continue; \ } \ lua_pushvalue(state, userdata_index); \ if (lua_pcall(state, 1, 0, 0)) { \ const lua_Integer plugin_id = lua_tonumber(state, -3); \ const char* e = lua_tolstring(state, -1, 0); \ printf("plugin callback %s error: %s\n", #APINAME, e); \ lua_pop(state, 2); \ _bolt_plugin_stop(plugin_id); \ } else { \ lua_pop(state, 1); \ } \ } \ lua_pop(state, 2); \ } \ static int api_setcallback##APINAME(lua_State *state) { \ _bolt_check_argc(state, 1, "setcallback"#APINAME); \ lua_pushinteger(state, ENUMNAME); \ if (lua_isfunction(state, 1)) { \ lua_pushvalue(state, 1); \ } else { \ lua_pushnil(state); \ } \ lua_settable(state, LUA_GLOBALSINDEX); \ return 0; \ } static int surface_gc(lua_State* state) { const struct SurfaceFunctions* functions = lua_touserdata(state, 1); surface_destroy(functions->userdata); return 0; } void _bolt_plugin_init(void (*_surface_init)(struct SurfaceFunctions*, unsigned int, unsigned int), void (*_surface_destroy)(void*)) { _bolt_plugin_ipc_init(&fd); const char* display_name = getenv("JX_DISPLAY_NAME"); if (display_name && *display_name) { size_t name_len = strlen(display_name); struct BoltIPCMessage message = {.message_type = IPC_MSG_IDENTIFY, .items = name_len}; _bolt_ipc_send(fd, &message, sizeof(message)); _bolt_ipc_send(fd, display_name, name_len); } surface_init = _surface_init; surface_destroy = _surface_destroy; state = luaL_newstate(); // Open just the specific libraries plugins are allowed to have lua_pushcfunction(state, luaopen_base); lua_call(state, 0, 0); lua_pushcfunction(state, luaopen_package); lua_call(state, 0, 0); lua_pushcfunction(state, luaopen_string); lua_call(state, 0, 0); lua_pushcfunction(state, luaopen_table); lua_call(state, 0, 0); lua_pushcfunction(state, luaopen_math); lua_call(state, 0, 0); // create the metatable for all RenderBatch2D objects lua_pushstring(state, BATCH2D_META_REGISTRYNAME); lua_newtable(state); lua_pushstring(state, "__index"); lua_createtable(state, 0, 12); API_ADD_SUB(vertexcount, batch2d) API_ADD_SUB(verticesperimage, batch2d) API_ADD_SUB(isminimap, batch2d) API_ADD_SUB(targetsize, batch2d) API_ADD_SUB(vertexxy, batch2d) API_ADD_SUB(vertexatlasxy, batch2d) API_ADD_SUB(vertexatlaswh, batch2d) API_ADD_SUB(vertexuv, batch2d) API_ADD_SUB(vertexcolour, batch2d) API_ADD_SUB(textureid, batch2d) API_ADD_SUB(texturesize, batch2d) API_ADD_SUB(texturecompare, batch2d) API_ADD_SUB(texturedata, batch2d) API_ADD_SUB_ALIAS(vertexcolour, vertexcolor, batch2d) lua_settable(state, -3); lua_settable(state, LUA_REGISTRYINDEX); // create the metatable for all Render3D objects lua_pushstring(state, RENDER3D_META_REGISTRYNAME); lua_newtable(state); lua_pushstring(state, "__index"); lua_createtable(state, 0, 13); API_ADD_SUB(vertexcount, render3d) API_ADD_SUB(vertexxyz, render3d) API_ADD_SUB(vertexmeta, render3d) API_ADD_SUB(atlasxywh, render3d) API_ADD_SUB(vertexuv, render3d) API_ADD_SUB(vertexcolour, render3d) API_ADD_SUB(textureid, render3d) API_ADD_SUB(texturesize, render3d) API_ADD_SUB(texturecompare, render3d) API_ADD_SUB(texturedata, render3d) API_ADD_SUB(toworldspace, render3d) API_ADD_SUB(toscreenspace, render3d) API_ADD_SUB(worldposition, render3d) API_ADD_SUB_ALIAS(vertexcolour, vertexcolor, render3d) lua_settable(state, -3); lua_settable(state, LUA_REGISTRYINDEX); // create the metatable for all RenderMinimap objects lua_pushstring(state, MINIMAP_META_REGISTRYNAME); lua_newtable(state); lua_pushstring(state, "__index"); lua_createtable(state, 0, 3); API_ADD_SUB(angle, minimap) API_ADD_SUB(scale, minimap) API_ADD_SUB(position, minimap) lua_settable(state, -3); lua_settable(state, LUA_REGISTRYINDEX); // create the metatable for all SwapBuffers objects lua_pushstring(state, SWAPBUFFERS_META_REGISTRYNAME); lua_newtable(state); lua_pushstring(state, "__index"); lua_createtable(state, 0, 0); lua_settable(state, -3); lua_settable(state, LUA_REGISTRYINDEX); // create the metatable for all Surface objects lua_pushstring(state, SURFACE_META_REGISTRYNAME); lua_newtable(state); lua_pushstring(state, "__index"); lua_createtable(state, 0, 2); API_ADD_SUB(clear, surface) API_ADD_SUB(drawtoscreen, surface) lua_settable(state, -3); lua_pushstring(state, "__gc"); lua_pushcfunction(state, surface_gc); lua_settable(state, -3); lua_settable(state, LUA_REGISTRYINDEX); // create registry["bolt"] as an empty table lua_pushstring(state, BOLT_REGISTRYNAME); lua_newtable(state); lua_settable(state, LUA_REGISTRYINDEX); // load Bolt API into package.preload, so that `require("bolt")` will find it lua_getfield(state, LUA_GLOBALSINDEX, "package"); lua_getfield(state, -1, "preload"); lua_pushstring(state, BOLT_REGISTRYNAME); lua_pushcfunction(state, _bolt_api_init); lua_settable(state, -3); lua_pop(state, 2); } static int _bolt_api_init(lua_State* state) { lua_createtable(state, 0, 5); API_ADD(apiversion) API_ADD(checkversion) API_ADD(time) API_ADD(setcallback2d) API_ADD(setcallback3d) API_ADD(setcallbackminimap) API_ADD(setcallbackswapbuffers) API_ADD(createsurface) return 1; } uint8_t _bolt_plugin_is_inited() { return state ? 1 : 0; } void _bolt_plugin_close() { lua_close(state); state = NULL; _bolt_plugin_ipc_close(fd); } void _bolt_plugin_handle_messages() { struct BoltIPCMessage message; while (_bolt_ipc_poll(fd)) { if (_bolt_ipc_receive(fd, &message, sizeof(message)) != 0) break; switch (message.message_type) { default: printf("unknown message type %u\n", message.message_type); break; } } } uint64_t _bolt_plugin_add(const char* lua) { // load the user-provided string as a lua function, putting that function on the stack if (luaL_loadstring(state, lua)) { const char* e = lua_tolstring(state, -1, 0); printf("plugin load error: %s\n", e); lua_pop(state, 1); return 0; } // create a new env for this plugin, and store it as registry["bolt"][i] where `i` is a unique ID const uint64_t plugin_id = next_plugin_id; next_plugin_id += 1; lua_newtable(state); lua_getfield(state, LUA_REGISTRYINDEX, BOLT_REGISTRYNAME); lua_pushinteger(state, plugin_id); lua_pushvalue(state, -3); lua_settable(state, -3); lua_pop(state, 1); // allow the new env access to the outer env via __index lua_newtable(state); lua_pushstring(state, "__index"); lua_pushvalue(state, LUA_GLOBALSINDEX); lua_settable(state, -3); lua_setmetatable(state, -2); // set the env of the function created by `lua_loadstring` to this new env we just made lua_setfenv(state, -2); // attempt to run the function if (lua_pcall(state, 0, 0, 0)) { const char* e = lua_tolstring(state, -1, 0); printf("plugin startup error: %s\n", e); lua_pop(state, 1); _bolt_plugin_stop(plugin_id); return 0; } else { return plugin_id; } } void _bolt_plugin_stop(uint64_t id) { lua_getfield(state, LUA_REGISTRYINDEX, BOLT_REGISTRYNAME); lua_pushinteger(state, id); lua_pushnil(state); lua_settable(state, -3); lua_pop(state, 1); } // Calls `error()` if arg count is incorrect static void _bolt_check_argc(lua_State* state, int expected_argc, const char* function_name) { char error_buffer[256]; const int argc = lua_gettop(state); if (argc != expected_argc) { sprintf(error_buffer, "incorrect argument count to '%s': expected %i, got %i", function_name, expected_argc, argc); lua_pushstring(state, error_buffer); lua_error(state); } } DEFINE_CALLBACK(swapbuffers, SWAPBUFFERS_META_REGISTRYNAME, SwapBuffersEvent, ENV_CALLBACK_SWAPBUFFERS) DEFINE_CALLBACK(2d, BATCH2D_META_REGISTRYNAME, RenderBatch2D, ENV_CALLBACK_2D) DEFINE_CALLBACK(3d, RENDER3D_META_REGISTRYNAME, Render3D, ENV_CALLBACK_3D) DEFINE_CALLBACK(minimap, MINIMAP_META_REGISTRYNAME, RenderMinimapEvent, ENV_CALLBACK_MINIMAP) static int api_apiversion(lua_State* state) { _bolt_check_argc(state, 0, "apiversion"); lua_pushnumber(state, API_VERSION_MAJOR); lua_pushnumber(state, API_VERSION_MINOR); return 2; } static int api_checkversion(lua_State* state) { _bolt_check_argc(state, 2, "checkversion"); char error_buffer[256]; lua_Integer expected_major = lua_tonumber(state, 1); lua_Integer expected_minor = lua_tonumber(state, 2); if (expected_major != API_VERSION_MAJOR) { sprintf(error_buffer, "checkversion major version mismatch: major version is %u, plugin expects %u", API_VERSION_MAJOR, (unsigned int)expected_major); lua_pushstring(state, error_buffer); lua_error(state); } if (expected_minor > API_VERSION_MINOR) { sprintf(error_buffer, "checkversion minor version mismatch: minor version is %u, plugin expects at least %u", API_VERSION_MINOR, (unsigned int)expected_minor); lua_pushstring(state, error_buffer); lua_error(state); } return 2; } static int api_time(lua_State* state) { _bolt_check_argc(state, 0, "time"); struct timespec s; clock_gettime(CLOCK_MONOTONIC, &s); const uint64_t microseconds = (s.tv_sec * 1000000) + (s.tv_nsec / 1000); lua_pushinteger(state, microseconds); return 1; } static int api_createsurface(lua_State* state) { _bolt_check_argc(state, 2, "createsurface"); const lua_Integer w = lua_tointeger(state, 1); const lua_Integer h = lua_tointeger(state, 2); struct SurfaceFunctions* functions = lua_newuserdata(state, sizeof(struct SurfaceFunctions)); surface_init(functions, w, h); lua_getfield(state, LUA_REGISTRYINDEX, SURFACE_META_REGISTRYNAME); lua_setmetatable(state, -2); return 1; } static int api_batch2d_vertexcount(lua_State* state) { _bolt_check_argc(state, 1, "batch2d_vertexcount"); struct RenderBatch2D* batch = lua_touserdata(state, 1); lua_pushinteger(state, batch->index_count); return 1; } static int api_batch2d_verticesperimage(lua_State* state) { _bolt_check_argc(state, 1, "batch2d_verticesperimage"); struct RenderBatch2D* batch = lua_touserdata(state, 1); lua_pushinteger(state, batch->vertices_per_icon); return 1; } static int api_batch2d_isminimap(lua_State* state) { _bolt_check_argc(state, 1, "batch2d_verticesperimage"); struct RenderBatch2D* batch = lua_touserdata(state, 1); lua_pushboolean(state, batch->is_minimap); return 1; } static int api_batch2d_targetsize(lua_State* state) { _bolt_check_argc(state, 1, "batch2d_targetsize"); struct RenderBatch2D* batch = lua_touserdata(state, 1); lua_pushinteger(state, batch->screen_width); lua_pushinteger(state, batch->screen_height); return 2; } static int api_batch2d_vertexxy(lua_State* state) { _bolt_check_argc(state, 2, "batch2d_vertexxy"); struct RenderBatch2D* batch = lua_touserdata(state, 1); const lua_Integer index = lua_tointeger(state, 2); int32_t xy[2]; batch->vertex_functions.xy(index - 1, batch->vertex_functions.userdata, xy); lua_pushinteger(state, xy[0]); lua_pushinteger(state, xy[1]); return 2; } static int api_batch2d_vertexatlasxy(lua_State* state) { _bolt_check_argc(state, 2, "batch2d_vertexatlasxy"); struct RenderBatch2D* batch = lua_touserdata(state, 1); const lua_Integer index = lua_tointeger(state, 2); int32_t xy[2]; batch->vertex_functions.atlas_xy(index - 1, batch->vertex_functions.userdata, xy); lua_pushinteger(state, xy[0]); lua_pushinteger(state, xy[1]); return 2; } static int api_batch2d_vertexatlaswh(lua_State* state) { _bolt_check_argc(state, 2, "batch2d_vertexatlaswh"); struct RenderBatch2D* batch = lua_touserdata(state, 1); const lua_Integer index = lua_tointeger(state, 2); int32_t wh[2]; batch->vertex_functions.atlas_wh(index - 1, batch->vertex_functions.userdata, wh); lua_pushinteger(state, wh[0]); lua_pushinteger(state, wh[1]); return 2; } static int api_batch2d_vertexuv(lua_State* state) { _bolt_check_argc(state, 2, "batch2d_vertexuv"); struct RenderBatch2D* batch = lua_touserdata(state, 1); const lua_Integer index = lua_tointeger(state, 2); double uv[2]; batch->vertex_functions.uv(index - 1, batch->vertex_functions.userdata, uv); lua_pushnumber(state, uv[0]); lua_pushnumber(state, uv[1]); return 2; } static int api_batch2d_vertexcolour(lua_State* state) { _bolt_check_argc(state, 4, "batch2d_vertexcolour"); struct RenderBatch2D* batch = lua_touserdata(state, 1); const lua_Integer index = lua_tointeger(state, 2); double colour[4]; batch->vertex_functions.colour(index - 1, batch->vertex_functions.userdata, colour); lua_pushnumber(state, colour[0]); lua_pushnumber(state, colour[1]); lua_pushnumber(state, colour[2]); lua_pushnumber(state, colour[3]); return 4; } static int api_batch2d_textureid(lua_State* state) { _bolt_check_argc(state, 1, "batch2d_textureid"); struct RenderBatch2D* render = lua_touserdata(state, 1); const size_t id = render->texture_functions.id(render->texture_functions.userdata); lua_pushinteger(state, id); return 1; } static int api_batch2d_texturesize(lua_State* state) { _bolt_check_argc(state, 1, "batch2d_texturesize"); struct RenderBatch2D* render = lua_touserdata(state, 1); size_t size[2]; render->texture_functions.size(render->texture_functions.userdata, size); lua_pushinteger(state, size[0]); lua_pushinteger(state, size[1]); return 2; } static int api_batch2d_texturecompare(lua_State* state) { _bolt_check_argc(state, 4, "batch2d_texturecompare"); struct RenderBatch2D* render = lua_touserdata(state, 1); const size_t x = lua_tointeger(state, 2); const size_t y = lua_tointeger(state, 3); size_t data_len; const unsigned char* data = (const unsigned char*)lua_tolstring(state, 4, &data_len); const uint8_t match = render->texture_functions.compare(render->texture_functions.userdata, x, y, data_len, data); lua_pushboolean(state, match); return 1; } static int api_batch2d_texturedata(lua_State* state) { _bolt_check_argc(state, 4, "batch2d_texturedata"); struct RenderBatch2D* render = lua_touserdata(state, 1); const size_t x = lua_tointeger(state, 2); const size_t y = lua_tointeger(state, 3); const size_t len = lua_tointeger(state, 4); const uint8_t* ret = render->texture_functions.data(render->texture_functions.userdata, x, y); lua_pushlstring(state, (const char*)ret, len); return 1; } static int api_minimap_angle(lua_State* state) { _bolt_check_argc(state, 1, "minimap_angle"); struct RenderMinimapEvent* render = lua_touserdata(state, 1); lua_pushnumber(state, render->angle); return 1; } static int api_minimap_scale(lua_State* state) { _bolt_check_argc(state, 1, "minimap_scale"); struct RenderMinimapEvent* render = lua_touserdata(state, 1); lua_pushnumber(state, render->scale); return 1; } static int api_minimap_position(lua_State* state) { _bolt_check_argc(state, 1, "minimap_position"); struct RenderMinimapEvent* render = lua_touserdata(state, 1); lua_pushnumber(state, render->x); lua_pushnumber(state, render->y); return 2; } static int api_surface_clear(lua_State* state) { char error_buffer[256]; const int argc = lua_gettop(state); switch (argc) { case 1: { const struct SurfaceFunctions* functions = lua_touserdata(state, 1); functions->clear(functions->userdata, 0.0, 0.0, 0.0, 0.0); break; } case 4: { const struct SurfaceFunctions* functions = lua_touserdata(state, 1); const double r = lua_tonumber(state, 2); const double g = lua_tonumber(state, 3); const double b = lua_tonumber(state, 4); functions->clear(functions->userdata, r, g, b, 1.0); break; } case 5: { const struct SurfaceFunctions* functions = lua_touserdata(state, 1); const double r = lua_tonumber(state, 2); const double g = lua_tonumber(state, 3); const double b = lua_tonumber(state, 4); const double a = lua_tonumber(state, 5); functions->clear(functions->userdata, r, g, b, a); break; } default: { sprintf(error_buffer, "incorrect argument count to 'surface_clear': expected 1, 4, or 5, but got %i", argc); lua_pushstring(state, error_buffer); lua_error(state); } } return 0; } static int api_surface_drawtoscreen(lua_State* state) { _bolt_check_argc(state, 9, "surface_drawtoscreen"); const struct SurfaceFunctions* functions = lua_touserdata(state, 1); const int sx = lua_tointeger(state, 2); const int sy = lua_tointeger(state, 3); const int sw = lua_tointeger(state, 4); const int sh = lua_tointeger(state, 5); const int dx = lua_tointeger(state, 6); const int dy = lua_tointeger(state, 7); const int dw = lua_tointeger(state, 8); const int dh = lua_tointeger(state, 9); functions->draw_to_screen(functions->userdata, sx, sy, sw, sh, dx, dy, dw, dh); return 0; } static int api_render3d_vertexcount(lua_State* state) { _bolt_check_argc(state, 1, "render3d_vertexcount"); const struct Render3D* render = lua_touserdata(state, 1); lua_pushinteger(state, render->vertex_count); return 1; } static int api_render3d_vertexxyz(lua_State* state) { _bolt_check_argc(state, 2, "render3d_vertexxyz"); const struct Render3D* render = lua_touserdata(state, 1); const lua_Integer index = lua_tointeger(state, 2); int32_t xyz[3]; render->vertex_functions.xyz(index - 1, render->vertex_functions.userdata, xyz); lua_pushinteger(state, xyz[0]); lua_pushinteger(state, xyz[1]); lua_pushinteger(state, xyz[2]); return 3; } static int api_render3d_vertexmeta(lua_State* state) { _bolt_check_argc(state, 2, "render3d_vertexmeta"); const struct Render3D* render = lua_touserdata(state, 1); const lua_Integer index = lua_tointeger(state, 2); size_t meta = render->vertex_functions.atlas_meta(index - 1, render->vertex_functions.userdata); lua_pushinteger(state, meta); return 1; } static int api_render3d_atlasxywh(lua_State* state) { _bolt_check_argc(state, 2, "render3d_atlasxywh"); const struct Render3D* render = lua_touserdata(state, 1); const lua_Integer meta = lua_tointeger(state, 2); int32_t xywh[4]; render->vertex_functions.atlas_xywh(meta, render->vertex_functions.userdata, xywh); lua_pushinteger(state, xywh[0]); lua_pushinteger(state, xywh[1]); lua_pushinteger(state, xywh[2]); lua_pushinteger(state, xywh[3]); return 4; } static int api_render3d_vertexuv(lua_State* state) { _bolt_check_argc(state, 2, "render3d_vertexuv"); const struct Render3D* render = lua_touserdata(state, 1); const lua_Integer index = lua_tointeger(state, 2); double uv[4]; render->vertex_functions.uv(index, render->vertex_functions.userdata, uv); lua_pushnumber(state, uv[0]); lua_pushnumber(state, uv[1]); return 2; } static int api_render3d_vertexcolour(lua_State* state) { _bolt_check_argc(state, 2, "render3d_vertexcolour"); const struct Render3D* render = lua_touserdata(state, 1); const lua_Integer index = lua_tointeger(state, 2); double col[4]; render->vertex_functions.colour(index, render->vertex_functions.userdata, col); lua_pushnumber(state, col[0]); lua_pushnumber(state, col[1]); lua_pushnumber(state, col[2]); lua_pushnumber(state, col[3]); return 2; } static int api_render3d_textureid(lua_State* state) { _bolt_check_argc(state, 1, "render3d_textureid"); struct Render3D* render = lua_touserdata(state, 1); const size_t id = render->texture_functions.id(render->texture_functions.userdata); lua_pushinteger(state, id); return 1; } static int api_render3d_texturesize(lua_State* state) { _bolt_check_argc(state, 1, "render3d_texturesize"); struct Render3D* render = lua_touserdata(state, 1); size_t size[2]; render->texture_functions.size(render->texture_functions.userdata, size); lua_pushinteger(state, size[0]); lua_pushinteger(state, size[1]); return 2; } static int api_render3d_texturecompare(lua_State* state) { _bolt_check_argc(state, 4, "render3d_texturecompare"); struct Render3D* render = lua_touserdata(state, 1); const size_t x = lua_tointeger(state, 2); const size_t y = lua_tointeger(state, 3); size_t data_len; const unsigned char* data = (const unsigned char*)lua_tolstring(state, 4, &data_len); const uint8_t match = render->texture_functions.compare(render->texture_functions.userdata, x, y, data_len, data); lua_pushboolean(state, match); return 1; } static int api_render3d_texturedata(lua_State* state) { _bolt_check_argc(state, 4, "render3d_texturedata"); struct Render3D* render = lua_touserdata(state, 1); const size_t x = lua_tointeger(state, 2); const size_t y = lua_tointeger(state, 3); const size_t len = lua_tointeger(state, 4); const uint8_t* ret = render->texture_functions.data(render->texture_functions.userdata, x, y); lua_pushlstring(state, (const char*)ret, len); return 1; } static int api_render3d_toworldspace(lua_State* state) { _bolt_check_argc(state, 4, "render3d_toworldspace"); struct Render3D* render = lua_touserdata(state, 1); const int x = lua_tointeger(state, 2); const int y = lua_tointeger(state, 3); const int z = lua_tointeger(state, 4); double out[3]; render->matrix_functions.to_world_space(x, y, z, render->matrix_functions.userdata, out); lua_pushnumber(state, out[0]); lua_pushnumber(state, out[1]); lua_pushnumber(state, out[2]); return 3; } static int api_render3d_toscreenspace(lua_State* state) { _bolt_check_argc(state, 4, "render3d_toscreenspace"); struct Render3D* render = lua_touserdata(state, 1); const int x = lua_tointeger(state, 2); const int y = lua_tointeger(state, 3); const int z = lua_tointeger(state, 4); double out[2]; render->matrix_functions.to_screen_space(x, y, z, render->matrix_functions.userdata, out); lua_pushnumber(state, out[0]); lua_pushnumber(state, out[1]); return 2; } static int api_render3d_worldposition(lua_State* state) { _bolt_check_argc(state, 1, "render3d_worldposition"); struct Render3D* render = lua_touserdata(state, 1); double out[3]; render->matrix_functions.world_pos(render->matrix_functions.userdata, out); lua_pushnumber(state, out[0]); lua_pushnumber(state, out[1]); lua_pushnumber(state, out[2]); return 3; }