bootpage implementation

This commit is contained in:
Hersi 2022-04-24 01:39:48 +02:00
parent eb55b8ffa0
commit e8552b44a4
4 changed files with 239 additions and 47 deletions

View File

@ -0,0 +1,66 @@
require("common.class")
local Util = require("common.util")
local ServiceField = require("titlescreen.fields.service.servicefield")
---@class CheckUpdateField: ServiceField
---@field onUpdateAvailable nil|fun(url: string, version: string)
---@field _timer number
local CheckUpdateField = {
__tostring = function() return "CheckUpdateField" end,
PROGRESS_FREQ = 1 / 5, -- 5Hz
CHECK_UPDATE_MOCK_DELAY = 3, -- seconds
}
---Create a new CheckUpdateField instance
---@param o? table # initial parameters
---@return CheckUpdateField
function CheckUpdateField:new(o)
o = o or {}
o._timer = o._timer or 0
o.onUpdateAvailable = o.onUpdateAvailable or nil
local this = Inherit(self, o, ServiceField)
this._url = nil
this._version = nil
return this
end
function CheckUpdateField:drawLabel(deltaTime)
local text = self.label
local progress = math.ceil(Util.lerp(self._timer % self.PROGRESS_FREQ,
0, 0, self.PROGRESS_FREQ, 4
))
text = text .. string.rep(".", progress)
gfx.FontSize(self.FONT_SIZE)
gfx.LoadSkinFont(self.FONT_FACE)
gfx.TextAlign(gfx.TEXT_ALIGN_LEFT | gfx.TEXT_ALIGN_TOP)
gfx.FillColor(table.unpack(self.FONT_COLOR))
gfx.Text(text, 0, 0)
end
function CheckUpdateField:drawValue(deltaTime)
end
function CheckUpdateField:tick(deltaTime)
if self._timer > self.CHECK_UPDATE_MOCK_DELAY then
self._url, self._version = game.UpdateAvailable()
if self._url then
self.onUpdateAvailable(self._url, self._version)
else
self:getParentPage().viewHandler:clear() -- Exit out of bootscreen
end
end
self._timer = self._timer + deltaTime
end
function CheckUpdateField:render(deltaTime)
self:tick(deltaTime)
ServiceField.render(self, deltaTime)
end
return CheckUpdateField

View File

@ -11,16 +11,24 @@ SelfTestStatusEnum = {
ERROR = 5 ERROR = 5
} }
local function statusToString(status)
local statusName = {"IDLE", "INPROGRESS", "OK", "PASS", "ERROR"}
return statusName[status]
end
---@class SelfTestField: ServiceField ---@class SelfTestField: ServiceField
---@field checkTask nil|fun(obj: any): SelfTestStatusEnum # a function that will run asynchronously on activating the Field
---@field status SelfTestStatusEnum ---@field status SelfTestStatusEnum
---@field callback nil|fun(obj: any): SelfTestStatusEnum ---@field onStatusChange nil|fun(status) # a callback function on finishing the checkTask
---@field _thread thread
---@field _timer number ---@field _timer number
local SelfTestField = { local SelfTestField = {
SELFTEST_COLOR_INPROGRESS = {255, 255, 255, 255}, __tostring = function () return "SelfTestField" end,
SELFTEST_COLOR_OK = {0, 255, 0, 255}, COLOR_INPROGRESS = {255, 255, 255, 255},
SELFTEST_COLOR_PASS = {255, 255, 0, 255}, COLOR_OK = {0, 255, 0, 255},
SELFTEST_COLOR_ERROR = {255, 0, 0, 255}, COLOR_PASS = {255, 255, 0, 255},
SELFTEST_INPROGRESS_FREQ = 1 / 20, --20Hz COLOR_ERROR = {255, 0, 0, 255},
INPROGRESS_FREQ = 1 / 20, --20Hz
} }
---Create a new SelfTestField instance ---Create a new SelfTestField instance
@ -30,18 +38,61 @@ function SelfTestField:new(o)
o = o or {} o = o or {}
o.status = o.status or SelfTestStatusEnum.IDLE o.status = o.status or SelfTestStatusEnum.IDLE
o.callback = o.callback or nil
o._timer = 0 o._timer = 0
o._thread = nil
assert((not o.onStatusChange) or (o.checkTask and o.onStatusChange),
"Failed to construct SelfTestField, checkTask is mandatory when onStatusChange is defined!\n" .. debug.traceback()
)
return Inherit(self, o, ServiceField) return Inherit(self, o, ServiceField)
end end
function SelfTestField:activate(obj) function SelfTestField:_closeThread()
if self.callback then if self._thread and coroutine.status(self._thread) ~= "dead" then
self.status = self.callback(obj) or SelfTestStatusEnum.INPROGRESS coroutine.close(self._thread)
end end
end end
function SelfTestField:_resumeThread()
if self._thread and coroutine.status(self._thread) == "suspended" then
local success, status = coroutine.resume(self._thread)
game.Log(self.label .. ": success: " .. tostring(success) ..
", status: " .. status .. " (" .. statusToString(status) .. ")",
game.LOGGER_DEBUG
)
if success and status ~= self.status then
self.status = status
if self.onStatusChange then
game.Log("SKIN CONFIG: onStatusChange(" .. status .. ") (" ..
statusToString(status) .. ")",
game.LOGGER_DEBUG
)
self.onStatusChange(status)
end
end
end
end
function SelfTestField:activate(obj)
self:_closeThread()
if self.checkTask then
self._thread = coroutine.create(self.checkTask)
self:_resumeThread()
end
end
function SelfTestField:deactivate(obj)
self:_closeThread()
end
function SelfTestField:tick(deltaTime)
self:_resumeThread()
self._timer = self._timer + deltaTime
end
function SelfTestField:drawValue(deltaTime) function SelfTestField:drawValue(deltaTime)
gfx.Translate(self.VALUE_OFFSETX, 0) gfx.Translate(self.VALUE_OFFSETX, 0)
@ -54,18 +105,19 @@ function SelfTestField:drawValue(deltaTime)
color = self.FONT_COLOR color = self.FONT_COLOR
text = "" text = ""
elseif self.status == SelfTestStatusEnum.INPROGRESS then elseif self.status == SelfTestStatusEnum.INPROGRESS then
self._timer = self._timer + deltaTime local progress = math.ceil(Util.lerp(self._timer % self.INPROGRESS_FREQ,
local progress = math.ceil(Util.lerp(self._timer % 1, 0, 0, 1, 4)) 0, 0, self.INPROGRESS_FREQ, 4
color = self.SELFTEST_COLOR_INPROGRESS ))
color = self.COLOR_INPROGRESS
text = string.rep(".", progress) text = string.rep(".", progress)
elseif self.status == SelfTestStatusEnum.OK then elseif self.status == SelfTestStatusEnum.OK then
color = self.SELFTEST_COLOR_OK color = self.COLOR_OK
text = "OK" text = "OK"
elseif self.status == SelfTestStatusEnum.PASS then elseif self.status == SelfTestStatusEnum.PASS then
color = self.SELFTEST_COLOR_PASS color = self.COLOR_PASS
text = "PASS" text = "PASS"
elseif self.status == SelfTestStatusEnum.ERROR then elseif self.status == SelfTestStatusEnum.ERROR then
color = self.SELFTEST_COLOR_ERROR color = self.COLOR_ERROR
text = "ERROR" text = "ERROR"
end end
@ -74,4 +126,9 @@ function SelfTestField:drawValue(deltaTime)
gfx.Text(text, 0, 0) gfx.Text(text, 0, 0)
end end
function SelfTestField:render(deltaTime)
self:tick(deltaTime)
ServiceField.render(self, deltaTime)
end
return SelfTestField return SelfTestField

View File

@ -3,34 +3,11 @@ require("common.filereader")
local Dim = require("common.dimensions") local Dim = require("common.dimensions")
local Version = require("common.version") local Version = require("common.version")
local Page = require("components.pager.page") local Page = require("components.pager.page")
local CheckUpdatePage = require("titlescreen.pages.boot.checkupdatepage")
local ServiceField = require("titlescreen.fields.service.servicefield") local ServiceField = require("titlescreen.fields.service.servicefield")
local ListField = require("titlescreen.fields.service.listfield") local ListField = require("titlescreen.fields.service.listfield")
local SelfTestField = require("titlescreen.fields.boot.selftestfield") local SelfTestField = require("titlescreen.fields.boot.selftestfield")
local function checkSkinConfig(obj)
local crewpath = "textures/crew/anim/" .. game.GetSkinSetting("single_idol")
if not IsDir(crewpath) then
return SelfTestStatusEnum.ERROR
end
end
local networkStatus = SelfTestStatusEnum.IDLE
local function irHeardbeat(res)
if res.statusCode == IRData.States.Success then
networkStatus = SelfTestStatusEnum.OK
else
networkStatus = SelfTestStatusEnum.ERROR
end
end
local function checkNetwork(obj)
if not IRData.Active then
return SelfTestStatusEnum.PASS
end
IR.Heartbeat(irHeardbeat)
end
---@class BootPage: Page ---@class BootPage: Page
local BootPage = { local BootPage = {
__tostring = function() return "BootPage" end, __tostring = function() return "BootPage" end,
@ -44,14 +21,71 @@ function BootPage:new(o)
local this = Inherit(self, o, Page) local this = Inherit(self, o, Page)
this._networkResult = {}
this:addField(ServiceField:new{posX = 32, posY = 32, label = Version.getLongVersion(), value = ""}) this:addField(ServiceField:new{posX = 32, posY = 32, label = Version.getLongVersion(), value = ""})
this:addField(ServiceField:new{posX = 64, posY = 64, label = "UNNAMED SDVX CLONE STARTUP...", value = ""}) this:addField(ServiceField:new{posX = 64, posY = 64, label = "UNNAMED SDVX CLONE STARTUP...", value = ""})
local valueOffX = 220 local valueOffX = 220
this._mainIoTestField = SelfTestField:new{label = "MAIN I/O", VALUE_OFFSETX = valueOffX}
this._mainIoTestField.checkTask = function(obj)
return SelfTestStatusEnum.OK
end
this._mainIoTestField.onStatusChange = function(status)
if status == SelfTestStatusEnum.OK then
this._skinConfigTestField:activate()
end
end
this._skinConfigTestField = SelfTestField:new{label = "SKIN CONFIG", VALUE_OFFSETX = valueOffX}
this._skinConfigTestField.checkTask = function(obj)
local crewpath = "skins/" .. game.GetSkin() .. "/textures/crew/anim/" .. game.GetSkinSetting("single_idol")
if not IsDir(crewpath) then
return SelfTestStatusEnum.ERROR
end
return SelfTestStatusEnum.OK
end
this._skinConfigTestField.onStatusChange = function(status)
if status == SelfTestStatusEnum.OK then
this._networkTestField:activate()
end
end
this._networkTestField = SelfTestField:new{label = "NETWORK", VALUE_OFFSETX = valueOffX}
-- set up async network check
this._networkTestField.checkTask = function(obj)
local status = SelfTestStatusEnum.INPROGRESS
if not IRData.Active then
return SelfTestStatusEnum.PASS
end
while status == SelfTestStatusEnum.INPROGRESS do
if this._networkResult.statusCode == IRData.States.Success then
status = SelfTestStatusEnum.OK
elseif this._networkResult.statusCode then
status = SelfTestStatusEnum.ERROR -- there's a response, but it's not success
end
coroutine.yield(status)
end
return status
end
this._networkTestField.onStatusChange = function(status)
if status == SelfTestStatusEnum.INPROGRESS then
IR.Heartbeat(function(res) this._networkResult = res end) -- IR doesn't like being called in a coroutine
elseif status == SelfTestStatusEnum.PASS or status == SelfTestStatusEnum.OK then
if this.viewHandler then
this.viewHandler:navigate(CheckUpdatePage:new())
end
end
end
local list = ListField:new{posX = 64, posY = 96} local list = ListField:new{posX = 64, posY = 96}
list:addField(SelfTestField:new{label = "MAIN I/O", VALUE_OFFSETX = valueOffX, status = SelfTestStatusEnum.OK}) list:addField(this._mainIoTestField)
list:addField(SelfTestField:new{label = "SKIN CONFIG", VALUE_OFFSETX = valueOffX, status = SelfTestStatusEnum.OK, callback = checkSkinConfig}) list:addField(this._skinConfigTestField)
list:addField(SelfTestField:new{label = "IR NETWORK", VALUE_OFFSETX = valueOffX, status = SelfTestStatusEnum.ERROR, callback = checkNetwork}) list:addField(this._networkTestField)
this:addField(list) this:addField(list)
return this return this
@ -65,12 +99,13 @@ function BootPage:drawBackground(deltaTime)
gfx.Fill() gfx.Fill()
end end
local first = true
function BootPage:render(deltaTime) function BootPage:render(deltaTime)
if first then
self._mainIoTestField:activate()
first = false
end
Page.render(self, deltaTime) Page.render(self, deltaTime)
local skinconfig = self.content[3].content[2] --this is hacktastic
local network = self.content[3].content[3] --this is also hacktastic
end end
return BootPage return BootPage

View File

@ -0,0 +1,34 @@
require("common.class")
local Dim = require("common.dimensions")
local Page = require("components.pager.page")
local CheckUpdateField = require("titlescreen.fields.boot.checkupdatefield")
---@class CheckUpdatePage: Page
local CheckUpdatePage = {
__tostring = function() return "CheckUpdatePage" end,
}
---Create a new CheckUpdatePage instance
---@param o? table # initial parameters
---@return CheckUpdatePage
function CheckUpdatePage:new(o)
local this = Inherit(self, o, Page)
this._checkUpdateField = CheckUpdateField:new{posX = 32, posY = 64, label = "update check"}
this._checkUpdateField.onUpdateAvailable = function(url, version)
end
this:addField(this._checkUpdateField)
return this
end
---@param deltaTime number # frametime in seconds
function CheckUpdatePage:drawBackground(deltaTime)
gfx.BeginPath()
gfx.FillColor(0, 0, 0)
gfx.Rect(0, 0, Dim.design.width, Dim.design.height)
gfx.Fill()
end
return CheckUpdatePage