源战役客户端
您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

424 行
12 KiB

  1. local HU = {}
  2. function HU.FailNotify(...)
  3. if HU.NotifyFunc then HU.NotifyFunc(...) end
  4. end
  5. function HU.DebugNofity(...)
  6. if HU.DebugNofityFunc then HU.DebugNofityFunc(...) end
  7. end
  8. local function GetWorkingDir()
  9. if HU.WorkingDir == nil then
  10. local p = io.popen("echo %cd%")
  11. if p then
  12. HU.WorkingDir = p:read("*l").."\\"
  13. p:close()
  14. end
  15. end
  16. return HU.WorkingDir
  17. end
  18. local function Normalize(path)
  19. path = path:gsub("/","\\")
  20. if path:find(":") == nil then
  21. path = GetWorkingDir()..path
  22. end
  23. local pathLen = #path
  24. if path:sub(pathLen, pathLen) == "\\" then
  25. path = path:sub(1, pathLen - 1)
  26. end
  27. local parts = { }
  28. for w in path:gmatch("[^\\]+") do
  29. if w == ".." and #parts ~=0 then table.remove(parts)
  30. elseif w ~= "." then table.insert(parts, w)
  31. end
  32. end
  33. return table.concat(parts, "\\")
  34. end
  35. function HU.InitFileMap(RootPath)
  36. for _, rootpath in pairs(RootPath) do
  37. rootpath = Normalize(rootpath)
  38. local file = io.popen("dir /S/B /A:A "..rootpath)
  39. io.input(file)
  40. for line in io.lines() do
  41. local FileName = string.match(line,".*\\(.*)%.lua")
  42. if FileName ~= nil then
  43. if HU.FileMap[FileName] == nil then
  44. HU.FileMap[FileName] = {}
  45. end
  46. local luapath = string.sub(line, #rootpath+2, #line-4)
  47. luapath = string.gsub(luapath, "\\", ".")
  48. HU.LuaPathToSysPath[luapath] = SysPath
  49. table.insert(HU.FileMap[FileName], {SysPath = line, LuaPath = luapath})
  50. end
  51. end
  52. file:close()
  53. end
  54. end
  55. function HU.InitFakeTable()
  56. local meta = {}
  57. HU.Meta = meta
  58. local function FakeT() return setmetatable({}, meta) end
  59. local function EmptyFunc() end
  60. local function pairs() return EmptyFunc end
  61. local function setmetatable(t, metaT)
  62. HU.MetaMap[t] = metaT
  63. return t
  64. end
  65. local function getmetatable(t, metaT)
  66. return setmetatable({}, t)
  67. end
  68. local function require(LuaPath)
  69. if not HU.RequireMap[LuaPath] then
  70. local FakeTable = FakeT()
  71. HU.RequireMap[LuaPath] = FakeTable
  72. end
  73. return HU.RequireMap[LuaPath]
  74. end
  75. function meta.__index(t, k)
  76. if k == "setmetatable" then
  77. return setmetatable
  78. elseif k == "pairs" or k == "ipairs" then
  79. return pairs
  80. elseif k == "next" then
  81. return EmptyFunc
  82. elseif k == "require" then
  83. return require
  84. else
  85. local FakeTable = FakeT()
  86. rawset(t, k, FakeTable)
  87. return FakeTable
  88. end
  89. end
  90. function meta.__newindex(t, k, v) rawset(t, k, v) end
  91. function meta.__call() return FakeT(), FakeT(), FakeT() end
  92. function meta.__add() return meta.__call() end
  93. function meta.__sub() return meta.__call() end
  94. function meta.__mul() return meta.__call() end
  95. function meta.__div() return meta.__call() end
  96. function meta.__mod() return meta.__call() end
  97. function meta.__pow() return meta.__call() end
  98. function meta.__unm() return meta.__call() end
  99. function meta.__concat() return meta.__call() end
  100. function meta.__eq() return meta.__call() end
  101. function meta.__lt() return meta.__call() end
  102. function meta.__le() return meta.__call() end
  103. function meta.__len() return meta.__call() end
  104. return FakeT
  105. end
  106. function HU.InitProtection()
  107. HU.Protection = {}
  108. HU.Protection[setmetatable] = true
  109. HU.Protection[pairs] = true
  110. HU.Protection[ipairs] = true
  111. HU.Protection[next] = true
  112. HU.Protection[require] = true
  113. HU.Protection[HU] = true
  114. HU.Protection[HU.Meta] = true
  115. HU.Protection[math] = true
  116. HU.Protection[string] = true
  117. HU.Protection[table] = true
  118. end
  119. function HU.AddFileFromHUList()
  120. package.loaded[HU.UpdateListFile] = nil
  121. local FileList = _G.originalRequire (HU.UpdateListFile)
  122. HU.ALL = false
  123. HU.HUMap = {}
  124. for _, file in pairs(FileList) do
  125. if file == "_ALL_" then
  126. HU.ALL = true
  127. for k, v in pairs(HU.FileMap) do
  128. for _, path in pairs(v) do
  129. HU.HUMap[path.LuaPath] = path.SysPath
  130. end
  131. end
  132. return
  133. end
  134. if HU.FileMap[file] then
  135. for _, path in pairs(HU.FileMap[file]) do
  136. HU.HUMap[path.LuaPath] = path.SysPath
  137. end
  138. elseif string.find(file, "$") then
  139. local search_str = string.sub(file, 2)
  140. for k, v in pairs(HU.FileMap) do
  141. for _, path in pairs(v) do
  142. local file_parts = Split(path.SysPath, "\\")
  143. if file_parts and #file_parts > 0 then
  144. local file_name = file_parts[#file_parts]
  145. local isFind = string.find(file_name, search_str)
  146. if isFind then
  147. HU.HUMap[path.LuaPath] = path.SysPath
  148. end
  149. end
  150. end
  151. end
  152. else
  153. HU.FailNotify("HotUpdate can't not find "..file)
  154. end
  155. end
  156. end
  157. function HU.ErrorHandle(e)
  158. HU.FailNotify("HotUpdate Error\n"..tostring(e))
  159. HU.ErrorHappen = true
  160. end
  161. function HU.BuildNewCode(SysPath, LuaPath)
  162. io.input(SysPath)
  163. local NewCode = io.read("*all")
  164. if HU.ALL and HU.OldCode[SysPath] == nil then
  165. HU.OldCode[SysPath] = NewCode
  166. return
  167. end
  168. if HU.OldCode[SysPath] == NewCode then
  169. io.input():close()
  170. return false
  171. end
  172. HU.DebugNofity(SysPath)
  173. io.input(SysPath)
  174. local chunk = "--[["..LuaPath.."]] "
  175. chunk = chunk..NewCode
  176. io.input():close()
  177. local NewFunction = loadstring(chunk)
  178. if not NewFunction then
  179. HU.FailNotify(SysPath.." has syntax error.")
  180. collectgarbage("collect")
  181. return false
  182. else
  183. HU.FakeENV = HU.FakeT()
  184. HU.MetaMap = {}
  185. HU.RequireMap = {}
  186. setfenv(NewFunction, HU.FakeENV)
  187. local NewObject
  188. HU.ErrorHappen = false
  189. xpcall(function () NewObject = NewFunction() end, HU.ErrorHandle)
  190. if not HU.ErrorHappen then
  191. HU.OldCode[SysPath] = NewCode
  192. return true, NewObject
  193. else
  194. collectgarbage("collect")
  195. return false
  196. end
  197. end
  198. end
  199. function HU.Travel_G()
  200. local visited = {}
  201. visited[HU] = true
  202. local function f(t)
  203. if (type(t) ~= "function" and type(t) ~= "table") or visited[t] or HU.Protection[t] then return end
  204. visited[t] = true
  205. if type(t) == "function" then
  206. for i = 1, math.huge do
  207. local name, value = debug.getupvalue(t, i)
  208. if not name then break end
  209. if type(value) == "function" then
  210. for _, funcs in ipairs(HU.ChangedFuncList) do
  211. if value == funcs[1] then
  212. debug.setupvalue(t, i, funcs[2])
  213. end
  214. end
  215. end
  216. f(value)
  217. end
  218. elseif type(t) == "table" then
  219. f(debug.getmetatable(t))
  220. local changeIndexs = {}
  221. for k,v in pairs(t) do
  222. f(k); f(v);
  223. if type(v) == "function" then
  224. for _, funcs in ipairs(HU.ChangedFuncList) do
  225. if v == funcs[1] then t[k] = funcs[2] end
  226. end
  227. end
  228. if type(k) == "function" then
  229. for index, funcs in ipairs(HU.ChangedFuncList) do
  230. if k == funcs[1] then changeIndexs[#changeIndexs+1] = index end
  231. end
  232. end
  233. end
  234. for _, index in ipairs(changeIndexs) do
  235. local funcs = HU.ChangedFuncList[index]
  236. t[funcs[2]] = t[funcs[1]]
  237. t[funcs[1]] = nil
  238. end
  239. end
  240. end
  241. f(_G)
  242. local registryTable = debug.getregistry()
  243. f(registryTable)
  244. for _, funcs in ipairs(HU.ChangedFuncList) do
  245. if funcs[3] == "HUDebug" then funcs[4]:HUDebug() end
  246. end
  247. end
  248. function HU.ReplaceOld(OldObject, NewObject, LuaPath, From, Deepth)
  249. if type(OldObject) == type(NewObject) then
  250. if type(NewObject) == "table" then
  251. HU.UpdateAllFunction(OldObject, NewObject, LuaPath, From, "")
  252. elseif type(NewObject) == "function" then
  253. HU.UpdateOneFunction(OldObject, NewObject, LuaPath, nil, From, "")
  254. end
  255. end
  256. end
  257. function HU.HotUpdateCode(LuaPath, SysPath)
  258. local OldObject = package.loaded[LuaPath]
  259. print("Cat:luahotupdate [264] OldObject: ",OldObject,LuaPath)
  260. if OldObject ~= nil then
  261. HU.VisitedSig = {}
  262. HU.ChangedFuncList = {}
  263. local Success, NewObject = HU.BuildNewCode(SysPath, LuaPath)
  264. if Success then
  265. HU.ReplaceOld(OldObject, NewObject, LuaPath, "Main", "")
  266. for LuaPath, NewObject in pairs(HU.RequireMap) do
  267. local OldObject = package.loaded[LuaPath]
  268. HU.ReplaceOld(OldObject, NewObject, LuaPath, "Main_require", "")
  269. end
  270. setmetatable(HU.FakeENV, nil)
  271. HU.UpdateAllFunction(HU.ENV, HU.FakeENV, " ENV ", "Main", "")
  272. if #HU.ChangedFuncList > 0 then
  273. HU.Travel_G()
  274. end
  275. collectgarbage("collect")
  276. end
  277. elseif HU.OldCode[SysPath] == nil then
  278. io.input(SysPath)
  279. HU.OldCode[SysPath] = io.read("*all")
  280. io.input():close()
  281. end
  282. end
  283. function HU.ResetENV(object, name, From, Deepth)
  284. local visited = {}
  285. local function f(object, name)
  286. if not object or visited[object] then return end
  287. visited[object] = true
  288. if type(object) == "function" then
  289. HU.DebugNofity(Deepth.."HU.ResetENV", name, " from:"..From)
  290. xpcall(function () setfenv(object, HU.ENV) end, HU.FailNotify)
  291. elseif type(object) == "table" then
  292. HU.DebugNofity(Deepth.."HU.ResetENV", name, " from:"..From)
  293. for k, v in pairs(object) do
  294. f(k, tostring(k).."__key", " HU.ResetENV ", Deepth.." " )
  295. f(v, tostring(k), " HU.ResetENV ", Deepth.." ")
  296. end
  297. end
  298. end
  299. f(object, name)
  300. end
  301. function HU.UpdateUpvalue(OldFunction, NewFunction, Name, From, Deepth)
  302. HU.DebugNofity(Deepth.."HU.UpdateUpvalue", Name, " from:"..From)
  303. local OldUpvalueMap = {}
  304. local OldExistName = {}
  305. for i = 1, math.huge do
  306. local name, value = debug.getupvalue(OldFunction, i)
  307. if not name then break end
  308. OldUpvalueMap[name] = value
  309. OldExistName[name] = true
  310. end
  311. for i = 1, math.huge do
  312. local name, value = debug.getupvalue(NewFunction, i)
  313. if not name then break end
  314. if OldExistName[name] then
  315. local OldValue = OldUpvalueMap[name]
  316. if type(OldValue) ~= type(value) then
  317. debug.setupvalue(NewFunction, i, OldValue)
  318. elseif type(OldValue) == "function" then
  319. HU.UpdateOneFunction(OldValue, value, name, nil, "HU.UpdateUpvalue", Deepth.." ")
  320. elseif type(OldValue) == "table" then
  321. HU.UpdateAllFunction(OldValue, value, name, "HU.UpdateUpvalue", Deepth.." ")
  322. debug.setupvalue(NewFunction, i, OldValue)
  323. else
  324. debug.setupvalue(NewFunction, i, OldValue)
  325. end
  326. else
  327. HU.ResetENV(value, name, "HU.UpdateUpvalue", Deepth.." ")
  328. end
  329. end
  330. end
  331. function HU.UpdateOneFunction(OldObject, NewObject, FuncName, OldTable, From, Deepth)
  332. if HU.Protection[OldObject] or HU.Protection[NewObject] then return end
  333. if OldObject == NewObject then return end
  334. local signature = tostring(OldObject)..tostring(NewObject)
  335. if HU.VisitedSig[signature] then return end
  336. HU.VisitedSig[signature] = true
  337. HU.DebugNofity(Deepth.."HU.UpdateOneFunction "..FuncName.." from:"..From)
  338. if pcall(debug.setfenv, NewObject, getfenv(OldObject)) then
  339. HU.UpdateUpvalue(OldObject, NewObject, FuncName, "HU.UpdateOneFunction", Deepth.." ")
  340. HU.ChangedFuncList[#HU.ChangedFuncList + 1] = {OldObject, NewObject, FuncName, OldTable}
  341. end
  342. end
  343. function HU.UpdateAllFunction(OldTable, NewTable, Name, From, Deepth)
  344. if HU.Protection[OldTable] or HU.Protection[NewTable] then return end
  345. if OldTable == NewTable then return end
  346. local function call_back( OldTable,NewTable )
  347. return tostring(OldTable or "")..tostring(NewTable or "")
  348. end
  349. local have_error,signature = pcall(call_back,OldTable,NewTable)
  350. if not have_error then
  351. --直接跳出
  352. print('----LZR luahotupdate.lua 你要重新点下热更')
  353. return
  354. end
  355. if HU.VisitedSig[signature] then return end
  356. HU.VisitedSig[signature] = true
  357. HU.DebugNofity(Deepth.."HU.UpdateAllFunction "..Name.." from:"..From)
  358. for ElementName, Element in pairs(NewTable) do
  359. local OldElement = OldTable[ElementName]
  360. if type(Element) == type(OldElement) then
  361. if type(Element) == "function" then
  362. HU.UpdateOneFunction(OldElement, Element, ElementName, OldTable, "HU.UpdateAllFunction", Deepth.." ")
  363. elseif type(Element) == "table" then
  364. HU.UpdateAllFunction(OldElement, Element, ElementName, "HU.UpdateAllFunction", Deepth.." ")
  365. end
  366. elseif OldElement == nil and type(Element) == "function" then
  367. if pcall(setfenv, Element, HU.ENV) then
  368. OldTable[ElementName] = Element
  369. end
  370. end
  371. end
  372. local OldMeta = debug.getmetatable(OldTable)
  373. local NewMeta = HU.MetaMap[NewTable]
  374. if type(OldMeta) == "table" and type(NewMeta) == "table" then
  375. HU.UpdateAllFunction(OldMeta, NewMeta, Name.."'s Meta", "HU.UpdateAllFunction", Deepth.." ")
  376. end
  377. end
  378. function HU.Init(UpdateListFile, RootPath, FailNotify, ENV)
  379. HU.UpdateListFile = UpdateListFile
  380. HU.HUMap = {}
  381. HU.FileMap = {}
  382. HU.NotifyFunc = FailNotify
  383. HU.OldCode = {}
  384. HU.ChangedFuncList = {}
  385. HU.VisitedSig = {}
  386. HU.FakeENV = nil
  387. HU.ENV = ENV or _G
  388. HU.LuaPathToSysPath = {}
  389. HU.InitFileMap(RootPath)
  390. HU.FakeT = HU.InitFakeTable()
  391. HU.InitProtection()
  392. HU.ALL = false
  393. end
  394. function HU.Update()
  395. HU.AddFileFromHUList()
  396. for LuaPath, SysPath in pairs(HU.HUMap) do
  397. HU.HotUpdateCode(LuaPath, SysPath)
  398. end
  399. end
  400. return HU