Hotfix_Tips.lua 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368
  1. --https://zhuanlan.zhihu.com/p/139549412 @参考地址
  2. ---不包括UIPanel和非UITemplate。(他们只能重载打开界面或者重新load template的时候生效。是直接用新mod覆盖老的,和hotfix_normal机制不一样。)
  3. ---Lua中可以新加变量
  4. ---有以下几种情况不能热重载
  5. ---(1)修改了原来table中已经有数据的变量(如果变量是nil,那不影响)
  6. ---(2)外部有对其函数有引用。(上面链接中有处理方式,但是不太合适。)
  7. ---(3) 修改的Lua文件的文件名如果是在_G里面没有找到则无法更新里面的函数。例如Defines里面定义的全局table
  8. local specialTo = {
  9. Scene_EventContainer = 'GamePlay/Scene/Scene',
  10. Scene_MessageContainer = 'GamePlay/Scene/Scene',
  11. GUI_Attr = 'devCore/GUI/GUI',
  12. GUI_CreateUI = 'devCore/GUI/GUI',
  13. SL_Meta = 'devCore/SL',
  14. SL_MetaEnum = 'devCore/SL',
  15. SL_Sys = 'devCore/SL',
  16. }
  17. local specialModule = {
  18. ["Module/SlotManager"] = true,
  19. ["Module/ModuleMainBase"] = true,
  20. ["Module/ModuleConfig"] = true,
  21. ["Module/ModuleManager"] = true,
  22. }
  23. function hotfix(filename)
  24. local isModule = string.startsWith(filename, "Module/") and not specialModule[filename]
  25. local moduleName
  26. if isModule then
  27. moduleName = string.match(filename, "Module/[%w]+/([%w]+)")
  28. end
  29. if string.contains(filename, 'UI/Panel/') then
  30. hotfix_uipanel(filename, isModule, moduleName)
  31. elseif string.contains(filename, 'UI/Template/') then
  32. hotfix_uitemplate(filename, isModule, moduleName)
  33. else
  34. hotfix_normal(filename, isModule, moduleName)
  35. end
  36. end
  37. --界面关闭的情况
  38. function hotfix_uipanel(filename, moduleName)
  39. --界面关闭之后,删除了界面。重写打开后。rtengineRequire 重写加载了Lua界面逻辑
  40. end
  41. function hotfix_uitemplate(filename, isModule, moduleName)
  42. local oldModule
  43. local newModule
  44. if package.loaded[filename] then
  45. oldModule = package.loaded[filename]
  46. package.loaded[filename] = nil
  47. else
  48. if DebugFlag.LogEnable then
  49. log('不需要替换 = ' .. filename)
  50. end
  51. return
  52. end
  53. local evnRequire = isModule and requireM or require
  54. local ok, err = pcall(evnRequire, filename)
  55. --if(package.loaded[filename] )then
  56. -- --log("new package.loaded not null")
  57. --end
  58. if not ok then
  59. package.loaded[filename] = oldModule
  60. logError('reload lua file failed.' .. err)
  61. return
  62. end
  63. newModule = package.loaded[filename]
  64. local Env = isModule and GetModuleENV(moduleName)[moduleName .. "Main"] or _G
  65. local envTemplates = Env['luaComponentTemplates']
  66. for k, v in pairs(envTemplates) do
  67. if v == oldModule then
  68. table.copyFrom(envTemplates[k],newModule)
  69. package.loaded[filename] = envTemplates[k]
  70. break
  71. end
  72. end
  73. if DebugFlag.LogEnable then
  74. log('替换 完成 = ' .. filename)
  75. end
  76. end
  77. function hotfix_normal(filename, isModule, moduleName)
  78. if DebugFlag.LogEnable then
  79. log("start hotfix:" .. filename)
  80. end
  81. --若此lua文件无返回值 则需要从_G中取数据
  82. --bGTable为true表示需要从_G中取数据 false表示从package.loaded中取数据
  83. local bGTable = false
  84. local namearr = string.split(filename, "/")
  85. local namestr = namearr[#namearr]
  86. local orginFilename = nil
  87. local orginModName = nil
  88. if specialTo[namestr] then
  89. orginFilename = specialTo[namestr]
  90. local strs = string.split(orginFilename, "/")
  91. orginModName = strs[#strs]
  92. end
  93. local modGName = orginModName or namestr
  94. local envTbl = isModule and GetModuleENV(moduleName) or _G
  95. local oldModuleG
  96. local oldModule
  97. local oldOrginModule
  98. if package.loaded[filename] then
  99. oldModule = package.loaded[filename]
  100. package.loaded[filename] = nil
  101. if orginFilename then
  102. oldOrginModule = package.loaded[orginFilename]
  103. package.loaded[orginFilename] = nil
  104. end
  105. if (type(oldModule) == "boolean") then
  106. oldModuleG = envTbl[modGName]
  107. if not oldModuleG then
  108. package.loaded[filename] = oldModule
  109. if orginFilename then
  110. package.loaded[filename] = oldOrginModule
  111. end
  112. logError('reload lua file failed.' .. filename .. ' lua名字和_G中变量名不对应')
  113. return
  114. end
  115. envTbl[modGName] = nil
  116. bGTable = true
  117. --log("oldModule type is "..type(oldModule))
  118. end
  119. --log('this file exist in package.loaded')
  120. else
  121. if DebugFlag.LogEnable then
  122. log('不需要替换 = ' .. filename)
  123. end
  124. --logError('this file is not loaded: '..filename)
  125. return
  126. end
  127. local evnRequire = isModule and requireM or require
  128. if orginFilename then
  129. local oOk, oErr = pcall(evnRequire, orginFilename)
  130. if not oOk then
  131. logError('reload lua file failed.' .. oErr)
  132. return
  133. end
  134. end
  135. local ok, err = pcall(evnRequire, filename)
  136. --if(package.loaded[filename] )then
  137. -- --log("new package.loaded not null")
  138. --end
  139. if not ok then
  140. package.loaded[filename] = oldModule
  141. if orginFilename then
  142. package.loaded[filename] = oldOrginModule
  143. end
  144. envTbl[modGName] = oldModuleG
  145. logError('reload lua file failed.' .. err)
  146. return
  147. end
  148. --如果想要保留旧文件的数据 需要启用下面的逻辑
  149. local newModule = bGTable and envTbl[modGName] or package.loaded[filename]
  150. local updated_tables = {}
  151. if (type(newModule) == "table") then
  152. --print("newModule is table")
  153. if (bGTable == true) then
  154. update_table(newModule, oldModuleG, updated_tables)
  155. else
  156. update_table(newModule, oldModule, updated_tables)
  157. end
  158. else
  159. --print("newModule not table。 type is "..type(newModule))
  160. --print("oldModule type is "..type(oldModule))
  161. end
  162. if (bGTable == true) then
  163. if oldModuleG.OnReload ~= nil then
  164. oldModuleG:OnReload()
  165. end
  166. else
  167. if oldModule.OnReload ~= nil then
  168. oldModule:OnReload()
  169. end
  170. end
  171. package.loaded[filename] = oldModule
  172. if orginFilename then
  173. package.loaded[orginFilename] = oldOrginModule
  174. end
  175. envTbl[modGName] = oldModuleG
  176. if DebugFlag.LogEnable then
  177. log('替换 完成 = ' .. filename)
  178. end
  179. end
  180. function update_func(new_func, old_func)
  181. assert("function" == type(new_func))
  182. assert("function" == type(old_func))
  183. -- Get upvalues of old function.
  184. local old_upvalue_map = {}
  185. if ("function" == type(old_func)) then
  186. for i = 1, math.huge do
  187. local name, value = debug.getupvalue(old_func, i)
  188. if not name then break end
  189. old_upvalue_map[name] = value
  190. end
  191. end
  192. -- Update new upvalues with old.
  193. for i = 1, math.huge do
  194. local name, value = debug.getupvalue(new_func, i)
  195. if not name then break end
  196. --print('set up value: name:',name)
  197. local old_value = old_upvalue_map[name]
  198. if old_value then
  199. debug.setupvalue(new_func, i, old_value)
  200. end
  201. end
  202. end
  203. function update_table(new_table, old_table, updated_tables)
  204. assert("table" == type(new_table))
  205. assert("table" == type(old_table))
  206. -- Compare 2 tables, and update old table.
  207. for key, value in pairs(new_table) do
  208. local old_value = old_table[key]
  209. local type_value = type(value)
  210. if type_value == "function" then
  211. update_func(value, old_value)
  212. update_func_eventOrMessageManager(value, old_value)
  213. old_table[key] = value
  214. elseif type_value == "table" then
  215. if (updated_tables[value] == nil) then
  216. updated_tables[value] = true
  217. update_table(value, old_value, updated_tables)
  218. end
  219. elseif old_value == nil then
  220. --如果旧表里面没有这个字段,就新加进去
  221. old_table[key] = value
  222. else
  223. if type(old_value) ~= type_value then
  224. if DebugFlag.LogEnable then
  225. log('字段修改无法热重载,自行确定是否有问题 = ' .. key .. ' oldType = ' .. hotfixGetPrintString(type(old_value)) .. ' newType = ' .. hotfixGetPrintString(type_value))
  226. end
  227. else
  228. if old_value ~= value then
  229. if DebugFlag.LogEnable then
  230. log('字段修改无法热重载,自行确定是否有问题 = ' .. key .. ' oldValue = ' .. hotfixGetPrintString(old_value) .. ' newValue = ' .. hotfixGetPrintString(value))
  231. end
  232. end
  233. end
  234. end
  235. end
  236. -- Update metatable.
  237. local old_meta = debug.getmetatable(old_table)
  238. local new_meta = debug.getmetatable(new_table)
  239. ---有些元表设置了自己,会导致死循环(类似Vector4),Vector4的判等被重载了
  240. if type(old_meta) == "table" and type(new_meta) == "table" then
  241. if old_meta.IsSelfMetatable and old_meta:IsSelfMetatable() then
  242. return
  243. end
  244. if old_meta ~= old_table and new_meta ~= new_table then
  245. update_table(new_meta, old_meta, updated_tables)
  246. end
  247. end
  248. end
  249. ---@param eventManagerBase EventManagerBase
  250. function update_func_eventOrMessageManager(newFunc, oldFunc)
  251. EventManager.HotFix_UpdateFunc(newFunc, oldFunc)
  252. NetManager.HotFix_UpdateFunc(newFunc, oldFunc)
  253. end
  254. function hotfixGetPrintString(v)
  255. if v == nil then
  256. return 'nil'
  257. end
  258. return tostring(v)
  259. end
  260. function hotfixKmlLua(keyPath)
  261. if DebugFlag.LogEnable then
  262. log('hotfixKmlLua='..keyPath)
  263. end
  264. local viewPath = keyPath.."View"
  265. local panelPath = string.empty
  266. if string.contains(keyPath, "Assets/Lua/") then
  267. panelPath = string.replace(keyPath, "Assets/Lua/", "")
  268. viewPath = string.replace(viewPath, "Assets/Lua/", "")
  269. else
  270. if DebugFlag.LogEnable then
  271. log('CS.TCFramework.StreamingAssetsFile712.XTRADevResBundlesDir='..CS.TCFramework.StreamingAssetsFile712.XTRADevResBundlesDir)
  272. end
  273. panelPath = string.replace(keyPath, CS.TCFramework.StreamingAssetsFile712.XTRADevResBundlesDir..'Lua/', "")
  274. viewPath = string.replace(viewPath, CS.TCFramework.StreamingAssetsFile712.XTRADevResBundlesDir..'Lua/', "")
  275. end
  276. if DebugFlag.LogEnable then
  277. log(viewPath)
  278. end
  279. if DebugFlag.LogEnable then
  280. log(panelPath)
  281. end
  282. local lastIndex = string.lastIndexOf(panelPath,'/')
  283. local className = string.sub(panelPath,lastIndex+1)
  284. if DebugFlag.LogEnable then
  285. log(className)
  286. end
  287. local panel = GUI:GetUI(panelPath,true)
  288. local args = nil
  289. local realPanelPath = ""
  290. if panel then
  291. if panel.baseUI then
  292. args = panel.baseUI.args
  293. realPanelPath = panel.baseUI.filePath
  294. else
  295. args = panel.args
  296. realPanelPath = panel.filePath
  297. end
  298. end
  299. if not string.isNullOrEmpty(realPanelPath) then
  300. if DebugFlag.LogEnable then
  301. log('realPanelPath='..realPanelPath)
  302. end
  303. GUI:UnPreLoadUI(realPanelPath)
  304. _G[className] = nil
  305. _G[className.."View"] = nil
  306. local outLuaEnv = GetOutLuaEnv()
  307. if outLuaEnv then
  308. outLuaEnv[realPanelPath] = nil
  309. outLuaEnv[realPanelPath.."View"] = nil
  310. end
  311. package.loaded[viewPath] = nil
  312. package.loaded[panelPath] = nil
  313. GUI:UIPanel_Open(realPanelPath,nil,nil,args)
  314. if DebugFlag.LogEnable then
  315. log('热重载完成='..realPanelPath)
  316. end
  317. return true
  318. else
  319. if GUI:InPreLoadUI(panelPath) then
  320. GUI:UnPreLoadUI(panelPath)
  321. end
  322. _G[className] = nil
  323. _G[className.."View"] = nil
  324. local outLuaEnv = GetOutLuaEnv()
  325. if outLuaEnv then
  326. outLuaEnv[keyPath] = nil
  327. outLuaEnv[keyPath.."View"] = nil
  328. end
  329. package.loaded[viewPath] = nil
  330. package.loaded[panelPath] = nil
  331. if DebugFlag.LogEnable then
  332. log('热重载完成='..realPanelPath)
  333. end
  334. return true
  335. end
  336. return false
  337. end
  338. function reopenKmlLua()
  339. end