Lua热重载

Lua热重载开源库地址:https://github.com/asqbtcupid/lua_hotupdate

只能更新函数逻辑,包括局部函数和全局函数,不能更新数据。

原理

  1. 首先需要知道的是变动的文件,在Unity中可以通过继承 AssetPostProcessor 类,并重写 OnPostProcessAllAssets 函数来监听文件的改变,从而可以得知改变的Lua文件,并更新到 hotupdatelist.lua 中去。
  2. 然后需要得到需要更新的函数,具体来说是旧函数和对应的新函数。对于变动的文件肯定需要重新 require,但是为了防止改变已经保存在内存中的数据,库中使用了 setfenv 来导入更新的lua文件,这样执行新文件之后,所有改变的东西都不会污染现有的 _G,而是会被保存到另一个table:HU.FakeENV 。我们需要的只是其中的函数,那些函数就是接下来需要更新的。对于这些新函数,还需要知道旧函数,也就是 FakeENV 中每个新函数对应 _G中的函数(热重载只会替换更新函数逻辑,不能新增函数)。其实很简单,就是从 _GFakeENV 访问路径相同的函数就当作是对应的。这里的访问路径不仅包括直接取table的value,还包括table的key、metatable、function、function的upvalue(如果牵扯到的又是table,则还要进一步递归进去),总之包含了可以想到的所有访问途径。
  3. 更新函数,这一步比较简单粗暴,直接按照上一步所说所有的访问途径从 _G开始遍历 HU.Travel_G(),把所有与旧函数判定相等(==)的函数全都替换成新函数。除了 _G 之外,debug.getregistry 也进行了相同的遍历替换操作(这里面保存了C#用到的Lua函数,比如一个Lua函数func1,在 LuaFunction 中对应的reference为100,那么就有registry[100]=func1)。

注意

虽说热重载不会更新数据,但是其实可以把函数本身也当成一种数据。按照热重载原理,在更新之后所有引用到旧函数的地方会全部被替换为新函数,也就是各种table里的数据都会发生改变。这也就解释了为什么已经被绑定的回调函数(不论是Lua的还是C#的)也会被更新。