mirror of
https://github.com/coop-deluxe/sm64coopdx.git
synced 2025-10-30 08:01:01 +00:00
fix require (#908)
* fix require * Refactor smlua_find_mod_file * formatting * initialize arrays for consistency * normalize relative path in mod_allocate_file instead (fix mod dev mode) * do cleanup * Remove useless branch + add comments * fix mod_file_exists * WIP: cache mod files at root (LIVE RELOAD IS BROKEN) * Fix live reload * remove useless declaration (??) * fix incorrect top handling * cleanup x2 * stupid * loading sentinel + mark top level files as loading * address dj's comment
This commit is contained in:
parent
9a934f8443
commit
25838f13bc
9 changed files with 171 additions and 109 deletions
|
|
@ -362,7 +362,19 @@ void smlua_init(void) {
|
|||
}
|
||||
|
||||
gLuaActiveModFile = file;
|
||||
smlua_load_script(mod, file, i, true);
|
||||
|
||||
// file has been required by some module before this
|
||||
if (!smlua_get_cached_module_result(L, mod, file)) {
|
||||
smlua_mark_module_as_loading(L, mod, file);
|
||||
|
||||
s32 prevTop = lua_gettop(L);
|
||||
int rc = smlua_load_script(mod, file, i, true);
|
||||
|
||||
if (rc == LUA_OK) {
|
||||
smlua_cache_module_result(L, mod, file, prevTop);
|
||||
}
|
||||
}
|
||||
|
||||
lua_settop(L, 0);
|
||||
}
|
||||
gLuaActiveMod = NULL;
|
||||
|
|
|
|||
|
|
@ -100,6 +100,7 @@ int smlua_hook_event(lua_State* L) {
|
|||
|
||||
hook->reference[hook->count] = ref;
|
||||
hook->mod[hook->count] = gLuaActiveMod;
|
||||
hook->modFile[hook->count] = gLuaActiveModFile;
|
||||
hook->count++;
|
||||
|
||||
return 1;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
#include <stdbool.h>
|
||||
#include "smlua.h"
|
||||
#include "smlua_require.h"
|
||||
#include "pc/mods/mods.h"
|
||||
#include "pc/mods/mods_utils.h"
|
||||
|
||||
|
|
@ -343,16 +344,8 @@ static void smlua_reload_module(lua_State *L, struct Mod* mod, struct ModFile *f
|
|||
// only handle loaded Lua modules
|
||||
if (!file->isLoadedLuaModule) { return; }
|
||||
|
||||
// build registry key for this mod's loaded table
|
||||
char registryKey[SYS_MAX_PATH + 16];
|
||||
snprintf(registryKey, sizeof(registryKey), "mod_loaded_%s", mod->relativePath);
|
||||
|
||||
// get per-mod "loaded" table
|
||||
lua_getfield(L, LUA_REGISTRYINDEX, registryKey); // ..., loadedTable
|
||||
if (!lua_istable(L, -1)) {
|
||||
lua_pop(L, 1);
|
||||
return;
|
||||
}
|
||||
smlua_get_or_create_mod_loaded_table(L, mod);
|
||||
|
||||
// get the old module table: loadedTable[file->relativePath]
|
||||
lua_getfield(L, -1, file->relativePath); // ..., loadedTable, oldMod
|
||||
|
|
|
|||
|
|
@ -4,47 +4,89 @@
|
|||
#include "pc/mods/mods_utils.h"
|
||||
#include "pc/fs/fmem.h"
|
||||
|
||||
#define LOADING_SENTINEL ((void*)-1)
|
||||
|
||||
// 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);
|
||||
void smlua_get_or_create_mod_loaded_table(lua_State* L, struct Mod* mod) {
|
||||
char registryKey[SYS_MAX_PATH + 16] = "";
|
||||
snprintf(registryKey, sizeof(registryKey), "mod_loaded_%s", mod->relativePath);
|
||||
|
||||
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);
|
||||
} else {
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
}
|
||||
|
||||
bool smlua_get_cached_module_result(lua_State* L, struct Mod* mod, struct ModFile* file) {
|
||||
smlua_get_or_create_mod_loaded_table(L, mod);
|
||||
lua_getfield(L, -1, file->relativePath);
|
||||
|
||||
if (lua_touserdata(L, -1) == LOADING_SENTINEL) {
|
||||
LOG_LUA_LINE("loop or previous error loading module '%s'", file->relativePath);
|
||||
lua_pop(L, 1); // pop sentinel
|
||||
lua_pushnil(L);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (lua_isnil(L, -1)) {
|
||||
// not cached
|
||||
lua_pop(L, 2); // pop nil and loaded table
|
||||
return false;
|
||||
}
|
||||
|
||||
// cached, remove loaded table and leave value on top
|
||||
lua_remove(L, -2);
|
||||
return true;
|
||||
}
|
||||
|
||||
void smlua_mark_module_as_loading(lua_State* L, struct Mod* mod, struct ModFile* file) {
|
||||
smlua_get_or_create_mod_loaded_table(L, mod);
|
||||
lua_pushlightuserdata(L, LOADING_SENTINEL);
|
||||
lua_setfield(L, -2, file->relativePath);
|
||||
lua_pop(L, 1); // pop loaded table
|
||||
}
|
||||
|
||||
void smlua_cache_module_result(lua_State* L, struct Mod* mod, struct ModFile* file, s32 prevTop) {
|
||||
if (lua_gettop(L) == prevTop) {
|
||||
lua_pushboolean(L, 1);
|
||||
} else if (lua_isnil(L, -1)) {
|
||||
lua_pop(L, 1);
|
||||
lua_pushboolean(L, 1);
|
||||
}
|
||||
|
||||
smlua_get_or_create_mod_loaded_table(L, mod);
|
||||
|
||||
lua_pushvalue(L, -2); // duplicate result
|
||||
lua_setfield(L, -2, file->relativePath); // loaded[file->relativePath] = result
|
||||
lua_pop(L, 1); // pop loaded table
|
||||
}
|
||||
|
||||
static struct ModFile* smlua_find_mod_file(const char* moduleName) {
|
||||
char relativeDir[SYS_MAX_PATH] = "";
|
||||
char basePath[SYS_MAX_PATH] = "";
|
||||
char absolutePath[SYS_MAX_PATH] = "";
|
||||
|
||||
if (!gLuaActiveMod) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// get the directory of the current file
|
||||
if (gLuaActiveModFile) {
|
||||
path_get_folder(gLuaActiveModFile->relativePath, relativeDir);
|
||||
path_get_folder(gLuaActiveModFile->relativePath, basePath);
|
||||
}
|
||||
|
||||
bool hasRelativeDir = strlen(relativeDir) > 0;
|
||||
// resolve moduleName to a path relative to mod root
|
||||
resolve_relative_path(basePath, moduleName, absolutePath);
|
||||
|
||||
struct ModFile* bestPick = NULL;
|
||||
int bestRelativeDepth = INT_MAX;
|
||||
int bestTotalDepth = INT_MAX;
|
||||
bool foundRelativeFile = false;
|
||||
|
||||
char rawName[SYS_MAX_PATH] = "";
|
||||
char luaName[SYS_MAX_PATH] = "";
|
||||
char luacName[SYS_MAX_PATH] = "";
|
||||
snprintf(rawName, SYS_MAX_PATH, "/%s", moduleName);
|
||||
snprintf(luaName, SYS_MAX_PATH, "/%s.lua", moduleName);
|
||||
snprintf(luacName, SYS_MAX_PATH, "/%s.luac", moduleName);
|
||||
snprintf(luaName, SYS_MAX_PATH, "%s.lua", absolutePath);
|
||||
snprintf(luacName, SYS_MAX_PATH, "%s.luac", absolutePath);
|
||||
|
||||
// since mods' relativePaths are relative to the mod's root, we can do a direct comparison
|
||||
for (int i = 0; i < gLuaActiveMod->fileCount; i++) {
|
||||
struct ModFile* file = &gLuaActiveMod->files[i];
|
||||
|
||||
|
|
@ -59,36 +101,12 @@ static struct ModFile* smlua_find_mod_file(const char* moduleName) {
|
|||
}
|
||||
|
||||
// check for match
|
||||
if (!str_ends_with(file->relativePath, rawName) && !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;
|
||||
if (!strcmp(file->relativePath, luaName) || !strcmp(file->relativePath, luacName)) {
|
||||
return file;
|
||||
}
|
||||
}
|
||||
|
||||
return bestPick;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int smlua_custom_require(lua_State* L) {
|
||||
|
|
@ -96,69 +114,47 @@ static int smlua_custom_require(lua_State* L) {
|
|||
|
||||
struct Mod* activeMod = gLuaActiveMod;
|
||||
if (!activeMod) {
|
||||
LOG_LUA("require() called outside of mod context");
|
||||
LOG_LUA_LINE("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);
|
||||
if (str_ends_with(moduleName, "/") || str_ends_with(moduleName, "\\")) {
|
||||
LOG_LUA_LINE("cannot require a directory");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 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
|
||||
LOG_LUA_LINE("module '%s' not found in mod files", moduleName);
|
||||
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);
|
||||
|
||||
// tag it as a loaded lua module
|
||||
file->isLoadedLuaModule = true;
|
||||
|
||||
// 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);
|
||||
// check cache first
|
||||
if (smlua_get_cached_module_result(L, activeMod, file)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
// store in loaded table
|
||||
lua_pushvalue(L, -1); // duplicate return value
|
||||
lua_setfield(L, -3, file->relativePath); // loaded[file->relativePath] = return_value
|
||||
// mark module as "loading" to prevent recursion
|
||||
smlua_mark_module_as_loading(L, activeMod, file);
|
||||
|
||||
// clean up stack
|
||||
lua_remove(L, -2);
|
||||
// cache the previous mod file
|
||||
struct ModFile* prevModFile = gLuaActiveModFile;
|
||||
|
||||
// load and execute
|
||||
gLuaActiveModFile = file;
|
||||
s32 prevTop = lua_gettop(L);
|
||||
|
||||
int rc = smlua_load_script(activeMod, file, activeMod->index, false);
|
||||
|
||||
if (rc == LUA_OK) {
|
||||
smlua_cache_module_result(L, activeMod, file, prevTop);
|
||||
}
|
||||
|
||||
gLuaActiveModFile = prevModFile;
|
||||
|
||||
return 1; // return the module value
|
||||
}
|
||||
|
|
@ -179,6 +175,7 @@ void smlua_init_require_system(void) {
|
|||
// 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);
|
||||
smlua_get_or_create_mod_loaded_table(L, mod);
|
||||
lua_pop(L, 1); // pop loaded table
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,9 +3,11 @@
|
|||
|
||||
#include "smlua.h"
|
||||
|
||||
void smlua_require_update(lua_State* L);
|
||||
void smlua_bind_custom_require(lua_State* L);
|
||||
void smlua_reload_module(lua_State *L, struct Mod* mod, struct ModFile *file);
|
||||
void smlua_get_or_create_mod_loaded_table(lua_State* L, struct Mod* mod);
|
||||
bool smlua_get_cached_module_result(lua_State* L, struct Mod* mod, struct ModFile* file);
|
||||
void smlua_mark_module_as_loading(lua_State* L, struct Mod* mod, struct ModFile* file);
|
||||
void smlua_cache_module_result(lua_State* L, struct Mod* mod, struct ModFile* file, s32 prevTop);
|
||||
void smlua_init_require_system(void);
|
||||
|
||||
#endif
|
||||
|
|
@ -539,9 +539,17 @@ void set_environment_region(u8 index, s16 value) {
|
|||
bool mod_file_exists(const char* filename) {
|
||||
if (gLuaActiveMod == NULL) { return false; }
|
||||
|
||||
char normPath[SYS_MAX_PATH] = { 0 };
|
||||
|
||||
if (snprintf(normPath, sizeof(normPath), "%s", filename) < 0) {
|
||||
LOG_ERROR("Failed to copy filename for normalization: %s", filename);
|
||||
}
|
||||
|
||||
normalize_path(normPath);
|
||||
|
||||
for (s32 i = 0; i < gLuaActiveMod->fileCount; i++) {
|
||||
struct ModFile* file = &gLuaActiveMod->files[i];
|
||||
if (!strcmp(file->relativePath, filename)) {
|
||||
if (!strcmp(file->relativePath, normPath)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -245,15 +245,22 @@ static struct ModFile* mod_allocate_file(struct Mod* mod, char* relativePath) {
|
|||
memset(file, 0, sizeof(struct ModFile));
|
||||
|
||||
// set relative path
|
||||
if (snprintf(file->relativePath, SYS_MAX_PATH - 1, "%s", relativePath) < 0) {
|
||||
LOG_ERROR("Failed to remember relative path '%s'", relativePath);
|
||||
char normPath[SYS_MAX_PATH] = { 0 };
|
||||
if (snprintf(normPath, sizeof(normPath), "%s", relativePath) < 0) {
|
||||
LOG_ERROR("Failed to copy relative path for normalization: %s", relativePath);
|
||||
}
|
||||
|
||||
normalize_path(normPath);
|
||||
|
||||
if (snprintf(file->relativePath, SYS_MAX_PATH - 1, "%s", normPath) < 0) {
|
||||
LOG_ERROR("Failed to remember relative path '%s'", normPath);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// figure out full path
|
||||
char fullPath[SYS_MAX_PATH] = { 0 };
|
||||
if (!mod_file_full_path(fullPath, mod, file)) {
|
||||
LOG_ERROR("Failed to concat path: '%s' + '%s'", mod->basePath, relativePath);
|
||||
LOG_ERROR("Failed to concat path: '%s' + '%s'", mod->basePath, normPath);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -244,6 +244,47 @@ int path_depth(const char* path) {
|
|||
return depth;
|
||||
}
|
||||
|
||||
void resolve_relative_path(const char* base, const char* path, char* output) {
|
||||
char combined[SYS_MAX_PATH] = "";
|
||||
|
||||
// If path is absolute, copy as is. Otherwise, combine base and relative path
|
||||
if (path[0] == '/' || path[0] == '\\') {
|
||||
snprintf(combined, sizeof(combined), "%s", path);
|
||||
} else {
|
||||
snprintf(combined, sizeof(combined), "%s/%s", base, path);
|
||||
}
|
||||
|
||||
char* tokens[64];
|
||||
int tokenCount = 0;
|
||||
|
||||
// Tokenize path by separators
|
||||
char* token = strtok(combined, "/\\");
|
||||
while (token && tokenCount < 64) {
|
||||
if (strcmp(token, "..") == 0) {
|
||||
// Pop last token to go up a directory
|
||||
if (tokenCount > 0) { tokenCount--; }
|
||||
|
||||
// Ignore "." (current directory) or empty tokens
|
||||
} else if (strcmp(token, ".") != 0 && token[0] != '\0') {
|
||||
tokens[tokenCount++] = token;
|
||||
}
|
||||
|
||||
token = strtok(NULL, "/\\");
|
||||
}
|
||||
|
||||
output[0] = '\0';
|
||||
|
||||
// Build output path from tokens
|
||||
for (int i = 0; i < tokenCount; i++) {
|
||||
if (i > 0) {
|
||||
strncat(output, "/", SYS_MAX_PATH - strlen(output) - 1);
|
||||
}
|
||||
strncat(output, tokens[i], SYS_MAX_PATH - strlen(output) - 1);
|
||||
}
|
||||
|
||||
normalize_path(output);
|
||||
}
|
||||
|
||||
bool path_is_relative_to(const char* fullPath, const char* baseDir) {
|
||||
return strncmp(fullPath, baseDir, strlen(baseDir)) == 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ 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);
|
||||
void resolve_relative_path(const char* base, const char* path, char* output);
|
||||
bool path_is_relative_to(const char* fullPath, const char* baseDir);
|
||||
bool directory_sanity_check(struct dirent* dir, char* dirPath, char* outPath);
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue