Files
Genarrative/docs/technical/PUZZLE_RUNTIME_REAL_LEADERBOARD_2026-04-27.md
高物 89ab1bf1c0
Some checks failed
CI / verify (push) Has been cancelled
1
2026-04-29 23:10:43 +08:00

5.0 KiB
Raw Permalink Blame History

拼图运行时真实排行榜落地说明

更新时间:2026-04-27

1. 背景

当前拼图关卡结束弹窗里的排行榜数据并不是真实用户成绩。

问题根因有两层:

  1. 前端本地运行态 src/services/puzzle-runtime/puzzleLocalRuntime.ts 在通关后会直接拼出几条演示昵称数据。
  2. server-rs 拼图运行时虽然已经预留了 leaderboardEntries 字段,但 module-puzzlespacetime-clientapi-server 还没有真实成绩表与聚合过程,因此接口层长期返回空数组。

这导致用户在结算弹窗里看到的是“看起来像真实排行榜,但实际上是本地假数据”的结果,和平台“真实用户数据”要求冲突。

2. 本次目标

本次改动只解决一个明确问题:

  1. 拼图关卡结束后的排行榜必须使用真实用户成绩。
  2. 删除现有前端演示昵称、演示耗时等假数据。
  3. 不在 UI 中默认塞入任何说明型占位文案。

3. 本次落地边界

为了控制改动范围,本次不把整套拼图运行态全部迁回后端,而是在当前“本地棋盘运行态”基础上补一条真实成绩回写链路:

  1. 拼图拖拽、交换、合并、拆分、通关判定,仍然沿用当前本地运行态。
  2. 玩家一旦通关,前端立即把当前关卡成绩提交到 server-rs
  3. server-rs 将成绩写入 SpacetimeDB 成绩表,并返回该关卡的真实排行榜。
  4. 结算弹窗只展示后端返回的真实成绩榜单,不再混入本地演示数据。

这意味着:

  1. 这次不是“完整后端裁决化”。
  2. 这次是“先把排行榜真相源收回后端”,满足真实成绩展示要求。

4. 成绩真相源设计

新增拼图成绩表,按“关卡作品 + 网格规格 + 用户”维护最佳成绩。

正式开始拼图关卡时还必须同步用户玩过作品明细:

  1. 作品自身统计继续更新 puzzle_work_profile.play_count
  2. 已登录用户写入 profile_played_worldworld_key = puzzle:{profile_id}
  3. profile_id 保存拼图作品号,world_type = PUZZLE
  4. world_title 使用关卡名,world_subtitle 使用作品摘要,owner_user_id 使用拼图作者用户 ID。
  5. 下一关切换到新 profile_id 时按同一规则再次写入。
  6. 排行榜提交携带的 elapsedMs 是本关可观测时长,后端按增量累计到 profile_dashboard_state.total_play_time_ms

建议字段:

  1. entry_id 唯一主键。
  2. profile_id 当前关卡作品 profile_id
  3. grid_size 当前成绩对应的拼图网格规格,至少区分 3x34x4
  4. user_id 成绩所属真实用户 ID。
  5. nickname 成绩展示昵称。当前优先使用提交时的用户显示名快照。
  6. best_elapsed_ms 用户在该关卡该规格下的最佳通关耗时。
  7. last_run_id 最近一次刷新该最佳成绩的运行态 run_id
  8. updated_at 最后一次刷新时间。

5. 排行榜口径

排行榜必须遵守下面规则:

  1. 只读真实成绩表。
  2. 同一用户在同一 profile_id + grid_size 下只保留 1 条最佳成绩。
  3. 排序按 best_elapsed_ms 从小到大。
  4. 同耗时按 updated_at 更早者优先,再按 user_id 稳定排序。
  5. 返回前 N 条,当前阶段固定 10 条即可。
  6. 当前用户如果在榜单内,需要标记 isCurrentPlayer = true

6. 接口落地

新增拼图排行榜提交接口:

POST /api/runtime/puzzle/runs/:runId/leaderboard

请求体至少包含:

  1. profileId
  2. gridSize
  3. elapsedMs
  4. nickname

返回体采用现有 PuzzleRunResponse,但要求:

  1. run.currentLevel.leaderboardEntries 返回真实榜单。
  2. run.leaderboardEntries 同步返回当前关卡真实榜单,方便现有结算弹窗兼容读取。

7. 前端改动规则

  1. 删除 puzzleLocalRuntime.ts 中本地演示榜单构造逻辑。
  2. 本地通关后,运行态只保留真实通关耗时,不再生成假昵称榜单。
  3. 结算弹窗显示时,如果真实榜单尚未回写完成,可以显示加载态;但不能回退到假数据。
  4. 下一关开始后,当前关卡榜单状态清空。

7.1 2026-04-29 与前端拖动裁决的对齐

当前拼图拖动、合并、拆分与通关判定完全由前端运行态负责,后端排行榜接口只负责真实成绩表与榜单聚合:

  1. 排行榜提交不得依赖 SpacetimeDB 里的旧棋盘快照已经通过后端拖动接口进入 cleared
  2. 后端仍校验 profileIdgridSize、昵称和成绩,并把当前提交写入真实成绩表。
  3. 后端响应里的 leaderboardEntries 是唯一需要合并回前端当前 run 的数据。
  4. 前端不能用排行榜响应里的旧棋盘快照覆盖本地拖动后的棋盘,否则会把刚刚通关的前端状态回滚。

8. 测试要求

至少覆盖:

  1. 通关后不会再生成本地假榜单。
  2. 同一用户重复通关同一关卡时,只保留更优成绩。
  3. 不同用户成绩会按耗时正确排序。
  4. 3x34x4 不混榜。
  5. 下一关开启后上一关榜单不会污染新关卡。