④T1: 存档 v6 生涯终身计数域——SAVE_FORMAT_VERSION 5→6(版本史注释随批换代)+meta 两新域 adventure_total〔总局数,开局即计含放弃与失败〕/adventure_cleared〔通关局数,三关全通才计 1——判定字段 summary.cleared 现成直用〕+迁移函数 _migrate_v5_v6(沿现役迁移链 _init 自注册;旧档缺键 _normalize 按默认补零、只增不改=幂等,v3→v4 先例)+只读访问器 adventure_total()/adventure_cleared()+写入单口=apply_adventure_summary 收束口头部自增(三结局同经本口恰计一次,禁第二写入点——契约 2;迁移链陈旧注释 v1→v3/v1→v6 顺手校正)+域清单 owner 注记(生涯.md)+registry save_file 条目 v4→v6 换代(v5 批漏账补齐)+冒烟随批:硬断言「写盘 version==5」两处改 6(迁移链段+v1 旧档段)+①往返段收束自增恰一/往返相等两断言+②新档段缺省全零断言+⑬新增段(v5 旧档升 v6 缺省补零且既有余额原样/升档幂等写盘再载零漂移/通关局 cleared+1 夹具)——冒烟 821/0(基线 814+7)

This commit is contained in:
DKChan committed 2026-10-07 08:35:47 +08:00
1 parent cf2aa03f87
commit 669c1c7d01
3 files changed
+75 -12

No files matched your search

+36 -6
View File
@@ -4655,7 +4655,7 @@ func _smoke_battle_log_view() -> void:
# ---- 存档序列化冒烟(SaveManager——读写往返/新档合法/余额拒花/锻造出域拒/高版本拒读禁写/
# 损坏兜底/迁移链/域界干净/摘要 cap/摘要集成)。测试文件统一 user://smoke_save_*.json 隔离,
# 损坏兜底/迁移链/域界干净/摘要 cap/摘要集成/生涯终身计数域 v6)。测试文件统一 user://smoke_save_*.json 隔离,
# 段首清场+段尾清理——绝不碰 user://save.json(红线)。
func _smoke_save() -> void:
print("--- SaveManager 存档读写/迁移/兜底 ---")
@@ -4665,7 +4665,8 @@ func _smoke_save() -> void:
"user://smoke_save_corrupt.json", "user://smoke_save_migrate.json",
"user://smoke_save_domain.json", "user://smoke_save_cap.json",
"user://smoke_save_integration.json", "user://smoke_save_breakpoint.json",
"user://smoke_save_v2compat.json"]
"user://smoke_save_v2compat.json", "user://smoke_save_v5upgrade.json",
"user://smoke_save_v5legacy.json"]
var stale: Array = []
for p in paths:
stale.append(p)
@@ -4687,6 +4688,8 @@ func _smoke_save() -> void:
"apply 原语入账(钥石 7+3、通关次数 1/1/0)")
check(w.boss_first_kill_marked(1) and w.boss_first_kill_marked(2) and not w.boss_first_kill_marked(3),
"首杀标记随通关关卡置位(1/2 有、3 无)")
check(w.adventure_total() == 1 and w.adventure_cleared() == 0,
"生涯终身计数随 apply 自增恰一(中止局:总局数 1、通关局数 0——迭代 18 卡④)")
check(w.write_to_disk(), "写盘成功")
var r = sm_script.new(paths[0])
check(r.load_from_disk(), "读回成功")
@@ -4697,6 +4700,7 @@ func _smoke_save() -> void:
check(r.clear_count(1) == 1 and r.clear_count(2) == 1 and r.clear_count(3) == 0, "通关次数表往返相等")
check(r.boss_first_kill_marked(1) and r.boss_first_kill_marked(2) and not r.boss_first_kill_marked(3),
"首杀标记往返相等")
check(r.adventure_total() == 1 and r.adventure_cleared() == 0, "生涯终身计数往返相等(1/0)")
var s0: Dictionary = r.recent_summaries()[0]
check(r.recent_summaries().size() == 1 and int(s0.get("master_seed", -1)) == 99 and int(s0.get("keystone_total", -1)) == 3,
"冒险摘要往返相等(1 条,seed 99)")
@@ -4706,6 +4710,8 @@ func _smoke_save() -> void:
check(f2.load_from_disk() and f2.keystone_balance() == 0 and f2.forge_level("atk") == 0
and not f2.item_unlocked("anything") and f2.clear_count(1) == 0 and f2.recent_summaries().is_empty(),
"新档合法态(load true + 全零默认)")
check(f2.adventure_total() == 0 and f2.adventure_cleared() == 0,
"新档生涯终身计数缺省全零(迭代 18 卡④两新域——白板存档完整可玩)")
# ③ spend 拒绝:余额不足 false 且零副作用(钥石只增不减——消费除外)
var f3 = sm_script.new(paths[2])
@@ -4748,8 +4754,8 @@ func _smoke_save() -> void:
fm.store_string(JSON.stringify({"version": 0, "keystone_old": 12}))
fm.close()
check(f7.load_from_disk() and f7.keystone_balance() == 12, "v0 旧档经迁移链入账(keystone_old 12 → 余额 12)")
check(f7.write_to_disk() and int(JSON.parse_string(FileAccess.open(paths[5], FileAccess.READ).get_as_text()).get("version", -1)) == 5,
"迁移链 0→1(测试钩)→5(生产) 写盘 version == 5(v5=天赋树域,迭代 12 T9——v4 被同调批占用顺延披露)")
check(f7.write_to_disk() and int(JSON.parse_string(FileAccess.open(paths[5], FileAccess.READ).get_as_text()).get("version", -1)) == 6,
"迁移链 0→1(测试钩)→6(生产) 写盘 version == 6(v6=生涯终身计数域,迭代 18 卡④——v5=天赋树域迭代 12 T9)")
# ⑧ 域界干净:meta 永久进度域不含局内资源键(行动点/RAP/GP/道具库存/生命账本不入档——契约 2;
# 迭代 8 卡①修订:扫描收窄到 meta 域——断点域=冒险进行中瞬态快照,生命/GP 按拍板面入域豁免)
@@ -4846,8 +4852,8 @@ func _smoke_save() -> void:
fold.close()
check(fo.load_from_disk() and fo.keystone_balance() == 4 and not fo.has_breakpoint(),
"v1 旧档迁移 v4:断点域补空(合法空态,无断点域不炸)")
check(fo.write_to_disk() and int(JSON.parse_string(FileAccess.open(paths[9], FileAccess.READ).get_as_text()).get("version", -1)) == 5,
"v1 旧档迁移后写盘 version == 5(v1→v5 链式——天赋树域 2026-10-04)")
check(fo.write_to_disk() and int(JSON.parse_string(FileAccess.open(paths[9], FileAccess.READ).get_as_text()).get("version", -1)) == 6,
"v1 旧档迁移后写盘 version == 6(v1→v6 链式——生涯终身计数域 2026-10-07)")
# ⑫ v2 旧档→v3 兼容(迭代 10 g 裁——semver 向后兼容:旧断点零丢失、新代码可续玩)
var fv2 = sm_script.new(paths[10])
@@ -4866,6 +4872,30 @@ func _smoke_save() -> void:
check(adr_v2.resume_adventure(fv2.breakpoint_snapshot(), {"hero_override": {}}),
"v2 旧断点续玩受理(缺 bonus 附随件走常规重建路径——g 裁 semver 口径)")
# ⑬ 生涯终身计数域(迭代 18 卡④):v5 旧档升 v6 缺省补零 + 升档幂等 + 收束自增恰一
#(真实旧档形态=计数域两键不存在;升档只增不改——既有 meta 值零触碰,存档红线 S2 断言面)
var f13 = sm_script.new(paths[11])
var fv5u := FileAccess.open(paths[11], FileAccess.WRITE)
fv5u.store_string(JSON.stringify({"version": 5, "meta": {"keystone_balance": 9}}))
fv5u.close()
check(f13.load_from_disk() and f13.adventure_total() == 0 and f13.adventure_cleared() == 0
and f13.keystone_balance() == 9,
"v5 旧档升 v6:计数域缺省补零且既有余额原样(迁移只增字段不改值)")
check(f13.write_to_disk() and f13.load_from_disk()
and int(JSON.parse_string(FileAccess.open(paths[11], FileAccess.READ).get_as_text()).get("version", -1)) == 6
and f13.adventure_total() == 0 and f13.adventure_cleared() == 0,
"升档幂等:v6 档写盘再载零漂移(重复迁移零副作用)")
var f13b = sm_script.new(paths[12])
f13b.load_from_disk()
f13b.apply_adventure_summary({"result": "completed", "cleared": true, "levels_cleared": 3,
"stopped_at": 0, "keystone_total": 0, "master_seed": 1})
check(f13b.adventure_total() == 1 and f13b.adventure_cleared() == 1,
"收束自增恰一·通关局(三关全通 cleared=true:总局数+1 且通关局数+1)")
f13b.apply_adventure_summary({"result": "abandoned", "cleared": false, "levels_cleared": 1,
"stopped_at": 2, "keystone_total": 0, "master_seed": 2})
check(f13b.adventure_total() == 2 and f13b.adventure_cleared() == 1,
"收束自增恰一·中止局(放弃局:仅总局数+1,通关局数不动)")
# 段尾清理(测试文件绝不残留)
for p in stale:
if FileAccess.file_exists(p):