205 lines
5.6 KiB
C++
205 lines
5.6 KiB
C++
/**
|
|
* 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;
|
|
}
|
|
} |