|
|
- local HU = {}
-
- function HU.FailNotify(...)
- if HU.NotifyFunc then HU.NotifyFunc(...) end
- end
- function HU.DebugNofity(...)
- if HU.DebugNofityFunc then HU.DebugNofityFunc(...) end
- end
-
- local function GetWorkingDir()
- if HU.WorkingDir == nil then
- local p = io.popen("echo %cd%")
- if p then
- HU.WorkingDir = p:read("*l").."\\"
- p:close()
- end
- end
- return HU.WorkingDir
- end
-
- local function Normalize(path)
- path = path:gsub("/","\\")
- if path:find(":") == nil then
- path = GetWorkingDir()..path
- end
- local pathLen = #path
- if path:sub(pathLen, pathLen) == "\\" then
- path = path:sub(1, pathLen - 1)
- end
-
- local parts = { }
- for w in path:gmatch("[^\\]+") do
- if w == ".." and #parts ~=0 then table.remove(parts)
- elseif w ~= "." then table.insert(parts, w)
- end
- end
- return table.concat(parts, "\\")
- end
-
- function HU.InitFileMap(RootPath)
- for _, rootpath in pairs(RootPath) do
- rootpath = Normalize(rootpath)
- local file = io.popen("dir /S/B /A:A "..rootpath)
- io.input(file)
- for line in io.lines() do
- local FileName = string.match(line,".*\\(.*)%.lua")
- if FileName ~= nil then
- if HU.FileMap[FileName] == nil then
- HU.FileMap[FileName] = {}
- end
- local luapath = string.sub(line, #rootpath+2, #line-4)
- luapath = string.gsub(luapath, "\\", ".")
- HU.LuaPathToSysPath[luapath] = SysPath
- table.insert(HU.FileMap[FileName], {SysPath = line, LuaPath = luapath})
- end
- end
- file:close()
- end
- end
-
- function HU.InitFakeTable()
- local meta = {}
- HU.Meta = meta
- local function FakeT() return setmetatable({}, meta) end
- local function EmptyFunc() end
- local function pairs() return EmptyFunc end
- local function setmetatable(t, metaT)
- HU.MetaMap[t] = metaT
- return t
- end
- local function getmetatable(t, metaT)
- return setmetatable({}, t)
- end
- local function require(LuaPath)
- if not HU.RequireMap[LuaPath] then
- local FakeTable = FakeT()
- HU.RequireMap[LuaPath] = FakeTable
- end
- return HU.RequireMap[LuaPath]
- end
- function meta.__index(t, k)
- if k == "setmetatable" then
- return setmetatable
- elseif k == "pairs" or k == "ipairs" then
- return pairs
- elseif k == "next" then
- return EmptyFunc
- elseif k == "require" then
- return require
- else
- local FakeTable = FakeT()
- rawset(t, k, FakeTable)
- return FakeTable
- end
- end
- function meta.__newindex(t, k, v) rawset(t, k, v) end
- function meta.__call() return FakeT(), FakeT(), FakeT() end
- function meta.__add() return meta.__call() end
- function meta.__sub() return meta.__call() end
- function meta.__mul() return meta.__call() end
- function meta.__div() return meta.__call() end
- function meta.__mod() return meta.__call() end
- function meta.__pow() return meta.__call() end
- function meta.__unm() return meta.__call() end
- function meta.__concat() return meta.__call() end
- function meta.__eq() return meta.__call() end
- function meta.__lt() return meta.__call() end
- function meta.__le() return meta.__call() end
- function meta.__len() return meta.__call() end
- return FakeT
- end
-
- function HU.InitProtection()
- HU.Protection = {}
- HU.Protection[setmetatable] = true
- HU.Protection[pairs] = true
- HU.Protection[ipairs] = true
- HU.Protection[next] = true
- HU.Protection[require] = true
- HU.Protection[HU] = true
- HU.Protection[HU.Meta] = true
- HU.Protection[math] = true
- HU.Protection[string] = true
- HU.Protection[table] = true
- end
-
- function HU.AddFileFromHUList()
- package.loaded[HU.UpdateListFile] = nil
- local FileList = _G.originalRequire (HU.UpdateListFile)
- HU.ALL = false
- HU.HUMap = {}
- for _, file in pairs(FileList) do
- if file == "_ALL_" then
- HU.ALL = true
- for k, v in pairs(HU.FileMap) do
- for _, path in pairs(v) do
- HU.HUMap[path.LuaPath] = path.SysPath
- end
- end
- return
- end
- if HU.FileMap[file] then
- for _, path in pairs(HU.FileMap[file]) do
- HU.HUMap[path.LuaPath] = path.SysPath
- end
- elseif string.find(file, "$") then
- local search_str = string.sub(file, 2)
- for k, v in pairs(HU.FileMap) do
- for _, path in pairs(v) do
- local file_parts = Split(path.SysPath, "\\")
- if file_parts and #file_parts > 0 then
- local file_name = file_parts[#file_parts]
- local isFind = string.find(file_name, search_str)
- if isFind then
- HU.HUMap[path.LuaPath] = path.SysPath
- end
- end
- end
- end
- else
- HU.FailNotify("HotUpdate can't not find "..file)
- end
- end
- end
-
- function HU.ErrorHandle(e)
- HU.FailNotify("HotUpdate Error\n"..tostring(e))
- HU.ErrorHappen = true
- end
-
- function HU.BuildNewCode(SysPath, LuaPath)
- io.input(SysPath)
- local NewCode = io.read("*all")
- if HU.ALL and HU.OldCode[SysPath] == nil then
- HU.OldCode[SysPath] = NewCode
- return
- end
- if HU.OldCode[SysPath] == NewCode then
- io.input():close()
- return false
- end
- HU.DebugNofity(SysPath)
- io.input(SysPath)
- local chunk = "--[["..LuaPath.."]] "
- chunk = chunk..NewCode
- io.input():close()
- local NewFunction = loadstring(chunk)
- if not NewFunction then
- HU.FailNotify(SysPath.." has syntax error.")
- collectgarbage("collect")
- return false
- else
- HU.FakeENV = HU.FakeT()
- HU.MetaMap = {}
- HU.RequireMap = {}
- setfenv(NewFunction, HU.FakeENV)
- local NewObject
- HU.ErrorHappen = false
- xpcall(function () NewObject = NewFunction() end, HU.ErrorHandle)
- if not HU.ErrorHappen then
- HU.OldCode[SysPath] = NewCode
- return true, NewObject
- else
- collectgarbage("collect")
- return false
- end
- end
- end
-
- function HU.Travel_G()
- local visited = {}
- visited[HU] = true
- local function f(t)
- if (type(t) ~= "function" and type(t) ~= "table") or visited[t] or HU.Protection[t] then return end
- visited[t] = true
- if type(t) == "function" then
- for i = 1, math.huge do
- local name, value = debug.getupvalue(t, i)
- if not name then break end
- if type(value) == "function" then
- for _, funcs in ipairs(HU.ChangedFuncList) do
- if value == funcs[1] then
- debug.setupvalue(t, i, funcs[2])
- end
- end
- end
- f(value)
- end
- elseif type(t) == "table" then
- f(debug.getmetatable(t))
- local changeIndexs = {}
- for k,v in pairs(t) do
- f(k); f(v);
- if type(v) == "function" then
- for _, funcs in ipairs(HU.ChangedFuncList) do
- if v == funcs[1] then t[k] = funcs[2] end
- end
- end
- if type(k) == "function" then
- for index, funcs in ipairs(HU.ChangedFuncList) do
- if k == funcs[1] then changeIndexs[#changeIndexs+1] = index end
- end
- end
- end
- for _, index in ipairs(changeIndexs) do
- local funcs = HU.ChangedFuncList[index]
- t[funcs[2]] = t[funcs[1]]
- t[funcs[1]] = nil
- end
- end
- end
-
- f(_G)
- local registryTable = debug.getregistry()
- f(registryTable)
-
- for _, funcs in ipairs(HU.ChangedFuncList) do
- if funcs[3] == "HUDebug" then funcs[4]:HUDebug() end
- end
- end
-
- function HU.ReplaceOld(OldObject, NewObject, LuaPath, From, Deepth)
- if type(OldObject) == type(NewObject) then
- if type(NewObject) == "table" then
- HU.UpdateAllFunction(OldObject, NewObject, LuaPath, From, "")
- elseif type(NewObject) == "function" then
- HU.UpdateOneFunction(OldObject, NewObject, LuaPath, nil, From, "")
- end
- end
- end
-
- function HU.HotUpdateCode(LuaPath, SysPath)
- local OldObject = package.loaded[LuaPath]
- print("Cat:luahotupdate [264] OldObject: ",OldObject,LuaPath)
- if OldObject ~= nil then
- HU.VisitedSig = {}
- HU.ChangedFuncList = {}
- local Success, NewObject = HU.BuildNewCode(SysPath, LuaPath)
- if Success then
- HU.ReplaceOld(OldObject, NewObject, LuaPath, "Main", "")
- for LuaPath, NewObject in pairs(HU.RequireMap) do
- local OldObject = package.loaded[LuaPath]
- HU.ReplaceOld(OldObject, NewObject, LuaPath, "Main_require", "")
- end
- setmetatable(HU.FakeENV, nil)
- HU.UpdateAllFunction(HU.ENV, HU.FakeENV, " ENV ", "Main", "")
- if #HU.ChangedFuncList > 0 then
- HU.Travel_G()
- end
- collectgarbage("collect")
- end
- elseif HU.OldCode[SysPath] == nil then
- io.input(SysPath)
- HU.OldCode[SysPath] = io.read("*all")
- io.input():close()
- end
- end
-
- function HU.ResetENV(object, name, From, Deepth)
- local visited = {}
- local function f(object, name)
- if not object or visited[object] then return end
- visited[object] = true
- if type(object) == "function" then
- HU.DebugNofity(Deepth.."HU.ResetENV", name, " from:"..From)
- xpcall(function () setfenv(object, HU.ENV) end, HU.FailNotify)
- elseif type(object) == "table" then
- HU.DebugNofity(Deepth.."HU.ResetENV", name, " from:"..From)
- for k, v in pairs(object) do
- f(k, tostring(k).."__key", " HU.ResetENV ", Deepth.." " )
- f(v, tostring(k), " HU.ResetENV ", Deepth.." ")
- end
- end
- end
- f(object, name)
- end
-
- function HU.UpdateUpvalue(OldFunction, NewFunction, Name, From, Deepth)
- HU.DebugNofity(Deepth.."HU.UpdateUpvalue", Name, " from:"..From)
- local OldUpvalueMap = {}
- local OldExistName = {}
- for i = 1, math.huge do
- local name, value = debug.getupvalue(OldFunction, i)
- if not name then break end
- OldUpvalueMap[name] = value
- OldExistName[name] = true
- end
- for i = 1, math.huge do
- local name, value = debug.getupvalue(NewFunction, i)
- if not name then break end
- if OldExistName[name] then
- local OldValue = OldUpvalueMap[name]
- if type(OldValue) ~= type(value) then
- debug.setupvalue(NewFunction, i, OldValue)
- elseif type(OldValue) == "function" then
- HU.UpdateOneFunction(OldValue, value, name, nil, "HU.UpdateUpvalue", Deepth.." ")
- elseif type(OldValue) == "table" then
- HU.UpdateAllFunction(OldValue, value, name, "HU.UpdateUpvalue", Deepth.." ")
- debug.setupvalue(NewFunction, i, OldValue)
- else
- debug.setupvalue(NewFunction, i, OldValue)
- end
- else
- HU.ResetENV(value, name, "HU.UpdateUpvalue", Deepth.." ")
- end
- end
- end
-
- function HU.UpdateOneFunction(OldObject, NewObject, FuncName, OldTable, From, Deepth)
- if HU.Protection[OldObject] or HU.Protection[NewObject] then return end
- if OldObject == NewObject then return end
- local signature = tostring(OldObject)..tostring(NewObject)
- if HU.VisitedSig[signature] then return end
- HU.VisitedSig[signature] = true
- HU.DebugNofity(Deepth.."HU.UpdateOneFunction "..FuncName.." from:"..From)
- if pcall(debug.setfenv, NewObject, getfenv(OldObject)) then
- HU.UpdateUpvalue(OldObject, NewObject, FuncName, "HU.UpdateOneFunction", Deepth.." ")
- HU.ChangedFuncList[#HU.ChangedFuncList + 1] = {OldObject, NewObject, FuncName, OldTable}
- end
- end
-
- function HU.UpdateAllFunction(OldTable, NewTable, Name, From, Deepth)
- if HU.Protection[OldTable] or HU.Protection[NewTable] then return end
- if OldTable == NewTable then return end
-
- local function call_back( OldTable,NewTable )
- return tostring(OldTable or "")..tostring(NewTable or "")
- end
- local have_error,signature = pcall(call_back,OldTable,NewTable)
- if not have_error then
- --直接跳出
- print('----LZR luahotupdate.lua 你要重新点下热更')
- return
- end
-
- if HU.VisitedSig[signature] then return end
- HU.VisitedSig[signature] = true
- HU.DebugNofity(Deepth.."HU.UpdateAllFunction "..Name.." from:"..From)
- for ElementName, Element in pairs(NewTable) do
- local OldElement = OldTable[ElementName]
- if type(Element) == type(OldElement) then
- if type(Element) == "function" then
- HU.UpdateOneFunction(OldElement, Element, ElementName, OldTable, "HU.UpdateAllFunction", Deepth.." ")
- elseif type(Element) == "table" then
- HU.UpdateAllFunction(OldElement, Element, ElementName, "HU.UpdateAllFunction", Deepth.." ")
- end
- elseif OldElement == nil and type(Element) == "function" then
- if pcall(setfenv, Element, HU.ENV) then
- OldTable[ElementName] = Element
- end
- end
- end
- local OldMeta = debug.getmetatable(OldTable)
- local NewMeta = HU.MetaMap[NewTable]
- if type(OldMeta) == "table" and type(NewMeta) == "table" then
- HU.UpdateAllFunction(OldMeta, NewMeta, Name.."'s Meta", "HU.UpdateAllFunction", Deepth.." ")
- end
- end
-
- function HU.Init(UpdateListFile, RootPath, FailNotify, ENV)
- HU.UpdateListFile = UpdateListFile
- HU.HUMap = {}
- HU.FileMap = {}
- HU.NotifyFunc = FailNotify
- HU.OldCode = {}
- HU.ChangedFuncList = {}
- HU.VisitedSig = {}
- HU.FakeENV = nil
- HU.ENV = ENV or _G
- HU.LuaPathToSysPath = {}
- HU.InitFileMap(RootPath)
- HU.FakeT = HU.InitFakeTable()
- HU.InitProtection()
- HU.ALL = false
- end
-
- function HU.Update()
- HU.AddFileFromHUList()
- for LuaPath, SysPath in pairs(HU.HUMap) do
- HU.HotUpdateCode(LuaPath, SysPath)
- end
- end
-
- return HU
|