Last active
May 26, 2025 20:46
-
-
Save waldnercharles/a3e314afccfac40ce4fbf42b83093c54 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
-- Shallow copy a table | |
local function shallow_copy(t) | |
local copy = {} | |
for k, v in pairs(t) do | |
copy[k] = v | |
end | |
return copy | |
end | |
-- Escape reserved characters | |
local function escape_reserved_characters(str) | |
return str:gsub("[%(%)%.%%%+%-%*%?%[%]%^%$]", "%%%1") | |
end | |
-- Remove leading and trailing whitepsace | |
local function trim(str, chars) | |
if not chars then | |
return str:match("^[%s]*(.-)[%s]*$") | |
end | |
chars = escape_reserved_characters(chars) | |
return str:match("^[" .. chars .. "]*(.-)[" .. chars .. "]*$") | |
end | |
-- Hotswap every module that has been loaded. | |
function hotswap_all() | |
for k, v in pairs(package.loaded) do | |
print(v) | |
local ok, err = hotswap(k) | |
end | |
end | |
-- Hotswap a specific module. This will update its values in the globals table with new values. | |
function hotswap(module_name) | |
local old_globals = shallow_copy(_G) | |
local already_updated = {} | |
local function update_table_recursive(old, new) | |
-- Break loops | |
if already_updated[old] then | |
return | |
end | |
already_updated[old] = true | |
local old_metatable, new_metatable = getmetatable(old), getmetatable(new) | |
if old_metatable and new_metatable then | |
update_table_recursive(old_metatable, new_metatable) | |
end | |
for k, v in pairs(new) do | |
if type(old[k]) == "table" and type(v) == "table" then | |
update_table_recursive(old[k], v) | |
else | |
old[k] = v | |
end | |
end | |
end | |
local success, module = pcall(require, module_name) | |
module = success and module or nil | |
local err | |
local function on_error(msg) | |
for k in pairs(_G) do | |
_G[k] = old_globals[k] | |
end | |
err = trim(msg) | |
end | |
xpcall(function() | |
package.loaded[module_name] = nil | |
local new_module = require(module_name) | |
if type(module) == "table" and type(new_module) == "table" then | |
update_table_recursive(module, new_module) | |
end | |
for k, v in pairs(old_globals) do | |
if v ~= _G[k] and type(v) == "table" and type(_G[k]) == "table" then | |
update_table_recursive(v, _G[k]) | |
_G[k] = v | |
end | |
end | |
end, on_error) | |
package.loaded[module_name] = module | |
if err then | |
return false, err | |
end | |
return true, nil | |
end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
namespace fs = std::filesystem; | |
std::string normalize_path(const fs::path &path) | |
{ | |
std::string path_str = path.string(); | |
std::replace(path_str.begin(), path_str.end(), '\\', '/'); | |
return path_str; | |
} | |
bool hot_reload_dir(const fs::path &base_dir) | |
{ | |
static fs::file_time_type last_write_time = fs::file_time_type::min(); | |
fs::file_time_type max_last_write_time = last_write_time; | |
std::stack<fs::path> dirs; | |
dirs.emplace(""); | |
while (!dirs.empty()) { | |
fs::path current_dir = dirs.top(); | |
dirs.pop(); | |
for (const auto &entry : fs::directory_iterator(base_dir / current_dir)) { | |
const fs::file_status entry_status = entry.status(); | |
fs::path entry_path = current_dir / entry.path().filename(); // I feel like using this operator is going to get me cancelled. | |
if (fs::is_regular_file(entry_status) && entry_path.extension() == ".lua") { | |
fs::file_time_type entry_last_write_time = fs::last_write_time(entry); | |
if (entry_last_write_time > last_write_time) { | |
max_last_write_time = max_last_write_time > entry_last_write_time ? max_last_write_time : entry_last_write_time; | |
bool ok; | |
String err; | |
String lua_module = normalize_path(entry_path.replace_extension()).c_str(); | |
REF_CallLuaFunction(L, "hotswap", { ok, err }, lua_module); | |
if (!ok) { | |
printf("Failed to hotswap %s: %s\n", lua_module.c_str(), err.c_str()); | |
} else { | |
printf("Hotswapped %s\n", lua_module.c_str()); | |
} | |
} | |
} else if (fs::is_directory(entry_status)) { | |
dirs.emplace(entry_path); | |
} | |
} | |
} | |
bool updated = max_last_write_time > last_write_time; | |
last_write_time = max_last_write_time; | |
return updated; | |
} | |
bool hot_reload() | |
{ | |
return hot_reload_dir(fs::current_path()); | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
void hot_reload() | |
{ | |
static uint64_t last_modified = 0; | |
uint64_t max_last_modified = last_modified; | |
const char *ext = sintern(".lua"); | |
for (const Path &f : Directory::enumerate("/")) { | |
CF_Stat stat = {}; | |
if (cf_sintern(cf_path_get_ext(f)) == ext && !cf_is_error(cf_fs_stat(f, &stat)) && stat.last_modified_time > last_modified) { | |
max_last_modified = cf_max(max_last_modified, stat.last_modified_time); | |
bool ok; | |
String err; | |
REF_CallLuaFunction(L, "hotswap", { ok, err }, f.filename_no_ext()); | |
if (!ok) { | |
printf("Failed to hotswap %s: %s\n", f.c_str(), err.c_str()); | |
} else { | |
printf("Hotswapped %s\n", f.c_str()); | |
} | |
} | |
} | |
last_modified = max_last_modified; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment