mirror of
https://github.com/bryanthaboi/gen1recomp.git
synced 2026-08-16 16:21:30 +02:00
feat: anchor first checkpoint for cold restart
This commit is contained in:
@@ -388,6 +388,47 @@ end
|
||||
|
||||
local emitRestored
|
||||
|
||||
-- Persist the current verified checkpoint as the ordinary progress anchor only
|
||||
-- when this playthrough has never had one. This is intentionally idempotent:
|
||||
-- durable checkpoint tools can make a first session resumable without turning
|
||||
-- every later checkpoint into a hidden normal SAVE. The live runtime must still
|
||||
-- match the supplied checkpoint, and the ordinary save.write veto/lifecycle
|
||||
-- remains authoritative through Game:writeSave().
|
||||
function Checkpoint.ensureNormalSave(game, checkpoint, injectedFs)
|
||||
local capability = Checkpoint.inspect(game)
|
||||
if not capability.canCapture then
|
||||
return false, capability.reason, capability.message
|
||||
end
|
||||
local validated, code, message = validate(game, checkpoint)
|
||||
if not validated then return false, code, message end
|
||||
|
||||
local info, infoCode, infoMessage =
|
||||
SaveData.selectedNormalSaveInfo(game.save, injectedFs)
|
||||
if not info then return false, infoCode, infoMessage end
|
||||
if info.exists then return true, "already_exists" end
|
||||
|
||||
local current, captureCode, captureMessage = Checkpoint.capture(game)
|
||||
if not current then return false, captureCode, captureMessage end
|
||||
if not equalData(current, validated) then
|
||||
return false, "checkpoint_not_current",
|
||||
"The active runtime changed after this checkpoint was captured."
|
||||
end
|
||||
if type(game.writeSave) ~= "function" then
|
||||
return false, "save_unavailable",
|
||||
"The active runtime cannot persist ordinary progress."
|
||||
end
|
||||
local ok, saved = pcall(game.writeSave, game)
|
||||
if not ok or saved == false then
|
||||
return false, "save_failed", "Could not create the first ordinary progress save."
|
||||
end
|
||||
local verified = SaveData.selectedNormalSaveInfo(game.save, injectedFs)
|
||||
if type(verified) ~= "table" or not verified.exists then
|
||||
return false, "save_verify_failed",
|
||||
"The first ordinary progress save could not be verified."
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
function Checkpoint.restore(game, checkpoint)
|
||||
local capability = Checkpoint.inspect(game)
|
||||
if not capability.canRestore then
|
||||
|
||||
+14
-4
@@ -941,7 +941,17 @@ local function rememberPlaythroughId(save, opts, injectedFs)
|
||||
if type(id) ~= "string" or id == "" then return opts, false end
|
||||
local version = save.version or GameVersion.get()
|
||||
local scope = playthroughScope(version, injectedFs)
|
||||
opts = opts or SaveData.loadOptions(injectedFs)
|
||||
local persisted = SaveData.loadOptions(injectedFs)
|
||||
if opts then
|
||||
-- Slot selection and opaque playthrough routing are engine-owned launcher
|
||||
-- state. A live game may carry an options snapshot from before a legacy
|
||||
-- save was promoted to slot1; writing that stale snapshot must not erase
|
||||
-- the freshly persisted routing and strand tool storage on next boot.
|
||||
opts.saveSlots = deepCopy(persisted.saveSlots)
|
||||
opts.playthroughIds = deepCopy(persisted.playthroughIds)
|
||||
else
|
||||
opts = persisted
|
||||
end
|
||||
opts.playthroughIds = opts.playthroughIds or {}
|
||||
opts.playthroughIds[version] = opts.playthroughIds[version] or {}
|
||||
local changed = opts.playthroughIds[version][scope] ~= id
|
||||
@@ -1021,18 +1031,18 @@ function SaveData.selectedNormalSaveInfo(save, injectedFs)
|
||||
or readTable(fs, staged)
|
||||
or readTable(fs, backup)
|
||||
if type(normal) ~= "table" or normal.version ~= version then
|
||||
return { savedAt = nil }
|
||||
return { exists = false, savedAt = nil }
|
||||
end
|
||||
local normalId = normal.meta and normal.meta.playthroughId
|
||||
if type(normalId) == "string" and normalId ~= "" and normalId ~= playthroughId then
|
||||
return { savedAt = nil }
|
||||
return { exists = false, savedAt = nil }
|
||||
end
|
||||
local savedAt = normal.meta and normal.meta.savedAt
|
||||
if type(savedAt) ~= "number" or savedAt < 0 or savedAt ~= savedAt
|
||||
or savedAt == math.huge or savedAt == -math.huge then
|
||||
savedAt = nil
|
||||
end
|
||||
return { savedAt = savedAt }
|
||||
return { exists = true, savedAt = savedAt }
|
||||
end
|
||||
|
||||
-- ------- meta
|
||||
|
||||
@@ -683,6 +683,9 @@ function Loader:_api(mod)
|
||||
resume = function(_, game, checkpoint)
|
||||
return Checkpoint.resume(game, checkpoint)
|
||||
end,
|
||||
ensureNormalSave = function(_, game, checkpoint)
|
||||
return Checkpoint.ensureNormalSave(game, checkpoint, loader.fs)
|
||||
end,
|
||||
},
|
||||
options = {
|
||||
define = function(_, schema)
|
||||
|
||||
Reference in New Issue
Block a user