Files
SoundGame/research/platform/01-identity.md
T
xsl 3704cd0792 research(platform): 无感用户系统 + PC/手机双端一致 三路调研(身份/键位与语音/数据同步)+ 汇总方案
- 01-identity:推荐自动匿名档案(首访 0 交互)+ Netflix 式切换器 + 配对码跨设备;passkey 因 LAN HTTP 不可用否决;审计出 7/8 游戏跨设备只 PUT 不 GET、大厅零 aria、玩家名不可朗读三个存量问题
- 02-input-parity:方向键/WASD+空格+回车(双击即时)键位(audiogames 社区惯例同构);语音双套资产路线(153 句含操作词,306 TTS/37min);MVP ~200 行 1-2 人日
- 03-data-sync:设备 token 即档案、localStorage 按 profileId 分桶(修同设备串档)、桶级 LWW + baseUpdatedAt 守卫、claim 可选认领 + PIN scrypt;公网前 8 条清单
- 00-synthesis:需求映射 + 架构收敛 + P0/P1 分期 + 3 个待拍板决策点
2026-09-09 23:09:24 +08:00

27 KiB
Raw Blame History

平台调研 01 · 无感多用户身份系统

课题:多个玩家(家庭 WiFi 手机/PC + 未来公网 game.xiangsilian.com)可区分、可保留各自进度与设置,且绝不能强迫注册。 调研日期:2026-09-09。方法:本仓库代码审计 + 业界模式检索(来源链接见 §3.6)。


0. 结论速览

主推:方案 B「自动匿名档案 + 设备档案切换器 + 配对码跨设备」——首次访问 0 输入 0 点击即自动建档(服务器静默发 token,玩家无感知),同设备多玩家用 Netflix 式档案切换,跨设备/找回用「旧设备出示 6 位配对码 → 新设备输入」;纯静态公网形态自动降级为方案 A(纯本地档案)。PIN 降级为可选的"认领"升级项(防兄弟恶作剧),passkey 留给公网化之后再评估(LAN HTTP 下技术上不可用,见 §3.3)。

三个待产品拍板的决策点见文末 §6。


1. 现状审计

1.1 服务器(web/server/server.js,310 行)

项 现状 评估
注册/登录 POST /api/register {username, pin} / POST /api/login,PIN 4~6 位数字 PIN 明文存 users.json;用户名可中文(USERNAME_RE)
Token 登录返回 24 字节随机 hex,内存 + sessions.json 永不过期、无登出端点——客户端"退出"只是删 localStorage,token 在服务器侧永久有效
进度 API GET/PUT /api/progress,Bearer token,data 自由格式但须带 summary(大厅卡片显示) 接口形状健康,可复用
并发 按文件串行 Promise 队列,tmp+rename 原子写 健康,可复用
防护 无速率限制、无 IP 封禁、HTTP 明文 HANDOVER §2.2 已自我标注"玩具级"

数据模型(web/server/data/ 实测):

users.json     { "uuu": "0858" }                        // username -> pin 明文
sessions.json  { "<48位hex>": "uuu" }                    // token -> username,无过期时间
uuu.json       { "_settings": {voice, speed, bgm, guide} } // 一用户一文件 {<game>: data}

1.2 客户端(web/index.html + 各游戏)

  • 登录态:localStorage soundgame.auth = {username, token},大厅与 7 个游戏各自直读此 key。
  • 游客态今天就能玩:不登录 → 全部 8 款游戏可玩,进度写各游戏自己的 localStorage key,设置写 soundgame.settings。服务器同步仅在登录时进行(PUT 静默跳过)。即"本地优先 + 可选账号"架构今天已经成立,只是"账号"这一半摩擦大(要起名+想 PIN+输两遍)且公网不可用。
  • 公网形态(docs/deploy.md):Nginx 纯静态、不跑任何 API;大厅检测 https://*.xiangsilian.com 即隐藏整个用户面板(index.html PUBLIC_STATIC)→ 公网访客全部是孤立的 localStorage 游客。deploy.md 已写明公网开放 API 的前置(127.0.0.1 绑定、只反代 /api/、limit_req、PIN 哈希、token 过期)。

localStorage key 全量清单(多档案方案的现实约束,见 §4):

key 归属 说明
soundgame.auth 大厅+7游戏 登录 token
soundgame.settings engine/settings.js 四项全局设置
soundgame.progress.rpg / .dungeon / .radio 各游戏 存档(命名不统一)
soundgame.rhythm.progress rhythm 存档
soundgame.simon.best / soundgame.space.best / soundgame.escape.stats 各游戏 最高分/统计
soundgame.rpg.tutIntroSeen / soundgame.escape.guideDone / soundgame.space.drillDone 各游戏 一次性教学标记
(storyline 无本地 key) — 无本地持久化

1.3 审计发现的关键缺口(新方案必须解决)

  1. 跨设备恢复只做了 1/8:只有 radio 在"本地无存档 + 已登录"时 GET 服务器存档(radio/game.js:325-342);rpg/dungeon/rhythm/escape/simon/space 只 PUT 不 GET——已登录用户换设备,7 款游戏进度照样丢(服务器上有副本却不拉回)。
  2. 设置是"设备级"而非"玩家级":一台手机一个 soundgame.settings;家里视障玩家 2.2x 语速与普通玩家 1.0x 共用一部手机时互相覆盖(虽然登录后会拉 _settings,但游客态无档案可分)。
  3. 大厅无读屏适配:index.html 零 aria 属性;登录表单是 JS 动态生成、仅 placeholder 无 <label>(读屏不友好);设置面板倒是用了正规 <label for>+<select>。卡片是真实 <a>,读屏可列出。→ 身份 UI 重做时必须补 ARIA 与可听化。
  4. "退出"不失效 token、sessions.json 无清理,长期只增不减。

1.4 语音技术栈对身份 UI 的约束(重要)

全仓库无 speechSynthesis 使用——所有语音都是离线 TTS 预生成音频资产(Qwen3-TTS 流水线),且设计规范禁止数字碎片拼接(数字按档位整句预生成)。推论:任意自定义玩家名无法被"说出来",档案名要么从预生成词表选、要么用预生成的"玩家一/二/三",要么为大厅引入浏览器 SpeechSynthesis(与整体录音质感不一致,需产品拍板,见 §6-D3)。


2. 业界模式调研发现

2.1 模式总表

模式 代表 首访摩擦 进度保留 换设备 启示
匿名设备账号(首访自动建档,后可升级) Firebase Anonymous Auth、Unity Authentication、腾讯 CloudBase 匿名登录 0 云端 需升级/绑定 业界标准做法:UID 服务端生成、linkWithCredential 升级同 UID 数据不迁移;官方警告匿名账号丢失凭证即孤儿数据,建议 30 天不活跃清理;升级提示放在"账号开始有价值的那一刻"
本地优先 + 可选账号链接 Wordle(原版→NYT)、Poki 0 localStorage/cookie 登录后云同步 Wordle 原版全 localStorage;迁移 NYT 时靠"旧域名最后访问一次"携带本地数据;后加免费 NYT 账号才跨设备。Poki FAQ 明说:无账号进度存浏览器 cookie,建账号可跨设备。SoundGame 现状就是这个模式的前半段
档案切换器(一设备多档案) Netflix、Nintendo Switch、Jellyfin/Emby 的诉求贴 选头像 1 击 每档案独立 档案级 家庭共设备场景的标准解;Switch 儿童档可选 PIN;自托管社区(Jellyfin #7059)反复索要同款
Passkey 无用户名一键建档 web.dev/Auth0/FIDO 1 击+生物识别 云端 iCloud/Google 密钥链自动同步 理论上最优雅;但见 §2.3 硬约束
QR/配对码跨设备 Jackbox(房间码+QR)、Google TV、WhatsApp Web — — 扫码/输码 "已有身份的设备给新设备发短期票据"是无邮箱场景的换设备通行做法
纯游客不留进度 chess.com Guest Play 0 不留(无 ELO 无历史) 不支持 反面教材:我们的核心诉求就是"游客也要留进度",不采用

2.2 无邮箱无手机号的"找回"业界处理

  • passkey 多设备同步:丢单台设备无忧(iCloud Keychain / Google Password Manager 同步);但全家桶全丢时,苹果恢复 iCloud 钥匙串本身可能要短信验证——没有邮箱/手机号时基本无解(sixcolors、Authsignal、r/Passkeys 讨论)。
  • 恢复码:通行替代(一次性长码,抄在纸上)。对家庭游戏场景等价于"配对码的长期版"。
  • 业界现实:小休闲产品(Wordle 时代、Poki 游客)干脆不支持找回,丢就丢;有价值的账号才给恢复通道。
  • 对本项目的翻译:找回 = 「另一台仍登录的设备出示配对码」(Jackbox/WhatsApp 模式),加一层可选 PIN 认领兜底"旧设备已刷机"的情况;不做邮箱/手机号。

2.3 Passkey 的硬约束(决定性发现)

WebAuthn 要求 Secure Context(HTTPS 或 localhost)(W3C WebAuthn L3、MDN)。http://192.168.x.x:8000 这种局域网 IP + HTTP 不满足,navigator.credentials 直接为 null(Corbado、StackOverflow #55971593、mkcert #210)。家庭 WiFi 主路径(serve.sh 打印的局域网地址)天然用不了 passkey;变通(ngrok/mkcert 全家设备装 CA)对家庭用户不现实。另:本项目服务器零依赖原则下,WebAuthn 服务端验证(COSE/ES256)要么引入 @simplewebauthn/server(破例)要么手搓密码学(危险)。→ passkey 只能作为公网 HTTPS 域名下的可选增强,且建议推迟。

2.4 双形态(公网+内网)的安全底线

不需要银行级,但要"防邻居蹭网互删进度":

层 局域网形态(现状 HTTP) 公网形态(未来 HTTPS)
传输 HTTP 明文 = 同 WiFi 者可嗅 token。威胁模型只能假设家庭 WiFi 可信(WPA2/3 密码不外传即可);要更强就得走公网域名(家庭内网也可访问 https://game.xiangsilian.com) TLS,嗅探关闭
认证 匿名档案 token + 可选 PIN 认领(防"家人恶作剧互删"级别) 同左 + 登录/配对端点限速
Token 加 TTL(滑动续期,如 180 天)+ 登出可吊销 同左
PIN(若设置) scrypt 哈希存储(node:crypto 自带,不破零依赖) 同左 + limit_req(deploy.md 已规划)
服务边界 服务器只绑 LAN 绑 127.0.0.1,Nginx 只反代 /api/(deploy.md「以后若要开放用户系统」原文照做)

deploy.md 已写的前置清单与本方案完全兼容——本方案把其中"PIN 改哈希、token 过期"消化进档案系统本身。

2.5 中文社区实践补充

前端圈通行做法与本报告方案一致:首次访问生成 UUID 存 localStorage(可 Cookie 双保险)作设备身份 → 服务器据此签发 token → 同一设备 ID 对应唯一匿名账号(腾讯 CloudBase 匿名登录文档明确"同一设备只能生成一个匿名用户、可后续绑定升级")。浏览器指纹(FingerprintJS)可作 localStorage 被清后的兜底,但隐私争议大且家庭场景收益低,不建议引入。

2.6 来源链接


3. 候选方案矩阵

维度说明:★ 越多越好(工作量列反着看:★ 越少越好=工作量越小)。"语音适配"= 视障用户可听、可手势操作的成本。

方案 首访摩擦 同设备多玩家 跨设备同玩家 隐私与安全 实现工作量(文件级) 语音适配
A. 纯本地档案(localStorage 多命名空间档案,无服务器身份) ★★★★★ 0 击(自动"玩家一") ★★★★★ ✗(只能整档导出/导入码) ★★★★★ 无攻击面 ★(新 engine/profile.js ~150 行 + index.html 改面板;游戏零改动若走"换档=换 key 快照") ★★★★(无配对旅程要念)
B. 自动匿名档案 + 切换器 + 配对码(首访静默 POST 建档拿 token;Netflix 式切换;跨设备=6 位配对码/QR;可选 PIN 认领) ★★★★★ 0 输入 0 击 ★★★★★ ★★★★(配对码:需旧设备在手;PIN 认领兜底) ★★★(token TTL+吊销、scrypt PIN;LAN HTTP 仍明文=家庭可信假设) ★★(server.js +~180 行含配对/迁移;engine/profile.js ~250 行;index.html 面板重做;8 个游戏零改动,见 §4.3 换档走服务器倒灌) ★★★(切换/配对语音线要新增台词资产)
C. 改良 PIN(自动建档但随机发 PIN 一次展示;保留现登录 UI 为跨设备主路径) ★★★(首访弹出"抄下你的 PIN"本身是打断+认知负担) ★★★(PIN 是档案口令,家人共用即互看) ★★★★(名+PIN 哪都能登) ★★★(须配 scrypt+限速,否则弱于 B) ★★(server 改动小于 B,无配对子系统) ★★★(输 PIN 数字键盘可听化)
D. Passkey 一键建档(WebAuthn discoverable credential,无用户名无密码) ★★★★(1 击+生物识别,但要系统弹窗理解成本) ★★★★(每档案一个 passkey) ★★★★★(iCloud/Google 同步白送) ★★★★★ ★★★★★(服务端 COSE/ES256 验证:引依赖或手搓密码学;LAN HTTP 不可用,见 §2.3) ★★★(系统弹窗有系统级旁白)
E. QR/配对码(独立小方案,只做"身份搬运") — — ★★★★ ★★★★(短期单次码) ★(+~60 行) ★★(QR 不可听,须配数字码朗读)

关键否决/保留理由:

  • D 否决为主方案:局域网 HTTP 私有 IP 下 WebAuthn API 直接不存在(§2.3),而家庭 WiFi 恰是主场景;零依赖约束下服务端实现成本最高。留作公网化之后的可选"认领"升级项。
  • C 不推荐但保留 API 兼容:PIN 从"人人必经"降级为"想要跨设备找回的人可选设置",现有 username+PIN 端点在迁移窗口保留。
  • E 不是独立方案,是 B 的跨设备旅程构件(A 若只做本地,也可后补导出码)。

4. 推荐方案(主推 B,A 为其离线/公网静态降级形态)

4.1 总体形态

                    ┌─ 首访:0 输入 → 静默 POST /api/profile → token + "玩家一"
局域网(serve.sh)──┤─ 切换:大厅档案面板(本地保存该设备所有 {id,name,token})
                    └─ 跨设备:旧设备"出示配对码"(6 位数字,10 分钟单次)→ 新设备输入 → 拉全部进度
公网(Nginx)────── API 未开放期:自动降级为方案 A(纯本地档案,localStorage 快照式换档)
                    API 开放期(deploy.md 前置完成后):与局域网同一套 B
可选升级 ────────── "认领":给档案设 PIN(scrypt 哈希),用于名+PIN 跨设备登录(旧设备不在手时兜底)

离线/无 API 时 B→A 的降级是同一份 profile.js 代码的两种存储后端(服务器或 localStorage 快照),不是两套系统——现状 index.html 的 api() 已有"服务器不可达静默降级"基因,延续即可。

4.2 三个旅程(语音 + 手势描述)

本仓库手势语言:上/下/左/右滑动、单击(约 300ms 延迟)、双击;全项目 8 个统一 earcon(engine/guide.js 运行时合成)。大厅新增语音资产需走 TTS 流水线(台词表加进 tools/gen_voice_dual.py,男女双声)。

旅程 1 · 首次进入(0 感知建档)

  • 打开大厅,无任何弹窗/表单。页面加载时后台静默建档(服务器形态拿 token;无服务器则纯本地)。
  • 首次完成任一游戏存档后,大厅顶部档案区轻声播一句(可被任何触摸打断,遵守引擎语音链规则):「已创建玩家档案:玩家一。你可以在大厅顶部切换或重命名。」同时卡片刷新出进度摘要。不打断优先级高于这句闲聊的任何游戏语音。
  • 视障路径:大厅是真实 DOM 按钮,TalkBack/VoiceOver 逐个可读;补 aria-live 播报上述句子;卡片本身已可读(真实 <a>)。

旅程 2 · 切换玩家(同设备,2 击)

  • 大厅顶部常驻「玩家:〔玩家一〕」按钮(真实 <button> + aria-haspopup)。单击 → 展开档案列表(每行一个 <button role="menuitemradio">:玩家一✓ / 玩家二 / +新玩家 / ⚙ 重命名·配对·设PIN)。
  • 单击某档案 → 播确认 earcon(guide 词表的 success 音)→ 语音「已切换到玩家二」→ 下方游戏卡进度摘要刷新。
  • 切换的数据搬运(详见 4.3):旧档案本地进度全部 PUT 上服务器 → 新档案全部进度 GET 倒灌回原有 localStorage key → 游戏代码零改动。离线(纯本地后端)则等价地在 localStorage 快照间搬运。
  • 听觉化档案名:预生成「玩家一~玩家九」音 + 每档案一个签名 earcon(8 音词表外允许第 9 种"档案身份音",或复用音高变体),任意汉字名不朗读只显示(决策点 D3)。

旅程 3 · 换设备(配对码)

  • 旧设备:档案面板 →「换设备 / 配对」→ 显示 6 位数字配对码 + QR(QR 给明眼人扫码用,同 LAN 下直开新设备页)。语音路径:逐位朗读数字(需预生成 0-9 单数字音——设计规范禁"数字碎片拼接"是对整句台词的;配对码逐位播报是系统级新场景,需设计侧裁一条豁免或用「第 x 位是 y」整句预生成,见决策点 D3 附注)。
  • 新设备:大厅档案面板 →「我是老玩家」→ 数字小键盘(大按键、无 IME)逐位输入,单击选键、输入完自动校验;错误播 error earcon +「配对码不正确,请重试」。
  • 成功 → 成功 earcon +「欢迎回来,玩家二」→ 新设备拿到 token,全部进度从服务器倒灌本地(顺带修复审计缺口 1:跨设备拉回从此对所有 8 款游戏生效,因为倒灌发生在大厅层而非各游戏内)。
  • 兜底:旧设备已丢失且档案设过 PIN →「用玩家名+PIN 登录」(保留现有 login UI 为这条低频路径);没设 PIN 也没旧设备 → 接受丢失、重新开始(与 Wordle/Poki 游客同水位,写入产品预期管理文案)。

4.3 服务器数据模型变更(users.json v2 + tokens + pairings)

data/users.json(v2,启动时自动从 v1 迁移)
{
  "u_a1b2c3": {
    "id": "u_a1b2c3",
    "name": "玩家一",
    "kind": "device",              // device=匿名档案 | claimed=已设PIN
    "pinHash": null,               // claimed: "scrypt$N$r$p$salt$hash"(node:crypto 自带)
    "createdAt": "…", "lastSeenAt": "…"
  },
  "uuu": { "id": "uuu", "name": "uuu", "kind": "claimed", "pinHash": "scrypt$…(由明文 0858 迁移时哈希)", … }
}
data/tokens.json   { "<hex>": { "uid": "u_a1b2c3", "exp": 1893456000, "lastUsedAt": … } }   // 滑动 180 天
data/pairings.json { "483920": { "uid": "u_a1b2c3", "exp": …, "used": false } }              // 10 分钟单次
data/<uid>.json    { "<game>": data }   // 结构不变;老用户 uid=旧用户名,文件零迁移

API 增删(server.js 内保持零依赖):

端点 说明
POST /api/profile(新) 无参或 {name?} → 建匿名档案 + 返回 {token, profile}。配简单 per-IP 频控(如 10 个/天)防刷
GET /api/profile(新) 当前 token 的档案信息
PUT /api/profile(新) 改名
POST /api/profile/pin(新) 设置/修改 PIN(kind→claimed,scrypt)
POST /api/pair/start(新) 已登录 → 生成 6 位配对码(10 分钟单次)
POST /api/pair/redeem(新) {code} → 新 token(配对码换登录)
POST /api/logout(新) 吊销当前 token
GET /api/progress / PUT /api/progress 不变
POST /api/register / POST /api/login 保留一个迁移窗口(等价于 claimed 档案),之后降级为内部专用
顺带修复 sessions.json → tokens.json 迁移时全部给 180 天滑动过期;写入时惰性清理过期项

4.4 向后兼容与迁移(现有用户 = data/ 里的 1 个真实账号 "uuu")

  1. server.js 启动检测 users.json 为 v1(值是字符串)→ 一次性转 v2:uid 沿用旧用户名(uuu.json 进度文件零迁移),PIN 明文即刻转 scrypt,sessions token 全量搬入 tokens.json 并加 exp。
  2. 客户端 soundgame.auth 旧结构 {username, token} 在 profile.js 初始化时识别 → 服务器换发(或直接沿用,token 仍有效)→ 包成单档案的档案表。老用户无感。
  3. 游客的 localStorage 进度:首次进入新档案系统时,把现有 soundgame.progress.* 等本地 key 认领进首个自动档案(数据不丢,升级为档案数据)——这是 Firebase linkWithCredential「UID 不变、数据原地升级」思想(§2.1)。
  4. 大厅 PUBLIC_STATIC 分支保留:公网无 API 期 → 档案面板仍在但走纯本地后端(比今天"整个面板藏掉"更好)。

4.5 文件级改动清单

文件 改动 规模估计
web/server/server.js 新端点(§4.3 表)、scrypt、token TTL/吊销、配对码、v1→v2 启动迁移、per-IP 简单频控 +~180 行
web/engine/profile.js(新) 档案表(本地 {id,name,token} 列表)、激活档案、换档搬运(flush 本地全部 key→PUT;GET→倒灌)、双后端(服务器/纯本地快照)、配对 UI 逻辑 ~250 行
web/index.html 登录面板 → 档案面板(列表+新玩家+重命名+配对+设PIN+名PIN登录折叠项);补 aria(动态表单加 label、aria-live 播报区);PUBLIC_STATIC 分支改为本地档案模式 ~150 行重写
8 个 web/*/game.js 零改动(换档搬运在大厅层完成;radio 自带的 GET 逻辑与新机制共存不冲突) 0
web/engine/settings.js 零改动(_settings 本来就走 progress API,档案化后自动变成玩家级设置) 0
web/serve.sh / web/server/README.md / HANDOVER.md §2.2、§6 文档更新(玩具级警告改写为实际水位) 小
tools/gen_voice_dual.py 台词表 新增大厅语音(sys_profile_created / switched / pair_code 逐位数字 / welcome_back 等,男女双声)+ 走一轮 TTS 流水线 若干条
docs/deploy.md 「以后若要开放用户系统」小节更新为本方案公网路径 小

4.6 风险与缓解

风险 等级 缓解
localStorage 被清/换浏览器 → 匿名档案孤儿(业界公认,Firebase 官方警告同款) 中 引导时机学 Firebase:在"第一次想跨设备/第二次设 PIN 机会点"提示设 PIN 或配对,而非首访打断;服务器对 180 天不活跃 device 档案做归档清理(防 users.json 无限膨胀)
LAN HTTP 明文下 token 可被同 WiFi 者嗅探 → "邻居删进度" 中 威胁模型写明:家庭 WiFi 密码可信即安全;要更强走 https://game.xiangsilian.com(内网也能访问公网域名)。认领 PIN 防的是"家人恶作剧"级别
换档搬运期间断网/杀页面 → 两档案数据交错 低 搬运顺序固定:先全部 flush 完成再切换;失败即中止切换并播 error;纯本地后端快照为原子 JSON
一次性教学标记(tutIntroSeen 等 3 个 key)跨档案泄漏(换了档案教学不再重播) 低 可接受(教学只教一次本就是产品取向);彻底修需游戏改动或 key 前缀化,列为后续可选项
配对码被念出来的体验(视障用户逐位听 6 位数字) 中 数字音预生成 + "重复播报"手势(双击重听);或改 4 位词表码(每词一个预生成音)——设计侧裁决
任意汉字名无法朗读,视障家人无法"听出"谁在玩 中 每档案绑定签名 earcon + 固定编号音(玩家一~九可听),自定义名只作视觉显示(决策点 D3)
匿名档案被脚本批量刷(公网开放后) 低 per-IP 频控 + limit_req(Nginx 层,deploy.md 已规划);家庭场景几乎无此风险

5. 备选方案说明

  • 若决定完全不上服务器身份(公网永远纯静态):直接做方案 A。损失:跨设备、玩家级设置云同步、家庭共用 PC 时的云端备份;得到:零攻击面、实现最省。A 是 B 的真子集(profile.js 双后端中的本地后端),先做 A 再补 B 不返工——这也是把 A 设计为 B 的降级形态的原因。
  • 若公网化后想上 passkey(D):在 claimed 档案上加第二凭据即可(passkey 与 PIN 并存),客户端改动集中在大厅,服务端需引入 WebAuthn 验证依赖或手搓(建议届时引入 @simplewebauthn/server 并为服务器破一次零依赖例,或用 Nginx+auth 子服务隔离依赖)。届时 B 的配对码仍是兜底通道,两者不冲突。

6. 三个关键设计决策点(需产品拍板)

  • D1 · 档案名怎么起:首访完全 0 感知(自动"玩家一",事后改名)还是首访点一次"起个名字/用默认"?(推荐前者:改名入口在档案面板,随时可改;起名不是注册,不该挡在玩游戏前面。)
  • D2 · 跨设备找回的底线:只配对码(旧设备必须在手)够不够?是否同时保留"名+PIN 登录"作为旧设备丢失时的兜底(推荐保留,PIN 变为可选认领项);还是彻底极简、丢了就丢(Wordle 同水位)?
  • D3 · 视障玩家的"名字可听化":自定义汉字名不朗读(只显示+签名 earcon+编号音)够不够?还是引入浏览器 SpeechSynthesis 念任意名(与全预生成语音的质感不一致、移动端质量参差)?附带:配对码逐位播报需要设计侧给"数字单音"或"第 x 位是 y"整句一个豁免裁定。