mirror of
https://github.com/OTCv8/otclientv8.git
synced 2025-10-19 14:13:27 +02:00
Version 0.95 BETA
This commit is contained in:
316
modules/client_updater/updater.lua
Normal file
316
modules/client_updater/updater.lua
Normal file
@@ -0,0 +1,316 @@
|
||||
Updater = { }
|
||||
|
||||
Updater.maxRetries = 5
|
||||
|
||||
--[[
|
||||
HOW IT WORKS:
|
||||
1. init
|
||||
2. show
|
||||
3. generateChecksum and get checksums from url
|
||||
4. compareChecksums
|
||||
5. download files with different chekcums
|
||||
6. call c++ update function
|
||||
]]--
|
||||
|
||||
local filesUrl = ""
|
||||
|
||||
local updaterWindow = nil
|
||||
local initialPanel = nil
|
||||
local updatePanel = nil
|
||||
local progressBar = nil
|
||||
local updateProgressBar = nil
|
||||
local downloadStatusLabel = nil
|
||||
local downloadProgressBar = nil
|
||||
local downloadRetries = 0
|
||||
|
||||
local generateChecksumsEvent = nil
|
||||
local updateableFiles = nil
|
||||
local binaryChecksum = nil
|
||||
local binaryFile = ""
|
||||
local fileChecksums = {}
|
||||
local checksumIter = 0
|
||||
local downloadIter = 0
|
||||
local aborted = false
|
||||
local statusData = nil
|
||||
local thingsUpdate = {}
|
||||
local toUpdate = {}
|
||||
local thingsUpdateOptionalError = nil
|
||||
|
||||
local function onDownload(path, checksum, err)
|
||||
if aborted then
|
||||
return
|
||||
end
|
||||
|
||||
if err then
|
||||
if downloadRetries > Updater.maxRetries then
|
||||
return updateError("Can't download file: " .. path .. ".\nError: " .. err)
|
||||
else
|
||||
downloadRetries = downloadRetries + 1
|
||||
return downloadNextFile(true)
|
||||
end
|
||||
end
|
||||
if statusData["files"][path] == nil then
|
||||
return updateError("Invalid file path: " .. path)
|
||||
elseif statusData["files"][path] ~= checksum then
|
||||
return updateError("Invalid file checksum.\nFile: " .. path .. "\nShould be:\n" .. statusData["files"][path] .. "\nIs:\n" .. checksum)
|
||||
end
|
||||
downloadIter = downloadIter + 1
|
||||
updateProgressBar:setPercent(math.ceil((100 * downloadIter) / #toUpdate))
|
||||
downloadProgressBar:setPercent(100)
|
||||
downloadProgressBar:setText("")
|
||||
downloadNextFile(false)
|
||||
end
|
||||
|
||||
local function onDownloadProgress(progress, speed)
|
||||
downloadProgressBar:setPercent(progress)
|
||||
downloadProgressBar:setText(speed .. " kbps")
|
||||
end
|
||||
|
||||
local function gotStatus(data, err)
|
||||
if err then
|
||||
return updateError(err)
|
||||
end
|
||||
if data["error"] ~= nil and data["error"]:len() > 0 then
|
||||
return updateError(data["error"])
|
||||
end
|
||||
if data["url"] == nil or data["files"] == nil or data["binary"] == nil then
|
||||
return updateError("Invalid json data from server")
|
||||
end
|
||||
if data["things"] ~= nil then
|
||||
for file, checksum in pairs(data["things"]) do
|
||||
if #checksum > 1 then
|
||||
for thingtype, thingdata in pairs(thingsUpdate) do
|
||||
if string.match(file:lower(), thingdata[1]:lower()) then
|
||||
data["files"][file] = checksum
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
statusData = data
|
||||
if checksumIter == 100 then
|
||||
compareChecksums()
|
||||
end
|
||||
end
|
||||
|
||||
-- public functions
|
||||
function Updater.init()
|
||||
updaterWindow = g_ui.displayUI('updater')
|
||||
updaterWindow:hide()
|
||||
|
||||
initialPanel = updaterWindow:getChildById('initialPanel')
|
||||
updatePanel = updaterWindow:getChildById('updatePanel')
|
||||
progressBar = initialPanel:getChildById('progressBar')
|
||||
updateProgressBar = updatePanel:getChildById('updateProgressBar')
|
||||
downloadStatusLabel = updatePanel:getChildById('downloadStatusLabel')
|
||||
downloadProgressBar = updatePanel:getChildById('downloadProgressBar')
|
||||
updatePanel:hide()
|
||||
|
||||
scheduleEvent(Updater.show, 200)
|
||||
end
|
||||
|
||||
function Updater.terminate()
|
||||
updaterWindow:destroy()
|
||||
updaterWindow = nil
|
||||
|
||||
removeEvent(generateChecksumsEvent)
|
||||
end
|
||||
|
||||
local function clear()
|
||||
removeEvent(generateChecksumsEvent)
|
||||
|
||||
updateableFiles = nil
|
||||
binaryChecksum = nil
|
||||
binaryFile = ""
|
||||
fileChecksums = {}
|
||||
checksumIter = 0
|
||||
downloadIter = 0
|
||||
aborted = false
|
||||
statusData = nil
|
||||
toUpdate = {}
|
||||
progressBar:setPercent(0)
|
||||
updateProgressBar:setPercent(0)
|
||||
downloadProgressBar:setPercent(0)
|
||||
downloadProgressBar:setText("")
|
||||
end
|
||||
|
||||
function Updater.show()
|
||||
if not g_resources.isLoadedFromArchive() or Services.updater == nil or Services.updater:len() < 4 then
|
||||
return Updater.hide()
|
||||
end
|
||||
if updaterWindow:isVisible() then
|
||||
return
|
||||
end
|
||||
updaterWindow:show()
|
||||
updaterWindow:raise()
|
||||
updaterWindow:focus()
|
||||
if EnterGame then
|
||||
EnterGame.hide()
|
||||
end
|
||||
|
||||
clear()
|
||||
|
||||
updateableFiles = g_resources.listUpdateableFiles()
|
||||
if #updateableFiles < 1 then
|
||||
return updateError("Can't get list of files")
|
||||
end
|
||||
binaryChecksum = g_resources.selfChecksum():lower()
|
||||
if binaryChecksum:len() ~= 32 then
|
||||
return updateError("Invalid binary checksum: " .. binaryChecksum)
|
||||
end
|
||||
|
||||
local data = {
|
||||
version = APP_VERSION,
|
||||
platform = g_window.getPlatformType(),
|
||||
uid = G.UUID,
|
||||
build_version = g_app.getVersion(),
|
||||
build_revision = g_app.getBuildRevision(),
|
||||
build_commit = g_app.getBuildCommit(),
|
||||
build_date = g_app.getBuildDate(),
|
||||
os = g_app.getOs(),
|
||||
os_name = g_platform.getOSName()
|
||||
}
|
||||
HTTP.postJSON(Services.updater, data, gotStatus)
|
||||
if generateChecksumsEvent == nil then
|
||||
generateChecksumsEvent = scheduleEvent(generateChecksum, 5)
|
||||
end
|
||||
end
|
||||
|
||||
function Updater.isVisible()
|
||||
return updaterWindow:isVisible()
|
||||
end
|
||||
|
||||
function Updater.updateThings(things, optionalError)
|
||||
thingsUpdate = things
|
||||
thingsUpdateOptionalError = optionalError
|
||||
Updater:show()
|
||||
end
|
||||
|
||||
function Updater.hide()
|
||||
updaterWindow:hide()
|
||||
if thingsUpdateOptionalError then
|
||||
local msgbox = displayErrorBox("Updater error", thingsUpdateOptionalError:trim())
|
||||
msgbox.onOk = function() if EnterGame then EnterGame.show() end end
|
||||
thingsUpdateOptionalError = nil
|
||||
elseif EnterGame then
|
||||
EnterGame.show()
|
||||
end
|
||||
end
|
||||
|
||||
function Updater.abort()
|
||||
aborted = true
|
||||
Updater:hide()
|
||||
end
|
||||
|
||||
function generateChecksum()
|
||||
local entries = #updateableFiles
|
||||
local fromEntry = math.floor((checksumIter) * (entries / 100))
|
||||
local toEntry = math.floor((checksumIter + 1) * (entries / 100))
|
||||
if checksumIter == 99 then
|
||||
toEntry = #updateableFiles
|
||||
end
|
||||
for i=fromEntry+1,toEntry do
|
||||
local fileName = updateableFiles[i]
|
||||
fileChecksums[fileName] = g_resources.fileChecksum(fileName):lower()
|
||||
end
|
||||
|
||||
checksumIter = checksumIter + 1
|
||||
if checksumIter == 100 then
|
||||
generateChecksumsEvent = nil
|
||||
gotChecksums()
|
||||
else
|
||||
progressBar:setPercent(math.ceil(checksumIter * 0.95))
|
||||
generateChecksumsEvent = scheduleEvent(generateChecksum, 5)
|
||||
end
|
||||
end
|
||||
|
||||
function gotChecksums()
|
||||
if statusData ~= nil then
|
||||
compareChecksums()
|
||||
end
|
||||
end
|
||||
|
||||
function compareChecksums()
|
||||
for file, checksum in pairs(statusData["files"]) do
|
||||
checksum = checksum:lower()
|
||||
if file == statusData["binary"] then
|
||||
if binaryChecksum ~= checksum then
|
||||
binaryFile = file
|
||||
table.insert(toUpdate, binaryFile)
|
||||
end
|
||||
else
|
||||
local localChecksum = fileChecksums[file]
|
||||
if localChecksum ~= checksum then
|
||||
table.insert(toUpdate, file)
|
||||
end
|
||||
end
|
||||
end
|
||||
if #toUpdate == 0 then
|
||||
return upToDate()
|
||||
end
|
||||
-- outdated
|
||||
filesUrl = statusData["url"]
|
||||
initialPanel:hide()
|
||||
updatePanel:show()
|
||||
updatePanel:getChildById('updateStatusLabel'):setText(tr("Updating %i files", #toUpdate))
|
||||
updaterWindow:setHeight(190)
|
||||
downloadNextFile(false)
|
||||
end
|
||||
|
||||
function upToDate()
|
||||
Updater.hide()
|
||||
end
|
||||
|
||||
function updateError(err)
|
||||
Updater.hide()
|
||||
local msgbox = displayErrorBox("Updater error", err)
|
||||
msgbox.onOk = function() if EnterGame then EnterGame.show() end end
|
||||
end
|
||||
|
||||
function urlencode(url)
|
||||
url = url:gsub("\n", "\r\n")
|
||||
url = url:gsub("([^%w ])", function(c) string.format("%%%02X", string.byte(c)) end)
|
||||
url = url:gsub(" ", "+")
|
||||
return url
|
||||
end
|
||||
|
||||
function downloadNextFile(retry)
|
||||
if aborted then
|
||||
return
|
||||
end
|
||||
|
||||
updaterWindow:show()
|
||||
updaterWindow:raise()
|
||||
updaterWindow:focus()
|
||||
|
||||
if downloadIter == #toUpdate then
|
||||
return downloadingFinished()
|
||||
end
|
||||
|
||||
if retry then
|
||||
retry = " (" .. downloadRetries .. " retry)"
|
||||
else
|
||||
retry = ""
|
||||
end
|
||||
|
||||
local file = toUpdate[downloadIter + 1]
|
||||
downloadStatusLabel:setText(tr("Downloading %i of %i%s:\n%s", downloadIter + 1, #toUpdate, retry, file))
|
||||
downloadProgressBar:setPercent(0)
|
||||
downloadProgressBar:setText("")
|
||||
HTTP.download(filesUrl .. urlencode(file), file, onDownload, onDownloadProgress)
|
||||
end
|
||||
|
||||
function downloadingFinished()
|
||||
thingsUpdateOptionalError = nil
|
||||
UIMessageBox.display(tr("Success"), tr("Download complate.\nUpdating client..."), {}, nil, nil)
|
||||
scheduleEvent(function()
|
||||
local files = {}
|
||||
for file, checksum in pairs(statusData["files"]) do
|
||||
table.insert(files, file)
|
||||
end
|
||||
g_settings.save()
|
||||
g_resources.updateClient(files, binaryFile)
|
||||
g_app.quick_exit()
|
||||
end, 1000)
|
||||
end
|
9
modules/client_updater/updater.otmod
Normal file
9
modules/client_updater/updater.otmod
Normal file
@@ -0,0 +1,9 @@
|
||||
Module
|
||||
name: client_updater
|
||||
description: Updates client
|
||||
author: otclient@otclient.ovh
|
||||
website: otclient.ovh
|
||||
reloadable: false
|
||||
scripts: [ updater ]
|
||||
@onLoad: Updater.init()
|
||||
@onUnload: Updater.terminate()
|
75
modules/client_updater/updater.otui
Normal file
75
modules/client_updater/updater.otui
Normal file
@@ -0,0 +1,75 @@
|
||||
StaticMainWindow
|
||||
id: updaterWindow
|
||||
!text: tr('Updater')
|
||||
height: 125
|
||||
width: 300
|
||||
|
||||
Panel
|
||||
id: initialPanel
|
||||
layout:
|
||||
type: verticalBox
|
||||
anchors.left: parent.left
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.right: parent.right
|
||||
margin: 0 5 5 5
|
||||
|
||||
Label
|
||||
id: statusLabel
|
||||
!text: tr('Checking for updates')
|
||||
text-align: center
|
||||
|
||||
ProgressBar
|
||||
id: progressBar
|
||||
height: 15
|
||||
background-color: #4444ff
|
||||
margin-bottom: 10
|
||||
margin-top: 10
|
||||
|
||||
Button
|
||||
!text: tr('Cancel')
|
||||
margin-left: 70
|
||||
margin-right: 70
|
||||
@onClick: Updater.abort()
|
||||
|
||||
Panel
|
||||
id: updatePanel
|
||||
layout:
|
||||
type: verticalBox
|
||||
anchors.left: parent.left
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.right: parent.right
|
||||
margin: 0 5 5 5
|
||||
|
||||
Label
|
||||
id: updateStatusLabel
|
||||
!text: tr('Updating')
|
||||
text-align: center
|
||||
|
||||
ProgressBar
|
||||
id: updateProgressBar
|
||||
height: 15
|
||||
background-color: #4444ff
|
||||
margin-bottom: 10
|
||||
margin-top: 10
|
||||
|
||||
Label
|
||||
id: downloadStatusLabel
|
||||
!text: tr('Downloading:')
|
||||
text-align: center
|
||||
margin-top: 5
|
||||
height: 25
|
||||
|
||||
ProgressBar
|
||||
id: downloadProgressBar
|
||||
height: 15
|
||||
background-color: #4444ff
|
||||
margin-bottom: 10
|
||||
margin-top: 10
|
||||
|
||||
Button
|
||||
!text: tr('Cancel')
|
||||
margin-left: 70
|
||||
margin-right: 70
|
||||
@onClick: Updater.abort()
|
Reference in New Issue
Block a user