forked from PathOfBuildingCommunity/PathOfBuilding
- Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathUpdateCheck.lua
More file actions
Latest commit
287 lines (272 loc) · 9.11 KB
/
Copy pathUpdateCheck.lua
File metadata and controls
287 lines (272 loc) · 9.11 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
#@
-- Path of Building
--
-- Module: Update Check
-- Checks for updates
--
localproxyURL=...
localxml=require("xml")
localsha1=require("sha1")
localcurl=require("lcurl.safe")
locallzip=require("lzip")
localglobalRetryLimit=10
localfunctiondownloadFileText(url)
fori=1, 5do
ifi>1then
ConPrintf("Retrying... (%d of 5)", i)
end
localtext=""
localeasy=curl.easy()
easy:setopt_url(url)
easy:setopt(curl.OPT_ACCEPT_ENCODING, "")
ifproxyURLthen
easy:setopt(curl.OPT_PROXY, proxyURL)
end
easy:setopt_writefunction(function(data)
text=text..data
returntrue
end)
local_, error=easy:perform()
easy:close()
ifnoterrorthen
returntext
end
ConPrintf("Download failed (%s)", error:msg())
ifglobalRetryLimit==0ori==5then
returnnil, error:msg()
end
globalRetryLimit=globalRetryLimit-1
end
end
localfunctiondownloadFile(url, outName)
localtext=downloadFileText(url)
iftextthen
localoutFile=io.open(outName, "wb")
outFile:write(text)
outFile:close()
else
returntrue
end
end
ConPrintf("Checking for update...")
localscriptPath=GetScriptPath()
localruntimePath=GetRuntimePath()
-- Load and process local manifest
locallocalVer
locallocalPlatform, localBranch
locallocalFiles= { }
locallocalManXML=xml.LoadXMLFile(scriptPath.."/manifest.xml")
locallocalSource
localruntimeExecutable
iflocalManXMLandlocalManXML[1].elem=="PoBVersion" then
for_, nodeinipairs(localManXML[1]) do
iftype(node) =="table" then
ifnode.elem=="Version" then
localVer=node.attrib.number
localPlatform=node.attrib.platform
localBranch=node.attrib.branch
elseifnode.elem=="Source" then
ifnode.attrib.part=="program" then
localSource=node.attrib.url
end
elseifnode.elem=="File" then
localfullPath
ifnode.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 }
ifnode.attrib.part=="runtime" andnode.attrib.name:match("Path of Building") then
runtimeExecutable=fullPath
end
end
end
end
end
ifnotlocalVerornotlocalSourceornotlocalBranchornotnext(localFiles) then
ConPrintf("Update check failed: invalid local manifest")
returnnil, "Invalid local manifest"
end
localSource=localSource:gsub("{branch}", localBranch)
-- Download and process remote manifest
localremoteVer
localremoteFiles= { }
localremoteSources= { }
localremoteManText, errMsg=downloadFileText(localSource.."manifest.xml")
ifnotremoteManTextthen
ConPrintf("Update check failed: couldn't download version manifest")
returnnil, "Couldn't download version manifest.\nReason: "..errMsg.."\nCheck your internet connectivity.\nIf you are using a proxy, specify it in Options."
end
localremoteManXML=xml.ParseXML(remoteManText)
ifremoteManXMLandremoteManXML[1].elem=="PoBVersion" then
for_, nodeinipairs(remoteManXML[1]) do
iftype(node) =="table" then
ifnode.elem=="Version" then
remoteVer=node.attrib.number
elseifnode.elem=="Source" then
ifnotremoteSources[node.attrib.part] then
remoteSources[node.attrib.part] = { }
end
remoteSources[node.attrib.part][node.attrib.platformor"any"] =node.attrib.url
elseifnode.elem=="File" then
ifnotnode.attrib.platformornode.attrib.platform==localPlatformthen
localfullPath
ifnode.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
ifnotremoteVerornotnext(remoteSources) ornotnext(remoteFiles) then
ConPrintf("Update check failed: invalid remote manifest")
returnnil, "Invalid remote manifest"
end
-- Build lists of files to be updated or deleted
localupdateFiles= { }
forname, datainpairs(remoteFiles) do
data.name=name
ifnotlocalFiles[name] orlocalFiles[name].sha1~=data.sha1then
table.insert(updateFiles, data)
elseiflocalFiles[name] then
localfile=io.open(localFiles[name].fullPath, "rb")
ifnotfilethen
ConPrintf("Warning: '%s' doesn't exist, it will be re-downloaded", data.name)
table.insert(updateFiles, data)
else
localcontent=file:read("*a")
file:close()
ifdata.sha1~=sha1(content) anddata.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
localdeleteFiles= { }
forname, datainpairs(localFiles) do
data.name=name
ifnotremoteFiles[name] then
table.insert(deleteFiles, data)
end
end
if#updateFiles==0and#deleteFiles==0then
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
localfailedFile=false
localzipFiles= { }
forindex, datainipairs(updateFiles) do
ifUpdateProgressthen
UpdateProgress("Downloading %d/%d", index, #updateFiles)
end
localpartSources=remoteSources[data.part]
localsource=partSources[localPlatform] orpartSources["any"]
source=source:gsub("{branch}", localBranch)
localfileName=scriptPath.."/Update/"..data.name:gsub("[\\/]","{slash}")
data.updateFileName=fileName
localcontent
localzipName=source:match("/([^/]+%.zip)$")
ifzipNamethen
ifnotzipFiles[zipName] then
ConPrintf("Downloading %s...", zipName)
localzipFileName=scriptPath.."/Update/"..zipName
downloadFile(source, zipFileName)
zipFiles[zipName] =lzip.open(zipFileName)
end
localzip=zipFiles[zipName]
ifzipthen
localzippedFile=zip:OpenFile(data.name)
ifzippedFilethen
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
ifcontentthen
ifdata.sha1~=sha1(content) anddata.sha1~=sha1(content:gsub("\n","\r\n")) then
ConPrintf("Hash mismatch on '%s'", data.name)
failedFile=true
else
localfile=io.open(fileName, "w+b")
file:write(content)
file:close()
end
else
failedFile=true
end
end
forname, zipinpairs(zipFiles) do
zip:Close()
os.remove(scriptPath.."/Update/"..name)
end
iffailedFilethen
ConPrintf("Update failed: one or more files couldn't be downloaded")
returnnil, "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 } })
forpart, platformsinpairs(remoteSources) do
forplatform, urlinpairs(platforms) do
table.insert(localManXML, { elem="Source", attrib= { part=part, platform=platform~="any" andplatform, url=url } })
end
end
forname, datainpairs(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
localupdateMode="normal"
localops= { }
localopsRuntime= { }
for_, datainpairs(updateFiles) do
-- Ensure that the destination path of this file exists
localdirStr=""
fordirindata.fullPath:gmatch("([^/]+/)") do
dirStr=dirStr..dir
MakeDir(dirStr)
end
ifdata.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_, datainpairs(deleteFiles) do
table.insert(ops, 'delete "'..data.fullPath..'"')
end
table.insert(ops, 'move "'..scriptPath..'/Update/manifest.xml" "'..scriptPath..'/manifest.xml"')
ifupdateMode=="basic" then
-- Update script will need to relaunch the normal environment after updating
table.insert(opsRuntime, 'start "'..runtimeExecutable..'"')
localopRuntimeFile=io.open(scriptPath.."/Update/opFileRuntime.txt", "w+")
opRuntimeFile:write(table.concat(opsRuntime, "\n"))
opRuntimeFile:close()
end
-- Write operations file
localopFile=io.open(scriptPath.."/Update/opFile.txt", "w+")
opFile:write(table.concat(ops, "\n"))
opFile:close()
ConPrintf("Update is ready.")
returnupdateMode