a rev bump has to actually reach the machines it was bumped for

It did not. pending() short-circuited on available(), and available() was
true for any checkout carrying assets/stadium -- so when REV went to 3 for
the shiny variants, such a machine was never pending, was never asked to
rebuild, and quietly went on serving the old set. ready() was false, so
readPack skipped the save-dir cache and read the shipped normal-only packs:
every shiny Pokemon drawn in its ordinary colours, with nothing on screen
saying why. A driver run sitting at "idle 0/151" for twelve thousand frames
is what surfaced it.

pending() is now keyed on ready(), not available(). A checkout with a ROM
spends one loading screen rebuilding a set it had files for; after that it is
current and never pending again. That was the cost the old short-circuit was
avoiding, and it is worth paying once to make a rev bump mean something.

The other half is the players who imported a ROM through the picker rather
than dropping it in baseroms/. beginFrom builds from the bytes and never
keeps them, so on a rev bump there is no ROM to rebuild from -- and with
available() keyed on ready(), the STADIUM rungs would have vanished off the
options row entirely. usable() now separates "these packs are readable" from
"these packs are current": format and count, deliberately not rev. Stale
packs keep the mode working and keep the rungs offered; only the recolour
waits for a rebuild. Losing the shinies until then is a blemish, losing the
mode is not.

readPack orders the two accordingly: a current cache always wins, a stale one
wins only when there is no shipped set to prefer instead -- so a cache from
an extractor rev we have since fixed cannot shadow good files, while a player
whose only copy IS that cache still gets Pokemon on the field.

Verified by rolling the marker back to rev 2 and launching: ready=false,
usable=true, available=true, pending=true, and the build runs unprompted.
This commit is contained in:
DramaticShape
2026-08-08 12:53:59 -04:00
parent b05c7265d6
commit eb69bc7db8
3 changed files with 56 additions and 11 deletions
+33 -9
View File
@@ -189,22 +189,46 @@ local function shipped()
return true return true
end end
-- Whether the STADIUM rungs can be offered at all: either the packs have been -- Whether the packs on disk can be READ, even if they are not current.
-- built from the player's ROM, or the mod folder already carries a set. --
-- Format and count, but deliberately NOT rev. The distinction matters on an
-- upgrade: a rev bump means the packs are out of date, not that they are
-- unreadable, and treating the two the same is what would make the STADIUM
-- rungs disappear off the options row for anyone whose cache predates it.
-- Losing the recolour until a rebuild is a blemish; losing the mode is not.
function StadiumInstall.usable()
local m = readMarker()
return (m ~= nil and m.format == StadiumInstall.FORMAT
and m.count == StadiumInstall.COUNT) and true or false
end
-- Whether the STADIUM rungs can be offered at all: the packs have been built
-- from the player's ROM (current or merely readable), or the mod folder
-- already carries a set.
function StadiumInstall.available() function StadiumInstall.available()
if StadiumInstall.ready() then return true end if StadiumInstall.ready() then return true end
if StadiumInstall.usable() then return true end
return shipped() return shipped()
end end
-- Whether there is work to do: something to build from, and nothing usable -- Whether there is work to do: a ROM to build from, and no CURRENT set.
-- yet.
-- --
-- A checkout that already carries a set is NOT pending. Building anyway would -- Keyed on ready() rather than available(), and that is the whole upgrade
-- be correct and would also mean a ten-second loading screen on the first run -- story. It used to short-circuit on available(), which meant a checkout
-- of every checkout, to arrive at the files that were already sitting there. -- carrying assets/stadium was never pending -- so when REV went to 3 for the
-- shiny variants, such a machine did not rebuild, was not asked to, and
-- quietly kept serving the old set: every shiny Pokemon drawn in its
-- ordinary colours, with nothing on screen to say why. That is exactly what
-- happened here, and it took a driver run sitting at "idle 0/151" to notice.
--
-- The cost this trades away is real and was the original reason: a checkout
-- with a ROM now spends one loading screen rebuilding a set it already had
-- files for. Once. After that ready() is true and it is not pending again --
-- and what it buys is that a rev bump actually reaches the people it was
-- bumped for.
function StadiumInstall.pending() function StadiumInstall.pending()
if StadiumInstall.available() then return false end if not StadiumInstall.romPresent() then return false end
return StadiumInstall.romPresent() return not StadiumInstall.ready()
end end
function StadiumInstall.forget() function StadiumInstall.forget()
+14 -2
View File
@@ -78,15 +78,27 @@ local function readPack(species, shiny)
-- half-written folder must not be read at all. Required lazily: Install -- half-written folder must not be read at all. Required lazily: Install
-- requires this module at load, so the reverse edge cannot be taken then. -- requires this module at load, so the reverse edge cannot be taken then.
local rel = packName(StadiumPack.CACHE_DIR, species, shiny) local rel = packName(StadiumPack.CACHE_DIR, species, shiny)
local install = V.require("StadiumInstall")
local mod = V.mod
local haveShipped = false
if mod and mod.read then
local okS, b = pcall(mod.read, mod, packName(StadiumPack.DIR, species, shiny))
haveShipped = okS and type(b) == "string" and #b > 4
end
-- A CURRENT cache always wins. A stale one (readable, but built by an older
-- extractor) wins only when there is no shipped set to prefer instead --
-- that ordering is what stops a cache from an extractor rev we have since
-- fixed shadowing good files, while still leaving something on screen for a
-- player whose only copy IS that cache. A half-written folder is caught by
-- the marker and satisfies neither.
if love and love.filesystem and love.filesystem.getInfo if love and love.filesystem and love.filesystem.getInfo
and V.require("StadiumInstall").ready() then and (install.ready() or (install.usable() and not haveShipped)) then
local okInfo, info = pcall(love.filesystem.getInfo, rel, "file") local okInfo, info = pcall(love.filesystem.getInfo, rel, "file")
if okInfo and info then if okInfo and info then
local ok, bytes = pcall(love.filesystem.read, rel) local ok, bytes = pcall(love.filesystem.read, rel)
if ok and type(bytes) == "string" and #bytes > 4 then return bytes end if ok and type(bytes) == "string" and #bytes > 4 then return bytes end
end end
end end
local mod = V.mod
if not (mod and mod.read) then return nil end if not (mod and mod.read) then return nil end
local ok, bytes = pcall(mod.read, mod, local ok, bytes = pcall(mod.read, mod,
packName(StadiumPack.DIR, species, shiny)) packName(StadiumPack.DIR, species, shiny))
+9
View File
@@ -58,6 +58,15 @@ return function(game)
-- unrecoloured Pokemon. Calling begin/step directly is both faster and -- unrecoloured Pokemon. Calling begin/step directly is both faster and
-- honest about what is being tested, which is the extraction, not the -- honest about what is being tested, which is the extraction, not the
-- screen that usually triggers it. -- screen that usually triggers it.
-- the upgrade question, asked before anything is built: with a stale
-- marker on disk, does this machine know it has work to do?
U.log(("upgrade check: ready=%s usable=%s available=%s pending=%s rom=%s")
:format(tostring(StadiumInstall.ready()),
tostring(StadiumInstall.usable()),
tostring(StadiumInstall.available()),
tostring(StadiumInstall.pending()),
tostring(StadiumInstall.romPresent())))
if not StadiumInstall.ready() then if not StadiumInstall.ready() then
local ok, err = StadiumInstall.begin() local ok, err = StadiumInstall.begin()
U.log(("stadium build: begin=%s %s"):format(tostring(ok), tostring(err or ""))) U.log(("stadium build: begin=%s %s"):format(tostring(ok), tostring(err or "")))