Add script context
Some checks failed
Build Dusk / build-linux (push) Successful in 1m41s
Build Dusk / build-psp (push) Failing after 2m36s

This commit is contained in:
2025-12-04 20:57:12 -06:00
parent 8c74ee31e0
commit 4e1b404820
13 changed files with 369 additions and 90 deletions

View File

@@ -11,7 +11,7 @@ set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/cmake/mod
if(NOT DEFINED DUSK_TARGET_SYSTEM)
set(DUSK_TARGET_SYSTEM "linux")
#set(DUSK_TARGET_SYSTEM "psp")
# set(DUSK_TARGET_SYSTEM "psp")
endif()
# Prep cache

View File

@@ -1 +1,7 @@
print("Test Lua script")
function testFunction()
print("Hello from testFunction!")
end
function doAdd(a, b)
return a + b
end

View File

@@ -12,9 +12,7 @@ void debugPrint(const char_t *message, ...) {
va_start(args, message);
vprintf(message, args);
va_end(args);
// For the time being just use standard printing functions.
printf(message, args);
fflush(stdout);
#if PSP
FILE *file = fopen("ms0:/PSP/GAME/Dusk/debug.log", "a");

View File

@@ -18,6 +18,8 @@
#include "script/scriptmanager.h"
#include "debug/debug.h"
#include "script/scriptcontext.h"
engine_t ENGINE;
errorret_t engineInit(const int32_t argc, const char_t **argv) {
@@ -38,10 +40,20 @@ errorret_t engineInit(const int32_t argc, const char_t **argv) {
errorChain(sceneManagerInit());
errorChain(scriptManagerInit());
// scriptManagerExec(
// "print('Hello from Lua!')\n"
// "luaCallable()\n"
// );
scriptcontext_t testCtx;
errorChain(scriptContextInit(&testCtx));
errorChain(scriptContextExecFile(&testCtx, "script/test.dsf"));
errorChain(scriptContextCallFunc(&testCtx, "testFunction", NULL, 0, NULL));
scriptvalue_t args[2] = {
{ .type = SCRIPT_VALUE_TYPE_INT, .value.intValue = 5 },
{ .type = SCRIPT_VALUE_TYPE_INT, .value.intValue = 7 }
};
scriptvalue_t ret = { .type = SCRIPT_VALUE_TYPE_INT };
errorChain(scriptContextCallFunc(&testCtx, "doAdd", args, 2, &ret));
printf("doAdd returned: %d\n", ret.value.intValue);
scriptContextDispose(&testCtx);
errorOk();
}
@@ -55,7 +67,6 @@ errorret_t engineUpdate(void) {
sceneManagerUpdate();
errorChain(displayUpdate());
if(inputPressed(INPUT_ACTION_RAGEQUIT)) ENGINE.running = false;
errorOk();

View File

@@ -7,4 +7,5 @@
target_sources(${DUSK_TARGET_NAME}
PRIVATE
scriptmanager.c
scriptcontext.c
)

196
src/script/scriptcontext.c Normal file
View File

@@ -0,0 +1,196 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "scriptcontext.h"
#include "assert/assert.h"
#include "asset/asset.h"
#include "util/memory.h"
#include "debug/debug.h"
errorret_t 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) {
errorThrow("Failed to init Lua state");
}
luaL_openlibs(context->luaState);
// Register shared functions
scriptContextRegFunc(context, "print", scriptContextPrint);
errorOk();
}
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);
}
errorret_t 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)) {
errorThrow("Function '%s' not found in script context", fnName);
}
// 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:
errorThrow("Unsupported argument type %d", 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);
errorThrow("Failed to call function '%s': %s", fnName, strErr);
}
// Was there a ret value?
if(retValue == NULL) {
errorOk();
}
// Get ret value
switch(retValue->type) {
case SCRIPT_VALUE_TYPE_INT:
if(!lua_isinteger(context->luaState, -1)) {
errorThrow("Expected integer return value from '%s'", fnName);
}
retValue->value.intValue = (int32_t)lua_tointeger(context->luaState, -1);
break;
case SCRIPT_VALUE_TYPE_FLOAT:
if(!lua_isnumber(context->luaState, -1)) {
errorThrow("Expected float return value from '%s'", fnName);
}
retValue->value.floatValue = (float)lua_tonumber(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:
errorThrow("Unsupported return value type %d", retValue->type);
}
errorOk();
}
errorret_t 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);
errorThrow("Failed to execute Lua: ", strErr);
}
errorOk();
}
errorret_t scriptContextExecFile(scriptcontext_t *ctx, const char_t *fname) {
assertNotNull(ctx, "Script context cannot be NULL");
assertNotNull(fname, "Filename cannot be NULL");
assetscript_t script;
errorChain(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);
errorThrow("Failed to load Lua script: %s", 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);
errorThrow("Failed to execute Lua script: %s", strErr);
}
errorChain(assetScriptDispose(&script));
errorOk();
}
int32_t scriptContextPrint(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
int n = lua_gettop(L);
luaL_Buffer b;
luaL_buffinit(L, &b);
for (int i = 1; i <= n; ++i) {
size_t len;
const char *s = luaL_tolstring(L, i, &len); // converts any value to string
luaL_addlstring(&b, s, len);
lua_pop(L, 1); // pop result of luaL_tolstring
if (i < n) luaL_addlstring(&b, "\t", 1);
}
luaL_pushresult(&b);
const char *msg = lua_tostring(L, -1);
debugPrint("%s\n", msg);
return 0; // no values returned to Lua
}
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;
}
}

View File

@@ -0,0 +1,90 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "error/error.h"
#include "scriptvalue.h"
#include <lua.h>
#include <lauxlib.h>
#include <lualib.h>
typedef struct scriptcontext_s {
lua_State *luaState;
} scriptcontext_t;
/**
* Initialize a script context.
*
* @param context The script context to initialize.
* @return The error return value.
*/
errorret_t scriptContextInit(scriptcontext_t *context);
/**
* Register a C function within a script context.
*
* @param context The script context to use.
* @param fnName The name of the function in Lua.
* @param function The C function to register.
*/
void scriptContextRegFunc(
scriptcontext_t *context,
const char_t *fnName,
lua_CFunction function
);
/**
* Call a Lua function within a script context.
*
* @param context The script context to use.
* @param fnName The name of the Lua function to call.
* @param args Array of args to pass to the function (or NULL for no args)
* @param argCount The number of arguments in the args array (omitable).
* @param retValue Output to store returned value (or NULL for no return value).
* @return The error return value.
*/
errorret_t scriptContextCallFunc(
scriptcontext_t *context,
const char_t *fnName,
const scriptvalue_t *args,
const int32_t argCount,
scriptvalue_t *retValue
);
/**
* Execute a script within a script context.
*
* @param context The script context to use.
* @param script The script to execute.
* @return The error return value.
*/
errorret_t scriptContextExec(scriptcontext_t *context, const char_t *script);
/**
* Execute a script from a file within a script context.
*
* @param ctx The script context to use.
* @param fname The filename of the script to execute.
* @return The error return value.
*/
errorret_t scriptContextExecFile(scriptcontext_t *ctx, const char_t *fname);
/**
* Overridden print function for Lua scripts to output to debug.
*
* @param L The Lua state.
* @return The number of return values.
*/
int32_t scriptContextPrint(lua_State *L);
/**
* Dispose of a script context.
*
* @param context The script context to dispose of.
*/
void scriptContextDispose(scriptcontext_t *context);

View File

@@ -8,74 +8,16 @@
#include "scriptmanager.h"
#include "util/memory.h"
#include "assert/assert.h"
#include "debug/debug.h"
#include "asset/asset.h"
int luaCallable(lua_State *L) {
printf("This function was called from Lua!\n");
return 0;
}
scriptmanager_t SCRIPT_MANAGER;
errorret_t scriptManagerInit() {
memoryZero(&SCRIPT_MANAGER, sizeof(scriptmanager_t));
SCRIPT_MANAGER.luaState = luaL_newstate();
if(SCRIPT_MANAGER.luaState == NULL) {
errorThrow("Failed to init Lua state");
}
luaL_openlibs(SCRIPT_MANAGER.luaState);
lua_register(SCRIPT_MANAGER.luaState, "luaCallable", luaCallable);
errorChain(scriptManagerExecFile("script/test.dsf"));
errorOk();
}
errorret_t scriptManagerExecString(const char_t *script) {
assertNotNull(script, "Script cannot be NULL");
assertNotNull(SCRIPT_MANAGER.luaState, "Lua state is not initialized");
if(luaL_dostring(SCRIPT_MANAGER.luaState, script) != LUA_OK) {
const char_t *strErr = lua_tostring(SCRIPT_MANAGER.luaState, -1);
lua_pop(SCRIPT_MANAGER.luaState, 1);
errorThrow("Failed to execute Lua: ", strErr);
}
errorOk();
}
errorret_t scriptManagerExecFile(const char_t *filename) {
assertNotNull(filename, "Filename cannot be NULL");
assertNotNull(SCRIPT_MANAGER.luaState, "Lua state is not initialized");
assetscript_t script;
errorChain(assetLoad(filename, &script));
if(lua_load(
SCRIPT_MANAGER.luaState, assetScriptReader, &script, filename, NULL
) != LUA_OK) {
const char_t *strErr = lua_tostring(SCRIPT_MANAGER.luaState, -1);
lua_pop(SCRIPT_MANAGER.luaState, 1);
errorThrow("Failed to load Lua script: %s", strErr);
}
if(lua_pcall(SCRIPT_MANAGER.luaState, 0, LUA_MULTRET, 0) != LUA_OK) {
const char_t *strErr = lua_tostring(SCRIPT_MANAGER.luaState, -1);
lua_pop(SCRIPT_MANAGER.luaState, 1);
errorThrow("Failed to execute Lua script: %s", strErr);
}
errorChain(assetScriptDispose(&script));
errorOk();
}
errorret_t scriptManagerDispose() {
if(SCRIPT_MANAGER.luaState != NULL) {
lua_close(SCRIPT_MANAGER.luaState);
SCRIPT_MANAGER.luaState = NULL;
}
errorOk();
}

View File

@@ -7,12 +7,9 @@
#pragma once
#include "error/error.h"
#include <lua.h>
#include <lauxlib.h>
#include <lualib.h>
typedef struct scriptmanager_s {
lua_State *luaState;
void *nothing;
} scriptmanager_t;
extern scriptmanager_t SCRIPT_MANAGER;
@@ -24,22 +21,6 @@ extern scriptmanager_t SCRIPT_MANAGER;
*/
errorret_t scriptManagerInit();
/**
* Execute a Lua script.
*
* @param script The script to execute.
* @return The error return value.
*/
errorret_t scriptManagerExecString(const char_t *script);
/**
* Execute a Lua script from a file.
*
* @param filename The filename of the script to execute.
* @return The error return value.
*/
errorret_t scriptManagerExecFile(const char_t *filename);
/**
* Dispose of the script manager.
*

26
src/script/scriptvalue.h Normal file
View File

@@ -0,0 +1,26 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "dusk.h"
#define SCRIPT_VALUE_TYPE_NIL 0
#define SCRIPT_VALUE_TYPE_INT 1
#define SCRIPT_VALUE_TYPE_FLOAT 2
#define SCRIPT_VALUE_TYPE_STRING 3
#define SCRIPT_VALUE_TYPE_BOOL 4
typedef struct scriptvalue_s {
uint8_t type;
union {
int32_t intValue;
float floatValue;
const char_t *strValue;
bool boolValue;
} value;
} scriptvalue_t;

View File

@@ -11,4 +11,7 @@ target_sources(${DUSK_TARGET_NAME}
uidebug.c
uiframe.c
uitextbox.c
)
)
# Subdirs
add_subdirectory(element)

View File

@@ -0,0 +1,9 @@
# Copyright (c) 2025 Dominic Masters
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
# Sources
target_sources(${DUSK_TARGET_NAME}
PRIVATE
)

View File

@@ -0,0 +1,16 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
typedef enum {
UI_ELEMENT_TYPE_NULL,
UI_ELEMENT_TYPE_TEXT,
UI_ELEMENT_TYPE_COUNT,
} uielementtype_t;