前言
感謝提供思路兩個博客主以下是博客地址:
https://gameinstitute.qq.com/community/detail/120538
https://www.jianshu.com/p/7b8ae23ecd81
基本知識(熱更新原理)
我們在require一個Lua文件的時候是將Lua文件加載到package.loaded[modelname]中。當我們加載模塊的時候,會先判斷是否在package.loaded中已存在,若存在則返回改模塊,不存在纔會加載(loadfile),防止重複加載。
package.loaded是一個Table,其中包含了全局表_G、默認加載的模塊(string, debug, package, io, os, table, math, coroutine)和用戶加載的模塊。
最基本的熱更新的方式(不可用)
第一種加載方式
將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