前言
感谢提供思路两个博客主以下是博客地址:
https://gameinstitute.qq.com/community/detail/120538
https://www.jianshu.com/p/7b8ae23ecd81
基本知识
我们在require一个Lua文件的时候是将Lua文件加载到package.loaded[modelname]中。当我们加载模块的时候会先从package.loaded中读取,如果文件不存在才会Load文件。
最基本的热更新的方式(不可用)
- 第一种加载方式
将package.loaded[modelname]的值置为nil,强制重新加载:
function reload_module(module_name)
package.loaded[modulename] = nil
require(modulename)
end
在上面的操作中我们直接将模块赋值为空,然后重新加载。在这样的过程中虽然我们能够重新加载model了,但是其他引用了该的地方是无法更新的,所以我们则需要找到所有引用了该文件的模块。
__G
_G是一个变量而不是一个函数,它掌控了整个全局环境,我们需要找到引用了该模块的其他模块也可以在此处找到
它的结构可能会如下
_G
├── string:
| ├── sub: function: 006AEB70
| ├── upper: function: 006AEBB8
| ├── len: function: 006AE290
| ├── gfind: function: 006AE170
| ├── rep: function: 006AE3F8
| ├── find: function: 006ADFC0
| ├── match: function: 006AE368
| ├── char: function: 006ADEA0
| ├── dump: function: 006ADF30
| ├── gmatch: function: 006AE170
| ├── reverse: function: 006AE440
| ├── byte: function: 006ADE10
| ├── format: function: 006AE050
| ├── gsub: function: 006AE200
| └── lower: function: 006AE2D8
├── xpcall: function: 006AA630
├── package:
| ├── preload:
| | └── { }
| ├── loadlib: function: 006AAD80
| ├── loaded:
| | └── { }
| ├── loaders:
| | ├── 1] function: 006AAEA0
| | ├── 2] function: 006AAEE8
| | ├── 4] function: 006AAF78
| | └── 3] function: 006AAF30
...
- _G保存了lua所用的所有全局函数和全局变量,初始情况是只包含lua程序库的函数和变量。
- lua程序中定义的全局函数和变量会自动加入到_G中,而局部函数和变量不会这样做。
- 但是我们可以把局部函数使用键值对的方式保存在_G中,这样我们就可以通过_G和名字来访问原来的局部函数了
完整热更新的方法
我们的做法是要加载新模块,然后再把新模块里的东西塞给老模块,这样其他引用的地方就能引用到新的模块了
function reload_module(module_name)
local old_module = _G[module_name] --获取老模块
package.loaded[module_name] = nil --赋值为空
require (module_name) --加载新模块
local new_module = _G[module_name] --获取新模块
for k, v in pairs(new_module) do --循环新模块中的所有属性方法等内容
old_module[k] = v --把心模块的改变都塞给老模块
end
package.loaded[module_name] = old_module --最后我们再把老模块塞回来。那么引用老模块的其他模块引用的东西也就改变了
end
Xlua热更新方案
按照上面的步骤其实任然无法做到Xlua运行时热更新。可能是跟XLua自身的机制有关,我们是无法直接替换方法,必须先将方法设置为空,然后再进行替换才可(此过程曲折波多,就不解释了)。
我们的__G也可以直接使用require获取到模块
function reload_module(module_name)
local old_module = require (module_name) --获取老模块
package.loaded[module_name] = nil --赋值为空
require (module_name) --加载新模块
local new_module = require (module_name) --获取新模块
for k, v in pairs(new_module) do --循环新模块中的所有属性方法等内容
old_module[k] = nil --把心模块的改变都塞给老模块
old_module[k] = v --把心模块的改变都塞给老模块
end
package.loaded[module_name] = old_module --最后我们再把老模块塞回来。那么引用老模块的其他模块引用的东西也就改变了
end