加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
克隆/下载
UpdateCheck.lua 9.56 KB
一键复制 编辑 原始数据 按行查看 历史
Tai 提交于 2021-08-10 09:24 . Initial commit
#@
-- Path of Building
--
-- Module: Update Check
-- Checks for updates
--
local proxyURL = ...
local xml = require("xml")
local sha1 = require("sha1")
local curl = require("lcurl.safe")
local lzip = require("lzip")
local globalRetryLimit = 10
local function downloadFileText(source, file)
for i = 1, 5 do
if i > 1 then
ConPrintf("Retrying... (%d of 5)", i)
end
local text = ""
local easy = curl.easy()
local escapedUrl = source..easy:escape(file)
easy:setopt_url(escapedUrl)
easy:setopt_httpheader({ "User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.9; rv:33.0) Gecko/20100101 Firefox/33.0" })
easy:setopt(curl.OPT_ACCEPT_ENCODING, "")
if proxyURL then
easy:setopt(curl.OPT_PROXY, proxyURL)
end
easy:setopt_writefunction(function(data)
text = text..data
return true
end)
local _, error = easy:perform()
easy:close()
if not error then
return text
end
ConPrintf("Download failed (%s)", error:msg())
if globalRetryLimit == 0 or i == 5 then
return nil, error:msg()
end
globalRetryLimit = globalRetryLimit - 1
end
end
local function downloadFile(source, file, outName)
local text = downloadFileText(source, file)
if text then
local outFile = io.open(outName, "wb")
outFile:write(text)
outFile:close()
else
return true
end
end
ConPrintf("Checking for update...")
local scriptPath = GetScriptPath()
local runtimePath = GetRuntimePath()
-- Load and process local manifest
local localVer
local localPlatform, localBranch
local localFiles = { }
local localManXML = xml.LoadXMLFile(scriptPath.."/manifest.xml")
local localSource
local runtimeExecutable
if localManXML and localManXML[1].elem == "PoBVersion" then
for _, node in ipairs(localManXML[1]) do
if type(node) == "table" then
if node.elem == "Version" then
localVer = node.attrib.number
localPlatform = node.attrib.platform
localBranch = node.attrib.branch
elseif node.elem == "Source" then
if node.attrib.part == "program" then
localSource = node.attrib.url
end
elseif node.elem == "File" then
local fullPath
if node.attrib.part == "runtime" then
fullPath = runtimePath .. "/" .. node.attrib.name
else
fullPath = scriptPath .. "/" .. node.attrib.name
end
localFiles[node.attrib.name] = { sha1 = node.attrib.sha1, part = node.attrib.part, platform = node.attrib.platform, fullPath = fullPath }
if node.attrib.part == "runtime" and node.attrib.name:match("Path of Building") then
runtimeExecutable = fullPath
end
end
end
end
end
if not localVer or not localSource or not localBranch or not next(localFiles) then
ConPrintf("Update check failed: invalid local manifest")
return nil, "Invalid local manifest"
end
localSource = localSource:gsub("{branch}", localBranch)
-- Download and process remote manifest
local remoteVer
local remoteFiles = { }
local remoteSources = { }
local remoteManText, errMsg = downloadFileText(localSource, "manifest.xml")
if not remoteManText then
ConPrintf("Update check failed: couldn't download version manifest")
return nil, "Couldn't download version manifest.\nReason: "..errMsg.."\nCheck your internet connectivity.\nIf you are using a proxy, specify it in Options."
end
local remoteManXML = xml.ParseXML(remoteManText)
if remoteManXML and remoteManXML[1].elem == "PoBVersion" then
for _, node in ipairs(remoteManXML[1]) do
if type(node) == "table" then
if node.elem == "Version" then
remoteVer = node.attrib.number
elseif node.elem == "Source" then
if not remoteSources[node.attrib.part] then
remoteSources[node.attrib.part] = { }
end
remoteSources[node.attrib.part][node.attrib.platform or "any"] = node.attrib.url
elseif node.elem == "File" then
if not node.attrib.platform or node.attrib.platform == localPlatform then
local fullPath
if node.attrib.part == "runtime" then
fullPath = runtimePath .. "/" .. node.attrib.name
else
fullPath = scriptPath .. "/" .. node.attrib.name
end
remoteFiles[node.attrib.name] = { sha1 = node.attrib.sha1, part = node.attrib.part, platform = node.attrib.platform, fullPath = fullPath }
end
end
end
end
end
if not remoteVer or not next(remoteSources) or not next(remoteFiles) then
ConPrintf("Update check failed: invalid remote manifest")
return nil, "Invalid remote manifest"
end
-- Build lists of files to be updated or deleted
local updateFiles = { }
for name, data in pairs(remoteFiles) do
data.name = name
if not localFiles[name] or localFiles[name].sha1 ~= data.sha1 then
table.insert(updateFiles, data)
elseif localFiles[name] then
local file = io.open(localFiles[name].fullPath, "rb")
if not file then
ConPrintf("Warning: '%s' doesn't exist, it will be re-downloaded", data.name)
table.insert(updateFiles, data)
else
local content = file:read("*a")
file:close()
if data.sha1 ~= sha1(content) and data.sha1 ~= sha1(content:gsub("\n","\r\n")) then
ConPrintf("Warning: Integrity check on '%s' failed, it will be replaced", data.name)
table.insert(updateFiles, data)
end
end
end
end
local deleteFiles = { }
for name, data in pairs(localFiles) do
data.name = name
if not remoteFiles[name] then
table.insert(deleteFiles, data)
end
end
if #updateFiles == 0 and #deleteFiles == 0 then
ConPrintf("No update available.")
return "none"
end
MakeDir("Update")
ConPrintf("Downloading update...")
-- Download changelog
downloadFile(localSource, "changelog.txt", scriptPath.."/changelog.txt")
-- Download files that need updating
local failedFile = false
local failedFileName=''
local zipFiles = { }
for index, data in ipairs(updateFiles) do
if UpdateProgress then
UpdateProgress("Downloading %d/%d", index, #updateFiles)
end
local partSources = remoteSources[data.part]
local source = partSources[localPlatform] or partSources["any"]
source = source:gsub("{branch}", localBranch)
local fileName = scriptPath.."/Update/"..data.name:gsub("[\\/]","{slash}")
data.updateFileName = fileName
local content
local zipName = source:match("/([^/]+%.zip)$")
if zipName then
if not zipFiles[zipName] then
ConPrintf("Downloading %s...", zipName)
local zipFileName = scriptPath.."/Update/"..zipName
downloadFile(source, "", zipFileName)
zipFiles[zipName] = lzip.open(zipFileName)
end
local zip = zipFiles[zipName]
if zip then
local zippedFile = zip:OpenFile(data.name)
if zippedFile then
content = zippedFile:Read("*a")
zippedFile:Close()
else
ConPrintf("Couldn't extract '%s' from '%s' (extract failed)", data.name, zipName)
end
else
ConPrintf("Couldn't extract '%s' from '%s' (zip open failed)", data.name, zipName)
end
else
ConPrintf("Downloading %s... (%d of %d)", data.name, index, #updateFiles)
content = downloadFileText(source, data.name)
end
if content then
if data.sha1 ~= sha1(content) and data.sha1 ~= sha1(content:gsub("\n","\r\n")) and data.name~="changelog.txt" then
ConPrintf("Hash mismatch on '%s'", data.name)
failedFile = true
failedFileName = data.name.." hash:"..data.sha1.."=VS="..sha1(content)
print("Error:"..failedFileName)
else
local file = io.open(fileName, "w+b")
file:write(content)
file:close()
end
else
failedFile = true
failedFileName = data.name
print("Error:"..failedFileName)
end
end
for name, zip in pairs(zipFiles) do
zip:Close()
os.remove(scriptPath.."/Update/"..name)
end
if failedFile then
ConPrintf("Update failed: one or more files couldn't be downloaded:"..failedFileName)
return nil, "One or more files couldn't be downloaded.\nCheck your internet connectivity,\nor try again later."
end
-- Create new manifest
localManXML = { elem = "PoBVersion" }
table.insert(localManXML, { elem = "Version", attrib = { number = remoteVer, platform = localPlatform, branch = localBranch } })
for part, platforms in pairs(remoteSources) do
for platform, url in pairs(platforms) do
table.insert(localManXML, { elem = "Source", attrib = { part = part, platform = platform ~= "any" and platform, url = url } })
end
end
for name, data in pairs(remoteFiles) do
table.insert(localManXML, { elem = "File", attrib = { name = data.name, sha1 = data.sha1, part = data.part, platform = data.platform } })
end
xml.SaveXMLFile(localManXML, scriptPath.."/Update/manifest.xml")
-- Build list of operations to apply the update
local updateMode = "normal"
local ops = { }
local opsRuntime = { }
for _, data in pairs(updateFiles) do
-- Ensure that the destination path of this file exists
local dirStr = ""
for dir in data.fullPath:gmatch("([^/]+/)") do
dirStr = dirStr .. dir
MakeDir(dirStr)
end
if data.part == "runtime" then
-- Core runtime file, will need to update from the basic environment
-- These files will be updated on the second pass of the update script, with the first pass being run within the normal environment
updateMode = "basic"
table.insert(opsRuntime, 'move "'..data.updateFileName..'" "'..data.fullPath..'"')
else
table.insert(ops, 'move "'..data.updateFileName..'" "'..data.fullPath..'"')
end
end
for _, data in pairs(deleteFiles) do
table.insert(ops, 'delete "'..data.fullPath..'"')
end
table.insert(ops, 'move "'..scriptPath..'/Update/manifest.xml" "'..scriptPath..'/manifest.xml"')
if updateMode == "basic" then
-- Update script will need to relaunch the normal environment after updating
table.insert(opsRuntime, 'start "'..runtimeExecutable..'"')
local opRuntimeFile = io.open(scriptPath.."/Update/opFileRuntime.txt", "w+")
opRuntimeFile:write(table.concat(opsRuntime, "\n"))
opRuntimeFile:close()
end
-- Write operations file
local opFile = io.open(scriptPath.."/Update/opFile.txt", "w+")
opFile:write(table.concat(ops, "\n"))
opFile:close()
ConPrintf("Update is ready.")
return updateMode
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化