MINESWEEPER 系统设计
原型文档 · 非正式发布返回游戏 ↗
规则与架构设计 v0.8.1

每日四榜规则与数据契约 v0.8.1#

2026-09-13 · 本轮已确认:每个UTC日期设置初级、中级、高级三个单项榜,再加综合榜,每榜Top10。设计态,未实现在线服务。配套规则1.5在线DDL

1. 目的与玩家体验#

玩家完成一档就能追求该档名次,不必完成三档才看到竞榜目标;擅长某档可冲单项榜,三档都稳定可冲综合榜。四榜独立比较,同一账号可同时出现在四榜,每榜最多一行。原初→中→高解锁顺序继续适用,单项榜独立不意味着可跳过关卡解锁。

board_kind名称资格排序值对应录像
easy初级榜当日初级有效正式无伤完成本人初级最佳elapsed_ms1段
medium中级榜当日中级有效正式无伤完成本人中级最佳elapsed_ms1段
expert高级榜当日高级有效正式无伤完成本人高级最佳elapsed_ms1段
overall综合榜当日三档有效正式无伤成绩齐全三档各自最佳elapsed_ms之和3段

单项排序键 (elapsed_ms, finished_at, entry_no);综合排序键 (sum(elapsed_ms), max(finished_at), entry_no),全部升序。时间/顺序来自服务器;单项名次不求和、不平均、不作为综合入榜条件。缺档综合状态为incomplete,不以0或极大数代替。三档可以跨设备、跨多次尝试组合,始终按账号/日期关联。

例:你50+180+300=530秒,玩家B40+150+400=590秒。B初、中领先,你高级和综合领先。即使你的三档单项名次都未进Top10,也不能直接判定综合无法进入Top10,因为各单项领先你的人可能是不同人。

“最佳”分两层:已裁决但录像尚未核验的个人潜在最佳用于预选;已核验完整过程的最佳用于公开榜。潜在改善验证失败不能替换已经公开的旧成绩。无效、受伤、历史练习不得成为任意四榜候选。

2. 一次改善如何更新四榜#

每完成一次新的无伤正式成绩:服务端更新潜在最佳;判断该档单项榜候选;若三档齐全,同时判断综合榜候选。只有更优的排序键才替换对应档最佳;相同毫秒但较晚完成不替换旧段。

  • 初级还未打完另外两档:可以进入初级榜,综合显示“1/3,待完成”。
  • 中级刷快:仅中级榜和综合榜需要重新计算;初级/高级榜的名次数据保持原值。
  • 中级更快但还没验完录像:继续展示旧的已验证中级/综合成绩,并显示“新纪录验证中”。
  • 同一新段同时满足单项和综合候选:上传/核验一次,两榜共享该game_id的segment。
  • 单项可先入榜;综合缺其他段证据时保持pending,不回滚已经合法发布的单项榜。
  • 综合入榜后,即使其中某段不是单项Top10,该段也必须公开可回放;不能只保存单项Top10段。
  • 日期观看锁/退榜作用于该日四榜,观看后不能换榜继续刷;此前已发布记录保留。日奖励仍按日期/难度唯一发放,名次和重试不新增经验。

名次提高反馈优先展示“中级提升12秒,中级第8→第5,综合第9→第7”;若未上Top10,展示“距当前第10名还差X秒”并标这是当前门槛,不能保证最终入榜。毫秒相同时结合破同分规则说明;榜未满不显示虚构差距。服务端只给可计算且已知的个人排名/差距,数据不足显示待核验,不假定全球精确名次。

3. 录像复用与跨设备#

本版沿用此前明确的“先报成绩、成为任一榜候选才上传官方压缩录像”路径;上一轮讨论的服务器全程临时存证是可选改案,未默认启用。个人收藏录像、录制草稿及其元数据仍仅本机。

单项候选只需要自己这一段;综合候选需要三段。已经在任何单项/综合候选流程核验过的段按game_id复用,不重复传、不按榜复制正文。上传权属于账号,对同账号的来源设备开放;不要求创建综合提交的设备持有所有文件。服务器向该账号在线设备通知缺失attempt IDs,各设备匹配自己的官方草稿并领取/使用归属受控授权;跨账号不可协助上传。

正式段结束后,来源设备可调用record-manifest登记官方参榜段的压缩格式、双hash、字节数和持有设备ID,正文仍不上传,不扫描个人收藏。manifest写入daily_attempts的record_device_id/record_manifest_json,绑定该账号已结束正式attempt及服务器锚点;同一记录只能幂等登记,不能改hash替换。这样另一设备发起综合预选时,可用 {attempt_id,registered_manifest:true} 引用已登记清单,无需读取原设备文件。没有登记或丢失草稿时只显示awaiting_source_manifest,不签发未绑定hash的上传授权;该状态是派生的参榜准备状态,尚不创建完整候选提交。

来源设备未在线时显示“等待初级录像上传”等分段状态,可在截止前回原设备继续;没有证据不能先入榜。如果所有缺失段已经通过单项榜上传核验,则换设备计算综合排名完全不依赖旧设备上线。

四榜并不消除所有跨端缺录像情况:某段从未进入单项候选,却在之后组成综合Top10,仍需原设备上传该段。原设备不可用又没有可用官方草稿时,无法补出数据;当前合格单项排名和私人XP不受影响。若要求这一情况也无感接续,应另将正式每日改为服务器临时存证,不能只增加四榜就声称已解决。

同一段可能被两个进行中的提交引用:先通过短写事务检查 restricted_segments.game_id 是否已存在合法段,已存在则直接复用;并发收到相同内容只保留一份最终Redis正文,同game不同hash拒绝。所有权/地图/日期/难度/内容hash检查不可省略;按账号做总字节限流,不能每榜各赠一份无限额度。

4. API差异#

下列替代v0.5.0对应榜单路由;所有敏感写接口沿用登录/CSRF/Idempotency-Key约束。

API契约
GET /daily/:date/leaderboards{date,status,generation,boards:[{board_kind,count,cutoff_ms,cutoff_sort_key}]},返回四榜摘要,不含棋盘
GET /daily/:date/leaderboards/:board_kind单榜Top10;响应含board_kind/common generation,每行total_ms(单项就是该档用时)及1或3段引用
GET /daily/:date/meboards:{easy,medium,expert,overall}各自state/rank?/gap_ms?/submission_id?,潜在与已核验best分开;没有资格不得伪造全球名次
POST /daily/:date/leaderboards/:board_kind/scores{attempts,replay_manifest,client_total_ms?,allow_public_replay:true};单项只允许对应一个key,overall强制easy/medium/expert三个key;只引用本人服务器attempt
GET /daily/:date/replay-sets/:user_id?board_kind=…对所选当前/冻结榜提供1或3段元信息;正文再走watch授权
POST /online/games/:id/record-manifest本人已完成正式段,登记格式/双hash/尺寸;device从会话取。无正文、无个人收藏信息;同段不同清单409
GET /daily/replay-uploads/pending本人未到期授权/缺失attempt元数据;任何本人已登录设备可查并上传其持有的段,不暴露个人收藏目录
POST /watch/sessions回放资源另带board_kind/date/generation;服务端检查该榜确引用此段,不能靠另一榜同rank编号越权
POST /daily/:date/withdraw该账号该日四榜一起撤公开;历史仅注记/撤播放,不重排

board_kind闭合枚举 easy|medium|expert|overall,缺失/未知422,不静默落到综合榜。每账号/日期/榜最多一个pending提交(最多四个),候选不占名次;HTTP幂等scope包含board_kind。上传pending的GET必须鉴权且no-store。WS仅通知 daily.changed(common generation、changed_boards)和本人缺失记录,不广播私人内容。

5. SQLite与原子发布#

无需为四榜建四套表。daily_rankings增加board_kind,主键 (date,generation,board_kind,user_id),名次唯一键 (date,generation,board_kind,rank);rank1..10。单项仅对应一个game_id非空,综合三个非空且不同,CHECK强制字段形状;同日期/档次/账号、sum/max的业务正确性由事务服务校验。

daily_attempts增加record_device_id与record_manifest_json,仅记录官方段上传清单及来源设备;正文仍按候选授权上传,账号删除清这两个字段。daily_score_submissions也加board_kind并使用相同1/3段形状;pending唯一键扩大到user/date/board。daily_rank_annotations绑定date/generation/board_kind/rank,初级第1不等于综合第1。restricted_segments.game_id UNIQUE使不同榜共享一份录像。

daily_boards仍是日期级生命周期;四榜共享一个generation发布指针和同一ends_at,避免四个独立截止任务留下半冻结状态。发布新generation时,在同一事务中重算受影响榜、复制未变化榜的行,再切指针。每榜最多10行,整代最多40行;不能只写改动榜然后切指针让其他三榜消失。读取四榜面板必须固定同一generation,前端不能混用缓存旧榜。

每次日内撤回/证据失效,重新计算所有引用受影响段的榜(含综合),并从已核验候补补位。若只是改进一档,通常影响该单项和综合。只有各榜各自完整证据齐全的结果可发布,不要求四榜同时有10名。

SQL护栏限制只能向未发布的下一generation写入;已发布行不可改/删/追加。日切后四榜统一冻结到最后generation,禁止迟到验证发布或日后递补。历史复核给受影响榜各自加注记并撤同一段播放,避免从另一榜绕过撤回。

6. 存储预算与清理#

最终每日期最多 3×10 + 10 = 40 个榜单席位(可重叠用户),录像引用最多 3×10×1 + 10×3 = 60 段;以game_id去重后最多60份不同过程。不能只存三个单项榜的30段,否则综合榜独有的段会丢失。

定义:S为实测平均压缩段字节,R为公开保留天数;最终公开原始记录量上界估算 60×S×R。若S=100KiB、R=30天,约176MiB,仅为纸面示例 [待测试];不含回放投影块、候选暂存、活跃记录、索引、WAL、备份、历代排名元数据。这是最终四榜引用的正文估算,不是Redis内存总量:曾入榜后被替换、或跨库回滚前已提升TTL的正文也会自然保留到public_until,容量必须按每天曾提升TTL的不同段数规划。

所有录像正文和播放块移出SQLite,只保留key/hash/字节数/期限元数据。Redis写入即带绝对TTL:上传暂存到授权截止,未公开候补到日切,准备公开的body提升到原日结束后30天的固定public_until。观看/跨榜引用/重试不滚动续期;日切冻结不再遍历所有key延长或清理。

为满足“不写录像到期清理job”,曾准备公开的旧段也保留到该固定截止,掉榜不马上删。最终四榜引用最多60段,但Redis可能存更多曾入榜/孤儿正文;到期全部由Redis自然回收。主动撤公开先DB撤权再UNLINK已知key,播放块至短TTL自行回收。历史名次与元数据仍保留,不需job逐条写expired。完整时序、故障和容量见Redis部分

7. 观看与范围#

本次只修改榜单组织;当日观看仍沿用三档确认完成门槛及整日观看锁,不把“初级榜已入榜”当成观看高级答案的资格。单项进榜不需要三档完成,观看权限与入榜权限是不同条件。历史四榜录像均取消通关门槛,不锁今天。若以后改为逐档观看/锁定,必须同时设计综合成绩采用观看前段的规则,不能只放宽一个播放按钮。

服务器生成每日、零伤条件、断网不补正式点击、私人XP补差、SQLite单实例、Redis限每日榜单缓存与官方录像、本版无PVP继续生效。

8. 版本记录#

版本日期变化
0.8.12026-09-13收紧录像大小/事件上限;双hash上传前去重;补流式拒收、配额、慢连接及验证资源保护
0.8.02026-09-13Redis限每日榜单缓存与官方录像TTL存储;录像正文移出SQLite,不设录像到期清理job;补跨库故障与持久化边界
0.7.02026-09-13移除直播入口/协议/三张专用表;观看仅限每日公榜录像,保留四榜与私人同步;整理审阅范围
0.6.02026-09-13四榜独立排名;单项早参榜、综合不依赖单项Top10;共享录像、共同冻结、跨端分段提交与容量边界