Important
RFC status: Draft
Scope: F8 Game History 端到端信息架构、数据模型与 Mod/API/backend contract
Implementation gate: 完成服务端源码核对与独立 red-team review,修订 RFC 并获得维护者确认后,才拆分和实施子任务。
Context
F8 Game History 目前把 Runs 与 Ghost 做成两个割裂的数据视图。目标应是统一的对局历史中心:
以 Run 为主时间线,查看该 Run 自己打过的本地 PVP。
在对应 Run / Day 下看到其他玩家后来挑战该构筑产生的 Ghost 对局。
Ghost 页保留全局收件箱能力,但可反向跳到来源 Run / Day。
上传云端后,分页拉取“BPP 云端已经记录到的相关对局”,而不是只展示本机固定数量的最近记录。
这里的“全部”只指 BPP 云端已成功接收并保存的对局 ;不承诺覆盖未安装 BPP、上传失败或官方服务未向 BPP 暴露的比赛。
Current state / evidence
Ghost 模式会隐藏 Runs 区域,两个视图没有关联入口:src/BazaarPlusPlus/Game/HistoryPanel/Ui/HistoryPanelUiToolkitView.cs:336-387。
选择 Run 后只查询 source = 'LOCAL' 的 battle:src/BazaarPlusPlus/Game/HistoryPanel/Storage/HistoryPanelRepository.cs:117-160。
Ghost 入库时写入 run_id = NULL,schema 也约束 Ghost 不得使用现有 run_id:src/BazaarPlusPlus/Game/HistoryPanel/Storage/HistoryPanelRepository.cs:367-405、src/BazaarPlusPlus.Storage/RunLog/RunLogSchema.cs:100-145。
当前 Ghost API 只有 player_account_id + limit,上限 200;返回 DTO 没有 origin Run / battle / day:src/BazaarPlusPlus.ModApi/Clients/GhostBattleClient.cs:24-73、src/BazaarPlusPlus.ModApi/Clients/GhostBattleClient.cs:169-213。
F8 本地加载固定为最近 40 Runs / 100 Ghost battles:src/BazaarPlusPlus/Game/HistoryPanel/HistoryPanelCoordinator.cs:118-155。
未下载 Ghost 元数据 14 天后会被本地软删除:src/BazaarPlusPlus/Game/HistoryPanel/Storage/HistoryPanelRepository.cs:519-537。
Run bundle 已上传双方 hand / skills snapshot,可供服务端建立稳定构筑指纹:src/BazaarPlusPlus/Game/RunLogging/Upload/RunBundleUploadStore.cs:174-245、src/BazaarPlusPlus/Game/RunLogging/Upload/RunBundleUploadStore.cs:438-455。
云端实现位于独立仓库 bazaarplusplus-server;本仓库只能验证 Mod/API 客户端:README.md:52-56。
Proposed decision: end-to-end F8 optimization
该需求不能定义成单纯 UI 调整。若目标包含“上传后按 Run 拉回后来发生的 Ghost 挑战”,后端必须参与 :
当前 Ghost 元数据没有来源 Run 标识,客户端无法完整、稳定地归属。
客户端只能对已下载 replay 做指纹匹配,无法覆盖 metadata-only 记录。
固定 100/200 条限制无法支持完整历史,需要 cursor pagination。
Ghost payload 现有 RunId 来自挑战者上传的 Run,不等于本地玩家被挑战构筑的来源 Run,不能复用:src/BazaarPlusPlus/Game/HistoryPanel/Ghost/GhostBattleSyncService.cs:265-315。
本父 Issue 覆盖完整产品与数据链路;确认服务端仓库后,在那里建立后端子 Issue,并从本 Issue 链接依赖。
Proposed domain model
Origin Run
└─ Origin Battle / Day Build
└─ 0..N Ghost Challenges
不要重载现有 battles.run_id。该字段继续表示本地/上传方真实 Run;Ghost 来源归属使用独立、可审计的派生关系,例如:
ghost_origin_links
- ghost_battle_id
- owner_account_id
- origin_run_id
- origin_battle_id
- origin_day
- fingerprint_version
- match_status # matched / ambiguous / unmatched
- match_method
- matched_at_utc
来源关系属于 BPP 的派生匹配结果,不应伪装成游戏官方提供的 truth。
Stable build fingerprint v1
包含:hero;hand / skills 分区;稳定 template ID;size/type;section/socket;tier;enchantment;重复卡数量。
忽略:运行时 instance ID、显示名、本地化文本。
day、level、victories、prestige、时间只用于缩小候选和消歧,不替代构筑内容。
只有唯一、精确、高置信匹配才写入 matched;多候选保留 ambiguous。
支持先上传来源 Run、后上传 Ghost,以及相反顺序;两条路径均需幂等回填。
Proposed F8 information architecture
Runs — primary timeline
Run 行显示聚合:Ghost 7 · 5胜2负。
展开 Run 后保留原有 local battle/day。
battle/day 行显示:被挑战 ×3 · 2胜1负。
继续展开后展示挑战者、时间、结果、replay 状态。
Ghost challenge 作为来源 battle 的关联活动,不伪装成本地玩家亲自打过的普通 battle。
Ghost — global inbox
保留 All / I Won / I Lost / time filters 和 replay 操作。
已关联记录显示 来自这局 · D13,可跳到 Runs 对应位置。
unmatched / ambiguous 仍正常显示,但不提供虚假的 Run 链接。
文案使用“云端已记录的 Ghost 对局”,避免表达成官方全量历史。
API direction
建议增加 cursor pagination 和服务端确认的来源关系:
GET /ghost-battles
? player_account_id = ...
& origin_run_id = ...
& cursor = ...
& limit = 50
响应记录新增:
origin_run_id
origin_battle_id
origin_day
origin_match_status
origin_match_method
服务端也可提供按 Run 查询的专用 endpoint;最终形式在读取 bazaarplusplus-server 后确定,但客户端不得在每次打开 F8 时重新猜来源。
Out of scope
从 The Bazaar 官方后端抓取 BPP 从未接收到的比赛。
用 display name、卡牌 instance ID 或 Ghost payload 的挑战者 RunId 做归属。
首版自动合并 ambiguous 记录。
删除本地 Run 时级联删除云端 Ghost;删除只能解除本地展示/缓存,不能删除他人产生的挑战事实。
Execution checklist
Phase 1 — contract and discovery
Phase 2 — backend
Phase 3 — Mod/API/storage
Phase 4 — F8 UI
Phase 5 — verification
Phase 6 — wrap-up
Acceptance criteria
Important
RFC status: Draft
Scope: F8 Game History 端到端信息架构、数据模型与 Mod/API/backend contract
Implementation gate: 完成服务端源码核对与独立 red-team review,修订 RFC 并获得维护者确认后,才拆分和实施子任务。
Context
F8
Game History目前把Runs与Ghost做成两个割裂的数据视图。目标应是统一的对局历史中心:这里的“全部”只指 BPP 云端已成功接收并保存的对局;不承诺覆盖未安装 BPP、上传失败或官方服务未向 BPP 暴露的比赛。
Current state / evidence
src/BazaarPlusPlus/Game/HistoryPanel/Ui/HistoryPanelUiToolkitView.cs:336-387。source = 'LOCAL'的 battle:src/BazaarPlusPlus/Game/HistoryPanel/Storage/HistoryPanelRepository.cs:117-160。run_id = NULL,schema 也约束 Ghost 不得使用现有run_id:src/BazaarPlusPlus/Game/HistoryPanel/Storage/HistoryPanelRepository.cs:367-405、src/BazaarPlusPlus.Storage/RunLog/RunLogSchema.cs:100-145。player_account_id + limit,上限 200;返回 DTO 没有 origin Run / battle / day:src/BazaarPlusPlus.ModApi/Clients/GhostBattleClient.cs:24-73、src/BazaarPlusPlus.ModApi/Clients/GhostBattleClient.cs:169-213。src/BazaarPlusPlus/Game/HistoryPanel/HistoryPanelCoordinator.cs:118-155。src/BazaarPlusPlus/Game/HistoryPanel/Storage/HistoryPanelRepository.cs:519-537。src/BazaarPlusPlus/Game/RunLogging/Upload/RunBundleUploadStore.cs:174-245、src/BazaarPlusPlus/Game/RunLogging/Upload/RunBundleUploadStore.cs:438-455。bazaarplusplus-server;本仓库只能验证 Mod/API 客户端:README.md:52-56。Proposed decision: end-to-end F8 optimization
该需求不能定义成单纯 UI 调整。若目标包含“上传后按 Run 拉回后来发生的 Ghost 挑战”,后端必须参与:
RunId来自挑战者上传的 Run,不等于本地玩家被挑战构筑的来源 Run,不能复用:src/BazaarPlusPlus/Game/HistoryPanel/Ghost/GhostBattleSyncService.cs:265-315。本父 Issue 覆盖完整产品与数据链路;确认服务端仓库后,在那里建立后端子 Issue,并从本 Issue 链接依赖。
Proposed domain model
不要重载现有
battles.run_id。该字段继续表示本地/上传方真实 Run;Ghost 来源归属使用独立、可审计的派生关系,例如:来源关系属于 BPP 的派生匹配结果,不应伪装成游戏官方提供的 truth。
Stable build fingerprint v1
matched;多候选保留ambiguous。Proposed F8 information architecture
Runs — primary timeline
Ghost 7 · 5胜2负。被挑战 ×3 · 2胜1负。Ghost — global inbox
来自这局 · D13,可跳到 Runs 对应位置。API direction
建议增加 cursor pagination 和服务端确认的来源关系:
响应记录新增:
服务端也可提供按 Run 查询的专用 endpoint;最终形式在读取
bazaarplusplus-server后确定,但客户端不得在每次打开 F8 时重新猜来源。Out of scope
RunId做归属。Execution checklist
Phase 1 — contract and discovery
Run、Local Battle、Origin Build、Ghost Challenge、Cloud-known的产品术语。bazaarplusplus-server当前 schema、upload、ghost query 与 artifact storage。Phase 2 — backend
Phase 3 — Mod/API/storage
BazaarPlusPlus.ModApiDTO/客户端,消费 cursor 和 origin fields。Phase 4 — F8 UI
Phase 5 — verification
./run.sh test、Debug/Release build。Phase 6 — wrap-up
Acceptance criteria