local LuaHotReload = {}; local visitedSign = {} local protection = { setmetatable = true, pairs = true, ipairs = true, next = true, require = true, _ENV = true, } -- function LuaHotReload.GetTableCount(srcTable) -- local returnCount = 0; -- if srcTable then -- for __, __ in pairs(srcTable) do -- returnCount = returnCount + 1; -- end -- end -- return returnCount; -- end function LuaHotReload.UpdateFunc(newFunc, oldFunc) local oldUpvalueMap = {} for i = 1, math.huge do local name, value = debug.getupvalue(oldFunc, i) if not name then break end oldUpvalueMap[name] = value end for i = 1, math.huge do local name, value = debug.getupvalue(newFunc, i) if not name then break end local oldValue = oldUpvalueMap[name] if value ~= oldValue then if oldValue then if type(oldValue) ~= type(value) then debug.setupvalue(newFunc, i, oldValue) elseif type(oldValue) == 'function' then LuaHotReload.UpdateFunc(value, oldValue) elseif type(oldValue) == 'table' then LuaHotReload.UpdateTable(value, oldValue) debug.setupvalue(newFunc, i, oldValue) else debug.setupvalue(newFunc, i, oldValue) end end end end end function LuaHotReload.UpdateTable(newTable, oldTable) if protection[newTable] or protection[oldTable] then return end if newTable == oldTable then return; end local signature = tostring(oldTable)..tostring(newTable); if visitedSign[signature] then return; end visitedSign[signature] = true; for name, value in pairs(newTable) do local oldValue = oldTable[name] if type(value) == type(oldValue) then if type(value) == 'function' then LuaHotReload.UpdateFunc(value, oldValue, name) oldTable[name] = value elseif type(value) == 'table' then LuaHotReload.UpdateTable(value, oldValue) end else oldTable[name] = value end end -- 刷新原表 local old_meta = debug.getmetatable(oldTable); local new_meta = debug.getmetatable(newTable); if type(old_meta) == 'table' and type(new_meta) == 'table' then LuaHotReload.UpdateTable(new_meta, old_meta); end end function LuaHotReload.ReloadFile(chunkPath) -- 搭建环境 local env = {}; setmetatable(env, { __index = _G }); local _ENV = env; visitedSign = {}; -- loadfile 编译但是不执行 local func = loadfile(chunkPath, 't'); if type(func) ~= "function" then -- 加载文件出错了 return; end setfenv(func, env); -- 设置执行环境 local ok, newEnvTable = pcall(func); assert(ok, newEnvTable); if not ok then return; end local chunkTable = string.gsub(chunkPath, "/", "%."); local oldEnvTable = require (chunkTable); if (type(newEnvTable) == "table") and (type(oldEnvTable) == "table") then LuaHotReload.UpdateTable(newEnvTable, oldEnvTable); end -- 刷新全局环境 for name, value in pairs(env) do if newEnvTable and newEnvTable.__cname == name then -- to do nothing else local gValue = _G[name]; if type(gValue) ~= type(value) then _G[name] = value; elseif type(value) == 'function' then LuaHotReload.UpdateFunc(value, gValue); _G[name] = value; elseif type(value) == 'table' then LuaHotReload.UpdateTable(value, gValue); else -- 这个else是用来更新全局变量的 _G[name] = value; end end end setfenv(func, _G) -- -- 环境回归 -- for name, value in pairs(env) do -- env[name] = _G[name]; -- end return 0; end return LuaHotReload;