mirror of
https://github.com/bryanthaboi/gen1recomp.git
synced 2026-08-19 20:20:19 +02:00
Merge pull request #1209 from MaxTomahawk/fix/checkpoint-engine-version-restore
fix(mods): allow checkpoint restore across engine versions
This commit is contained in:
@@ -313,6 +313,16 @@ local function equalData(a, b)
|
|||||||
return okA and okB and encodedA == encodedB
|
return okA and okB and encodedA == encodedB
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local function normalizeVerificationMetadata(actual, expected)
|
||||||
|
if type(actual) == "table" and type(actual.identity) == "table"
|
||||||
|
and type(expected) == "table" and type(expected.identity) == "table" then
|
||||||
|
-- RFC 0004 treats engineVersion as compatibility metadata, not runtime
|
||||||
|
-- state. A fresh recapture stamps the running engine, so normalize only
|
||||||
|
-- that metadata before differential verification.
|
||||||
|
actual.identity.engineVersion = expected.identity.engineVersion
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
local function firstDifference(a, b, path)
|
local function firstDifference(a, b, path)
|
||||||
path = path or "$"
|
path = path or "$"
|
||||||
if type(a) ~= type(b) then return path .. " (type)" end
|
if type(a) ~= type(b) then return path .. " (type)" end
|
||||||
@@ -394,6 +404,7 @@ function Checkpoint.restore(game, checkpoint)
|
|||||||
if ok then
|
if ok then
|
||||||
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
|
||||||
|
normalizeVerificationMetadata(restored, validated)
|
||||||
if restored and equalData(restored, validated) then
|
if restored and equalData(restored, validated) then
|
||||||
emitRestored(game, validated)
|
emitRestored(game, validated)
|
||||||
return true
|
return true
|
||||||
@@ -474,6 +485,7 @@ function Checkpoint.resume(game, checkpoint)
|
|||||||
if ok then
|
if ok then
|
||||||
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
|
||||||
|
normalizeVerificationMetadata(restored, validated)
|
||||||
if restored and equalData(restored, validated) then
|
if restored and equalData(restored, validated) then
|
||||||
emitRestored(game, validated)
|
emitRestored(game, validated)
|
||||||
return true
|
return true
|
||||||
|
|||||||
@@ -207,6 +207,11 @@ if type(storage) == "table" then
|
|||||||
T.check(type(normalBytes) == "string" and normalBytes ~= "",
|
T.check(type(normalBytes) == "string" and normalBytes ~= "",
|
||||||
"first checkpoint anchor is durably represented before restart")
|
"first checkpoint anchor is durably represented before restart")
|
||||||
local anchoredAt = SaveSerializer.decode(normalBytes).meta.savedAt
|
local anchoredAt = SaveSerializer.decode(normalBytes).meta.savedAt
|
||||||
|
|
||||||
|
-- Model a durable checkpoint captured by the previous shipped engine.
|
||||||
|
-- RFC 0004 treats engineVersion as compatibility metadata, not runtime state.
|
||||||
|
checkpoint.identity.engineVersion = "0.1.79"
|
||||||
|
|
||||||
SaveData.resetSlotState()
|
SaveData.resetSlotState()
|
||||||
local titleRuntime = makeRuntime(SaveData.newGame({ version = "red" }), true)
|
local titleRuntime = makeRuntime(SaveData.newGame({ version = "red" }), true)
|
||||||
titleRuntime.save.options = { volume = 9, bindings = {} }
|
titleRuntime.save.options = { volume = 9, bindings = {} }
|
||||||
@@ -224,7 +229,14 @@ if type(storage) == "table" then
|
|||||||
version = "red", meta = { playthroughId = originalId },
|
version = "red", meta = { playthroughId = originalId },
|
||||||
}, fs).savedAt, anchoredAt,
|
}, fs).savedAt, anchoredAt,
|
||||||
"title bootstrap never rewrites the first normal save")
|
"title bootstrap never rewrites the first normal save")
|
||||||
T.same(checkpoints:capture(titleRuntime), checkpoint,
|
local recaptured = checkpoints:capture(titleRuntime)
|
||||||
|
T.eq(recaptured and recaptured.identity
|
||||||
|
and recaptured.identity.engineVersion, Version.engine,
|
||||||
|
"cross-version resume recaptures the running engine version")
|
||||||
|
if recaptured and recaptured.identity then
|
||||||
|
recaptured.identity.engineVersion = checkpoint.identity.engineVersion
|
||||||
|
end
|
||||||
|
T.same(recaptured, checkpoint,
|
||||||
"bootstrapped overworld differentially recaptures the selected checkpoint")
|
"bootstrapped overworld differentially recaptures the selected checkpoint")
|
||||||
T.eq(_G.MOD_TITLE_RESTORE_COUNT, 1,
|
T.eq(_G.MOD_TITLE_RESTORE_COUNT, 1,
|
||||||
"a successfully verified title resume emits checkpoint.restored exactly once")
|
"a successfully verified title resume emits checkpoint.restored exactly once")
|
||||||
|
|||||||
Reference in New Issue
Block a user