fix(mods): emit restore lifecycle after title resume

This commit is contained in:
MaxTomahawk
2026-08-10 20:39:01 +02:00
parent 1c37867e52
commit 4e20f4585f
2 changed files with 24 additions and 6 deletions
+13 -6
View File
@@ -386,6 +386,8 @@ local function firstDifference(a, b, path)
return nil return nil
end end
local emitRestored
function Checkpoint.restore(game, checkpoint) function Checkpoint.restore(game, checkpoint)
local capability = Checkpoint.inspect(game) local capability = Checkpoint.inspect(game)
if not capability.canRestore then if not capability.canRestore then
@@ -403,12 +405,7 @@ function Checkpoint.restore(game, checkpoint)
local restored, verifyCode = Checkpoint.capture(game) local restored, verifyCode = Checkpoint.capture(game)
if restored and validated.rng == nil then restored.rng = nil end if restored and validated.rng == nil then restored.rng = nil end
if restored and equalData(restored, validated) then if restored and equalData(restored, validated) then
if ModRuntime.wants("checkpoint.restored") then emitRestored(game, validated)
ModRuntime.emit("checkpoint.restored", {
game = game,
kind = validated.kind,
})
end
return true return true
end end
err = restored and ("restored state differed at " err = restored and ("restored state differed at "
@@ -424,6 +421,15 @@ function Checkpoint.restore(game, checkpoint)
return false, "restore_failed", "Checkpoint restoration failed: " .. tostring(err) return false, "restore_failed", "Checkpoint restoration failed: " .. tostring(err)
end end
emitRestored = function(game, checkpoint)
if ModRuntime.wants("checkpoint.restored") then
ModRuntime.emit("checkpoint.restored", {
game = game,
kind = checkpoint.kind,
})
end
end
local function isTitleSession(game) local function isTitleSession(game)
local states = game and game.stack and game.stack.states local states = game and game.stack and game.stack.states
if type(states) ~= "table" then return false end if type(states) ~= "table" then return false end
@@ -479,6 +485,7 @@ function Checkpoint.resume(game, checkpoint)
local restored, verifyCode = Checkpoint.capture(game) local restored, verifyCode = Checkpoint.capture(game)
if restored and validated.rng == nil then restored.rng = nil end if restored and validated.rng == nil then restored.rng = nil end
if restored and equalData(restored, validated) then if restored and equalData(restored, validated) then
emitRestored(game, validated)
return true return true
end end
err = restored and ("resumed state differed at " err = restored and ("resumed state differed at "
@@ -60,6 +60,10 @@ local files = {
return function(mod) return function(mod)
_G.MOD_TITLE_STORAGE = mod.storage _G.MOD_TITLE_STORAGE = mod.storage
_G.MOD_TITLE_CHECKPOINTS = mod.checkpoints _G.MOD_TITLE_CHECKPOINTS = mod.checkpoints
mod.events:on("checkpoint.restored", function(ev)
_G.MOD_TITLE_RESTORE_COUNT = (_G.MOD_TITLE_RESTORE_COUNT or 0) + 1
_G.MOD_TITLE_RESTORE_KIND = ev.kind
end)
end end
]], ]],
} }
@@ -194,6 +198,10 @@ if type(storage) == "table" then
"title bootstrap never creates a normal Pokémon save as a side effect") "title bootstrap never creates a normal Pokémon save as a side effect")
T.same(checkpoints:capture(titleRuntime), checkpoint, T.same(checkpoints:capture(titleRuntime), checkpoint,
"bootstrapped overworld differentially recaptures the selected checkpoint") "bootstrapped overworld differentially recaptures the selected checkpoint")
T.eq(_G.MOD_TITLE_RESTORE_COUNT, 1,
"a successfully verified title resume emits checkpoint.restored exactly once")
T.eq(_G.MOD_TITLE_RESTORE_KIND, "overworld",
"title resume lifecycle reports the reconstructed checkpoint kind")
-- Force a failure after restoreCheckpointSave has already installed the -- Force a failure after restoreCheckpointSave has already installed the
-- checkpoint's canonical save and overworld. Title has no live checkpoint -- checkpoint's canonical save and overworld. Title has no live checkpoint
@@ -217,6 +225,8 @@ if type(storage) == "table" then
"failed title reconstruction retains current title options") "failed title reconstruction retains current title options")
T.check(files["save.lua"] == nil, T.check(files["save.lua"] == nil,
"failed title reconstruction never writes a normal Pokémon save") "failed title reconstruction never writes a normal Pokémon save")
T.eq(_G.MOD_TITLE_RESTORE_COUNT, 1,
"failed title reconstruction emits no additional restored lifecycle event")
end end
-- A title policy may compare its own durable checkpoint chronology with the -- A title policy may compare its own durable checkpoint chronology with the
@@ -252,6 +262,7 @@ Runtime.currentMod = nil
_G.MOD_TITLE_STORAGE = nil _G.MOD_TITLE_STORAGE = nil
_G.MOD_TITLE_CHECKPOINTS = nil _G.MOD_TITLE_CHECKPOINTS = nil
_G.MOD_TITLE_RESTORE_COUNT = nil _G.MOD_TITLE_RESTORE_COUNT = nil
_G.MOD_TITLE_RESTORE_KIND = nil
SaveData.resetSlotState() SaveData.resetSlotState()
SaveData.loadOptions = originalLoadOptions SaveData.loadOptions = originalLoadOptions