From 24b92ecc2a4da7730976bebc6cf7e77280b68e6d Mon Sep 17 00:00:00 2001 From: djoslin0 Date: Sat, 14 Jun 2025 02:49:07 -0700 Subject: [PATCH] Add a safer version of Lua's require() (#847) I didn't add standard Lua require() because I've always been afraid of it. I'm not sure we can guarantee which files it will read (or not read). Instead, here is a custom implementation. It should work more or less the same and allow for more modular code. For backwards compatibility reasons, all of the lua files in the base mod folder will be loaded as in the past. Aka one at a time and alphabetically. However, now coop will look for Lua files in subdirectories and will load them in when another Lua file calls require(). The file search order is more reasonable than normal Lua require(). It will first look for files relative to the currently running script. If there is no matching relative file, it will pick from any Lua file that is in any of the mod's subdirectories. --------- Co-authored-by: MysterD --- autogen/gen_hooks.py | 2 +- data/dynos.c.h | 6 +- data/dynos.cpp.h | 7 +- data/dynos_c.cpp | 12 +- data/dynos_mgr_actor.cpp | 11 +- data/dynos_mgr_bhv.cpp | 8 +- src/engine/behavior_script.c | 46 ++++-- src/game/mario_misc.c | 19 ++- src/pc/lua/smlua.c | 97 +++++++----- src/pc/lua/smlua.h | 2 + src/pc/lua/smlua_functions.c | 4 +- src/pc/lua/smlua_hook_events_autogen.inl | 112 +++++++------- src/pc/lua/smlua_hooks.c | 39 +++-- src/pc/lua/smlua_hooks.h | 3 +- src/pc/lua/smlua_require.c | 180 +++++++++++++++++++++++ src/pc/lua/smlua_require.h | 9 ++ src/pc/lua/smlua_sync_table.c | 10 +- src/pc/mods/mod.c | 39 ++++- src/pc/mods/mods_utils.c | 14 ++ src/pc/mods/mods_utils.h | 2 + 20 files changed, 473 insertions(+), 149 deletions(-) create mode 100644 src/pc/lua/smlua_require.c create mode 100644 src/pc/lua/smlua_require.h diff --git a/autogen/gen_hooks.py b/autogen/gen_hooks.py index fd76b5534..3ad78c07c 100644 --- a/autogen/gen_hooks.py +++ b/autogen/gen_hooks.py @@ -29,7 +29,7 @@ SMLUA_CALL_EVENT_HOOKS_SET_HOOK_RESULT = """ SMLUA_CALL_EVENT_HOOKS_CALLBACK = """ // call the callback - if (0 != smlua_call_hook(L, {n_inputs}, {n_outputs}, 0, hook->mod[i])) {{ + if (0 != smlua_call_hook(L, {n_inputs}, {n_outputs}, 0, hook->mod[i], hook->modFile[i])) {{ LOG_LUA("Failed to call the callback for hook %s", sLuaHookedEventTypeName[{hook_type}]); continue; }}{set_hook_result} diff --git a/data/dynos.c.h b/data/dynos.c.h index 56f65b360..bbfc0d74c 100644 --- a/data/dynos.c.h +++ b/data/dynos.c.h @@ -36,9 +36,9 @@ void dynos_generate_packs(const char* directory); // -- geos -- // void dynos_actor_override(struct Object* obj, void** aSharedChild); -void dynos_add_actor_custom(s32 modIndex, const char *filePath, const char* geoName); +void dynos_add_actor_custom(s32 modIndex, s32 modFileIndex, const char *filePath, const char* geoName); const void* dynos_geolayout_get(const char *name); -bool dynos_actor_get_mod_index_and_token(struct GraphNode *graphNode, u32 tokenIndex, s32 *modIndex, const char **token); +bool dynos_actor_get_mod_index_and_token(struct GraphNode *graphNode, u32 tokenIndex, s32 *modIndex, s32 *modFileIndex, const char **token); void dynos_actor_register_modified_graph_node(struct GraphNode *node); // -- collisions -- // @@ -72,7 +72,7 @@ Collision *dynos_level_get_collision(u32 level, u16 area); // -- behaviors -- // void dynos_add_behavior(s32 modIndex, const char *filePath, const char *behaviorName); -s32 dynos_behavior_get_active_mod_index(BehaviorScript *bhvScript); +bool dynos_behavior_get_active_mod_index(BehaviorScript *bhvScript, s32 *modIndex, s32 *modFileIndex); const char *dynos_behavior_get_token(BehaviorScript *bhvScript, u32 index); void dynos_behavior_hook_all_custom_behaviors(void); diff --git a/data/dynos.cpp.h b/data/dynos.cpp.h index d3bfa3261..2065b3321 100644 --- a/data/dynos.cpp.h +++ b/data/dynos.cpp.h @@ -572,6 +572,7 @@ struct GfxData : NoCopy { s32 mErrorCount = 0; u32 mModelIdentifier = 0; s32 mModIndex = 0; + s32 mModFileIndex = 0; SysPath mPackFolder; Array mPointerList; Array> mPointerOffsetList; @@ -889,9 +890,9 @@ void DynOS_Pack_AddTex(PackData* aPackData, DataNode* aTexData); // std::map &DynOS_Actor_GetValidActors(); -void DynOS_Actor_AddCustom(s32 aModIndex, const SysPath &aFilename, const char *aActorName); +void DynOS_Actor_AddCustom(s32 aModIndex, s32 aModFileIndex, const SysPath &aFilename, const char *aActorName); const void *DynOS_Actor_GetLayoutFromName(const char *aActorName); -bool DynOS_Actor_GetModIndexAndToken(const GraphNode *aGraphNode, u32 aTokenIndex, s32 *outModIndex, const char **outToken); +bool DynOS_Actor_GetModIndexAndToken(const GraphNode *aGraphNode, u32 aTokenIndex, s32 *outModIndex, s32 *outModFileIndex, const char **outToken); ActorGfx* DynOS_Actor_GetActorGfx(const GraphNode* aGraphNode); void DynOS_Actor_Valid(const void* aGeoref, ActorGfx& aActorGfx); void DynOS_Actor_Invalid(const void* aGeoref, s32 aPackIndex); @@ -946,7 +947,7 @@ void DynOS_Lvl_ModShutdown(); Array> &DynOS_Bhv_GetArray(); void DynOS_Bhv_Activate(s32 modIndex, const SysPath &aFilename, const char *aBehaviorName); GfxData *DynOS_Bhv_GetActiveGfx(BehaviorScript *bhvScript); -s32 DynOS_Bhv_GetActiveModIndex(BehaviorScript *bhvScript); +bool DynOS_Bhv_GetActiveModIndex(BehaviorScript *bhvScript, s32 *modIndex, s32 *modFileIndex); const char *DynOS_Bhv_GetToken(BehaviorScript *bhvScript, u32 index); void DynOS_Bhv_HookAllCustomBehaviors(); void DynOS_Bhv_ModShutdown(); diff --git a/data/dynos_c.cpp b/data/dynos_c.cpp index bac30dd43..5b59d32f8 100644 --- a/data/dynos_c.cpp +++ b/data/dynos_c.cpp @@ -111,16 +111,16 @@ void dynos_actor_override(struct Object* obj, void** aSharedChild) { DynOS_Actor_Override(obj, aSharedChild); } -void dynos_add_actor_custom(s32 modIndex, const char *filePath, const char* geoName) { - DynOS_Actor_AddCustom(modIndex, filePath, geoName); +void dynos_add_actor_custom(s32 modIndex, s32 modFileIndex, const char *filePath, const char* geoName) { + DynOS_Actor_AddCustom(modIndex, modFileIndex, filePath, geoName); } const void* dynos_geolayout_get(const char *name) { return DynOS_Actor_GetLayoutFromName(name); } -bool dynos_actor_get_mod_index_and_token(struct GraphNode *graphNode, u32 tokenIndex, s32 *modIndex, const char **token) { - return DynOS_Actor_GetModIndexAndToken(graphNode, tokenIndex, modIndex, token); +bool dynos_actor_get_mod_index_and_token(struct GraphNode *graphNode, u32 tokenIndex, s32 *modIndex, s32 *modFileIndex, const char **token) { + return DynOS_Actor_GetModIndexAndToken(graphNode, tokenIndex, modIndex, modFileIndex, token); } void dynos_actor_register_modified_graph_node(struct GraphNode *node) { @@ -232,8 +232,8 @@ void dynos_add_behavior(s32 modIndex, const char *filePath, const char *behavior DynOS_Bhv_Activate(modIndex, filePath, behaviorName); } -s32 dynos_behavior_get_active_mod_index(BehaviorScript *bhvScript) { - return DynOS_Bhv_GetActiveModIndex(bhvScript); +bool dynos_behavior_get_active_mod_index(BehaviorScript *bhvScript, s32 *modIndex, s32 *modFileIndex) { + return DynOS_Bhv_GetActiveModIndex(bhvScript, modIndex, modFileIndex); } const char *dynos_behavior_get_token(BehaviorScript *bhvScript, u32 index) { diff --git a/data/dynos_mgr_actor.cpp b/data/dynos_mgr_actor.cpp index aa9056fb0..92fa14ee8 100644 --- a/data/dynos_mgr_actor.cpp +++ b/data/dynos_mgr_actor.cpp @@ -31,7 +31,7 @@ std::map &DynOS_Actor_GetValidActors() { return DynosValidActors(); } -void DynOS_Actor_AddCustom(s32 aModIndex, const SysPath &aFilename, const char *aActorName) { +void DynOS_Actor_AddCustom(s32 aModIndex, s32 aModFileIndex, const SysPath &aFilename, const char *aActorName) { const void* georef = DynOS_Builtin_Actor_GetFromName(aActorName); u16 actorLen = strlen(aActorName); @@ -45,6 +45,7 @@ void DynOS_Actor_AddCustom(s32 aModIndex, const SysPath &aFilename, const char * return; } _GfxData->mModIndex = aModIndex; + _GfxData->mModFileIndex = aModFileIndex; void* geoLayout = (*(_GfxData->mGeoLayouts.end() - 1))->mData; if (!geoLayout) { @@ -117,7 +118,7 @@ const void *DynOS_Actor_GetLayoutFromName(const char *aActorName) { return NULL; } -bool DynOS_Actor_GetModIndexAndToken(const GraphNode *aGraphNode, u32 aTokenIndex, s32 *outModIndex, const char **outToken) { +bool DynOS_Actor_GetModIndexAndToken(const GraphNode *aGraphNode, u32 aTokenIndex, s32 *outModIndex, s32 *outModFileIndex, const char **outToken) { ActorGfx *_ActorGfx = DynOS_Actor_GetActorGfx(aGraphNode); if (_ActorGfx) { GfxData *_GfxData = _ActorGfx->mGfxData; @@ -125,6 +126,9 @@ bool DynOS_Actor_GetModIndexAndToken(const GraphNode *aGraphNode, u32 aTokenInde if (outModIndex) { *outModIndex = _GfxData->mModIndex; } + if (outModFileIndex) { + *outModFileIndex = _GfxData->mModFileIndex; + } if (outToken) { if (!aTokenIndex || aTokenIndex > _GfxData->mLuaTokenList.Count()) { return false; @@ -139,6 +143,9 @@ bool DynOS_Actor_GetModIndexAndToken(const GraphNode *aGraphNode, u32 aTokenInde if (outModIndex) { *outModIndex = _GfxData->mModIndex; } + if (outModFileIndex) { + *outModFileIndex = _GfxData->mModFileIndex; + } if (outToken) { if (!aTokenIndex || aTokenIndex > _GfxData->mLuaTokenList.Count()) { return false; diff --git a/data/dynos_mgr_bhv.cpp b/data/dynos_mgr_bhv.cpp index 51d3b2731..9f501d577 100644 --- a/data/dynos_mgr_bhv.cpp +++ b/data/dynos_mgr_bhv.cpp @@ -62,7 +62,7 @@ GfxData *DynOS_Bhv_GetActiveGfx(BehaviorScript *bhvScript) { return NULL; } -s32 DynOS_Bhv_GetActiveModIndex(BehaviorScript *bhvScript) { +bool DynOS_Bhv_GetActiveModIndex(BehaviorScript *bhvScript, s32 *modIndex, s32 *modFileIndex) { auto &_CustomBehaviorScripts = DynOS_Bhv_GetArray(); for (s32 i = 0; i < _CustomBehaviorScripts.Count(); ++i) { @@ -70,10 +70,12 @@ s32 DynOS_Bhv_GetActiveModIndex(BehaviorScript *bhvScript) { auto &scripts = gfxData->mBehaviorScripts; if (scripts.Count() == 0) { continue; } if (bhvScript == scripts[scripts.Count() - 1]->mData) { - return gfxData->mModIndex; + *modIndex = gfxData->mModIndex; + *modFileIndex = gfxData->mModFileIndex; + return true; } } - return -1; + return false; } const char *DynOS_Bhv_GetToken(BehaviorScript *bhvScript, u32 index) { diff --git a/src/engine/behavior_script.c b/src/engine/behavior_script.c index 0d06d3b5d..8bfbdc3aa 100644 --- a/src/engine/behavior_script.c +++ b/src/engine/behavior_script.c @@ -913,8 +913,9 @@ static s32 bhv_cmd_call_ext(void) { BehaviorScript *behavior = (BehaviorScript *)gCurrentObject->behavior; - s32 modIndex = dynos_behavior_get_active_mod_index(behavior); - if (modIndex == -1) { + s32 modIndex = -1; + s32 modFileIndex = -1; + if (!dynos_behavior_get_active_mod_index(behavior, &modIndex, &modFileIndex)) { LOG_ERROR("Could not find behavior script mod index."); return BHV_PROC_CONTINUE; } @@ -946,8 +947,9 @@ static s32 bhv_cmd_call_ext(void) { static s32 bhv_cmd_goto_ext(void) { BehaviorScript *behavior = (BehaviorScript *)gCurrentObject->behavior; - s32 modIndex = dynos_behavior_get_active_mod_index(behavior); - if (modIndex == -1) { + s32 modIndex = -1; + s32 modFileIndex = -1; + if (!dynos_behavior_get_active_mod_index(behavior, &modIndex, &modFileIndex)) { LOG_ERROR("Could not find behavior script mod index."); return BHV_PROC_CONTINUE; } @@ -976,8 +978,9 @@ static s32 bhv_cmd_goto_ext(void) { static s32 bhv_cmd_call_native_ext(void) { BehaviorScript *behavior = (BehaviorScript *)gCurrentObject->behavior; - s32 modIndex = dynos_behavior_get_active_mod_index(behavior); - if (modIndex == -1) { + s32 modIndex = -1; + s32 modFileIndex = -1; + if (!dynos_behavior_get_active_mod_index(behavior, &modIndex, &modFileIndex)) { LOG_ERROR("Could not find behavior script mod index."); gCurBhvCommand += 2; return BHV_PROC_CONTINUE; @@ -994,19 +997,27 @@ static s32 bhv_cmd_call_native_ext(void) { } if (!gSmLuaConvertSuccess || funcRef == 0) { - LOG_LUA("Failed to call lua function, could not find lua function '%s'", funcStr); + LOG_LUA("Failed to call lua behavior function, could not find lua function '%s'", funcStr); gCurBhvCommand += 2; return BHV_PROC_CONTINUE; } // Get our mod. - if (modIndex >= gActiveMods.entryCount) { - LOG_LUA("Failed to call lua function, could not find mod"); + if (modIndex < 0 || modIndex >= gActiveMods.entryCount) { + LOG_LUA("Failed to call lua behavior function, could not find mod"); gCurBhvCommand += 2; return BHV_PROC_CONTINUE; } struct Mod *mod = gActiveMods.entries[modIndex]; + // Get our mod file + if (modFileIndex < 0 || modFileIndex >= mod->fileCount) { + LOG_LUA("Failed to call lua behavior function, could not find mod file %d", modFileIndex); + gCurBhvCommand += 2; + return BHV_PROC_CONTINUE; + } + struct ModFile *modFile = &mod->files[modFileIndex]; + // Push the callback onto the stack lua_rawgeti(gLuaState, LUA_REGISTRYINDEX, funcRef); @@ -1014,7 +1025,7 @@ static s32 bhv_cmd_call_native_ext(void) { smlua_push_object(gLuaState, LOT_OBJECT, gCurrentObject, NULL); // Call the callback - if (0 != smlua_call_hook(gLuaState, 1, 0, 0, mod)) { + if (0 != smlua_call_hook(gLuaState, 1, 0, 0, mod, modFile)) { LOG_LUA("Failed to call the function callback: '%s'", funcStr); } @@ -1029,8 +1040,9 @@ static s32 bhv_cmd_spawn_child_ext(void) { BehaviorScript *behavior = (BehaviorScript *)gCurrentObject->behavior; - s32 modIndex = dynos_behavior_get_active_mod_index(behavior); - if (modIndex == -1) { + s32 modIndex = -1; + s32 modFileIndex = -1; + if (!dynos_behavior_get_active_mod_index(behavior, &modIndex, &modFileIndex)) { LOG_ERROR("Could not find behavior script mod index."); gCurBhvCommand += 3; return BHV_PROC_CONTINUE; @@ -1076,8 +1088,9 @@ static s32 bhv_cmd_spawn_child_with_param_ext(void) { BehaviorScript *behavior = (BehaviorScript *)gCurrentObject->behavior; - s32 modIndex = dynos_behavior_get_active_mod_index(behavior); - if (modIndex == -1) { + s32 modIndex = -1; + s32 modFileIndex = -1; + if (!dynos_behavior_get_active_mod_index(behavior, &modIndex, &modFileIndex)) { LOG_ERROR("Could not find behavior script mod index."); gCurBhvCommand += 3; return BHV_PROC_CONTINUE; @@ -1123,8 +1136,9 @@ static s32 bhv_cmd_spawn_obj_ext(void) { BehaviorScript *behavior = (BehaviorScript *)gCurrentObject->behavior; - s32 modIndex = dynos_behavior_get_active_mod_index(behavior); - if (modIndex == -1) { + s32 modIndex = -1; + s32 modFileIndex = -1; + if (!dynos_behavior_get_active_mod_index(behavior, &modIndex, &modFileIndex)) { LOG_ERROR("Could not find behavior script mod index."); gCurBhvCommand += 3; return BHV_PROC_CONTINUE; diff --git a/src/game/mario_misc.c b/src/game/mario_misc.c index 02644b79b..e3071cf7e 100644 --- a/src/game/mario_misc.c +++ b/src/game/mario_misc.c @@ -877,8 +877,9 @@ Gfx *geo_process_lua_function(s32 callContext, struct GraphNode *node, UNUSED Ma // Retrieve mod index and function name s32 modIndex = -1; + s32 modFileIndex = -1; const char *funcStr = NULL; - if (!dynos_actor_get_mod_index_and_token(sharedChild, fnNode->luaTokenIndex, &modIndex, &funcStr)) { + if (!dynos_actor_get_mod_index_and_token(sharedChild, fnNode->luaTokenIndex, &modIndex, &modFileIndex, &funcStr)) { if (modIndex == -1) { LOG_ERROR("Could not find graph node mod index"); } else if (funcStr == NULL) { @@ -895,24 +896,32 @@ Gfx *geo_process_lua_function(s32 callContext, struct GraphNode *node, UNUSED Ma funcRef = smlua_get_any_function_mod_variable(funcStr); } if (!gSmLuaConvertSuccess || funcRef == 0) { - LOG_LUA("Failed to call lua function, could not find lua function '%s'", funcStr); + LOG_LUA("Failed to call lua geo function, could not find lua function '%s'", funcStr); return NULL; } // Get the mod - if (modIndex >= gActiveMods.entryCount) { - LOG_LUA("Failed to call lua function, could not find mod"); + if (modIndex < 0 || modIndex >= gActiveMods.entryCount) { + LOG_LUA("Failed to call lua geo function, could not find mod"); return NULL; } struct Mod *mod = gActiveMods.entries[modIndex]; + // Get our mod file + if (modFileIndex < 0 || modFileIndex >= mod->fileCount) { + LOG_LUA("Failed to call lua geo function, could not find mod file %d", modFileIndex); + gCurBhvCommand += 2; + return BHV_PROC_CONTINUE; + } + struct ModFile *modFile = &mod->files[modFileIndex]; + // Push the callback, the graph node and the current matrix stack index lua_rawgeti(L, LUA_REGISTRYINDEX, funcRef); smlua_push_object(L, LOT_GRAPHNODE, node, NULL); lua_pushinteger(L, gMatStackIndex); // Call the callback - if (0 != smlua_call_hook(L, 2, 0, 0, mod)) { + if (0 != smlua_call_hook(L, 2, 0, 0, mod, modFile)) { LOG_LUA("Failed to call the function callback: '%s'", funcStr); } diff --git a/src/pc/lua/smlua.c b/src/pc/lua/smlua.c index 9924b3546..49dc4c7aa 100644 --- a/src/pc/lua/smlua.c +++ b/src/pc/lua/smlua.c @@ -1,4 +1,5 @@ #include "smlua.h" +#include "pc/lua/smlua_require.h" #include "game/hardcoded.h" #include "pc/mods/mods.h" #include "pc/mods/mods_utils.h" @@ -16,6 +17,7 @@ u8 gLuaInitializingScript = 0; u8 gSmLuaSuppressErrors = 0; struct Mod* gLuaLoadingMod = NULL; struct Mod* gLuaActiveMod = NULL; +struct ModFile* gLuaActiveModFile = NULL; struct Mod* gLuaLastHookMod = NULL; void smlua_mod_error(void) { @@ -190,12 +192,11 @@ static bool smlua_check_binary_header(struct ModFile *file) { return false; } -static void smlua_load_script(struct Mod* mod, struct ModFile* file, u16 remoteIndex) { +void smlua_load_script(struct Mod* mod, struct ModFile* file, u16 remoteIndex, bool isModInit) { if (!smlua_check_binary_header(file)) return; - lua_State* L = gLuaState; - lua_settop(L, 0); + s32 prevTop = lua_gettop(L); gSmLuaConvertSuccess = true; gLuaInitializingScript = 1; @@ -205,6 +206,7 @@ static void smlua_load_script(struct Mod* mod, struct ModFile* file, u16 remoteI if (!f) { LOG_LUA("Failed to load lua script '%s': File not found.", file->cachedPath); gLuaInitializingScript = 0; + lua_settop(L, prevTop); return; } @@ -214,6 +216,7 @@ static void smlua_load_script(struct Mod* mod, struct ModFile* file, u16 remoteI if (!buffer) { LOG_LUA("Failed to load lua script '%s': Cannot allocate buffer.", file->cachedPath); gLuaInitializingScript = 0; + lua_settop(L, prevTop); return; } @@ -221,6 +224,7 @@ static void smlua_load_script(struct Mod* mod, struct ModFile* file, u16 remoteI if (f_read(buffer, 1, length, f) < length) { LOG_LUA("Failed to load lua script '%s': Unexpected early end of file.", file->cachedPath); gLuaInitializingScript = 0; + lua_settop(L, prevTop); return; } f_close(f); @@ -231,51 +235,66 @@ static void smlua_load_script(struct Mod* mod, struct ModFile* file, u16 remoteI LOG_LUA("%s", smlua_to_string(L, lua_gettop(L))); gLuaInitializingScript = 0; free(buffer); + lua_settop(L, prevTop); return; } free(buffer); - // check if this is the first time this mod has been loaded - lua_getfield(L, LUA_REGISTRYINDEX, mod->relativePath); - bool firstInit = (lua_type(L, -1) == LUA_TNIL); - lua_pop(L, 1); + if (isModInit) { + // check if this is the first time this mod has been loaded + lua_getfield(L, LUA_REGISTRYINDEX, mod->relativePath); + bool firstInit = (lua_type(L, -1) == LUA_TNIL); + lua_pop(L, 1); - // create mod's "global" table - if (firstInit) { - lua_newtable(L); // create _ENV tables - lua_newtable(L); // create metatable - lua_getglobal(L, "_G"); // get global table + // create mod's "global" table + if (firstInit) { + lua_newtable(L); // create _ENV tables + lua_newtable(L); // create metatable + lua_getglobal(L, "_G"); // get global table - // remove certain default functions - lua_pushstring(L, "load"); lua_pushnil(L); lua_settable(L, -3); - lua_pushstring(L, "loadfile"); lua_pushnil(L); lua_settable(L, -3); - lua_pushstring(L, "loadstring"); lua_pushnil(L); lua_settable(L, -3); - lua_pushstring(L, "collectgarbage"); lua_pushnil(L); lua_settable(L, -3); - lua_pushstring(L, "dofile"); lua_pushnil(L); lua_settable(L, -3); + // remove certain default functions + lua_pushstring(L, "load"); lua_pushnil(L); lua_settable(L, -3); + lua_pushstring(L, "loadfile"); lua_pushnil(L); lua_settable(L, -3); + lua_pushstring(L, "loadstring"); lua_pushnil(L); lua_settable(L, -3); + lua_pushstring(L, "collectgarbage"); lua_pushnil(L); lua_settable(L, -3); + lua_pushstring(L, "dofile"); lua_pushnil(L); lua_settable(L, -3); - // set global as the metatable - lua_setfield(L, -2, "__index"); - lua_setmetatable(L, -2); + // set global as the metatable + lua_setfield(L, -2, "__index"); + lua_setmetatable(L, -2); - // push to registry with path as name (must be unique) - lua_setfield(L, LUA_REGISTRYINDEX, mod->relativePath); - } + // push to registry with path as name (must be unique) + lua_setfield(L, LUA_REGISTRYINDEX, mod->relativePath); + } - // load mod's "global" table - lua_getfield(L, LUA_REGISTRYINDEX, mod->relativePath); - lua_setupvalue(L, 1, 1); // set upvalue (_ENV) + // load mod's "global" table + lua_getfield(L, LUA_REGISTRYINDEX, mod->relativePath); + lua_setupvalue(L, 1, 1); // set upvalue (_ENV) - // load per-file globals - if (firstInit) { - smlua_sync_table_init_globals(mod->relativePath, remoteIndex); - smlua_cobject_init_per_file_globals(mod->relativePath); + // load per-file globals + if (firstInit) { + smlua_sync_table_init_globals(mod->relativePath, remoteIndex); + smlua_cobject_init_per_file_globals(mod->relativePath); + } + } else { + // this block is run on files that are loaded for 'require' function + // get the mod's global table + lua_getfield(L, LUA_REGISTRYINDEX, mod->relativePath); + if (lua_isnil(L, -1)) { + lua_pop(L, 1); + LOG_LUA("mod environment not found"); + lua_settop(L, prevTop); + return; + } + lua_setupvalue(L, -2, 1); // set _ENV } // run chunks LOG_INFO("Executing '%s'", file->relativePath); - if (smlua_pcall(L, 0, LUA_MULTRET, 0) != LUA_OK) { + if (smlua_pcall(L, 0, 1, 0) != LUA_OK) { LOG_LUA("Failed to execute lua script '%s'.", file->cachedPath); } + gLuaInitializingScript = 0; } @@ -304,6 +323,7 @@ void smlua_init(void) { smlua_bind_functions(); smlua_bind_functions_autogen(); smlua_bind_sync_table(); + smlua_init_require_system(); extern char gSmluaConstants[]; smlua_exec_str(gSmluaConstants); @@ -324,12 +344,22 @@ void smlua_init(void) { gPcDebug.lastModRun = gLuaActiveMod; for (int j = 0; j < mod->fileCount; j++) { struct ModFile* file = &mod->files[j]; + // skip loading non-lua files if (!(str_ends_with(file->relativePath, ".lua") || str_ends_with(file->relativePath, ".luac"))) { continue; } - smlua_load_script(mod, file, i); + + // skip loading scripts in subdirectories + if (strchr(file->relativePath, '/') != NULL || strchr(file->relativePath, '\\') != NULL) { + continue; + } + + gLuaActiveModFile = file; + smlua_load_script(mod, file, i, true); + lua_settop(L, 0); } gLuaActiveMod = NULL; + gLuaActiveModFile = NULL; gLuaLoadingMod = NULL; } @@ -374,5 +404,6 @@ void smlua_shutdown(void) { } gLuaLoadingMod = NULL; gLuaActiveMod = NULL; + gLuaActiveModFile = NULL; gLuaLastHookMod = NULL; } diff --git a/src/pc/lua/smlua.h b/src/pc/lua/smlua.h index 3704460b1..185ae978f 100644 --- a/src/pc/lua/smlua.h +++ b/src/pc/lua/smlua.h @@ -38,6 +38,7 @@ extern u8 gLuaInitializingScript; extern u8 gSmLuaSuppressErrors; extern struct Mod* gLuaLoadingMod; extern struct Mod* gLuaActiveMod; +extern struct ModFile* gLuaActiveModFile; extern struct Mod* gLuaLastHookMod; void smlua_mod_error(void); @@ -46,6 +47,7 @@ int smlua_error_handler(UNUSED lua_State* L); int smlua_pcall(lua_State* L, int nargs, int nresults, int errfunc); void smlua_exec_file(const char* path); void smlua_exec_str(const char* str); +void smlua_load_script(struct Mod* mod, struct ModFile* file, u16 remoteIndex, bool isModInit); void smlua_init(void); void smlua_update(void); diff --git a/src/pc/lua/smlua_functions.c b/src/pc/lua/smlua_functions.c index 7a21c50ef..cc7daf8b0 100644 --- a/src/pc/lua/smlua_functions.c +++ b/src/pc/lua/smlua_functions.c @@ -475,6 +475,7 @@ int smlua_func_texture_override_reset(lua_State* L) { struct LuaLevelScriptParse { int reference; struct Mod* mod; + struct ModFile* modFile; }; struct LuaLevelScriptParse sLevelScriptParse = { 0 }; @@ -635,7 +636,7 @@ s32 smlua_func_level_script_parse_callback(u8 type, void *cmd) { } // call the callback - if (0 != smlua_call_hook(L, 5, 0, 0, preprocess->mod)) { + if (0 != smlua_call_hook(L, 5, 0, 0, preprocess->mod, preprocess->modFile)) { LOG_LUA("Failed to call the callback behaviors: %u", type); return 0; } @@ -664,6 +665,7 @@ void smlua_func_level_script_parse(lua_State* L) { preprocess->reference = ref; preprocess->mod = gLuaActiveMod; + preprocess->modFile = gLuaActiveModFile; void *script = dynos_level_get_script(levelNum); if (script == NULL) { diff --git a/src/pc/lua/smlua_hook_events_autogen.inl b/src/pc/lua/smlua_hook_events_autogen.inl index 3c9ac1386..e8f39042a 100644 --- a/src/pc/lua/smlua_hook_events_autogen.inl +++ b/src/pc/lua/smlua_hook_events_autogen.inl @@ -15,7 +15,7 @@ bool smlua_call_event_hooks_HOOK_UPDATE() { lua_rawgeti(L, LUA_REGISTRYINDEX, hook->reference[i]); // call the callback - if (0 != smlua_call_hook(L, 0, 0, 0, hook->mod[i])) { + if (0 != smlua_call_hook(L, 0, 0, 0, hook->mod[i], hook->modFile[i])) { LOG_LUA("Failed to call the callback for hook %s", sLuaHookedEventTypeName[HOOK_UPDATE]); continue; } @@ -45,7 +45,7 @@ bool smlua_call_event_hooks_HOOK_MARIO_UPDATE(struct MarioState *m) { lua_remove(L, -2); // call the callback - if (0 != smlua_call_hook(L, 1, 0, 0, hook->mod[i])) { + if (0 != smlua_call_hook(L, 1, 0, 0, hook->mod[i], hook->modFile[i])) { LOG_LUA("Failed to call the callback for hook %s", sLuaHookedEventTypeName[HOOK_MARIO_UPDATE]); continue; } @@ -75,7 +75,7 @@ bool smlua_call_event_hooks_HOOK_BEFORE_MARIO_UPDATE(struct MarioState *m) { lua_remove(L, -2); // call the callback - if (0 != smlua_call_hook(L, 1, 0, 0, hook->mod[i])) { + if (0 != smlua_call_hook(L, 1, 0, 0, hook->mod[i], hook->modFile[i])) { LOG_LUA("Failed to call the callback for hook %s", sLuaHookedEventTypeName[HOOK_BEFORE_MARIO_UPDATE]); continue; } @@ -105,7 +105,7 @@ bool smlua_call_event_hooks_HOOK_ON_SET_MARIO_ACTION(struct MarioState *m) { lua_remove(L, -2); // call the callback - if (0 != smlua_call_hook(L, 1, 0, 0, hook->mod[i])) { + if (0 != smlua_call_hook(L, 1, 0, 0, hook->mod[i], hook->modFile[i])) { LOG_LUA("Failed to call the callback for hook %s", sLuaHookedEventTypeName[HOOK_ON_SET_MARIO_ACTION]); continue; } @@ -140,7 +140,7 @@ bool smlua_call_event_hooks_HOOK_BEFORE_PHYS_STEP(struct MarioState *m, s32 step lua_pushinteger(L, stepArg); // call the callback - if (0 != smlua_call_hook(L, 3, 1, 0, hook->mod[i])) { + if (0 != smlua_call_hook(L, 3, 1, 0, hook->mod[i], hook->modFile[i])) { LOG_LUA("Failed to call the callback for hook %s", sLuaHookedEventTypeName[HOOK_BEFORE_PHYS_STEP]); continue; } @@ -185,7 +185,7 @@ bool smlua_call_event_hooks_HOOK_ALLOW_PVP_ATTACK(struct MarioState *attacker, s lua_pushinteger(L, interaction); // call the callback - if (0 != smlua_call_hook(L, 3, 1, 0, hook->mod[i])) { + if (0 != smlua_call_hook(L, 3, 1, 0, hook->mod[i], hook->modFile[i])) { LOG_LUA("Failed to call the callback for hook %s", sLuaHookedEventTypeName[HOOK_ALLOW_PVP_ATTACK]); continue; } @@ -229,7 +229,7 @@ bool smlua_call_event_hooks_HOOK_ON_PVP_ATTACK(struct MarioState *attacker, stru lua_pushinteger(L, interaction); // call the callback - if (0 != smlua_call_hook(L, 3, 0, 0, hook->mod[i])) { + if (0 != smlua_call_hook(L, 3, 0, 0, hook->mod[i], hook->modFile[i])) { LOG_LUA("Failed to call the callback for hook %s", sLuaHookedEventTypeName[HOOK_ON_PVP_ATTACK]); continue; } @@ -259,7 +259,7 @@ bool smlua_call_event_hooks_HOOK_ON_PLAYER_CONNECTED(struct MarioState *m) { lua_remove(L, -2); // call the callback - if (0 != smlua_call_hook(L, 1, 0, 0, hook->mod[i])) { + if (0 != smlua_call_hook(L, 1, 0, 0, hook->mod[i], hook->modFile[i])) { LOG_LUA("Failed to call the callback for hook %s", sLuaHookedEventTypeName[HOOK_ON_PLAYER_CONNECTED]); continue; } @@ -289,7 +289,7 @@ bool smlua_call_event_hooks_HOOK_ON_PLAYER_DISCONNECTED(struct MarioState *m) { lua_remove(L, -2); // call the callback - if (0 != smlua_call_hook(L, 1, 0, 0, hook->mod[i])) { + if (0 != smlua_call_hook(L, 1, 0, 0, hook->mod[i], hook->modFile[i])) { LOG_LUA("Failed to call the callback for hook %s", sLuaHookedEventTypeName[HOOK_ON_PLAYER_DISCONNECTED]); continue; } @@ -325,7 +325,7 @@ bool smlua_call_event_hooks_HOOK_ALLOW_INTERACT(struct MarioState *m, struct Obj lua_pushinteger(L, interactType); // call the callback - if (0 != smlua_call_hook(L, 3, 1, 0, hook->mod[i])) { + if (0 != smlua_call_hook(L, 3, 1, 0, hook->mod[i], hook->modFile[i])) { LOG_LUA("Failed to call the callback for hook %s", sLuaHookedEventTypeName[HOOK_ALLOW_INTERACT]); continue; } @@ -369,7 +369,7 @@ bool smlua_call_event_hooks_HOOK_ON_INTERACT(struct MarioState *m, struct Object lua_pushboolean(L, interactValue); // call the callback - if (0 != smlua_call_hook(L, 4, 0, 0, hook->mod[i])) { + if (0 != smlua_call_hook(L, 4, 0, 0, hook->mod[i], hook->modFile[i])) { LOG_LUA("Failed to call the callback for hook %s", sLuaHookedEventTypeName[HOOK_ON_INTERACT]); continue; } @@ -408,7 +408,7 @@ bool smlua_call_event_hooks_HOOK_ON_LEVEL_INIT(u8 warpType, s16 levelNum, u8 are lua_pushinteger(L, warpArg); // call the callback - if (0 != smlua_call_hook(L, 5, 0, 0, hook->mod[i])) { + if (0 != smlua_call_hook(L, 5, 0, 0, hook->mod[i], hook->modFile[i])) { LOG_LUA("Failed to call the callback for hook %s", sLuaHookedEventTypeName[HOOK_ON_LEVEL_INIT]); continue; } @@ -447,7 +447,7 @@ bool smlua_call_event_hooks_HOOK_ON_WARP(u8 warpType, s16 levelNum, u8 areaIdx, lua_pushinteger(L, warpArg); // call the callback - if (0 != smlua_call_hook(L, 5, 0, 0, hook->mod[i])) { + if (0 != smlua_call_hook(L, 5, 0, 0, hook->mod[i], hook->modFile[i])) { LOG_LUA("Failed to call the callback for hook %s", sLuaHookedEventTypeName[HOOK_ON_WARP]); continue; } @@ -471,7 +471,7 @@ bool smlua_call_event_hooks_HOOK_ON_SYNC_VALID() { lua_rawgeti(L, LUA_REGISTRYINDEX, hook->reference[i]); // call the callback - if (0 != smlua_call_hook(L, 0, 0, 0, hook->mod[i])) { + if (0 != smlua_call_hook(L, 0, 0, 0, hook->mod[i], hook->modFile[i])) { LOG_LUA("Failed to call the callback for hook %s", sLuaHookedEventTypeName[HOOK_ON_SYNC_VALID]); continue; } @@ -498,7 +498,7 @@ bool smlua_call_event_hooks_HOOK_ON_OBJECT_UNLOAD(struct Object *obj) { smlua_push_object(L, LOT_OBJECT, obj, NULL); // call the callback - if (0 != smlua_call_hook(L, 1, 0, 0, hook->mod[i])) { + if (0 != smlua_call_hook(L, 1, 0, 0, hook->mod[i], hook->modFile[i])) { LOG_LUA("Failed to call the callback for hook %s", sLuaHookedEventTypeName[HOOK_ON_OBJECT_UNLOAD]); continue; } @@ -525,7 +525,7 @@ bool smlua_call_event_hooks_HOOK_ON_SYNC_OBJECT_UNLOAD(struct Object *obj) { smlua_push_object(L, LOT_OBJECT, obj, NULL); // call the callback - if (0 != smlua_call_hook(L, 1, 0, 0, hook->mod[i])) { + if (0 != smlua_call_hook(L, 1, 0, 0, hook->mod[i], hook->modFile[i])) { LOG_LUA("Failed to call the callback for hook %s", sLuaHookedEventTypeName[HOOK_ON_SYNC_OBJECT_UNLOAD]); continue; } @@ -552,7 +552,7 @@ bool smlua_call_event_hooks_HOOK_ON_PAUSE_EXIT(bool usedExitToCastle, bool *allo lua_pushboolean(L, usedExitToCastle); // call the callback - if (0 != smlua_call_hook(L, 1, 1, 0, hook->mod[i])) { + if (0 != smlua_call_hook(L, 1, 1, 0, hook->mod[i], hook->modFile[i])) { LOG_LUA("Failed to call the callback for hook %s", sLuaHookedEventTypeName[HOOK_ON_PAUSE_EXIT]); continue; } @@ -580,7 +580,7 @@ bool smlua_call_event_hooks_HOOK_GET_STAR_COLLECTION_DIALOG(s32 *dialogID) { lua_rawgeti(L, LUA_REGISTRYINDEX, hook->reference[i]); // call the callback - if (0 != smlua_call_hook(L, 0, 1, 0, hook->mod[i])) { + if (0 != smlua_call_hook(L, 0, 1, 0, hook->mod[i], hook->modFile[i])) { LOG_LUA("Failed to call the callback for hook %s", sLuaHookedEventTypeName[HOOK_GET_STAR_COLLECTION_DIALOG]); continue; } @@ -618,7 +618,7 @@ bool smlua_call_event_hooks_HOOK_ON_SET_CAMERA_MODE(struct Camera *c, s16 mode, lua_pushinteger(L, frames); // call the callback - if (0 != smlua_call_hook(L, 3, 1, 0, hook->mod[i])) { + if (0 != smlua_call_hook(L, 3, 1, 0, hook->mod[i], hook->modFile[i])) { LOG_LUA("Failed to call the callback for hook %s", sLuaHookedEventTypeName[HOOK_ON_SET_CAMERA_MODE]); continue; } @@ -650,7 +650,7 @@ bool smlua_call_event_hooks_HOOK_ON_OBJECT_RENDER(struct Object *obj) { smlua_push_object(L, LOT_OBJECT, obj, NULL); // call the callback - if (0 != smlua_call_hook(L, 1, 0, 0, hook->mod[i])) { + if (0 != smlua_call_hook(L, 1, 0, 0, hook->mod[i], hook->modFile[i])) { LOG_LUA("Failed to call the callback for hook %s", sLuaHookedEventTypeName[HOOK_ON_OBJECT_RENDER]); continue; } @@ -680,7 +680,7 @@ bool smlua_call_event_hooks_HOOK_ON_DEATH(struct MarioState *m, bool *allowDeath lua_remove(L, -2); // call the callback - if (0 != smlua_call_hook(L, 1, 1, 0, hook->mod[i])) { + if (0 != smlua_call_hook(L, 1, 1, 0, hook->mod[i], hook->modFile[i])) { LOG_LUA("Failed to call the callback for hook %s", sLuaHookedEventTypeName[HOOK_ON_DEATH]); continue; } @@ -715,7 +715,7 @@ bool smlua_call_event_hooks_HOOK_ON_PACKET_RECEIVE(s32 modIndex, s32 valueIndex) lua_pushinteger(L, valueIndex); // call the callback - if (0 != smlua_call_hook(L, 2, 0, 0, hook->mod[i])) { + if (0 != smlua_call_hook(L, 2, 0, 0, hook->mod[i], hook->modFile[i])) { LOG_LUA("Failed to call the callback for hook %s", sLuaHookedEventTypeName[HOOK_ON_PACKET_RECEIVE]); continue; } @@ -741,7 +741,7 @@ bool smlua_call_event_hooks_HOOK_USE_ACT_SELECT(s32 levelNum, bool *useActSelect lua_pushinteger(L, levelNum); // call the callback - if (0 != smlua_call_hook(L, 1, 1, 0, hook->mod[i])) { + if (0 != smlua_call_hook(L, 1, 1, 0, hook->mod[i], hook->modFile[i])) { LOG_LUA("Failed to call the callback for hook %s", sLuaHookedEventTypeName[HOOK_USE_ACT_SELECT]); continue; } @@ -774,7 +774,7 @@ bool smlua_call_event_hooks_HOOK_ON_CHANGE_CAMERA_ANGLE(s32 camAngleType, bool * lua_pushinteger(L, camAngleType); // call the callback - if (0 != smlua_call_hook(L, 1, 1, 0, hook->mod[i])) { + if (0 != smlua_call_hook(L, 1, 1, 0, hook->mod[i], hook->modFile[i])) { LOG_LUA("Failed to call the callback for hook %s", sLuaHookedEventTypeName[HOOK_ON_CHANGE_CAMERA_ANGLE]); continue; } @@ -806,7 +806,7 @@ bool smlua_call_event_hooks_HOOK_ON_SCREEN_TRANSITION(s32 transitionType, bool * lua_pushinteger(L, transitionType); // call the callback - if (0 != smlua_call_hook(L, 1, 1, 0, hook->mod[i])) { + if (0 != smlua_call_hook(L, 1, 1, 0, hook->mod[i], hook->modFile[i])) { LOG_LUA("Failed to call the callback for hook %s", sLuaHookedEventTypeName[HOOK_ON_SCREEN_TRANSITION]); continue; } @@ -844,7 +844,7 @@ bool smlua_call_event_hooks_HOOK_ALLOW_HAZARD_SURFACE(struct MarioState *m, s32 lua_pushinteger(L, hazardType); // call the callback - if (0 != smlua_call_hook(L, 2, 1, 0, hook->mod[i])) { + if (0 != smlua_call_hook(L, 2, 1, 0, hook->mod[i], hook->modFile[i])) { LOG_LUA("Failed to call the callback for hook %s", sLuaHookedEventTypeName[HOOK_ALLOW_HAZARD_SURFACE]); continue; } @@ -882,7 +882,7 @@ bool smlua_call_event_hooks_HOOK_ON_CHAT_MESSAGE(struct MarioState *m, const cha lua_pushstring(L, message); // call the callback - if (0 != smlua_call_hook(L, 2, 1, 0, hook->mod[i])) { + if (0 != smlua_call_hook(L, 2, 1, 0, hook->mod[i], hook->modFile[i])) { LOG_LUA("Failed to call the callback for hook %s", sLuaHookedEventTypeName[HOOK_ON_CHAT_MESSAGE]); continue; } @@ -920,7 +920,7 @@ bool smlua_call_event_hooks_HOOK_OBJECT_SET_MODEL(struct Object *obj, s32 modelI lua_pushinteger(L, modelExtendedId); // call the callback - if (0 != smlua_call_hook(L, 3, 0, 0, hook->mod[i])) { + if (0 != smlua_call_hook(L, 3, 0, 0, hook->mod[i], hook->modFile[i])) { LOG_LUA("Failed to call the callback for hook %s", sLuaHookedEventTypeName[HOOK_OBJECT_SET_MODEL]); continue; } @@ -952,7 +952,7 @@ bool smlua_call_event_hooks_HOOK_CHARACTER_SOUND(struct MarioState *m, enum Char lua_pushinteger(L, characterSound); // call the callback - if (0 != smlua_call_hook(L, 2, 1, 0, hook->mod[i])) { + if (0 != smlua_call_hook(L, 2, 1, 0, hook->mod[i], hook->modFile[i])) { LOG_LUA("Failed to call the callback for hook %s", sLuaHookedEventTypeName[HOOK_CHARACTER_SOUND]); continue; } @@ -994,7 +994,7 @@ bool smlua_call_event_hooks_HOOK_BEFORE_SET_MARIO_ACTION(struct MarioState *m, u lua_pushinteger(L, actionArg); // call the callback - if (0 != smlua_call_hook(L, 3, 1, 0, hook->mod[i])) { + if (0 != smlua_call_hook(L, 3, 1, 0, hook->mod[i], hook->modFile[i])) { LOG_LUA("Failed to call the callback for hook %s", sLuaHookedEventTypeName[HOOK_BEFORE_SET_MARIO_ACTION]); continue; } @@ -1023,7 +1023,7 @@ bool smlua_call_event_hooks_HOOK_JOINED_GAME() { lua_rawgeti(L, LUA_REGISTRYINDEX, hook->reference[i]); // call the callback - if (0 != smlua_call_hook(L, 0, 0, 0, hook->mod[i])) { + if (0 != smlua_call_hook(L, 0, 0, 0, hook->mod[i], hook->modFile[i])) { LOG_LUA("Failed to call the callback for hook %s", sLuaHookedEventTypeName[HOOK_JOINED_GAME]); continue; } @@ -1050,7 +1050,7 @@ bool smlua_call_event_hooks_HOOK_ON_OBJECT_ANIM_UPDATE(struct Object *obj) { smlua_push_object(L, LOT_OBJECT, obj, NULL); // call the callback - if (0 != smlua_call_hook(L, 1, 0, 0, hook->mod[i])) { + if (0 != smlua_call_hook(L, 1, 0, 0, hook->mod[i], hook->modFile[i])) { LOG_LUA("Failed to call the callback for hook %s", sLuaHookedEventTypeName[HOOK_ON_OBJECT_ANIM_UPDATE]); continue; } @@ -1077,7 +1077,7 @@ bool smlua_call_event_hooks_HOOK_ON_DIALOG(s32 dialogID, bool *openDialogBox, co lua_pushinteger(L, dialogID); // call the callback - if (0 != smlua_call_hook(L, 1, 2, 0, hook->mod[i])) { + if (0 != smlua_call_hook(L, 1, 2, 0, hook->mod[i], hook->modFile[i])) { LOG_LUA("Failed to call the callback for hook %s", sLuaHookedEventTypeName[HOOK_ON_DIALOG]); continue; } @@ -1111,7 +1111,7 @@ bool smlua_call_event_hooks_HOOK_ON_EXIT() { lua_rawgeti(L, LUA_REGISTRYINDEX, hook->reference[i]); // call the callback - if (0 != smlua_call_hook(L, 0, 0, 0, hook->mod[i])) { + if (0 != smlua_call_hook(L, 0, 0, 0, hook->mod[i], hook->modFile[i])) { LOG_LUA("Failed to call the callback for hook %s", sLuaHookedEventTypeName[HOOK_ON_EXIT]); continue; } @@ -1137,7 +1137,7 @@ bool smlua_call_event_hooks_HOOK_DIALOG_SOUND(s32 speaker, s32 *speakerOverride) lua_pushinteger(L, speaker); // call the callback - if (0 != smlua_call_hook(L, 1, 1, 0, hook->mod[i])) { + if (0 != smlua_call_hook(L, 1, 1, 0, hook->mod[i], hook->modFile[i])) { LOG_LUA("Failed to call the callback for hook %s", sLuaHookedEventTypeName[HOOK_DIALOG_SOUND]); continue; } @@ -1173,7 +1173,7 @@ bool smlua_call_event_hooks_HOOK_ON_COLLIDE_LEVEL_BOUNDS(struct MarioState *m) { lua_remove(L, -2); // call the callback - if (0 != smlua_call_hook(L, 1, 0, 0, hook->mod[i])) { + if (0 != smlua_call_hook(L, 1, 0, 0, hook->mod[i], hook->modFile[i])) { LOG_LUA("Failed to call the callback for hook %s", sLuaHookedEventTypeName[HOOK_ON_COLLIDE_LEVEL_BOUNDS]); continue; } @@ -1203,7 +1203,7 @@ bool smlua_call_event_hooks_HOOK_MIRROR_MARIO_RENDER(struct GraphNodeObject *mir lua_pushinteger(L, playerIndex); // call the callback - if (0 != smlua_call_hook(L, 2, 0, 0, hook->mod[i])) { + if (0 != smlua_call_hook(L, 2, 0, 0, hook->mod[i], hook->modFile[i])) { LOG_LUA("Failed to call the callback for hook %s", sLuaHookedEventTypeName[HOOK_MIRROR_MARIO_RENDER]); continue; } @@ -1232,7 +1232,7 @@ bool smlua_call_event_hooks_HOOK_MARIO_OVERRIDE_PHYS_STEP_DEFACTO_SPEED(struct M lua_remove(L, -2); // call the callback - if (0 != smlua_call_hook(L, 1, 1, 0, hook->mod[i])) { + if (0 != smlua_call_hook(L, 1, 1, 0, hook->mod[i], hook->modFile[i])) { LOG_LUA("Failed to call the callback for hook %s", sLuaHookedEventTypeName[HOOK_MARIO_OVERRIDE_PHYS_STEP_DEFACTO_SPEED]); continue; } @@ -1264,7 +1264,7 @@ bool smlua_call_event_hooks_HOOK_ON_OBJECT_LOAD(struct Object *obj) { smlua_push_object(L, LOT_OBJECT, obj, NULL); // call the callback - if (0 != smlua_call_hook(L, 1, 0, 0, hook->mod[i])) { + if (0 != smlua_call_hook(L, 1, 0, 0, hook->mod[i], hook->modFile[i])) { LOG_LUA("Failed to call the callback for hook %s", sLuaHookedEventTypeName[HOOK_ON_OBJECT_LOAD]); continue; } @@ -1294,7 +1294,7 @@ bool smlua_call_event_hooks_HOOK_ON_PLAY_SOUND(s32 soundBits, Vec3f pos, s32 *so smlua_new_vec3f(pos); // call the callback - if (0 != smlua_call_hook(L, 2, 1, 0, hook->mod[i])) { + if (0 != smlua_call_hook(L, 2, 1, 0, hook->mod[i], hook->modFile[i])) { LOG_LUA("Failed to call the callback for hook %s", sLuaHookedEventTypeName[HOOK_ON_PLAY_SOUND]); continue; } @@ -1332,7 +1332,7 @@ bool smlua_call_event_hooks_HOOK_ON_SEQ_LOAD(u32 seqPlayer, u32 seqId, s32 loadA lua_pushinteger(L, loadAsync); // call the callback - if (0 != smlua_call_hook(L, 3, 1, 0, hook->mod[i])) { + if (0 != smlua_call_hook(L, 3, 1, 0, hook->mod[i], hook->modFile[i])) { LOG_LUA("Failed to call the callback for hook %s", sLuaHookedEventTypeName[HOOK_ON_SEQ_LOAD]); continue; } @@ -1374,7 +1374,7 @@ bool smlua_call_event_hooks_HOOK_ON_ATTACK_OBJECT(struct MarioState *m, struct O lua_pushinteger(L, interaction); // call the callback - if (0 != smlua_call_hook(L, 3, 0, 0, hook->mod[i])) { + if (0 != smlua_call_hook(L, 3, 0, 0, hook->mod[i], hook->modFile[i])) { LOG_LUA("Failed to call the callback for hook %s", sLuaHookedEventTypeName[HOOK_ON_ATTACK_OBJECT]); continue; } @@ -1401,7 +1401,7 @@ bool smlua_call_event_hooks_HOOK_ON_LANGUAGE_CHANGED(const char *langName) { lua_pushstring(L, langName); // call the callback - if (0 != smlua_call_hook(L, 1, 0, 0, hook->mod[i])) { + if (0 != smlua_call_hook(L, 1, 0, 0, hook->mod[i], hook->modFile[i])) { LOG_LUA("Failed to call the callback for hook %s", sLuaHookedEventTypeName[HOOK_ON_LANGUAGE_CHANGED]); continue; } @@ -1425,7 +1425,7 @@ bool smlua_call_event_hooks_HOOK_ON_MODS_LOADED() { lua_rawgeti(L, LUA_REGISTRYINDEX, hook->reference[i]); // call the callback - if (0 != smlua_call_hook(L, 0, 0, 0, hook->mod[i])) { + if (0 != smlua_call_hook(L, 0, 0, 0, hook->mod[i], hook->modFile[i])) { LOG_LUA("Failed to call the callback for hook %s", sLuaHookedEventTypeName[HOOK_ON_MODS_LOADED]); continue; } @@ -1449,7 +1449,7 @@ bool smlua_call_event_hooks_HOOK_ON_DJUI_THEME_CHANGED() { lua_rawgeti(L, LUA_REGISTRYINDEX, hook->reference[i]); // call the callback - if (0 != smlua_call_hook(L, 0, 0, 0, hook->mod[i])) { + if (0 != smlua_call_hook(L, 0, 0, 0, hook->mod[i], hook->modFile[i])) { LOG_LUA("Failed to call the callback for hook %s", sLuaHookedEventTypeName[HOOK_ON_DJUI_THEME_CHANGED]); continue; } @@ -1479,7 +1479,7 @@ bool smlua_call_event_hooks_HOOK_ON_GEO_PROCESS(struct GraphNode *node, s32 matS lua_pushinteger(L, matStackIndex); // call the callback - if (0 != smlua_call_hook(L, 2, 0, 0, hook->mod[i])) { + if (0 != smlua_call_hook(L, 2, 0, 0, hook->mod[i], hook->modFile[i])) { LOG_LUA("Failed to call the callback for hook %s", sLuaHookedEventTypeName[HOOK_ON_GEO_PROCESS]); continue; } @@ -1509,7 +1509,7 @@ bool smlua_call_event_hooks_HOOK_BEFORE_GEO_PROCESS(struct GraphNode *node, s32 lua_pushinteger(L, matStackIndex); // call the callback - if (0 != smlua_call_hook(L, 2, 0, 0, hook->mod[i])) { + if (0 != smlua_call_hook(L, 2, 0, 0, hook->mod[i], hook->modFile[i])) { LOG_LUA("Failed to call the callback for hook %s", sLuaHookedEventTypeName[HOOK_BEFORE_GEO_PROCESS]); continue; } @@ -1539,7 +1539,7 @@ bool smlua_call_event_hooks_HOOK_ON_GEO_PROCESS_CHILDREN(struct GraphNode *paren lua_pushinteger(L, matStackIndex); // call the callback - if (0 != smlua_call_hook(L, 2, 0, 0, hook->mod[i])) { + if (0 != smlua_call_hook(L, 2, 0, 0, hook->mod[i], hook->modFile[i])) { LOG_LUA("Failed to call the callback for hook %s", sLuaHookedEventTypeName[HOOK_ON_GEO_PROCESS_CHILDREN]); continue; } @@ -1569,7 +1569,7 @@ bool smlua_call_event_hooks_HOOK_MARIO_OVERRIDE_GEOMETRY_INPUTS(struct MarioStat lua_remove(L, -2); // call the callback - if (0 != smlua_call_hook(L, 1, 1, 0, hook->mod[i])) { + if (0 != smlua_call_hook(L, 1, 1, 0, hook->mod[i], hook->modFile[i])) { LOG_LUA("Failed to call the callback for hook %s", sLuaHookedEventTypeName[HOOK_MARIO_OVERRIDE_GEOMETRY_INPUTS]); continue; } @@ -1604,7 +1604,7 @@ bool smlua_call_event_hooks_HOOK_ON_INTERACTIONS(struct MarioState *m) { lua_remove(L, -2); // call the callback - if (0 != smlua_call_hook(L, 1, 0, 0, hook->mod[i])) { + if (0 != smlua_call_hook(L, 1, 0, 0, hook->mod[i], hook->modFile[i])) { LOG_LUA("Failed to call the callback for hook %s", sLuaHookedEventTypeName[HOOK_ON_INTERACTIONS]); continue; } @@ -1637,7 +1637,7 @@ bool smlua_call_event_hooks_HOOK_ALLOW_FORCE_WATER_ACTION(struct MarioState *m, lua_pushboolean(L, isInWaterAction); // call the callback - if (0 != smlua_call_hook(L, 2, 1, 0, hook->mod[i])) { + if (0 != smlua_call_hook(L, 2, 1, 0, hook->mod[i], hook->modFile[i])) { LOG_LUA("Failed to call the callback for hook %s", sLuaHookedEventTypeName[HOOK_ALLOW_FORCE_WATER_ACTION]); continue; } @@ -1677,7 +1677,7 @@ bool smlua_call_event_hooks_HOOK_BEFORE_WARP(s16 destLevel, s16 destArea, s16 de lua_pushinteger(L, arg); // call the callback - if (0 != smlua_call_hook(L, 4, 1, 0, hook->mod[i])) { + if (0 != smlua_call_hook(L, 4, 1, 0, hook->mod[i], hook->modFile[i])) { LOG_LUA("Failed to call the callback for hook %s", sLuaHookedEventTypeName[HOOK_BEFORE_WARP]); continue; } @@ -1735,7 +1735,7 @@ bool smlua_call_event_hooks_HOOK_ON_INSTANT_WARP(u8 areaIdx, u8 nodeId, Vec3s di smlua_new_vec3s(displacement); // call the callback - if (0 != smlua_call_hook(L, 3, 0, 0, hook->mod[i])) { + if (0 != smlua_call_hook(L, 3, 0, 0, hook->mod[i], hook->modFile[i])) { LOG_LUA("Failed to call the callback for hook %s", sLuaHookedEventTypeName[HOOK_ON_INSTANT_WARP]); continue; } @@ -1767,7 +1767,7 @@ bool smlua_call_event_hooks_HOOK_MARIO_OVERRIDE_FLOOR_CLASS(struct MarioState *m lua_pushinteger(L, floorClass); // call the callback - if (0 != smlua_call_hook(L, 2, 1, 0, hook->mod[i])) { + if (0 != smlua_call_hook(L, 2, 1, 0, hook->mod[i], hook->modFile[i])) { LOG_LUA("Failed to call the callback for hook %s", sLuaHookedEventTypeName[HOOK_MARIO_OVERRIDE_FLOOR_CLASS]); continue; } @@ -1803,7 +1803,7 @@ bool smlua_call_event_hooks_HOOK_ON_ADD_SURFACE(struct Surface *surface, bool dy lua_pushboolean(L, dynamic); // call the callback - if (0 != smlua_call_hook(L, 2, 0, 0, hook->mod[i])) { + if (0 != smlua_call_hook(L, 2, 0, 0, hook->mod[i], hook->modFile[i])) { LOG_LUA("Failed to call the callback for hook %s", sLuaHookedEventTypeName[HOOK_ON_ADD_SURFACE]); continue; } @@ -1827,7 +1827,7 @@ bool smlua_call_event_hooks_HOOK_ON_CLEAR_AREAS() { lua_rawgeti(L, LUA_REGISTRYINDEX, hook->reference[i]); // call the callback - if (0 != smlua_call_hook(L, 0, 0, 0, hook->mod[i])) { + if (0 != smlua_call_hook(L, 0, 0, 0, hook->mod[i], hook->modFile[i])) { LOG_LUA("Failed to call the callback for hook %s", sLuaHookedEventTypeName[HOOK_ON_CLEAR_AREAS]); continue; } diff --git a/src/pc/lua/smlua_hooks.c b/src/pc/lua/smlua_hooks.c index 2c7ed2329..ccf1e637c 100644 --- a/src/pc/lua/smlua_hooks.c +++ b/src/pc/lua/smlua_hooks.c @@ -34,6 +34,7 @@ u64* gBehaviorOffset = &gPcDebug.bhvOffset; struct LuaHookedEvent { int reference[MAX_HOOKED_REFERENCES]; struct Mod* mod[MAX_HOOKED_REFERENCES]; + struct ModFile* modFile[MAX_HOOKED_REFERENCES]; int count; }; @@ -46,11 +47,14 @@ static const char* sLuaHookedEventTypeName[] = { "HOOK_MAX" }; -int smlua_call_hook(lua_State* L, int nargs, int nresults, int errfunc, struct Mod* activeMod) { +int smlua_call_hook(lua_State* L, int nargs, int nresults, int errfunc, struct Mod* activeMod, struct ModFile* activeModFile) { if (!gGameInited) { return 0; } // Don't call hooks while the game is booting - struct Mod* prev = gLuaActiveMod; + struct Mod* prevActiveMod = gLuaActiveMod; + struct ModFile* prevActiveModFile = gLuaActiveModFile; + gLuaActiveMod = activeMod; + gLuaActiveModFile = activeModFile; gLuaLastHookMod = activeMod; gPcDebug.lastModRun = activeMod; @@ -62,7 +66,8 @@ int smlua_call_hook(lua_State* L, int nargs, int nresults, int errfunc, struct M lua_profiler_stop_counter(activeMod); - gLuaActiveMod = prev; + gLuaActiveMod = prevActiveMod; + gLuaActiveModFile = prevActiveModFile; return rc; } @@ -131,7 +136,7 @@ static bool smlua_call_event_hooks_on_hud_render(void (*resetFunc)(void), bool r lua_rawgeti(L, LUA_REGISTRYINDEX, hook->reference[i]); // call the callback - if (0 != smlua_call_hook(L, 0, 0, 0, hook->mod[i])) { + if (0 != smlua_call_hook(L, 0, 0, 0, hook->mod[i], hook->modFile[i])) { LOG_LUA("Failed to call the callback for hook %s", sLuaHookedEventTypeName[hookType]); } else { hookResult = true; @@ -170,7 +175,7 @@ bool smlua_call_event_hooks_HOOK_ON_NAMETAGS_RENDER(s32 playerIndex, Vec3f pos, smlua_new_vec3f(pos); // call the callback - if (0 != smlua_call_hook(L, 2, 1, 0, hook->mod[i])) { + if (0 != smlua_call_hook(L, 2, 1, 0, hook->mod[i], hook->modFile[i])) { LOG_LUA("Failed to call the callback for hook %s", sLuaHookedEventTypeName[HOOK_ON_NAMETAGS_RENDER]); continue; } @@ -223,6 +228,7 @@ struct LuaHookedMarioAction { u32 interactionType; int actionHookRefs[ACTION_HOOK_MAX]; struct Mod* mod; + struct ModFile* modFile; }; #define MAX_HOOKED_ACTIONS (ACT_NUM_GROUPS * ACT_NUM_ACTIONS_PER_GROUP) @@ -311,6 +317,7 @@ int smlua_hook_mario_action(lua_State* L) { hooked->action = action; hooked->interactionType = interactionType; hooked->mod = gLuaActiveMod; + hooked->modFile = gLuaActiveModFile; sHookedMarioActionsCount++; return 1; @@ -334,7 +341,7 @@ bool smlua_call_action_hook(enum LuaActionHookType hookType, struct MarioState* lua_remove(L, -2); // call the callback - if (0 != smlua_call_hook(L, 1, 1, 0, hook->mod)) { + if (0 != smlua_call_hook(L, 1, 1, 0, hook->mod, hook->modFile)) { LOG_LUA("Failed to call the action callback: %u", m->action); continue; } @@ -381,6 +388,7 @@ struct LuaHookedBehavior { bool replace; bool luaBehavior; struct Mod* mod; + struct ModFile* modFile; }; #define MAX_HOOKED_BEHAVIORS 1024 @@ -474,6 +482,7 @@ int smlua_hook_custom_bhv(BehaviorScript *bhvScript, const char *bhvName) { hooked->replace = true; hooked->luaBehavior = false; hooked->mod = gLuaActiveMod; + hooked->modFile = gLuaActiveModFile; sHookedBehaviorsCount++; @@ -620,6 +629,7 @@ int smlua_hook_behavior(lua_State* L) { hooked->replace = replaceBehavior; hooked->luaBehavior = true; hooked->mod = gLuaActiveMod; + hooked->modFile = gLuaActiveModFile; sHookedBehaviorsCount++; @@ -677,7 +687,7 @@ bool smlua_call_behavior_hook(const BehaviorScript** behavior, struct Object* ob smlua_push_object(L, LOT_OBJECT, object, NULL); // call the callback - if (0 != smlua_call_hook(L, 1, 0, 0, hooked->mod)) { + if (0 != smlua_call_hook(L, 1, 0, 0, hooked->mod, hooked->modFile)) { LOG_LUA("Failed to call the behavior callback: %u", hooked->behaviorId); return true; } @@ -698,6 +708,7 @@ struct LuaHookedChatCommand { char* description; int reference; struct Mod* mod; + struct ModFile* modFile; }; #define MAX_HOOKED_CHAT_COMMANDS 512 @@ -742,6 +753,7 @@ int smlua_hook_chat_command(lua_State* L) { hooked->description = strdup(description); hooked->reference = ref; hooked->mod = gLuaActiveMod; + hooked->modFile = gLuaActiveModFile; sHookedChatCommandsCount++; return 1; @@ -805,7 +817,7 @@ bool smlua_call_chat_command_hook(char* command) { lua_pushstring(L, params); // call the callback - if (0 != smlua_call_hook(L, 1, 1, 0, hook->mod)) { + if (0 != smlua_call_hook(L, 1, 1, 0, hook->mod, hook->modFile)) { LOG_LUA("Failed to call the chat command callback: %s", command); continue; } @@ -1108,6 +1120,7 @@ int smlua_hook_mod_menu_text(lua_State* L) { hooked->sliderMax = 0; hooked->reference = 0; hooked->mod = gLuaActiveMod; + hooked->modFile = gLuaActiveModFile; lua_pushinteger(L, gHookedModMenuElementsCount); gHookedModMenuElementsCount++; @@ -1146,6 +1159,7 @@ int smlua_hook_mod_menu_button(lua_State* L) { hooked->sliderMax = 0; hooked->reference = ref; hooked->mod = gLuaActiveMod; + hooked->modFile = gLuaActiveModFile; lua_pushinteger(L, gHookedModMenuElementsCount); gHookedModMenuElementsCount++; @@ -1190,6 +1204,7 @@ int smlua_hook_mod_menu_checkbox(lua_State* L) { hooked->sliderMax = 0; hooked->reference = ref; hooked->mod = gLuaActiveMod; + hooked->modFile = gLuaActiveModFile; lua_pushinteger(L, gHookedModMenuElementsCount); gHookedModMenuElementsCount++; @@ -1246,6 +1261,7 @@ int smlua_hook_mod_menu_slider(lua_State* L) { hooked->sliderMax = sliderMax; hooked->reference = ref; hooked->mod = gLuaActiveMod; + hooked->modFile = gLuaActiveModFile; lua_pushinteger(L, gHookedModMenuElementsCount); gHookedModMenuElementsCount++; @@ -1297,6 +1313,7 @@ int smlua_hook_mod_menu_inputbox(lua_State* L) { hooked->sliderMax = 0; hooked->reference = ref; hooked->mod = gLuaActiveMod; + hooked->modFile = gLuaActiveModFile; lua_pushinteger(L, gHookedModMenuElementsCount); gHookedModMenuElementsCount++; @@ -1428,7 +1445,7 @@ void smlua_call_mod_menu_element_hook(struct LuaHookedModMenuElement* hooked, in } // call the callback - if (0 != smlua_call_hook(L, params, 1, 0, hooked->mod)) { + if (0 != smlua_call_hook(L, params, 1, 0, hooked->mod, hooked->modFile)) { LOG_LUA("Failed to call the mod menu element callback: %s", hooked->name); return; } @@ -1453,6 +1470,7 @@ void smlua_clear_hooks(void) { struct LuaHookedMarioAction* hooked = &sHookedMarioActions[i]; hooked->action = 0; hooked->mod = NULL; + hooked->modFile = NULL; memset(hooked->actionHookRefs, 0, sizeof(hooked->actionHookRefs)); } sHookedMarioActionsCount = 0; @@ -1467,6 +1485,7 @@ void smlua_clear_hooks(void) { hooked->reference = 0; hooked->mod = NULL; + hooked->modFile = NULL; } sHookedChatCommandsCount = 0; @@ -1482,6 +1501,7 @@ void smlua_clear_hooks(void) { hooked->sliderMax = 0; hooked->reference = 0; hooked->mod = NULL; + hooked->modFile = NULL; } gHookedModMenuElementsCount = 0; @@ -1510,6 +1530,7 @@ void smlua_clear_hooks(void) { hooked->replace = false; hooked->luaBehavior = false; hooked->mod = NULL; + hooked->modFile = NULL; } sHookedBehaviorsCount = 0; memset(gLuaMarioActionIndex, 0, sizeof(gLuaMarioActionIndex)); diff --git a/src/pc/lua/smlua_hooks.h b/src/pc/lua/smlua_hooks.h index b81841e4b..280bd129d 100644 --- a/src/pc/lua/smlua_hooks.h +++ b/src/pc/lua/smlua_hooks.h @@ -117,6 +117,7 @@ struct LuaHookedModMenuElement { u32 sliderMax; int reference; struct Mod* mod; + struct ModFile* modFile; }; extern u32 gLuaMarioActionIndex[]; @@ -140,7 +141,7 @@ bool smlua_is_behavior_hooked(const BehaviorScript *behavior); const char* smlua_get_name_from_hooked_behavior_id(enum BehaviorId id); bool smlua_call_behavior_hook(const BehaviorScript** behavior, struct Object* object, bool before); -int smlua_call_hook(lua_State* L, int nargs, int nresults, int errfunc, struct Mod* activeMod); +int smlua_call_hook(lua_State* L, int nargs, int nresults, int errfunc, struct Mod* activeMod, struct ModFile* activeModFile); bool smlua_call_action_hook(enum LuaActionHookType hookType, struct MarioState* m, s32* returnValue); u32 smlua_get_action_interaction_type(struct MarioState* m); diff --git a/src/pc/lua/smlua_require.c b/src/pc/lua/smlua_require.c new file mode 100644 index 000000000..813a7430b --- /dev/null +++ b/src/pc/lua/smlua_require.c @@ -0,0 +1,180 @@ +#include +#include "smlua.h" +#include "pc/mods/mods.h" +#include "pc/mods/mods_utils.h" +#include "pc/fs/fmem.h" + + +// table to track loaded modules per mod +static void smlua_init_mod_loaded_table(lua_State* L, const char* modPath) { + // Create a unique registry key for this mod's loaded table + char registryKey[SYS_MAX_PATH + 16]; + snprintf(registryKey, sizeof(registryKey), "mod_loaded_%s", modPath); + + lua_getfield(L, LUA_REGISTRYINDEX, registryKey); + if (lua_isnil(L, -1)) { + lua_pop(L, 1); + lua_newtable(L); + lua_setfield(L, LUA_REGISTRYINDEX, registryKey); + } else { + lua_pop(L, 1); + } +} + +static struct ModFile* smlua_find_mod_file(const char* moduleName) { + char relativeDir[SYS_MAX_PATH] = ""; + + if (!gLuaActiveMod) { + return NULL; + } + + if (gLuaActiveModFile) { + path_get_folder(gLuaActiveModFile->relativePath, relativeDir); + } + + bool hasRelativeDir = strlen(relativeDir) > 0; + + struct ModFile* bestPick = NULL; + int bestRelativeDepth = INT_MAX; + int bestTotalDepth = INT_MAX; + bool foundRelativeFile = false; + + char luaName[SYS_MAX_PATH] = ""; + char luacName[SYS_MAX_PATH] = ""; + snprintf(luaName, SYS_MAX_PATH, "%s.lua", moduleName); + snprintf(luacName, SYS_MAX_PATH, "%s.luac", moduleName); + + for (int i = 0; i < gLuaActiveMod->fileCount; i++) { + struct ModFile* file = &gLuaActiveMod->files[i]; + + // don't consider the currently running file + if (file == gLuaActiveModFile) { + continue; + } + + // only consider lua files + if (!str_ends_with(file->relativePath, ".lua") && !str_ends_with(file->relativePath, ".luac")) { + continue; + } + + // check for match + if (!str_ends_with(file->relativePath, moduleName) && !str_ends_with(file->relativePath, luaName) && !str_ends_with(file->relativePath, luacName)) { + continue; + } + + // get total path depth + int totalDepth = path_depth(file->relativePath); + + // make sure we never load the old-style lua files with require() + if (totalDepth < 1) { + continue; + } + + // get relative path depth + int relativeDepth = INT_MAX; + if (hasRelativeDir && path_is_relative_to(file->relativePath, relativeDir)) { + relativeDepth = path_depth(file->relativePath + strlen(relativeDir)); + foundRelativeFile = true; + } + + // pick new best + // relative files will always win against absolute files + // other than that, shallower files will win + if (relativeDepth < bestRelativeDepth || (!foundRelativeFile && totalDepth < bestTotalDepth)) { + bestPick = file; + bestRelativeDepth = relativeDepth; + bestTotalDepth = totalDepth; + } + } + + return bestPick; +} + +static int smlua_custom_require(lua_State* L) { + const char* moduleName = luaL_checkstring(L, 1); + + struct Mod* activeMod = gLuaActiveMod; + if (!activeMod) { + LOG_LUA("require() called outside of mod context"); + return 0; + } + + // create registry key for this mod's loaded table + char registryKey[SYS_MAX_PATH + 16] = ""; + snprintf(registryKey, sizeof(registryKey), "mod_loaded_%s", activeMod->relativePath); + + // get or create the mod's loaded table + lua_getfield(L, LUA_REGISTRYINDEX, registryKey); + if (lua_isnil(L, -1)) { + lua_pop(L, 1); + lua_newtable(L); + lua_pushvalue(L, -1); + lua_setfield(L, LUA_REGISTRYINDEX, registryKey); + } + + // find the file in mod files + struct ModFile* file = smlua_find_mod_file(moduleName); + if (!file) { + LOG_LUA("module '%s' not found in mod files", moduleName); + lua_pop(L, 1); // pop table + return 0; + } + + // check if module is already loaded + lua_getfield(L, -1, file->relativePath); + if (!lua_isnil(L, -1)) { + // module already loaded, return it + return 1; + } + lua_pop(L, 1); // pop nil value + + // mark module as "loading" to prevent recursion + lua_pushboolean(L, 1); + lua_setfield(L, -2, file->relativePath); + + // cache the previous mod file + struct ModFile* prevModFile = gLuaActiveModFile; + s32 prevTop = lua_gettop(L); + + // load and execute + gLuaActiveModFile = file; + smlua_load_script(activeMod, file, activeMod->index, false); + gLuaActiveModFile = prevModFile; + + // if the module didn't return anything, use true + if (prevTop == lua_gettop(L)) { + lua_pushboolean(L, 1); + } else if (lua_isnil(L, -1)) { + lua_pop(L, 1); + lua_pushboolean(L, 1); + } + + // store in loaded table + lua_pushvalue(L, -1); // duplicate return value + lua_setfield(L, -3, file->relativePath); // loaded[file->relativePath] = return_value + + // clean up stack + lua_remove(L, -2); + + return 1; // return the module value +} + +void smlua_bind_custom_require(lua_State* L) { + // replace the global require function + lua_pushcfunction(L, smlua_custom_require); + lua_setglobal(L, "require"); +} + +void smlua_init_require_system(void) { + lua_State* L = gLuaState; + if (!L) return; + + // initialize the custom require function + smlua_bind_custom_require(L); + + // initialize loaded tables for each mod + for (int i = 0; i < gActiveMods.entryCount; i++) { + struct Mod* mod = gActiveMods.entries[i]; + smlua_init_mod_loaded_table(L, mod->relativePath); + } +} \ No newline at end of file diff --git a/src/pc/lua/smlua_require.h b/src/pc/lua/smlua_require.h new file mode 100644 index 000000000..489fe1dc5 --- /dev/null +++ b/src/pc/lua/smlua_require.h @@ -0,0 +1,9 @@ +#ifndef SMLUA_REQUIRE_H +#define SMLUA_REQUIRE_H + +#include "smlua.h" + +void smlua_bind_custom_require(lua_State* L); +void smlua_init_require_system(void); + +#endif \ No newline at end of file diff --git a/src/pc/lua/smlua_sync_table.c b/src/pc/lua/smlua_sync_table.c index 3a984696f..2e800cf23 100644 --- a/src/pc/lua/smlua_sync_table.c +++ b/src/pc/lua/smlua_sync_table.c @@ -143,14 +143,20 @@ static void smlua_sync_table_call_hook(int syncTableIndex, int keyIndex, int pre struct Mod* mod = gActiveMods.entries[modRemoteIndex]; // call hook - struct Mod* prev = gLuaActiveMod; + struct Mod* prevActiveMod = gLuaActiveMod; + struct ModFile* prevActiveModFile = gLuaActiveModFile; + gLuaActiveMod = mod; + gLuaActiveModFile = NULL; gLuaLastHookMod = mod; gPcDebug.lastModRun = mod; + if (0 != smlua_pcall(L, 3, 0, 0)) { LOG_LUA_LINE("Failed to call the hook_on_changed callback"); } - gLuaActiveMod = prev; + + gLuaActiveMod = prevActiveMod; + prevActiveModFile = prevActiveModFile; } lua_pop(L, 1); // pop _hook_on_changed's value diff --git a/src/pc/mods/mod.c b/src/pc/mods/mod.c index dbc97c93e..f9c4871e9 100644 --- a/src/pc/mods/mod.c +++ b/src/pc/mods/mod.c @@ -1,3 +1,4 @@ +#include #include "mod.h" #include "mods.h" #include "mods_utils.h" @@ -39,9 +40,13 @@ static void mod_activate_bin(struct Mod* mod, struct ModFile* file) { g++; } + // get mod file index + s32 fileIndex = (file - &mod->files[0]); + if (fileIndex < 0 || fileIndex >= mod->fileCount) { fileIndex = 0; } + // Add to custom actors LOG_INFO("Activating DynOS bin: '%s', '%s'", file->cachedPath, geoName); - dynos_add_actor_custom(mod->index, file->cachedPath, geoName); + dynos_add_actor_custom(mod->index, fileIndex, file->cachedPath, geoName); } static void mod_activate_col(struct ModFile* file) { @@ -261,7 +266,7 @@ static struct ModFile* mod_allocate_file(struct Mod* mod, char* relativePath) { return file; } -static bool mod_load_files_dir(struct Mod* mod, char* fullPath, const char* subDir, const char** fileTypes) { +static bool mod_load_files_dir(struct Mod* mod, char* fullPath, const char* subDir, const char** fileTypes, bool recursive) { // concat directory char dirPath[SYS_MAX_PATH] = { 0 }; @@ -285,15 +290,33 @@ static bool mod_load_files_dir(struct Mod* mod, char* fullPath, const char* subD if (strlen(subDir) > 0) { if (snprintf(relativePath, SYS_MAX_PATH - 1, "%s/%s", subDir, dir->d_name) < 0) { LOG_ERROR("Could not concat %s path!", subDir); + closedir(d); return false; } } else { if (snprintf(relativePath, SYS_MAX_PATH - 1, "%s", dir->d_name) < 0) { LOG_ERROR("Could not concat %s path!", subDir); + closedir(d); return false; } } + // Check if this is a directory + struct stat st = { 0 }; + if (recursive && stat(path, &st) == 0 && S_ISDIR(st.st_mode)) { + // Skip . and .. directories + if (strcmp(dir->d_name, ".") == 0 || strcmp(dir->d_name, "..") == 0) { + continue; + } + + // Recursively process subdirectory + if (!mod_load_files_dir(mod, fullPath, relativePath, fileTypes, recursive)) { + closedir(d); + return false; + } + continue; + } + // only consider certain file types bool fileTypeMatch = false; const char** ft = fileTypes; @@ -325,37 +348,37 @@ static bool mod_load_files(struct Mod* mod, char* modName, char* fullPath) { // deal with mod directory { const char* fileTypes[] = { ".lua", ".luac", NULL }; - if (!mod_load_files_dir(mod, fullPath, "", fileTypes)) { return false; } + if (!mod_load_files_dir(mod, fullPath, "", fileTypes, true)) { return false; } } // deal with actors directory { const char* fileTypes[] = { ".bin", ".col", NULL }; - if (!mod_load_files_dir(mod, fullPath, "actors", fileTypes)) { return false; } + if (!mod_load_files_dir(mod, fullPath, "actors", fileTypes, false)) { return false; } } // deal with behaviors directory { const char* fileTypes[] = { ".bhv", NULL }; - if (!mod_load_files_dir(mod, fullPath, "data", fileTypes)) { return false; } + if (!mod_load_files_dir(mod, fullPath, "data", fileTypes, false)) { return false; } } // deal with textures directory { const char* fileTypes[] = { ".tex", NULL }; - if (!mod_load_files_dir(mod, fullPath, "textures", fileTypes)) { return false; } + if (!mod_load_files_dir(mod, fullPath, "textures", fileTypes, false)) { return false; } } // deal with levels directory { const char* fileTypes[] = { ".lvl", NULL }; - if (!mod_load_files_dir(mod, fullPath, "levels", fileTypes)) { return false; } + if (!mod_load_files_dir(mod, fullPath, "levels", fileTypes, false)) { return false; } } // deal with sound directory { const char* fileTypes[] = { ".m64", ".mp3", ".aiff", ".ogg", NULL }; - if (!mod_load_files_dir(mod, fullPath, "sound", fileTypes)) { return false; } + if (!mod_load_files_dir(mod, fullPath, "sound", fileTypes, false)) { return false; } } return true; diff --git a/src/pc/mods/mods_utils.c b/src/pc/mods/mods_utils.c index 734442656..429a22628 100644 --- a/src/pc/mods/mods_utils.c +++ b/src/pc/mods/mods_utils.c @@ -234,6 +234,20 @@ void path_get_folder(char* path, char* outpath) { *o = '\0'; } +int path_depth(const char* path) { + int depth = 0; + for (; *path; path++) { + if (*path == '/' || *path == '\\') { + depth++; + } + } + return depth; +} + +bool path_is_relative_to(const char* fullPath, const char* baseDir) { + return strncmp(fullPath, baseDir, strlen(baseDir)) == 0; +} + bool directory_sanity_check(struct dirent* dir, char* dirPath, char* outPath) { // skip non-portable filenames if (!fs_sys_filename_is_portable(dir->d_name)) { return false; } diff --git a/src/pc/mods/mods_utils.h b/src/pc/mods/mods_utils.h index d7d9d24b5..ad07e8216 100644 --- a/src/pc/mods/mods_utils.h +++ b/src/pc/mods/mods_utils.h @@ -20,6 +20,8 @@ void normalize_path(char* path); bool concat_path(char* destination, char* path, char* fname); char* path_basename(char* path); void path_get_folder(char* path, char* outpath); +int path_depth(const char* path); +bool path_is_relative_to(const char* fullPath, const char* baseDir); bool directory_sanity_check(struct dirent* dir, char* dirPath, char* outPath); #endif \ No newline at end of file