在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
不管是 现在开发中的游戏服务端, 还是近期love2D 开发的前端, 都使用 Lua 做脚本引擎, 需要涉及到 脚本的修改和重启. 第一种方法是 写个封装函数, 里面进行对所有 lua 脚本文件的 require() 操作, 这就要求 : 1.对每个支持重新加载的文件进行 package.loaded[ filename] = nil require( filename) 2.文件加载要保持一定的顺序, 以免造成资源的错乱. 就当前使用 love2D 前端来看, 其实只有一个 "启动"文件: main.lua, 并在其内进行 各个子功能脚本的 require 加载.如果在 重新加载时, 自动按照 main.lua 提供的 require(...) 顺序进行自动加载就好了, 并且无需像上面的针对每个文件编写: function reload_files() require( f1) require( f2) ... end 整体目标有几个: 2.能够按照当前文件中各个文件的 顺序进行加载, 即如果 --main.lua require( "config") require( "function") require( "globals") require( "gameplayer") require( "scene") 这种顺序编写main.lua( 或其他文件), 都尽量保持 config > function > globals > gameplayer > scene 的顺序进行重新加载; 4.能够避免 嵌套递归加载; 5.能够对 外部库进行识别, 即 require( "bit") 是在加载 "位操作"的 库 bit.dll , 而不是 bit.lua, 不应该进行 嵌套加载; -- global.lua require( "skill_cfg") require( "effect_cfg") g_object_list = {} global.lua 文件本身不能被 多次require(), 不然 g_object_list 全局变量会被重置, 但又能够不会影响 skill_cfg 和 effect_cfg 的重新加载; 7.应该要支持 "后序" 方式进行加载, 记载加载 main.lua 过程中, 应该现在递归加载完 子脚本文件: require( "config") require( "function") require( "globals") require( "gameplayer") require( "scene") 然后在进行 加载 main.lua 的后序内容: NeedReset = true ... 8.能够 识别 文件中的 require(...) 行.
大概这 8 点目标 和要求, 但对于第7点, 有个问题: 假设 重新加载 的 递归函数为 function recursive_reload( filename) package.loaded[ filename] = nil 并且main.lua 的内容简单有如: --main.lua require( "config") require( "function") require( "globals") require( "gameplayer") require( "scene") NeedReset = true
在 触发重新加载的 入口中: function main_reloader() recursive_reload( "mian" ) end 调用 main_reloader() 进行重新加载的过程 展开将会如: --先递归地使用 recursive_reload() 重新加载子文件 package.loaded[ 'config'] = nil require( 'config') package.loaded[ 'function'] = nil require( 'function') package.loaded[ 'globals'] = nil require( 'globals') package.loaded[ 'gameplayer'] = nil require( 'gameplayer') package.loaded[ 'scene'] = nil require( 'scene') --再最后加载 main.lua package.loaded[ 'main'] = nil require( 'main') --但就在这个操作中, 还会涉及到嵌套的: require( "config") require( "function") require( "globals") require( "gameplayer") require( "scene") NeedReset = true
这 5 个 文件不就会被 多次 require() 了吗? 虽然 完整的 recursive_reload() 能够防止 "显示的" 重复require(), 但是不能禁止 "隐式的" require() 其实, 就算第二次的 "隐式" requre() 确实会调用, 但不会重新加载 实际的物理文件, 见于 lua 开发手册上: require (modname) Loads the given module. The function starts by looking into the package.loaded table to determine whether modname is already loaded. 即是说, 只要曾经加载了 文件, 并在 package.loaded 内有记录, 后序的 requre() 将会直接返回. 这 5 个 文件不就会被 多次 require() 了吗? 虽然 完整的 recursive_reload() 能够防止 "显示的" 重复require(), 但是不能禁止 "隐式的" require() 其实, 就算第二次的 "隐式" requre() 确实会调用, 但不会重新加载 实际的物理文件, 见于 lua 开发手册上: require (modname) Loads the given module. The function starts by looking into the package.loaded table to determine whether modname is already loaded. If it is, then require returns the value stored at package.loaded[modname]. Otherwise, it tries to find a loader for the module. 即是说, 只要曾经加载了 文件, 并在 package.loaded 内有记录, 后序的 requre() 将会直接返回.
具体运行效果: 只是具体的实现代码:
-- 外部库 登记 local package_list = { bit = true } -- 全局性质类/或禁止重新加载的文件记录 local ignored_file_list = { global = true , } --已重新加载的文件记录 local loaded_file_list = {} --视图排版控制 function leading_tag( indent ) -- body if indent < 1 then return '' else return string.rep( ' |', indent - 1 ) .. ' ' end end --关键递归重新加载函数 --filename 文件名 --indent 递归深度, 用于控制排版显示 function recursive_reload( filename, indent ) -- body if package_list[ filename] then --对于 外部库, 只进行重新加载, 不做递归子文件 --卸载旧文件 package.loaded[ filename] = nil --装载信文件 require( filename ) --标记"已被重新加载" loaded_file_list[ filename] = true print( leading_tag(indent) .. filename .. "... done" ) return true end --普通文件 --进行 "已被重新加载" 检测 if loaded_file_list[ filename] then print( leading_tag(indent) .. filename .. "...already been reloaded IGNORED" ) return true end --读取当前文件内容, 以进行子文件递归重新加载 local file, err = io.open( filename..".lua" ) if file == nil then print( string.format( "failed to reaload file(%s), with error:%s", filename, err or "unknown" ) ) return false end print( leading_tag(indent) .. filename .. "..." ) --读取每一行 for line in file:lines() do --识别 require(...)行, 正则表达? 模式匹配? 并拾取文件名 到 subFileName line = string.gsub( line, '%s', '' ) local subFileName = nil local ret = string.gsub( line, '^require%("(.+)"%)', function ( s ) subFileName = s end ) if subFileName then --进行递归 local success = recursive_reload( subFileName, indent + 1 ) if not success then print( string.format( "failed to reload sub file of (%s)", filename ) ) return false end end end -- "后序" 处理当前文件... if ignored_file_list[ filename] then --忽略 "禁止被重新加载"的文件 print( leading_tag(indent) .. filename .. "... IGNORED" ) return true else --卸载旧文件 package.loaded[ filename] = nil --装载新文件 require( filename ) --设置"已被重新加载" 标记 loaded_file_list[ filename] = true print( leading_tag(indent) .. filename .. "... done" ) return true end end --主入口函数 function reload_script_files() print( "[reload_script_files...]") loaded_file_list = {} --本项目是以 main.lua 为主文件 recursive_reload( "main", 0 ) print( "[reload_script_files...done]") return "reload ok" end 备注: 该机制只支持简单文件目录
|
请发表评论