在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
¶
¶¶
¶
¶------------------------- --代码动态加载模块 v0.1 -- --基于Lua 5.2版本的loadfile ------------------------ ----模块管理表,类似于package.loaded local Modules = {} --通过loadfile将新代码载入到newModule function Load(pathname, modulename) local newModule = {} --_ENV被设置为空表,载入文件的访问空间被限制在模块内部 local func, err = loadfile(pathname, "bt", newModule) if not func then print("ERROR: " .. err) else func() Modules[moduleName] = newModule end end 测试模块 Game.lua --------------------------------- ---- 模拟一个游戏定时活动玩法逻辑 ---- ---- 活动中获奖用户记录下来,活动结束后发奖 ---- ---- 此代码有逻辑错误 ----------------------------------- -- 活动中获奖用户列表 rewardedUser = {} -- 活动开始结束时间设置 local starttime = {hour = 10, min = 0, sec = 0} local endtime = {hour = 12, min = 0, sec = 0} -- 记录活动中中奖用户 function RecordReward(charId) rewardedUser[char] = true -- 报错,char = nil --更新后的正确代码 -- rewardedUser[charId] = true end 测试驱动模块 Main.lua require("Modules.lua") GAME = Load("Game.lua") GAME.RecordReward(1001) --报错 -- 更新错误以后重新加载 GAME = Load("Game.lua") GAME.RecordReward(1001) -- rewardedUser = { [1001] = true} local data =GAME.rewardedUser local func = GAME.RecordReward --local 导致当前GAME.RecordReward、GAME.rewardedUser引用被当作upvalue绑定在 closure 里 GAME = Load("Game.lua") -- GAME.rewardedUser = {} 被重新载入的代码所取代, { [1001] = true}数据丢失,导致用户无法领奖,业务逻辑发生异常。 GAME.RecordReward(1002) -- rewardedUser = { [1002] = true} -- data引用的是保存在upvalue中的旧引用,值为{ [1001] = true} ,与当前GAME.RecordReward不同步了,出现逻辑异常 func() -- 仍然报错,因为其引用的是保存在upvalue中的旧引用,而不是更新后的GAME.RecordReward ¶通过上面的代码实验,我们发现以下几个问题
同时我们也可以看到,通过loadfile加载的代码,是以table形式引用的,这就为我们解决上述问题打开了思路。 ------------------------- --代码动态加载模块 v0.1 -- --基于Lua 5.2版本的loadfile ------------------------ --模块管理表,类似于package.loaded local Modules = {} -- 参数: -- string pathname 加载模块文件名,含路径 -- string name 模块名,一般和pathname一直即可 -- boolean reload 是否强制更新重载 -- 返回: -- table module 模块,如果加载失败返回nil -- string err 如果module为nil,返回错误信息 function Load(pathname, moduleName, reload) moduleName = moduleName or pathname local oldModule = Modules[moduleName] if not oldModule then -- 第一次加载模块,全新加载 local newModule = {} --通过metatable机制允许模块环境访问_G setmetatable(newModule, {__index = _G}) local func, err = loadfile(pathname, "bt", newModule) if not func then print("ERROR: " .. err) return nil, err end func() Modules[moduleName] = newModule return newModule else -- 重复加载,不需要更新时直接返回缓存 if not reload then return oldModule else --先缓存更新前模块内的table数据 local oldCache = {} for k, v in pairs(oldModule) do if type(v) == "table" then oldCache[k] = v end oldModule[k] = nil end --原模块直接作为新的环境使用 local newModule = oldModule --原模块被完全更新 local func, err = loadfile(pathname, "bt", newModule) if not func then print("ERROR: " .. err) return nil, err end func() --恢复table数据,既保持原有数据,也保持了其他模块的既有引用 --因为此引用机制只能作用于table,函数upvalue依然得不到更新 for k, v in pairs(oldCache) do --将metatable换成新的即可实现函数段更新 local mt = getmetatable(newModule[k]) if mt then setmetatable(v, mt) end --对于已存在的table,数据段保持不变 newModule[k] = v end return newModule end end end 小结:
|
请发表评论