Files
Genarrative/docs/technical/MATCH3D_CREATION_AND_RUNTIME_MINIMAL_IMPLEMENTATION_2026-04-30.md
五香丸子 22f6e6f4e7
Some checks failed
CI / verify (push) Has been cancelled
抓大鹅A0实现
2026-04-30 20:07:46 +08:00

22 KiB
Raw Blame History

抓大鹅 Match3D 创作与运行态最小落地技术方案 2026-04-30

1. 文档目的

本文件承接 PRD《AI 原生抓大鹅 Match3D 玩法创作工具与玩法系统 PRD》冻结首版 demo 的最小可开发方案。

本轮目标不是先做一个纯前端临时小游戏,而是在当前平台内新增独立 match3d 玩法域,跑通下面这条最小主链:

  1. 平台创作入口选择“抓大鹅”。
  2. Agent 对话确认题材、需要消除次数和难度。
  3. 编译 Match3D 草稿。
  4. 进入结果页编辑游戏名称、标签和封面图。
  5. 发布前试玩,可随时停止并返回修改。
  6. 发布作品。
  7. 玩家进入单局运行态。
  8. 前端即时呈现点击、飞入、入槽、三消、腾格、胜负过渡。
  9. 后端权威确认点击、入槽、消除、失败、胜利和成绩。

本文是后续并行开发的工程合同。若实现过程中发现字段、路由、表结构或前后端职责需要变化,必须先更新本文,再进入对应编码分支。


2. 本轮明确不做

  1. 不做多关卡链。
  2. 不做排行榜展示。
  3. 不做道具逻辑,只预留字段和扩展点。
  4. 不做真实 3D 模型和真实 3D 物理遮挡。
  5. 不做洗牌、重置、旋转、放大等局内操作。
  6. 不做必须试玩通关才能发布。
  7. 不做前端本地最终规则真相。
  8. 不接入 server-node 或 PostgreSQL。
  9. 不把 Match3D 挂到 RPG、拼图或大鱼吃小鱼旧命名下。

3. 分层边界

3.1 前端

前端继续使用当前 React + TypeScript + Vite 平台壳层,负责所有即时呈现的局内反馈:

  1. 创作入口、Agent 工作区、结果页、试玩和运行态 UI。
  2. 参考图片上传入口。
  3. 运行态圆形空间、2D 物品、倒计时和 7 格备选栏展示。
  4. 基于后端快照做 2D 命中检测、悬停、按压、选中反馈。
  5. 在等待后端确认期间,先行播放飞入、入槽、三消、腾格、胜利和失败过渡。
  6. 收到后端确认后,以权威快照校正本地表现。
  7. 后端拒绝或版本冲突时,回滚本次即时反馈。

前端禁止:

  1. 把本地即时反馈作为最终规则真相。
  2. 本地生成可提交成绩。
  3. 本地伪造胜利、失败、消除计数或运行态持久化结果。
  4. 在 UI 中默认展示长篇玩法规则说明。

3.2 api-server

server-rs/crates/api-server 负责 Match3D 对外 HTTP facade

  1. 鉴权、请求上下文、错误 envelope。
  2. 创作 Agent 的 LLM turn 编排。
  3. 参考图片上传复用现有资产/OSS 能力。
  4. 调用 spacetime-client 读写 Match3D 会话、作品和运行态。
  5. 对前端返回稳定 HTTP DTO不泄露 SpacetimeDB 内部表结构。

3.3 SpacetimeDB

server-rs/crates/spacetime-module 负责 Match3D 真相态:

  1. 存储 Agent session / message。
  2. 存储作品 profile。
  3. 存储运行态 run snapshot。
  4. 通过 procedure 同步返回会话、作品和运行态快照。
  5. 在 reducer/procedure 内保持确定性,不做网络、文件系统或外部模型调用。

3.4 纯领域 crate

新增:

server-rs/crates/module-match3d

职责:

  1. 创作配置校验。
  2. 物品类型规划。
  3. 初始布局生成输入/输出模型。
  4. 2D 遮挡与可点击快照计算。
  5. 点击确认。
  6. 入槽与三消确认。
  7. 胜负确认。
  8. 成绩基础数据计算。

module-match3d 不直接依赖 Axum、不访问 OSS、不调用 LLM、不读写 SpacetimeDB 表。


4. 共享契约

4.1 TypeScript shared contracts

新增:

packages/shared/src/contracts/match3dAgent.ts
packages/shared/src/contracts/match3dWorks.ts
packages/shared/src/contracts/match3dRuntime.ts

match3dAgent.ts

承载:

  1. Match3DAgentSession
  2. Match3DAgentMessage
  3. Match3DCreatorConfig
  4. Match3DCompileDraftRequest
  5. Match3DCompileDraftResult

match3dWorks.ts

承载:

  1. Match3DWorkProfile
  2. Match3DWorkSummary
  3. Match3DWorkUpdateRequest
  4. Match3DPublishRequest
  5. Match3DPublishResult

match3dRuntime.ts

承载:

  1. Match3DRunSnapshot
  2. Match3DItemSnapshot
  3. Match3DTraySlot
  4. Match3DStartRunRequest
  5. Match3DClickItemRequest
  6. Match3DClickItemResult
  7. Match3DStopRunRequest
  8. Match3DRestartRunRequest

4.2 Rust shared contracts

新增:

server-rs/crates/shared-contracts/src/match3d_agent.rs
server-rs/crates/shared-contracts/src/match3d_works.rs
server-rs/crates/shared-contracts/src/match3d_runtime.rs

并在 server-rs/crates/shared-contracts/src/lib.rs 导出。

Rust DTO 只承载 HTTP contract 和跨 crate 稳定模型,不直接暴露 module-match3d 内部结构。

4.3 命名约束

  1. 对外展示:抓大鹅。
  2. 工程域:match3d
  3. TypeScript 类型前缀:Match3D
  4. Rust 类型前缀:Match3D
  5. HTTP path/api/creation/match3d/*/api/runtime/match3d/*
  6. SpacetimeDB 表与 procedure 前缀:match3d_

5. SpacetimeDB 表

首版保持最小闭环,复杂结构统一使用结构化字段 + snapshot_json / draft_json,避免过早拆出多张高耦合子表。

新增表属于安全 schema 演进;后续如果改字段,必须遵守 SPACETIMEDB_SCHEMA_CHANGE_CONSTRAINTS.md,不能直接删除、重排或改名已有列。表结构变更后必须同步对齐 migration.rs

5.1 match3d_agent_session

作用:保存 Match3D 创作 Agent 会话、配置草稿和发布指针。

字段:

  1. session_id: String,主键。
  2. owner_user_id: String,索引。
  3. seed_text: String,用户初始输入或自动配置摘要。
  4. current_turn: u32
  5. progress_percent: u32
  6. stage: String,建议值:CollectingReadyToCompileDraftCompiledPublished
  7. config_json: String,序列化 Match3DCreatorConfig
  8. draft_json: String,序列化草稿结果。
  9. last_assistant_reply: String
  10. published_profile_id: String,未发布为空字符串。
  11. created_at: i64
  12. updated_at: i64

5.2 match3d_agent_message

作用:保存 Match3D 创作 Agent 消息流水。

字段:

  1. message_id: String,主键。
  2. session_id: String,索引。
  3. role: String,建议值:userassistantsystem
  4. kind: String,建议值:textactionerror
  5. text: String
  6. created_at: i64

5.3 match3d_work_profile

作用:保存 Match3D 作品主表和发布状态。

字段:

  1. profile_id: String,主键。
  2. owner_user_id: String,索引。
  3. source_session_id: String
  4. author_display_name: String
  5. game_name: String
  6. theme_text: String
  7. summary_text: String
  8. tags_json: String
  9. cover_image_src: String
  10. cover_asset_id: String
  11. clear_count: u32
  12. difficulty: u32
  13. config_json: String
  14. publication_status: String,建议值:DraftPublished
  15. play_count: u32
  16. updated_at: i64
  17. published_at: i64,未发布为 0

5.4 match3d_runtime_run

作用:保存 Match3D 单局运行态快照和成绩基础数据。

字段:

  1. run_id: String,主键。
  2. owner_user_id: String,索引。
  3. profile_id: String,索引。
  4. status: String,建议值:RunningWonFailedStopped
  5. snapshot_version: u32
  6. started_at_ms: i64
  7. duration_limit_ms: i64,首版固定 600000
  8. finished_at_ms: i64,未结束为 0
  9. elapsed_ms: i64
  10. clear_count: u32
  11. total_item_count: u32
  12. cleared_item_count: u32
  13. failure_reason: String,建议值为空、TimeUpTrayFull
  14. snapshot_json: String,序列化 Match3DRunSnapshot
  15. created_at: i64
  16. updated_at: i64

5.5 match3d_play_record

首版可选,若本轮不做排行榜,可先不建表,只在 match3d_runtime_run 保留成绩字段。

若实现成绩历史,字段建议:

  1. record_id: String,主键。
  2. profile_id: String,索引。
  3. owner_user_id: String,索引。
  4. run_id: String
  5. status: String
  6. elapsed_ms: i64
  7. cleared_item_count: u32
  8. total_item_count: u32
  9. created_at: i64

6. SpacetimeDB procedure

本轮全部使用 procedure 同步返回快照,避免 api-server 在写入后再读 private table。

6.1 创作链

  1. create_match3d_agent_session(input)
    创建会话,写入初始配置或空配置,返回 session snapshot。

  2. get_match3d_agent_session(input)
    获取会话、消息和当前 draft。

  3. submit_match3d_agent_message(input)
    只写 user message不调用 LLM不生成 assistant 回复。

  4. finalize_match3d_agent_message_turn(input)
    api-server LLM turn 完成后写入 assistant message、配置状态、进度和 last_assistant_reply

  5. compile_match3d_draft(input)
    校验题材、需要消除次数、难度,生成草稿和作品 draft profile。

6.2 作品链

  1. update_match3d_work(input)
    更新游戏名称、标签、封面、题材、需要消除次数和难度。

  2. publish_match3d_work(input)
    校验基础信息完整后发布作品,不要求试玩通关。

  3. list_match3d_works(input)
    查询当前用户作品。

  4. get_match3d_work_detail(input)
    查询作品详情,支持结果页恢复和作品详情页。

  5. delete_match3d_work(input)
    可后置;若接入创作中心删除,需要与其他玩法卡片删除语义一致。

6.3 运行态链

  1. start_match3d_run(input)
    基于作品配置生成单局快照,返回 Match3DRunSnapshot

  2. get_match3d_run(input)
    返回当前权威运行态快照。

  3. click_match3d_item(input)
    根据 run_id / item_instance_id / client_snapshot_version 权威确认点击、入槽、三消、失败或胜利,返回新快照和确认结果。

  4. stop_match3d_run(input)
    把运行态标记为 Stopped,供试玩中止和返回结果页使用。

  5. restart_match3d_run(input)
    复用同一作品配置创建新 run返回新快照。

  6. finish_match3d_time_up(input)
    可选。若倒计时由前端触发,前端在倒计时归零时调用该 procedure后端确认 TimeUp。也可以由 click_match3d_itemget_match3d_run 懒确认超时。

6.4 procedure 输入输出约束

  1. 所有 mutation 输入必须带 owner_user_id 或由 api-server 注入用户上下文SpacetimeDB 内部仍需以可信身份或 owner 字段校验归属。
  2. 运行态 mutation 必须携带 client_snapshot_version
  3. 若版本不匹配,返回 VersionConflict,并携带最新快照。
  4. procedure 返回字符串化 JSON 时,spacetime-client 必须负责反序列化和错误归一化。

7. 运行态确认协议

Match3D 首版采用“前端即时反馈 + 后端权威确认”。

7.1 点击流程

玩家点击物品
-> 前端基于最新快照做 2D 命中检测
-> 前端立即播放按压/选中/飞入表现
-> 前端调用 click_match3d_item
-> 后端确认点击是否合法
-> 后端返回新快照与确认结果
-> 前端按确认结果固化或回滚表现

7.2 点击请求

interface Match3DClickItemRequest {
  runId: string;
  itemInstanceId: string;
  clientSnapshotVersion: number;
  clientEventId: string;
  clickedAtMs: number;
}

字段说明:

  1. clientSnapshotVersion 用于发现前端基于旧快照操作。
  2. clientEventId 用于前端去重和日志定位。
  3. clickedAtMs 只用于观测,不作为成绩可信时间源。

7.3 点击结果

type Match3DClickConfirmStatus =
  | 'Accepted'
  | 'RejectedNotClickable'
  | 'RejectedAlreadyMoved'
  | 'RejectedTrayFull'
  | 'VersionConflict'
  | 'RunFinished';

interface Match3DClickItemResult {
  status: Match3DClickConfirmStatus;
  run: Match3DRunSnapshot;
  acceptedItemInstanceId?: string;
  clearedItemInstanceIds: string[];
  failureReason?: 'TimeUp' | 'TrayFull';
}

7.4 前端回滚规则

  1. Accepted:固化飞入、入槽、消除或胜负表现。
  2. RejectedNotClickable:被点物品回到原位,备选栏恢复。
  3. RejectedAlreadyMoved:直接应用后端最新快照。
  4. RejectedTrayFull:应用后端失败快照。
  5. VersionConflict:取消当前局部动画,应用最新快照,允许用户继续操作。
  6. RunFinished:应用后端胜负快照,进入结算。

7.5 快照版本

  1. 每次后端接受会改变运行态的操作,snapshot_version 必须递增。
  2. 前端所有即时反馈都基于某个明确版本。
  3. 前端同时只能有一个未确认的点击操作;首版不做多点击并发队列。
  4. 如果动画期间用户再次点击,前端应忽略或排队到当前确认完成后再处理;首版建议忽略。

8. 运行态快照

8.1 Match3DRunSnapshot

interface Match3DRunSnapshot {
  runId: string;
  profileId: string;
  status: 'Running' | 'Won' | 'Failed' | 'Stopped';
  snapshotVersion: number;
  startedAtMs: number;
  durationLimitMs: number;
  serverNowMs: number;
  remainingMs: number;
  clearCount: number;
  totalItemCount: number;
  clearedItemCount: number;
  traySlots: Match3DTraySlot[];
  items: Match3DItemSnapshot[];
  failureReason?: 'TimeUp' | 'TrayFull';
}

说明:

  1. serverNowMs 用于前端校准倒计时。
  2. remainingMs 由后端按 durationLimitMs 和服务端时间计算。
  3. 前端可以本地递减倒计时,但归零失败必须调用后端确认或等待下一次后端确认。

8.2 Match3DItemSnapshot

interface Match3DItemSnapshot {
  itemInstanceId: string;
  itemTypeId: string;
  visualKey: string;
  x: number;
  y: number;
  radius: number;
  layer: number;
  state: 'InBoard' | 'Flying' | 'InTray' | 'Cleared';
  clickable: boolean;
}

说明:

  1. Flying 可以作为前端表现态使用,不要求后端逐帧落库。
  2. 后端主要确认 InBoard -> InTray -> Cleared 的权威状态变化。
  3. clickable 是后端计算给前端的可点击快照,前端命中检测必须尊重它。

8.3 Match3DTraySlot

interface Match3DTraySlot {
  slotIndex: number;
  itemInstanceId?: string;
  itemTypeId?: string;
  visualKey?: string;
}

8.4 2D 遮挡口径

首版不做真实物理遮挡。

建议后端按以下输入计算 clickable

  1. 物品圆形或近似圆形碰撞范围。
  2. layer 越大越靠上。
  3. 被更高层物品覆盖到低于可点击阈值时,标记为不可点击。
  4. 阈值首版作为领域常量,后续体验后再参数化。

前端基于 clickable 和自身命中检测呈现即时反馈;后端仍在点击确认时再次校验。


9. 领域规则冻结

9.1 创作配置

interface Match3DCreatorConfig {
  themeText: string;
  referenceImageSrc?: string;
  clearCount: number;
  difficulty: number;
}

规则:

  1. themeText 必填。
  2. clearCount 必须为正整数。
  3. difficulty 范围 1~10
  4. referenceImageSrc 首版只支持图片,不支持视频。

9.2 物品数量

totalItemCount = clearCount * 3

每种 itemTypeId 的数量必须是 3 的倍数。

9.3 demo 视觉素材

首版使用 10 种颜色形状组合素材。

  1. visualKey 固定为内置素材 key。
  2. 题材主题先进入作品配置和 Agent 文案,不强制生成题材素材。
  3. 后续接入真实题材素材前,必须另补资产生成方案。

9.4 难度

首版 difficulty 只作为布局和生成算法参数。

后端需要保留参数入口,但难度公式先保持简洁:

  1. 难度越高,物品尺寸可整体略小。
  2. 难度越高,堆叠层级可略深。
  3. 难度越高,首屏可直接三消的可见组合可略少。

具体数值不在 A0 冻死,由 B1 领域 crate 分支给出首版常量并通过测试覆盖。


10. api-server HTTP facade

10.1 创作链

POST /api/creation/match3d/sessions
GET  /api/creation/match3d/sessions/:sessionId
POST /api/creation/match3d/sessions/:sessionId/messages
POST /api/creation/match3d/sessions/:sessionId/messages/stream
POST /api/creation/match3d/sessions/:sessionId/compile

说明:

  1. 同步消息接口用于普通提交。
  2. 流式接口复用现有 Agent SSE 基建。
  3. messages 只写 user messageLLM 推理由 api-server 完成后 finalize 到 SpacetimeDB。
  4. compile 不生成额外素材,只生成 Match3D 草稿和作品 draft。

10.2 作品链

PATCH /api/creation/match3d/works/:profileId
POST  /api/creation/match3d/works/:profileId/publish
GET   /api/creation/match3d/works
GET   /api/creation/match3d/works/:profileId

首版发布不要求试玩通关。

10.3 运行态链

POST /api/runtime/match3d/works/:profileId/runs
GET  /api/runtime/match3d/runs/:runId
POST /api/runtime/match3d/runs/:runId/click
POST /api/runtime/match3d/runs/:runId/stop
POST /api/runtime/match3d/runs/:runId/restart
POST /api/runtime/match3d/runs/:runId/time-up

time-up 可后置;若不单独实现,get 或下一次 click 必须能懒确认超时失败。

10.4 错误语义

HTTP 层使用现有 API envelope。

建议错误码:

  1. MATCH3D_SESSION_NOT_FOUND
  2. MATCH3D_WORK_NOT_FOUND
  3. MATCH3D_RUN_NOT_FOUND
  4. MATCH3D_INVALID_CONFIG
  5. MATCH3D_PUBLISH_BLOCKED
  6. MATCH3D_RUN_VERSION_CONFLICT
  7. MATCH3D_RUN_ALREADY_FINISHED

11. 前端落点

11.1 contracts 与 service

新增:

src/services/match3d-creation/
src/services/match3d-works/
src/services/match3d-runtime/

分别负责 Agent/草稿、作品/发布、运行态请求。

11.2 组件

新增:

src/components/match3d-creation/
src/components/match3d-result/
src/components/match3d-runtime/

11.3 平台入口

需要接入:

  1. src/components/platform-entry/platformEntryCreationTypes.ts
  2. src/components/platform-entry/PlatformEntryCreationTypeModal.tsx
  3. src/components/platform-entry/usePlatformCreationAgentFlowController.ts

入口展示:

  1. 名称:抓大鹅
  2. 子标题:经典消除玩法

11.4 运行态 UI

首版运行态必须移动端优先:

  1. 圆形空间占据主要区域。
  2. 备选栏固定 7 格。
  3. 倒计时清晰但不遮挡物品。
  4. 物品点击区域稳定,不因动画造成布局跳动。
  5. 胜利/失败结算使用独立面板,不在当前面板下方展开。

11.5 本地 mock 口径

F3 运行态即时反馈分支可以先用本地 mock snapshot 开发,但必须满足:

  1. mock 类型来自 packages/shared/src/contracts/match3dRuntime.ts
  2. mock 字段不得脱离 A0 文档。
  3. 接入真实 API 时删除或降级为测试 fixture。

12. 并行开发包

12.1 第二波并行

B1 + B2领域 crate 与 shared contracts

写入范围:

  1. server-rs/crates/module-match3d/
  2. server-rs/Cargo.toml
  3. server-rs/crates/shared-contracts/src/match3d_*.rs
  4. packages/shared/src/contracts/match3d*.ts

交付:

  1. 领域规则单测。
  2. DTO 编译通过。
  3. 不接 SpacetimeDB。

B3SpacetimeDB 表与 procedure

写入范围:

  1. server-rs/crates/spacetime-module/src/match3d/
  2. server-rs/crates/spacetime-module/src/lib.rs
  3. server-rs/crates/spacetime-module/src/migration.rs
  4. 生成后的 bindings 由后续 B4 处理。

交付:

  1. 表和 procedure 定义。
  2. module-match3d 规则接线。
  3. spacetime build 或仓库现有等价脚本通过。

F1创作入口与 Agent UI

写入范围:

  1. src/components/platform-entry/
  2. src/components/match3d-creation/
  3. src/services/match3d-creation/

交付:

  1. 平台入口可见。
  2. Agent 工作区能收集题材、需要消除次数和难度。
  3. 可用 mock client等待 B5 接口。

F3运行态即时反馈 UI

写入范围:

  1. src/components/match3d-runtime/
  2. src/services/match3d-runtime/

交付:

  1. 圆形空间、2D 物品、7 格备选栏。
  2. 点击命中、飞入、入槽、三消、腾格、胜负过渡。
  3. 后端确认失败时的回滚和快照校正逻辑。
  4. 先用 mock snapshot。

12.2 第三波并行

B4 + B5spacetime-client 与 api-server facade

写入范围:

  1. server-rs/crates/spacetime-client/src/match3d.rs
  2. server-rs/crates/spacetime-client/src/lib.rs
  3. server-rs/crates/api-server/src/match3d.rs
  4. server-rs/crates/api-server/src/app.rs
  5. server-rs/crates/api-server/src/main.rs 如需注册模块

交付:

  1. HTTP facade 可调用 SpacetimeDB procedure。
  2. 创作、作品、运行态接口返回 shared-contract DTO。
  3. 后端定向测试通过。

F2结果页与发布

写入范围:

  1. src/components/match3d-result/
  2. src/services/match3d-works/
  3. 创作中心作品恢复相关最小接线。

交付:

  1. 编辑游戏名称、标签、封面图。
  2. 试玩入口。
  3. 发布入口。

F4平台分发最小接入

写入范围:

  1. 创作中心作品货架。
  2. 首页/分类/广场卡片映射。
  3. 作品详情启动运行态入口。

交付:

  1. 已发布 Match3D 作品可进入平台列表。
  2. 卡片可进入详情或运行态。

12.3 最后集成

Q1集成验收

交付:

  1. 创作到发布到试玩主链通过。
  2. 运行态点击、入槽、三消、失败、胜利通过。
  3. 移动端视口检查通过。
  4. npm run api-server:maincloud 通过。
  5. 对应测试与 npm run check:encoding 通过。

13. 合并顺序

建议合并顺序:

  1. A0本文档。
  2. B1 + B2领域 crate 与 shared contracts。
  3. B3SpacetimeDB 表和 procedure。
  4. B4 + B5spacetime-client 与 api-server facade。
  5. F1 / F2 / F3前端创作、结果页、运行态。
  6. F4平台分发。
  7. Q1集成收口。

如果 F1/F3 先完成,应只以 mock client 保持可编译,不直接修改后端合同。


14. 验收命令

后续编码分支按改动范围执行。

文档分支:

npm run check:encoding -- docs/technical/MATCH3D_CREATION_AND_RUNTIME_MINIMAL_IMPLEMENTATION_2026-04-30.md docs/technical/README.md

后端分支:

cargo test -p module-match3d
cargo test -p shared-contracts
npm run api-server:maincloud
npm run check:encoding

SpacetimeDB 分支按仓库现有发布脚本执行,并在需要生成绑定时使用 spacetime generate 或仓库封装脚本。不得手写生成文件。

前端分支:

npm run check:encoding
npm run typecheck

若新增定向测试,应补跑对应 vitest


15. 一句话结论

Match3D 首版按独立玩法域落地:前端负责所有局内即时反馈以保证手感,后端通过 SpacetimeDB procedure 权威确认规则和成绩api-server 只暴露稳定 HTTP facade后续并行分支必须围绕本文冻结的 DTO、表、procedure 和路由推进。