From b640295be2f8223671d5c9e008481992c3b46a0a Mon Sep 17 00:00:00 2001 From: Dominic Masters Date: Mon, 20 Apr 2026 15:34:24 -0500 Subject: [PATCH] Scene script --- src/dusk/asset/asset.c | 4 +- src/dusk/asset/assetfile.h | 2 + src/dusk/console/console.c | 2 +- src/dusk/engine/engine.c | 8 +- src/dusk/scene/scene.c | 102 ++++++++++++++++++++- src/dusk/scene/scene.h | 21 ++++- src/dusk/script/module/scene/modulescene.c | 7 +- src/dusk/util/string.c | 1 + src/duskdolphin/asset/assetdolphin.c | 4 +- src/duskdolphin/asset/assetdolphin.h | 1 + src/dusklinux/asset/assetlinux.c | 12 +-- src/dusklinux/asset/assetlinux.h | 3 +- src/duskpsp/asset/assetpbp.c | 2 +- src/duskpsp/asset/assetpbp.h | 1 + 14 files changed, 143 insertions(+), 27 deletions(-) diff --git a/src/dusk/asset/asset.c b/src/dusk/asset/asset.c index 64b8ab2f..2b937967 100644 --- a/src/dusk/asset/asset.c +++ b/src/dusk/asset/asset.c @@ -25,7 +25,7 @@ errorret_t assetInit(void) { } bool_t assetFileExists(const char_t *filename) { - assertStrLenMax(filename, FILENAME_MAX, "Filename too long."); + assertStrLenMax(filename, ASSET_FILE_PATH_MAX, "Filename too long."); zip_int64_t idx = zip_name_locate(ASSET.zip, filename, 0); if(idx < 0) return false; @@ -38,7 +38,7 @@ errorret_t assetLoad( void *params, void *output ) { - assertStrLenMax(filename, FILENAME_MAX, "Filename too long."); + assertStrLenMax(filename, ASSET_FILE_PATH_MAX, "Filename too long."); assertNotNull(output, "Output pointer cannot be NULL."); assertNotNull(loader, "Asset file loader cannot be NULL."); diff --git a/src/dusk/asset/assetfile.h b/src/dusk/asset/assetfile.h index ac807ed0..243232f0 100644 --- a/src/dusk/asset/assetfile.h +++ b/src/dusk/asset/assetfile.h @@ -9,6 +9,8 @@ #include "error/error.h" #include +#define ASSET_FILE_PATH_MAX FILENAME_MAX + typedef struct assetfile_s assetfile_t; typedef errorret_t (*assetfileloader_t)(assetfile_t *file); diff --git a/src/dusk/console/console.c b/src/dusk/console/console.c index e8b31983..02cfae8d 100644 --- a/src/dusk/console/console.c +++ b/src/dusk/console/console.c @@ -31,7 +31,7 @@ void consoleInit() { // Register vars #define REG(name, value) consoleRegVar(name, value, NULL) - REG("console", "0"); + REG("console", "1"); #undef REG // Register cmdss diff --git a/src/dusk/engine/engine.c b/src/dusk/engine/engine.c index ad440d32..ef17bde7 100644 --- a/src/dusk/engine/engine.c +++ b/src/dusk/engine/engine.c @@ -55,22 +55,28 @@ errorret_t engineInit(const int32_t argc, const char_t **argv) { errorChain(scriptContextExecFile(&ctx, "init.lua")); scriptContextDispose(&ctx); + sceneSet("test/scene.lua"); + errorOk(); } errorret_t engineUpdate(void) { + // Order here is important. errorChain(networkUpdate()); timeUpdate(); inputUpdate(); consoleUpdate(); uiUpdate(); - errorChain(sceneUpdate()); physicsManagerUpdate(); errorChain(gameUpdate()); errorChain(displayUpdate()); if(inputPressed(INPUT_ACTION_RAGEQUIT)) ENGINE.running = false; + // Scene update occurs last because only after rendering would we want to do + // scene switching, refer to sceneSet() for information. + errorChain(sceneUpdate()); + errorOk(); } diff --git a/src/dusk/scene/scene.c b/src/dusk/scene/scene.c index 57e7c3f9..fb77aba0 100644 --- a/src/dusk/scene/scene.c +++ b/src/dusk/scene/scene.c @@ -16,6 +16,7 @@ #include "display/text/text.h" #include "display/screen/screen.h" #include "console/console.h" +#include "util/string.h" scene_t SCENE; @@ -30,6 +31,27 @@ errorret_t sceneUpdate(void) { errorOk(); } #endif + + // Is the scene changing? + if(stringCompare(SCENE.sceneNext, SCENE.sceneCurrent) != 0) { + errorChain(sceneSetImmediate(SCENE.sceneNext)); + } + + // Call scene update function if script loaded + if(!SCENE.scriptActive) { + errorOk(); + } + + assertNotNull(SCENE.script.luaState, "Script context null?"); + lua_getglobal(SCENE.script.luaState, "sceneUpdate"); + if(lua_isfunction(SCENE.script.luaState, -1)) { + if(lua_pcall(SCENE.script.luaState, 0, 0, 0) != LUA_OK) { + errorThrow( + "Failed to call sceneUpdate function in script: %s\n", + lua_tostring(SCENE.script.luaState, -1) + ); + } + } errorOk(); } @@ -42,7 +64,6 @@ errorret_t sceneRender(void) { COMPONENT_TYPE_CAMERA, camEnts, camComps ); - // Prep Matricies mat4 view, proj, model; @@ -142,10 +163,85 @@ errorret_t sceneRender(void) { errorOk(); } -errorret_t sceneSet(const char_t *script) { +errorret_t sceneSetImmediate(const char_t *script) { + // Update the next scene name (shouldn't really happen but its for safety). + if(script != SCENE.sceneNext) { + stringCopy( + SCENE.sceneNext, + script == NULL ? "" : script, + ASSET_FILE_PATH_MAX + ); + } + + // If there's currently a scene active, dispose of it first. + if(SCENE.scriptActive) { + assertNotNull(SCENE.script.luaState, "Script context null?"); + + // Call sceneDispose function + lua_getglobal(SCENE.script.luaState, "sceneDispose"); + if(lua_isfunction(SCENE.script.luaState, -1)) { + if(lua_pcall(SCENE.script.luaState, 0, 0, 0) != LUA_OK) { + errorThrow( + "Failed to call sceneDispose function in script: %s\n", + lua_tostring(SCENE.script.luaState, -1) + ); + } + } + + // Scenes should ideally clean themselves up, this is just a warning because + // you can technically move entities between scenes, but it's messy. + for(entityid_t i = 0; i < ENTITY_COUNT_MAX; i++) { + if(ENTITY_MANAGER.entities[i].state & ENTITY_STATE_ACTIVE) { + consolePrint( + "Entity %d is active after scene dispose\n", i + ); + } + } + + // Unload script context. + scriptContextDispose(&SCENE.script); + SCENE.scriptActive = false; + } + + // At this point the existing scene is unloaded, update the current scene. + stringCopy( + SCENE.sceneCurrent, + script == NULL ? "" : script, + ASSET_FILE_PATH_MAX + ); + + // If we have a new scene, execute it. + if(script != NULL) { + errorChain(scriptContextInit(&SCENE.script)); + SCENE.scriptActive = true; + errorChain(scriptContextExecFile(&SCENE.script, script)); + + // Call the init function. + lua_getglobal(SCENE.script.luaState, "sceneInit"); + if(lua_isfunction(SCENE.script.luaState, -1)) { + if(lua_pcall(SCENE.script.luaState, 0, 0, 0) != LUA_OK) { + errorThrow( + "Failed to call sceneInit function in script: %s\n", + lua_tostring(SCENE.script.luaState, -1) + ); + } + } + } + errorOk(); } -void sceneDispose(void) { +void sceneSet(const char_t *script) { + // Tell the scene that next update it needs to change scenes. This will always + // occur on the NEXT frame. + stringCopy( + SCENE.sceneNext, + script == NULL ? "" : script, + ASSET_FILE_PATH_MAX + ); +} +errorret_t sceneDispose(void) { + errorChain(sceneSetImmediate(NULL)); + errorOk(); } \ No newline at end of file diff --git a/src/dusk/scene/scene.h b/src/dusk/scene/scene.h index 6e0a079d..f52ba82a 100644 --- a/src/dusk/scene/scene.h +++ b/src/dusk/scene/scene.h @@ -6,10 +6,14 @@ */ #pragma once -#include "error/error.h" +#include "script/scriptcontext.h" +#include "asset/assetfile.h" typedef struct { - void *nothing; + scriptcontext_t script; + bool_t scriptActive; + char_t sceneCurrent[ASSET_FILE_PATH_MAX]; + char_t sceneNext[ASSET_FILE_PATH_MAX]; } scene_t; extern scene_t SCENE; @@ -35,14 +39,23 @@ errorret_t sceneUpdate(void); */ errorret_t sceneRender(void); +/** + * Internal only method, immediately sets the scene. This will basically crash + * Lua if called from its tree. + */ +errorret_t sceneSetImmediate(const char_t *script); + /** * Set the current scene by script name. * * @param script The script name of the scene to set. + * @return The error return value. */ -errorret_t sceneSet(const char_t *script); +void sceneSet(const char_t *script); /** * Dispose of the scene subsystem. + * + * @return The error return value. */ -void sceneDispose(void); \ No newline at end of file +errorret_t sceneDispose(void); \ No newline at end of file diff --git a/src/dusk/script/module/scene/modulescene.c b/src/dusk/script/module/scene/modulescene.c index 30df0eb7..b0024a5c 100644 --- a/src/dusk/script/module/scene/modulescene.c +++ b/src/dusk/script/module/scene/modulescene.c @@ -26,12 +26,7 @@ int moduleSceneSet(lua_State *L) { const char_t *script = lua_tostring(L, 1); - errorret_t err = sceneSet(script); - if(err.code != ERROR_OK) { - errorCatch(errorPrint(err)); - luaL_error(L, "Failed to set scene"); - return 0; - } + sceneSet(script); return 0; } \ No newline at end of file diff --git a/src/dusk/util/string.c b/src/dusk/util/string.c index 4e00cdb1..d4e108ed 100644 --- a/src/dusk/util/string.c +++ b/src/dusk/util/string.c @@ -18,6 +18,7 @@ void stringCopy(char_t *dest, const char_t *src, const size_t destSize) { assertNotNull(src, "src must not be NULL"); assertTrue(destSize > 0, "destSize must be greater than 0"); assertStrLenMax(src, destSize, "src is too long"); + assertTrue(dest != src, "Cannot copy string to itself."); memoryCopy(dest, src, strlen(src) + 1); } diff --git a/src/duskdolphin/asset/assetdolphin.c b/src/duskdolphin/asset/assetdolphin.c index 29dc367b..f441ece3 100644 --- a/src/duskdolphin/asset/assetdolphin.c +++ b/src/duskdolphin/asset/assetdolphin.c @@ -21,7 +21,7 @@ errorret_t assetInitDolphin(void) { if(!fatInitDefault()) errorThrow("Failed to initialize FAT filesystem."); char_t **dolphinSearchPath = (char_t **)ASSET_DOLPHIN_PATHS; - char_t foundPath[FILENAME_MAX]; + char_t foundPath[ASSET_FILE_PATH_MAX]; foundPath[0] = '\0'; do { // Try open dir @@ -40,7 +40,7 @@ errorret_t assetInitDolphin(void) { // Copy out filename snprintf( foundPath, - FILENAME_MAX, + ASSET_FILE_PATH_MAX, "%s/%s", *dolphinSearchPath, ASSET_FILE_NAME diff --git a/src/duskdolphin/asset/assetdolphin.h b/src/duskdolphin/asset/assetdolphin.h index 0ad62e07..f55dc81f 100644 --- a/src/duskdolphin/asset/assetdolphin.h +++ b/src/duskdolphin/asset/assetdolphin.h @@ -7,6 +7,7 @@ #pragma once #include "error/error.h" +#include "asset/assetfile.h" static const char_t *ASSET_DOLPHIN_PATHS[] = { "/", diff --git a/src/dusklinux/asset/assetlinux.c b/src/dusklinux/asset/assetlinux.c index 025e3a38..a7e93b5c 100644 --- a/src/dusklinux/asset/assetlinux.c +++ b/src/dusklinux/asset/assetlinux.c @@ -14,12 +14,12 @@ errorret_t assetInitLinux(void) { // Engine may have been provided the launch path if(ENGINE.argc > 0) { // Get the directory of the executable - char_t buffer[FILENAME_MAX]; - stringCopy(buffer, ENGINE.argv[0], FILENAME_MAX); + char_t buffer[ASSET_FILE_PATH_MAX]; + stringCopy(buffer, ENGINE.argv[0], ASSET_FILE_PATH_MAX); size_t len = strlen(buffer); // Normalize slashes - for(size_t i = 0; i < FILENAME_MAX; i++) { + for(size_t i = 0; i < ASSET_FILE_PATH_MAX; i++) { if(buffer[i] == '\0') break; if(buffer[i] == '\\') buffer[i] = '/'; } @@ -38,15 +38,15 @@ errorret_t assetInitLinux(void) { // Did we find a slash? if(end != buffer) { // We found the directory, set as system path - stringCopy(ASSET.platform.systemPath, buffer, FILENAME_MAX); + stringCopy(ASSET.platform.systemPath, buffer, ASSET_FILE_PATH_MAX); } } // Default system path, intended to be overridden by the platform - stringCopy(ASSET.platform.systemPath, ".", FILENAME_MAX); + stringCopy(ASSET.platform.systemPath, ".", ASSET_FILE_PATH_MAX); // Open zip file - char_t searchPath[FILENAME_MAX]; + char_t searchPath[ASSET_FILE_PATH_MAX]; const char_t **path = ASSET_LINUX_SEARCH_PATHS; int32_t error; do { diff --git a/src/dusklinux/asset/assetlinux.h b/src/dusklinux/asset/assetlinux.h index eda85a42..2bd7cddc 100644 --- a/src/dusklinux/asset/assetlinux.h +++ b/src/dusklinux/asset/assetlinux.h @@ -7,6 +7,7 @@ #pragma once #include "error/error.h" +#include "asset/assetfile.h" static const char_t *ASSET_LINUX_SEARCH_PATHS[] = { "%s/%s", @@ -19,7 +20,7 @@ static const char_t *ASSET_LINUX_SEARCH_PATHS[] = { }; typedef struct { - char_t systemPath[FILENAME_MAX]; + char_t systemPath[ASSET_FILE_PATH_MAX]; } assetlinux_t; /** diff --git a/src/duskpsp/asset/assetpbp.c b/src/duskpsp/asset/assetpbp.c index 5b970e09..e97237d9 100644 --- a/src/duskpsp/asset/assetpbp.c +++ b/src/duskpsp/asset/assetpbp.c @@ -12,7 +12,7 @@ errorret_t assetInitPBP(const char_t *pbpPath) { assertNotNull(pbpPath, "PBP path cannot be null."); assertStrLenMin(pbpPath, 1, "PBP path cannot be empty."); - assertStrLenMax(pbpPath, FILENAME_MAX, "PBP path is too long."); + assertStrLenMax(pbpPath, ASSET_FILE_PATH_MAX, "PBP path is too long."); ASSET.platform.pbpFile = fopen(pbpPath, "rb"); if(ASSET.platform.pbpFile == NULL) { diff --git a/src/duskpsp/asset/assetpbp.h b/src/duskpsp/asset/assetpbp.h index 88ca078a..40fc2187 100644 --- a/src/duskpsp/asset/assetpbp.h +++ b/src/duskpsp/asset/assetpbp.h @@ -7,6 +7,7 @@ #pragma once #include "error/error.h" +#include "asset/assetfile.h" #define ASSET_PBP_SIGNATURE_SIZE 4 #define ASSET_PBP_SIGNATURE "\0PBP"