/** * Copyright (c) 2025 Dominic Masters * * This software is released under the MIT License. * https://opensource.org/licenses/MIT */ #include "scriptcontext.hpp" #include "assert/Assert.hpp" #include "asset/asset.hpp" #include "util/memory.hpp" #include "debug/debug.hpp" #include "script/func/scriptfunccamera.hpp" #include "script/func/scriptfuncentity.hpp" #include "script/func/scriptfuncsystem.hpp" void scriptContextInit(scriptcontext_t *context) { assertNotNull(context, "Script context cannot be NULL"); memoryZero(context, sizeof(scriptcontext_t)); // Create a new Lua state for this context. context->luaState = luaL_newstate(); if(context->luaState == NULL) { throw std::runtime_error("Failed to init Lua state"); } luaL_openlibs(context->luaState); // Register functions scriptFuncSystem(context); scriptFuncEntity(context); } void scriptContextRegFunc( scriptcontext_t *context, const char_t *fnName, lua_CFunction function ) { assertNotNull(context, "Script context cannot be NULL"); assertNotNull(fnName, "Function name cannot be NULL"); assertNotNull(function, "Function cannot be NULL"); lua_register(context->luaState, fnName, function); } void scriptContextCallFunc( scriptcontext_t *context, const char_t *fnName, const scriptvalue_t *args, const int32_t argCount, scriptvalue_t *retValue ) { assertNotNull(context, "Script context cannot be NULL"); assertNotNull(fnName, "Function name cannot be NULL"); assertTrue(args == NULL || argCount >= 0, "Invalid arg count"); // Get func lua_getglobal(context->luaState, fnName); if(!lua_isfunction(context->luaState, -1)) { throw std::runtime_error( "Function '" + std::string(fnName) + "' not found in script context" ); } // Push args for(int32_t i = 0; i < argCount; i++) { const scriptvalue_t *arg = &args[i]; switch(arg->type) { case SCRIPT_VALUE_TYPE_INT: lua_pushinteger(context->luaState, arg->value.intValue); break; case SCRIPT_VALUE_TYPE_FLOAT: lua_pushnumber(context->luaState, arg->value.floatValue); break; case SCRIPT_VALUE_TYPE_STRING: lua_pushstring(context->luaState, arg->value.strValue); break; default: throw std::runtime_error( "Unsupported argument type " + std::to_string(arg->type) ); } } // Call func if(lua_pcall( context->luaState, args ? argCount : 0, retValue ? 1 : 0, 0 ) != LUA_OK) { const char_t *strErr = lua_tostring(context->luaState, -1); lua_pop(context->luaState, 1); throw std::runtime_error( "Failed to call Lua function '" + std::string(fnName) + "': " + std::string(strErr) ); } // Was there a ret value? if(retValue == NULL) return; // Get ret value switch(retValue->type) { case SCRIPT_VALUE_TYPE_INT: if(!lua_isinteger(context->luaState, -1)) { throw std::runtime_error( "Expected integer return value from '" + std::string(fnName) + "'" ); } retValue->value.intValue = (int32_t)lua_tointeger(context->luaState, -1); break; case SCRIPT_VALUE_TYPE_FLOAT: if(!lua_isnumber(context->luaState, -1)) { throw std::runtime_error( "Expected float return value from '" + std::string(fnName) + "'" ); } retValue->value.floatValue = (float)lua_tonumber(context->luaState, -1); break; case SCRIPT_VALUE_TYPE_BOOL: if(!lua_isboolean(context->luaState, -1)) { throw std::runtime_error( "Expected boolean return value from '" + std::string(fnName) + "'" ); } retValue->value.boolValue = lua_toboolean(context->luaState, -1); break; // case SCRIPT_VALUE_TYPE_STRING: // if(!lua_isstring(context->luaState, -1)) { // errorThrow("Expected string return value from '%s'", fnName); // } // retValue->value.strValue = lua_tostring(context->luaState, -1); // break; default: throw std::runtime_error( "Unsupported return value type " + std::to_string(retValue->type) ); } } void scriptContextExec(scriptcontext_t *context, const char_t *script) { assertNotNull(context, "Script context cannot be NULL"); assertNotNull(script, "Script cannot be NULL"); if(luaL_dostring(context->luaState, script) != LUA_OK) { const char_t *strErr = lua_tostring(context->luaState, -1); lua_pop(context->luaState, 1); throw std::runtime_error("Failed to execute Lua: " + std::string(strErr)); } } void scriptContextExecFile(scriptcontext_t *ctx, const char_t *fname) { assertNotNull(ctx, "Script context cannot be NULL"); assertNotNull(fname, "Filename cannot be NULL"); assetscript_t script; assetLoad(fname, &script); if(lua_load( ctx->luaState, assetScriptReader, &script, fname, NULL ) != LUA_OK) { const char_t *strErr = lua_tostring(ctx->luaState, -1); lua_pop(ctx->luaState, 1); throw std::runtime_error( "Failed to load Lua script: " + std::string(strErr) ); } if(lua_pcall(ctx->luaState, 0, LUA_MULTRET, 0) != LUA_OK) { const char_t *strErr = lua_tostring(ctx->luaState, -1); lua_pop(ctx->luaState, 1); throw std::runtime_error( "Failed to execute Lua script: " + std::string(strErr) ); } assetScriptDispose(&script); } void scriptContextDispose(scriptcontext_t *context) { assertNotNull(context, "Script context cannot be NULL"); assertNotNull(context->luaState, "Lua state is not initialized"); if(context->luaState != NULL) { lua_close(context->luaState); context->luaState = NULL; } }