Files
gen1recomp/src/core/Prelaunch.lua
T

194 lines
5.6 KiB
Lua

local Prelaunch = {}
Prelaunch.__index = Prelaunch
Prelaunch.UPDATE_BUDGET = 25
Prelaunch.SYNC_BUDGET = 30
Prelaunch.MARKER = "launch_update.txt"
local function loveFs()
local l = rawget(_G, "love")
return l and l.filesystem or nil
end
local function consumeMarker(fs)
if not (fs and fs.getInfo) then return false end
local ok, info = pcall(fs.getInfo, Prelaunch.MARKER)
if not ok or not info then return false end
pcall(fs.remove, Prelaunch.MARKER)
return true
end
local function updateAllowed(opts, fs)
if opts.allowUpdate ~= nil then return opts.allowUpdate and true or false end
local ok, Version = pcall(require, "src.core.Version")
if ok and type(Version) == "table" and Version.isDev and Version.isDev() then
return false
end
local okp, Platform = pcall(require, "src.core.Platform")
if okp and type(Platform) == "table" and Platform.networkValidated
and not Platform.networkValidated() then
return false
end
if not (fs and fs.isFused and fs.isFused()) then return false end
return true
end
local function syncSupported()
local ok, HostShell = pcall(require, "src.core.HostShell")
if not ok or type(HostShell) ~= "table"
or type(HostShell.canHttpRequest) ~= "function" then
return true
end
local asked, can = pcall(HostShell.canHttpRequest)
return (not asked) or (can and true or false)
end
local function sharedEngine(opts)
if opts.engine ~= nil then return opts.engine or nil end
if not syncSupported() then return nil end
local ok, SyncEngine = pcall(require, "src.sync.SyncEngine")
if not ok or type(SyncEngine) ~= "table" then return nil end
local got, eng = pcall(SyncEngine.shared)
return (got and type(eng) == "table") and eng or nil
end
local function engineWanted(eng)
if type(eng) ~= "table" then return false end
if not (eng.state and eng.state.enabled) then return false end
local ok, linked = pcall(eng.linked, eng)
return (ok and linked) and true or false
end
function Prelaunch.new(opts)
opts = opts or {}
local self = setmetatable({}, Prelaunch)
self.version = opts.version
self.done = opts.done
self.fs = opts.fs
if self.fs == nil then self.fs = loveFs() end
if self.fs == false then self.fs = nil end
self.restart = opts.restart or function()
require("src.core.HostShell").restart()
end
local tasks = opts.tasks or {}
local stages = {}
local resumed = consumeMarker(self.fs)
if tasks.update and not resumed and updateAllowed(opts, self.fs) then
local ok, check = pcall(function()
return opts.check or require("src.update.Check")
end)
if ok and type(check) == "table" then
self.check = check
stages[#stages + 1] = "update"
end
end
if tasks.sync ~= false then
local eng = sharedEngine(opts)
if engineWanted(eng) then
self.engine = eng
stages[#stages + 1] = "sync"
end
end
if #stages == 0 then return nil end
self.stages = stages
self.index = 0
self.elapsed = 0
self.status = ""
self:_advance()
return self
end
function Prelaunch:_finish(outcome)
if self.finished then return end
self.finished = true
self.outcome = outcome
if self.done then self.done(outcome) end
end
function Prelaunch:_advance()
self.index = (self.index or 0) + 1
self.elapsed = 0
self.stage = self.stages[self.index]
if self.stage == nil then return self:_finish("boot") end
if self.stage == "update" then
self.status = "Checking for updates..."
pcall(self.check.start, true)
elseif self.stage == "sync" then
self.status = "Syncing saves..."
pcall(self.engine.syncNow, self.engine)
end
end
function Prelaunch:_updateStage()
local ok, st = pcall(self.check.state)
st = (ok and type(st) == "table") and st or nil
local status = st and st.status or "error"
if status == "available" then
self.status = "Downloading update..."
pcall(self.check.download)
elseif status == "downloading" then
self.status = string.format("Updating %d%%",
math.floor((tonumber(st.progress) or 0) * 100))
elseif status == "ready" then
if self.fs and self.fs.write then
pcall(self.fs.write, Prelaunch.MARKER, "1")
end
self.status = "Restarting..."
pcall(self.restart)
return self:_finish("restart")
elseif status ~= "checking" then
return self:_advance()
end
if self.elapsed >= Prelaunch.UPDATE_BUDGET then return self:_advance() end
end
function Prelaunch:_syncStage(dt)
local eng = self.engine
pcall(eng.update, eng, dt)
if type(eng.status) == "string" and eng.status ~= "" then
self.status = eng.status
end
if eng.phase == "conflict" then return self:_finish("launcher") end
if eng.phase == "idle" or eng.phase == "error" then
return self:_advance()
end
if self.elapsed >= Prelaunch.SYNC_BUDGET then return self:_advance() end
end
function Prelaunch:update(dt)
if self.finished then return end
dt = tonumber(dt) or 0
self.elapsed = (self.elapsed or 0) + dt
if self.stage == "update" then
self:_updateStage()
elseif self.stage == "sync" then
self:_syncStage(dt)
end
end
function Prelaunch:cancel()
self:_finish("boot")
end
function Prelaunch:keypressed()
self:cancel()
end
function Prelaunch:draw()
local l = rawget(_G, "love")
local g = l and l.graphics
if not (g and g.printf) then return end
g.clear(0, 0, 0, 1)
g.setColor(1, 1, 1, 1)
local w = g.getWidth and g.getWidth() or 0
local h = g.getHeight and g.getHeight() or 0
g.printf(tostring(self.status or ""), 0, math.floor(h / 2) - 12, w, "center")
g.setColor(1, 1, 1, 0.55)
g.printf("Press any button to skip", 0, math.floor(h / 2) + 8, w, "center")
g.setColor(1, 1, 1, 1)
end
return Prelaunch