修复拼消消素材生成与切图质量问题 #56

Merged
kdletters merged 6 commits from codex/puzzle-clear-template-runtime-fixes into master 2026-06-06 20:10:49 +08:00
134 changed files with 20659 additions and 98 deletions

View File

@@ -16,6 +16,23 @@
---
## 2026-06-03 拼消消收敛为单关 6x6 与 4-sheet 素材策略
- 背景:最初 4 关 / 135 次消除 / 单张大 atlas 方案生图数量和空间一致性成本过高,真实 image2 结果容易被布局提示词诱导成带文字、边框或编号的说明图,不适合运行态 1x1 切片。
- 决策:拼消消运行态收敛为单关 `6x6 / 35 次消除 / 600 秒`,直接解锁 `1x2``1x3``2x2``2x3`;素材生成改为 4 张 `1024x1536` 竖版 sheet每张按 `4x6`、每格 `256x256` 切片,再由服务端合成 `10x10 / 2560x2560` 最终 atlas。形状配比固定为 `1x2=23``1x3=5``2x2=4``2x3=3`,总计 35 个复合图案组和 95 个 1x1 卡牌切片。
- 影响范围:`module-puzzle-clear` 关卡与图案组规划、api-server 拼消消素材生成编排、前端草稿试玩本地 runtime、结果页 atlas 预览、拼消消 PRD / 技术方案 / 平台链路文档。
- 验证方式:`cargo test -p module-puzzle-clear --manifest-path server-rs/Cargo.toml``cargo test -p api-server puzzle_clear --manifest-path server-rs/Cargo.toml -- --nocapture``npm run test -- src/services/puzzle-clear/puzzleClearLocalRuntime.test.ts``npm run test -- src/components/puzzle-clear-result/PuzzleClearResultView.test.tsx src/components/puzzle-clear-runtime/PuzzleClearRuntimeShell.test.tsx`
- 关联文档:`docs/prd/【玩法创作】拼消消玩法模板PRD-2026-05-30.md``docs/technical/【玩法创作】拼消消玩法模板技术方案-2026-05-30.md``docs/【玩法创作】平台入口与玩法链路-2026-05-15.md``docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md`
## 2026-05-30 拼消消按独立玩法公开闭环接入
- 背景:拼消消以拼图交换手感为基础,但核心规则从“拼完整单图过关”变为“拼成多个复合图案组后逐个消除”,同时需要顶部补牌、防死局、半锁定局部拼接组和正式统计,不能继续复用拼图运行态规则本体。
- 决策:`puzzle-clear` 作为独立玩法域接入,公开作品码前缀固定为 `PC-`;创作链路采用表单 / 图片输入工作台 -> 独立生成页 -> 结果页 -> 试玩 -> 发布 -> 统一作品详情 -> 正式 runtime。领域规则落在 `module-puzzle-clear`SpacetimeDB 新增 `puzzle_clear_*` 表 / procedure / view并接入统一 `public_work_gallery_entry` / `public_work_detail_entry`;前端只表现后端 snapshot/action 结果,不把胜负、补牌或消除裁决做成前端事实源。
- 补充约束:草稿编译和发布都必须拒绝缺失或 `placeholder` atlas / card assets不允许后端 facade 或 SpacetimeDB 合成临时素材;当前单关正式 runtime 终态事件使用 `run-finished``level-failed`,并写入包含 `status``level``clears``clearDelta``elapsedMs` 的结果 JSON。
- 补充约束:拼消消结果页草稿试玩使用前端本地 `runtimeMode=draft` snapshot不调用 `/api/runtime/puzzle-clear/runs`,不写正式 run 统计;公开详情和推荐流正式运行继续走后端 `/api/runtime/puzzle-clear/*`,客户端需要区分创作详情 `/api/creation/puzzle-clear/works/{profileId}` 与公开运行态详情 `/api/runtime/puzzle-clear/works/{profileId}`
- 影响范围:`CONTEXT.md`、拼消消 PRD / 技术方案、平台玩法链路文档、`shared-contracts` / `packages/shared``api-server``spacetime-module``spacetime-client`、作品架 / 广场 / 统一作品详情 / runtime 前端分流。
- 验证方式PRD 和技术方案必须覆盖资产槽位、素材工作表风险、切片验证、恢复语义、API 命名空间和验证命令;实现侧至少运行 `npm run spacetime:generate``npm run check:spacetime-schema``npm run check:spacetime-runtime-access``npm run check:server-rs-ddd``npm run typecheck``npm run check:encoding`、相关前端测试和 `cargo test -p module-puzzle-clear --manifest-path server-rs/Cargo.toml`
- 关联文档:`docs/prd/【玩法创作】拼消消玩法模板PRD-2026-05-30.md``docs/technical/【玩法创作】拼消消玩法模板技术方案-2026-05-30.md``docs/【玩法创作】平台入口与玩法链路-2026-05-15.md``docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md`
## 2026-06-05 Server-Provision 全程在目标部署 agent 执行且不安装构建链
- 背景:`Genarrative-Server-Provision``DEPLOY_TARGET=development` 语义是部署到 dev 服务器,不是构建机 dry-run。旧流水线把 development 映射到 `linux && genarrative-build`,还先在 build 节点准备 `provision-tools/` 再 stash 给后续阶段,导致真实 dev 初始化可能跑到 Jenkins controller / build 节点;脚本还安装 clang / lld / pkg-config / OpenSSL headers / sccache 等构建链依赖,超出了服务器初始化职责。
@@ -1280,3 +1297,11 @@
- 影响范围:`server-rs/crates/api-server/src/state.rs``server-rs/crates/module-auth/src/lib.rs``server-rs/crates/spacetime-module/src/auth/procedures.rs``server-rs/crates/spacetime-client/src/auth.rs`、对应生成 bindings。
- 验证方式:`cargo check -p module-auth --manifest-path server-rs/Cargo.toml``cargo check -p api-server --manifest-path server-rs/Cargo.toml``cargo test -p module-auth password --manifest-path server-rs/Cargo.toml -- --nocapture``npm run check:spacetime-schema``npm run check:encoding``cargo test -p api-server spacetime_unavailable_router_returns_service_unavailable_for_requests --manifest-path server-rs/Cargo.toml -- --nocapture`
- 关联文档:`docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md``docs/【开发运维】本地开发验证与生产运维-2026-05-15.md`
## 2026-05-31 拼消消底图 prompt 与 atlas 切片提示词收口
- 背景:拼消消生成资产检查时,用户需要区分主题词、场地底图主题词和复合图 atlas prompt 的职责;若小图案显式画出切分线或边框,运行态 1x1 切片会显得像错误素材。
- 决策:`boardBackgroundPrompt` 成为中央场地底图的优先 prompt 来源,只有该字段为空时才回退读取 `themePrompt`;用户上传底图时只执行平台资产持久化和换签,不用主题词重写上传资产。复合图 atlas prompt 只描述“可被服务端按等大 1x1 方格切分”,禁止模型在图案上绘制切分线、边框、网格线或裁切参考线。
- 影响范围:拼消消工作台 payload、`shared-contracts` / `packages/shared` 契约、api-server 生成编排、SpacetimeDB session/work snapshot、文档与生成进度展示。
- 验证方式:`npm run spacetime:generate``npm run check:encoding``npm run check:server-rs-ddd``cargo test -p module-puzzle-clear``cargo test -p spacetime-client puzzle_clear -- --nocapture``npm run test -- src/components/puzzle-clear-creation/PuzzleClearWorkspace.test.tsx src/services/miniGameDraftGenerationProgress.test.ts src/routing/appPageRoutes.test.ts src/services/publicWorkCode.test.ts`
- 关联文档:`docs/prd/【玩法创作】拼消消玩法模板PRD-2026-05-30.md``docs/technical/【玩法创作】拼消消玩法模板技术方案-2026-05-30.md``docs/【玩法创作】平台入口与玩法链路-2026-05-15.md`

View File

@@ -1,4 +1,4 @@
# 踩坑与排障记录
# 踩坑与排障记录
> 用途:记录已验证、未来很可能再次遇到的问题。每条都应包含现象、原因、处理方式和验证方式。
@@ -104,6 +104,30 @@
- 验证:`npm test -- src/components/rpg-entry/RpgEntryFlowShell.agent.interaction.test.tsx -t \"puzzle draft generation auto starts trial and runtime back opens draft result\"`,确认 `window.location.pathname === '/runtime/puzzle'``window.location.search` 同时包含 `runtimeProfileId``runtimeSessionId`
- 关联:`src/components/platform-entry/PlatformEntryFlowShellImpl.tsx``src/services/puzzleRuntimeUrlState.ts``src/routing/appPageRoutes.ts``docs/【玩法创作】平台入口与玩法链路-2026-05-15.md`
## 拼消消草稿试玩不能只测 swap 回调
- 现象:拼消消结果页和 runtime shell 的单测都能通过,但真实页面里卡片只是交换,完全不会消除,顶部准备区还会因为已知的卡背占位路径显示坏图。
- 原因:草稿试玩走的是前端本地 runtime早期测试只覆盖了 `onSwapCards` 回调和局部状态,没有验证完整的消除、重力补牌、关卡完成和资源兜底链路;同时顶部卡背对 `puzzle-clear-card-back.webp` 这类已知缺失资源没有前置回退。
- 处理:草稿试玩的回归测试必须覆盖“交换 -> 完整图案消除 -> 补牌 -> 关卡完成”闭环,并在组件测试里验证真实点击/拖拽序列;顶部准备区卡背遇到已知占位路径时直接回退到 `puzzle.webp` 这类可用参考图,不等图片加载失败后再兜底。
- 验证:`npm run test -- src/services/puzzle-clear/puzzleClearLocalRuntime.test.ts src/components/puzzle-clear-runtime/PuzzleClearRuntimeShell.test.tsx` 通过,浏览器 smoke 页实测可完成一次消除并弹出“本关完成”。
- 关联:`src/services/puzzle-clear/puzzleClearLocalRuntime.ts``src/services/puzzle-clear/puzzleClearLocalRuntime.test.ts``src/components/puzzle-clear-runtime/PuzzleClearRuntimeShell.tsx``src/components/puzzle-clear-runtime/PuzzleClearRuntimeShell.test.tsx`
## 拼消消消除过渡不能隐藏已有卡片的最终下沉格
- 现象:消除补牌过程中偶尔看起来下方有空位,但同列上方卡片没有落下来。
- 原因:后端和本地 runtime 的重力补牌已经把已有卡片压到底;真正的问题在前端过渡层。消除动画曾按旧消除坐标隐藏棋盘格,掉落动画也曾隐藏所有 drop 目标格。当某个旧卡下沉到刚被消除的格子时,最终 snapshot 里的真实卡片会被隐藏,视觉上像补牌没有落下。
- 处理:消除 / 掉落覆盖层只负责动画表现,不再隐藏已有场上卡片的最终格;只有从顶部准备区新补入、前一帧棋盘不存在的卡片,才允许临时隐藏底层目标格来配合下落动画。
- 验证:`npm run test -- src/components/puzzle-clear-runtime/PuzzleClearRuntimeShell.test.tsx -t "已有卡片因重力下沉时目标格不被过渡状态隐藏成空位"`,并保留领域侧 `cargo test -p module-puzzle-clear refill --manifest-path server-rs/Cargo.toml`
- 关联:`src/components/puzzle-clear-runtime/PuzzleClearRuntimeShell.tsx``src/components/puzzle-clear-runtime/PuzzleClearRuntimeShell.test.tsx``server-rs/crates/module-puzzle-clear/src/application.rs``docs/technical/【玩法创作】拼消消玩法模板技术方案-2026-05-30.md`
## 拼消消完整消除反馈不要让补牌抢帧
- 现象:玩家正确拼完整组后,卡片几乎瞬间消失,顶部补牌马上出现或下落,导致“拼对了”的确认反馈很弱。
- 原因:前端一收到新 snapshot 就同时播放消除和掉落叠层,旧消除动画时长较短;新补入卡牌的下落延迟接近 0ms视觉上会抢在消除反馈之前开始。
- 处理:局部正确拼合但未消除时只给锁定组做一次高光;完整消除时让旧卡片在消除叠层中短暂放大展示再淡出;新补入卡牌的下落延迟到淡出尾段,并继续只隐藏新补入目标格,不隐藏已有场上卡片下沉后的最终格。
- 验证:`npm run test -- src/components/puzzle-clear-runtime/PuzzleClearRuntimeShell.test.tsx`,浏览器里确认局部拼合会闪、完整消除会放大淡出、补牌在淡出后段才开始掉落。
- 关联:`src/components/puzzle-clear-runtime/PuzzleClearRuntimeShell.tsx``src/index.css``src/components/puzzle-clear-runtime/PuzzleClearRuntimeShell.test.tsx`
## 首页推荐分流参数不能条件性调用 hook
- 现象:桌面首页或移动首页在 HMR、断点切换或重新渲染后直接报 React hook 顺序错误,页面停在“正在加载内容”。
@@ -1026,6 +1050,14 @@
- 验证:`cargo test -p api-server phone_auth_sms_provider_errors_keep_upstream_http_semantics --manifest-path server-rs/Cargo.toml`,真实 provider 频控时接口不再返回 `500`
- 关联:`server-rs/crates/module-auth/src/errors.rs``server-rs/crates/api-server/src/phone_auth.rs``docs/technical/PHONE_SMS_PROVIDER_ERROR_HTTP_MAPPING_FIX_2026-05-08.md`
## 本地短信 smoke 先确认 SMS provider
- 现象:浏览器里短信验证码发送成功,但提交 `123456` 仍然报验证码错误,或者短信登录后又回到未登录态。
- 原因:当前运行中的 `api-server` 如果读取到 `.env.local` 里的 `SMS_AUTH_PROVIDER=aliyun`,就会走真实短信 provider 口径;这时 mock 验证码 `123456` 不会被接受。之前本地调试时常见的误判是把 `.env.local` 改成 mock 了,但没有重启 `npm run dev`,或者旧的 `scripts/dev.mjs` 进程还在沿用旧环境。
- 处理:本地只做 UI / 账号链路 smoke 时,把 `.env.local` 显式设为 `SMS_AUTH_PROVIDER=mock` 且配置 `SMS_AUTH_MOCK_VERIFY_CODE=123456`,然后重启 `npm run dev``npm run dev:api-server`。要做真实短信联调时,再切回 `SMS_AUTH_PROVIDER=aliyun` 并重启。
- 验证:`POST /api/auth/phone/send-code` 应返回 `providerRequestId=mock-request-id``POST /api/auth/phone/login``123456` 应返回 `200``user.loginMethod=phone`。浏览器侧短信登录成功后,会先进入邀请码弹窗或我的页面,不应再提示“验证码错误”。
- 关联:`scripts/dev-utils.mjs``scripts/dev-utils.test.ts``scripts/dev.mjs``server-rs/crates/api-server/src/config.rs`
## 手机验证码登录成功后又瞬间回到未登录
- 现象:手机号验证码登录先成功,随后 UI 又闪回“未登录”,登录弹窗可能重新出现。
@@ -1853,6 +1885,124 @@
- 验证:`npx vitest run src/components/rpg-entry/RpgEntryHomeView.recharge.test.tsx` 覆盖访客态纵向滑动不弹登录且触发下一条推荐。
- 关联:`src/components/rpg-entry/RpgEntryHomeView.tsx``src/components/rpg-entry/RpgEntryHomeView.recharge.test.tsx`
## Windows junction worktree 下 Vitest 定向路径失败先切真实路径
- 现象:在 `C:\Users\...\ .codex\worktrees\...` 这类 junction 工作区运行 `npm run test -- src/...`Vitest 可能报 `Failed to load url C:/Users/... (resolved id: F:/DevWorktrees/...)`,同一测试文件明明存在却被判定找不到。
- 原因Vite / Vitest 在 Windows 下会把测试入口 realpath 到真实 worktree 路径;如果命令从 junction 路径传入相对文件参数,入口路径和 resolved id 可能跨盘符不一致。
- 处理:前端定向测试优先从 `Get-Item <worktree> | Format-List Target` 显示的真实路径运行,例如 `F:\DevWorktrees\codex\worktrees\f584\Genarrative`;不要把这类文件加载失败误判成组件或路由断言失败。
- 验证:同一命令从真实路径执行应正常收集并运行测试,例如 `npm run test -- src/components/puzzle-clear-runtime/PuzzleClearRuntimeShell.test.tsx`
- 关联:`src/components/puzzle-clear-creation/PuzzleClearWorkspace.test.tsx``src/components/puzzle-clear-result/PuzzleClearResultView.test.tsx``src/components/puzzle-clear-runtime/PuzzleClearRuntimeShell.test.tsx``src/routing/appPageRoutes.test.ts`
## 拼消消草稿试玩要和正式 runtime 分流
- 现象:拼消消结果页点击“试玩”后如果仍然调用 `/api/runtime/puzzle-clear/runs`,草稿试玩会被正式 run 规则和统计约束卡住,公开作品又可能和草稿恢复串台。
- 原因:拼消消既有草稿生成 / 结果页 / 发布闭环,也有正式公开 runtime如果把结果页试玩和公开运行态复用同一个后端 startRun 入口,`work detail` 读取路径和统计口径都会混在一起。
- 处理:结果页试玩改走前端本地 `runtimeMode=draft` snapshot只用于草稿试玩和关卡切换不写正式 run公开详情和推荐流进入正式 runtime 时才走后端 `/api/runtime/puzzle-clear/*`。客户端读取作品详情时也要区分创作详情 `/api/creation/puzzle-clear/works/{profileId}` 与公开运行态详情 `/api/runtime/puzzle-clear/works/{profileId}`
- 验证:点击拼消消结果页的试玩按钮,不应再请求 `/api/runtime/puzzle-clear/runs`;公开详情入口仍应能读取后端运行态详情。
- 关联:`src/components/platform-entry/PlatformEntryFlowShellImpl.tsx``src/services/puzzle-clear/puzzleClearClient.ts``src/services/puzzle-clear/puzzleClearLocalRuntime.ts``docs/prd/【玩法创作】拼消消玩法模板PRD-2026-05-30.md`
## 拼消消 runtime 必须继承拼图模板的原生交互基线
- 现象:拼消消卡片在浏览器里会出现原生图片拖拽 / 下载手柄,或窗口拉伸后棋盘和卡片被拉成矩形。
- 原因:拼消消 runtime 早期只继承了“交换 / 消除”的业务逻辑,没有完整继承拼图模板在基础交互上的防护:`touch-none``select-none``aspect-square``draggable={false}``onDragStart(event.preventDefault())``-webkit-user-drag: none`
- 处理:棋盘容器必须保持正方形约束,卡片按钮和内层 `<img>` 都要显式禁用浏览器原生拖拽,样式层也要补 `user-select: none``-webkit-user-drag: none`,不能只靠业务指针逻辑。
- 验证:浏览器中检查棋盘 `getBoundingClientRect().width === height`,卡片图片 `draggable="false"``-webkit-user-drag``none`;真实拖拽只应进入交换逻辑,不应触发原生图片拖拽。
- 关联:`src/components/puzzle-clear-runtime/PuzzleClearRuntimeShell.tsx``src/index.css``src/components/puzzle-runtime/PuzzleRuntimeShell.tsx``src/components/puzzle-clear-runtime/PuzzleClearRuntimeShell.test.tsx`
## 拼消消拖拽浮层要挂到页面级 portal
- 现象:拼消消拖拽时图片看起来没有贴在鼠标或手指上,尤其是平台壳层本身带有 transform 时更明显。
- 原因:拖拽 ghost 用了 `position: fixed`,但如果还挂在会被 transform 的局部容器里,浏览器会把 fixed 当成相对该祖先定位;`clientX/clientY` 读到的是视口坐标,两个坐标系一混就会出现肉眼可见的偏移。
- 处理:拖拽浮层必须通过 portal 挂到 `document.body` 这一层,再继续使用 `clientX/clientY - pointerOffset` 计算 left/top不要把 ghost 留在平台壳或任何会参与 transform 的容器里。
- 验证:`npm run test -- src/components/puzzle-clear-runtime/PuzzleClearRuntimeShell.test.tsx` 应断言拖拽浮层父节点是 `document.body`,且 left/top 与按下点偏移一致。
- 关联:`src/components/puzzle-clear-runtime/PuzzleClearRuntimeShell.tsx``src/components/puzzle-clear-runtime/PuzzleClearRuntimeShell.test.tsx`
## 拼消消要继承拼图模板的动作语言,不只是规则
- 现象:拼消消如果只实现“交换后裁决”,但没有开局翻牌、按下留空位、被替换卡快速飞回、以及局部拼接块整体拖动,玩家会直觉上觉得比原拼图更笨重。
- 原因:早期实现容易把“规则独立”误读成“动作语言也要重写”,结果只保留了交换逻辑,没有沿用拼图模板里已经验证过的拖拽反馈、空位让位和合并块连续感。
- 处理:拼消消运行态要继承拼图模板的基础手感:只在开局保留入场翻牌,拖起时源位立即呈空,放下时被替换卡要有明确飞向空位的位移感,连通块要作为整体拖动和整体呈现。
- 验证:浏览器拖拽时能看到跟手 ghost、源位空槽、落点飞入和整组拼接层`src/components/puzzle-clear-runtime/PuzzleClearRuntimeShell.test.tsx` 应覆盖这些行为。
- 关联:`src/components/puzzle-clear-runtime/PuzzleClearRuntimeShell.tsx``src/components/puzzle-clear-runtime/PuzzleClearRuntimeShell.test.tsx``src/index.css`
## 拼消消空格位必须允许落位,不能当成不可交互死格
- 现象:运行到某一关后,棋盘里出现空格位,用户能看见空洞但拖不进去,也点不动。
- 原因:空格位被前端交互或后端裁决误当成“无效目标”,只保留了交换逻辑,没有把“源卡落入空位、源位清空”当成合法移动。
- 处理:空格位必须保留 button 交互态和落点命中逻辑;前端拖拽 / 点击落到空格时直接提交移动,后端和本地 runtime 都要把源卡移动到目标格并清空源格,不再走失败交换。
- 验证:`npm run test -- src/components/puzzle-clear-runtime/PuzzleClearRuntimeShell.test.tsx``npm run test -- src/services/puzzle-clear/puzzleClearLocalRuntime.test.ts``cargo test -p module-puzzle-clear --manifest-path server-rs/Cargo.toml player_move_can_drop_card_into_empty_target_cell -- --nocapture`
- 关联:`src/components/puzzle-clear-runtime/PuzzleClearRuntimeShell.tsx``src/services/puzzle-clear/puzzleClearLocalRuntime.ts``server-rs/crates/module-puzzle-clear/src/application.rs`
## 拼消消空位落卡后必须立即补位,不能把空洞留成真空格
- 现象:卡牌成功落进空格后,源位仍然留空,玩家会误以为那个格子坏掉了。
- 原因:移动逻辑只处理了“落到空位”,没有在未消除时同步走一遍重力补位,所以源列会短暂或永久留下空洞。
- 处理:只要移动后棋盘存在空位,就立即走补位和可解性修复;这样源位会从顶部准备区补卡,不会留下不可交互空洞。
- 验证:`npm run test -- src/services/puzzle-clear/puzzleClearLocalRuntime.test.ts``cargo test -p module-puzzle-clear --manifest-path server-rs/Cargo.toml player_move_can_drop_card_into_empty_target_cell -- --nocapture`
- 关联:`src/services/puzzle-clear/puzzleClearLocalRuntime.ts``server-rs/crates/module-puzzle-clear/src/application.rs`
## 拼消消素材错位先查 sheet 质量门禁
- 现象:一张卡牌切片里同时出现两个或多个错位图案,或空白格、相邻编号区域里混入其他图案碎片。
- 原因provider 生成的 `1024x1536 / 4x6` 工作表可能违反视觉契约;旧流程只校验布局元数据和切片数量,无法发现图像内容已经主体缺失或污染空白格。边界贴边检测容易把正常铺满主体误判成跨格污染,不能作为高可靠硬门禁。
- 处理:先强化 atlas prompt要求每个 `256x256` 单元独立查看时只能包含一个主体或同一主体单一局部;服务端在 sheet 切片前做像素级质量门禁,硬拦截非空格前景占比过低和空白格污染,严重多边非同组边界贴边只记录 warning 供排查,不直接让创作失败。硬门禁失败的 sheet 最多尝试 4 次,仍失败则拒绝持久化脏 atlas。
- 追加处理:照片式微场景素材必须把每个 `256x256` 单元收束为一张完整的单场景照片裁片;同编号连续格表示同一视觉家族,不是随机独立小图,要求共享同一场景锚点、主色和道具语言。禁止单格内部出现两张照片、两个不同场景、拼接线、内部竖切、内部横切或左右 / 上下两块不同背景;质量门禁只在单格内部强色差直线贯穿大部分高度或宽度,且两侧都像低纹理人工平铺色块时,按“单格内部疑似拼接线”硬失败并重试 sheet避免把窗框、桌沿、地平线等自然场景强边缘误杀。
- 追加处理sheet 生成时如果 VectorEngine 返回 `retryable=true``502``504``429` 或请求超时,例如 nginx HTML `502 Bad Gateway`,不要立刻把草稿置为 failed应消耗同一 sheet 的下一次 attempt仍失败再回写失败状态。
- 追加处理:`sheet-03` 原本唯一空白格容易被模型画入主题主体,导致第 6 行第 4 列反复报“空白格有主体”并消耗多次 image2 请求。该格改为 `FILL` 补位格允许生成主题小图但服务端切片、atlas 合成和运行态全部丢弃;前端拼消消 action 等待窗口同步提高到 40 分钟,避免上游单图慢返回时用户侧 20 分钟超时。
- 验证:`cargo test -p api-server puzzle_clear --manifest-path server-rs/Cargo.toml -- --nocapture``cargo check -p api-server --manifest-path server-rs/Cargo.toml`
- 关联:`server-rs/crates/api-server/src/puzzle_clear.rs``docs/technical/【玩法创作】拼消消玩法模板技术方案-2026-05-30.md`
## 拼消消锁定组覆盖层必须锚定在棋盘本身
- 现象:消除或补牌过程中,局部完成的组图偶尔会看起来从格子里“飘出去”,并且大小会随着窗口和外层面板变化而异常拉伸。
- 原因:锁定组视觉层用了 `absolute inset-0`,但棋盘容器本身不是 `position: relative`,于是覆盖层实际锚到了更外层的运行态面板,`gridColumn` / `gridRow` 只能在错误坐标系里排版。
- 处理:棋盘容器必须显式 `relative`,让锁定组覆盖层、拖拽鬼影和格子坐标都在同一正方形棋盘坐标系内排版;不要把这类覆盖层锚到外层 `section` 或整页容器。
- 验证:浏览器里棋盘 `getBoundingClientRect()` 和锁定组覆盖层应共享同一块正方形区域,窗口缩放后组图不应再出现越界或被拉伸的现象;`PuzzleClearRuntimeShell.test.tsx` 需要断言棋盘 class 包含 `relative`
- 关联:`src/components/puzzle-clear-runtime/PuzzleClearRuntimeShell.tsx``src/components/puzzle-clear-runtime/PuzzleClearRuntimeShell.test.tsx`
## 拼消消中央场地底图必须挂在棋盘内部
- 现象:创作阶段选择了中央场地底图,但运行态消除卡片后只看到浅色格子或空点,看不到底图。
- 原因:底图被渲染成整页氛围背景,并被页面渐变、棋盘面板和格子 `bg-white/78` 遮住;棋盘内部没有静态底图层,空格仍保留不透明卡片底色。
- 处理:`boardBackgroundAsset.imageSrc` 必须作为 `puzzle-clear-board` 内部的 `absolute inset-0` 静态底图渲染;空格、消除空位和拖拽源位必须透明或近透明,不能继续使用实体卡片白底。
- 验证:`PuzzleClearRuntimeShell.test.tsx` 断言 `puzzle-clear-board-background` 在棋盘内,`/board-bg.png` 只出现一次,空格 class 包含 `bg-transparent` 且不包含 `bg-white/78`
- 关联:`src/components/puzzle-clear-runtime/PuzzleClearRuntimeShell.tsx``src/components/puzzle-clear-runtime/PuzzleClearRuntimeShell.test.tsx``docs/【玩法创作】平台入口与玩法链路-2026-05-15.md`
## 创作入口突然消失先查前后端是否串到不同 worktree
- 现象:`http://127.0.0.1:3000/` 可访问,但创作 Tab 里新增玩法入口消失;例如 `puzzle-clear` 已在代码默认种子中存在,浏览器仍看不到“拼消消”。
- 原因Vite 可能来自当前 worktree但代理目标的 `api-server` 仍是另一个 worktree 的旧进程,或者 `api-server` 连到旧 SpacetimeDB 模块;此时 `/api/creation-entry/config` 会返回旧入口配置。
- 处理:先用 `Get-NetTCPConnection -State Listen -LocalPort 3000,8083,3103` 结合 `Get-CimInstance Win32_Process` 确认端口进程路径;停止串线的旧 `api-server`,再用当前 worktree 的 `npm run dev:spacetime -- --spacetime-port <port> --database <database>``npm run dev:api-server -- --api-port <port> --spacetime-port <port> --database <database>` 拉起同一套服务。
- 验证:`GET /api/creation-entry/config` 应包含目标入口,且监听端口的命令行都指向同一个 worktree浏览器创作 Tab 对应分类应显示入口卡。
- 关联:`scripts/dev.mjs``.hermes/skills/genarrative-dev-stack-port-routing/SKILL.md``docs/【玩法创作】平台入口与玩法链路-2026-05-15.md`
## Windows junction 工作区下 dev.mjs 直接执行入口要用 realpath 判断
- 现象:在 `C:\Users\...\ .codex\worktrees\...` 这类 junction 路径里运行 `npm run dev:web`,进程会秒退,`3000` 不监听,但同一脚本从真实 worktree 路径能正常启动。
- 原因:`scripts/dev.mjs` 的入口判断只比对 `process.argv[1]``import.meta.url` 的字面路径junction 路径和 realpath 路径不一致时会误判成“不是直接执行”,于是主流程根本不进入。
- 处理:入口判断改成基于 `realpathSync(...)``isDirectModuleExecution(...)`,让 junction 路径和真实 worktree 路径指向同一个模块;同时补回归测试覆盖该场景。
- 验证:`npm run test -- scripts/dev.test.ts scripts/dev-stack-port-utils.test.ts` 通过后,`npm run dev:web -- --web-port 3000 --api-port 8083 --no-interactive` 应能稳定把 `0.0.0.0:3000` 监听起来。
- 关联:`scripts/dev.mjs``scripts/dev.test.ts`
## Vitest 定向测试在 Windows junction 工作区要切真实路径
- 现象:在 `C:\Users\...\ .codex\worktrees\...` 这类 junction 路径里跑 `npm run test -- src/...`Vitest 会报 `Failed to load url ... (resolved id: F:/DevWorktrees/...)`,看起来像文件不存在。
- 原因Vite / Vitest 会把入口 realpath 到真实 worktree 路径;如果命令从 junction 路径传入相对文件参数,入口路径和 resolved id 可能跨盘符不一致。
- 处理:前端定向测试优先从真实路径 `F:\DevWorktrees\codex\worktrees\f584\Genarrative` 运行,不要把这类文件加载失败误判成组件或路由断言失败。
- 验证:同一命令从真实路径执行应正常收集并运行测试。
- 关联:`src/components/puzzle-clear-creation/PuzzleClearWorkspace.test.tsx``src/components/puzzle-clear-runtime/PuzzleClearRuntimeShell.test.tsx``src/routing/appPageRoutes.test.ts`
- 现象:新增或扩展 `*-generating` 页面后,生成卡只渲染首帧,`已耗时` / `预计等待` 停在进入页那一刻不动。
- 原因:平台壳层的共享 `miniGameGenerationProgressNowMs` 时钟没有把新生成阶段纳入 tick 条件,或者该阶段的 `buildMiniGameDraftGenerationProgress(..., nowMs)` 没有接入同一时钟。
- 处理:任何共享生成页都要通过平台壳层统一的时钟判断和 `nowMs` 传递刷新,新增生成阶段时要同时补 `selectionStage` 判定、`useEffect` 依赖和进度调用点。
- 验证:浏览器里进入对应生成页后,`已耗时` / `预计等待` 应持续变化,不应停在首帧。
## 拼消消要用真实可消除判断,不要把“已相邻”当成可解
- 现象:拼消消开局或补牌后会直接出现已完成的图案组,或者 `1x2` 被当成半锁定局部留在场上。
- 原因:早期把可解性写成“场上已经有同组相邻卡”或“只要有一对相邻同组卡就算可解”,这会把已完成盘面误当成合法盘面;同时半锁定规则没有排除 `1x2`
- 处理:开局和补牌后的重排必须先排除现成消除,再用真实交换 / 落位模拟判断是否会产生新消除;`1x2` 永远不进入半锁定组,半锁定只允许 `1x3``2x2``2x3`
- 验证:`npm run test -- src/services/puzzle-clear/puzzleClearLocalRuntime.test.ts src/components/puzzle-clear-runtime/PuzzleClearRuntimeShell.test.tsx``cargo test -p module-puzzle-clear --manifest-path server-rs/Cargo.toml -- --nocapture` 通过后,开局盘面不应直接出现 completed group。
- 关联:`src/services/puzzle-clear/puzzleClearLocalRuntime.ts``server-rs/crates/module-puzzle-clear/src/application.rs`
## 推荐页作品 key 漏玩法会导致运行内容和标题作者错位
- 现象:移动端推荐页进入跳一跳或敲木鱼等作品时,游戏运行内容已经切到当前作品,但下方标题、作者和头像仍显示第一条拼图或其它推荐作品。

View File

@@ -1,6 +1,6 @@
# Genarrative 项目共享概览
更新时间:`2026-05-29`
更新时间:`2026-06-03`
## 一句话定位
@@ -10,6 +10,7 @@ Genarrative / 陶泥儿是一个 AI 原生互动内容与小游戏平台,把 A
- RPG / 自定义世界创作与运行时。
- 拼图玩法创作、草稿、发布、运行态和排行榜。
- 拼消消玩法创作、素材图集生成、结果页、发布、统一作品详情、正式运行态和基础统计。
- 敲木鱼玩法创作、草稿、发布、运行态、公开详情和分享码。
- 抓大鹅 Match3D 创作、2D 多视角素材生成、发布和运行态。
- 大鱼吃小鱼、方洞挑战、视觉小说、汪汪声浪和儿童向寓教于乐玩法。

View File

@@ -18,6 +18,32 @@ _Avoid_: 为每个玩法单独发明素材流水线、把系列素材建模成
## Language
### Puzzle Clear
**拼消消**:
基于拼图交换 / 拖拽手感的新玩法模板,玩家移动 1x1 卡牌碎片,把同一复合图案组拼成完整矩形后消除,并由顶部对应纵列补牌继续游玩。
_Avoid_: 拼图整图过关、三消槽位玩法、前端本地裁决
**复合图案组**:
拼消消中可被消除的一幅小图,由 `1x2``1x3``2x2``2x3` 的 1x1 卡牌碎片组成;只有组内碎片按正确相对位置拼成完整矩形后才消除。
_Avoid_: 单张卡牌、整关大图、任意相邻同色块
**1x1 卡牌碎片**:
复合图案组被服务端切成的最小可移动单位,带有所属组、形状、组内坐标和图片资产。
_Avoid_: 前端临时裁图、无所属图案的普通方块
**半锁定拼接组**:
非 2 格复合图案组中已经局部完成的拼接状态,可作为整体拖动;玩家用外部单格撞入组内某格时只交换该格,其余部分保留并退回半完成状态。
_Avoid_: 永久锁死、补牌打散、完整消除
**顶部卡牌准备区**:
拼消消棋盘上方按纵列排列的背面卡牌队列;某列产生空位时,准备区对应列的卡牌从顶部下落补齐。
_Avoid_: 全局随机发牌槽、底部三消槽
**防死局发牌**:
拼消消开局和每次补牌后由后端保证至少存在一步可拼接;补牌时至少有一张新掉落卡能与场上剩余某张卡对应。
_Avoid_: 前端提示代替可解性、完全随机补牌
### Wooden Fish
**敲木鱼**:

View File

@@ -0,0 +1,77 @@
# 拼消消玩法模板 PRD
日期:`2026-05-30`
## 目标
新增玩法模板 **拼消消**,工程域与 `playId` 均为 `puzzle-clear`,公开作品码前缀为 `PC-`。拼消消以拼图的交换 / 拖拽手感为原型,但运行态规则独立:玩家移动 1x1 卡牌碎片,把同一复合图案组拼成完整矩形后消除;消除产生空位后,由顶部对应纵列的卡牌准备区下落补位。
首版必须完成公开闭环:
```text
创作入口 -> 轻表单工作台 -> 独立生成页 -> 结果页 -> 试玩 -> 发布 -> 统一作品详情 -> 正式 runtime -> 基础统计 / 作品架 / 广场
```
## 创作工具平台接入声明
- 工作台模式:表单 / 图片输入创作工作台。
- 创作链路:入口 -> 工作台 -> 生成页 -> 结果页 -> 试玩 -> 发布 -> 运行态。
- 单图资产槽位:
- `board-background` / `ui-background` / `中央场地底图` / `boardBackgroundPrompt` 优先、空值时回退 `themePrompt`,并支持用户上传图 / 写回 `draft.boardBackgroundAsset``draft.boardBackgroundPrompt``work.boardBackgroundAsset``work.boardBackgroundPrompt` / 允许历史图 / 允许 AI 重绘。
- 中央场地底图的字段名沿用平台表面口径实际作用是玩家逐步消除清空中央棋盘后慢慢看到的主题目标图AI 生成尺寸必须与中央棋盘一致,使用 1:1 正方形画面。prompt 必须强绑定主题、画面精致、强表现力并一眼体现主题,带来探索、揭开全貌和追求目标完成的感受;不得继续要求“画面干净”或“适合作为卡牌棋盘底图”。
- 系列素材槽位:
- `batchId=puzzle-clear-pattern-atlas-v1`
- `sheetSpec`4 张素材工作表,每张 `1024x1536` 竖版,后台按 `4 列 x 6 行` 裁切,每个 1x1 单元为 `256x256`;服务端再把切片合成一张 `10x10 / 2560x2560` 最终 atlas。复合图案组总数为 `35`,形状配比 `1x2=23``1x3=5``2x2=4``2x3=3`,总计 `95` 个 1x1 卡牌切片。
- `slotSpecs`:每个复合图案组一个 `patternGroup`,服务端预排 `groupId``shape`、atlas 坐标和 1x1 切片坐标。
- 切图规则:生图 prompt 只要求复合图案组能按 4x6 素材工作表均等切成 1x1 方形小份,不允许模型在图上绘制切分线、边框、网格线或裁切参考线;服务端按 sheet 布局直接裁出 1x1 卡牌碎片,校验每个编号占格数与领域图案组面积一致,再合成最终 atlas写入 `patternGroups[]``cardAssets[]`
- 透明化规则:首版保留完整方形卡面,不强制透明化;若 provider 输出带边框、切分线、网格、裁切参考线或文字,生成任务失败并回写审计。
- 失败回写:生成页写回 `generationStatus=failed` 与失败阶段;结果页保留重试入口。
- 局部重生成v1 允许整批 4 张素材工作表重试,不做单组局部重生。
- API 命名空间:`/api/creation/puzzle-clear/...``/api/runtime/puzzle-clear/...`
- 业务真相草稿、发布、runtime snapshot、胜负、补牌、防死局、统计均由后端裁决前端只做动画和交互表现。
- 创作工具模式例外:无。
- 验证命令:`npm run check:encoding``npm run typecheck``npm run test -- src/services/puzzle-clear/puzzleClearLocalRuntime.test.ts``npm run test -- src/components/puzzle-clear-result/PuzzleClearResultView.test.tsx src/components/puzzle-clear-runtime/PuzzleClearRuntimeShell.test.tsx``cargo test -p module-puzzle-clear --manifest-path server-rs/Cargo.toml``cargo test -p api-server puzzle_clear --manifest-path server-rs/Cargo.toml -- --nocapture`;涉及 SpacetimeDB schema 后运行 `npm run spacetime:generate``npm run check:spacetime-runtime-access``npm run check:spacetime-schema``npm run check:server-rs-ddd`
## 工作台字段
| 字段 | 契约字段 | 默认值 | 校验 | 落库 |
| --- | --- | --- | --- | --- |
| 作品标题 | `workTitle` | 空 | 必填1-30 字 | session draft / work profile |
| 简介 | `workDescription` | 空 | 0-120 字 | session draft / work profile |
| 主题词 | `themePrompt` | 空 | 必填1-80 字 | 生成 prompt 与草稿 |
| 场地底图主题词 | `boardBackgroundPrompt` | 空 | 0-80 字;为空时底图生成回退 `themePrompt` | session draft / work profile / 主题目标图生成 prompt |
| 中央场地底图 | `boardBackgroundAsset` | 空 | 上传或 AI 生成至少一种 | 单图资产槽位 |
| AI 生成底图 | `generateBoardBackground` | `true` | boolean | 生成编排参数 |
规则参数不开放创作者编辑:棋盘尺寸、倒计时、消除次数、形状解锁、防死局发牌和半锁定规则固定。
## 运行规则
| 关卡 | 棋盘 | 目标消除 | 倒计时 | 解锁形状 |
| --- | --- | --- | --- | --- |
| 1 | 6x6 | 35 | 10 分钟 | 1x2、1x3、2x2、2x3 |
- 开局每个小格子从背面翻向正面。
- 可消除图由横向或纵向复合图案组组成,最小消除单位为两张图拼接。
- 完成一个复合图案组后,该组所有 1x1 卡牌碎片消除。
- 消除后空位按列由顶部卡牌准备区下落补齐。
- 每次补牌至少保证掉落卡中有一张可以与场上剩余某张卡拼接,防止死局。
- 非 2 格消除时,若场上已有局部完成的半锁定拼接组,补牌不得破坏它。
- 半锁定拼接组可整体拖动;玩家用外部单格撞入组内某格时,只交换该格,组其余部分保留,组状态退回半完成。
- 超时只判当前关失败,可重试当前关;完成 35 次目标并清空当前棋盘后整局完成。
## 结果页
结果页展示:素材 atlas、中央场地底图、发布状态、试玩入口和失败重试。结果页不写功能说明类文案不开放规则编辑器不新增排行榜配置。
## 统计
首版只记录正式 `published` run
- 开局。
- 全局完成。
- 当前关失败。
- 耗时。
- 消除统计。
草稿试玩不写正式统计不进入排行榜v1 不做排行榜。

View File

@@ -0,0 +1,123 @@
# 拼消消玩法模板技术方案
日期:`2026-05-30`
## 总体边界
拼消消使用独立工程域 `puzzle-clear`,不复用拼图运行态规则本体。实现按 DDD 分层:
- `module-puzzle-clear`:纯领域规则,覆盖图案组规划、棋盘、交换、半锁定、消除、补牌、防死局、关卡状态。
- `shared-contracts` / `packages/shared`工作台输入、生成素材、结果页、作品摘要、runtime snapshot 与 action DTO。
- `spacetime-module`session、work profile、runtime run、事件 / 统计、公开 source view。
- `spacetime-client`typed facade 与 row mapper。
- `api-server`Axum 路由、鉴权、入口熔断、生成编排、资产持久化、BFF。
- `platform-image` / OSS / asset object图片生成、切图、上传、换签和失败审计。
- 前端:轻表单、生成页、结果页与 runtime 动画,不承接正式业务真相。
## 资产生成方案
素材目标从“单张超大 atlas 生图”收敛为 4 张素材工作表,再由服务端合成最终 atlas
- image2 调用4 次,每次生成 1 张 `1024x1536` 竖版素材工作表。
- sheet 裁切:每张按 `4 列 x 6 行` 裁切,每个 1x1 单元为 `256x256`
- 最终 atlas服务端把 95 个切片按领域坐标合成 `10x10 / 2560x2560` PNG空单元保留浅色背景。
- 运行态素材:最终写回 `35` 个复合图案组和 `95` 个 1x1 卡牌切片;`sheet-03` 的第 6 行第 4 列为 `FILL` 补位格,只为填满 4x6 工作表,生成后会被服务端丢弃,不进入最终 atlas 或运行态卡牌。
服务端固定布局如下:
| 形状 | 数量 | 单组单元数 | 解锁 |
| --- | ---: | ---: | --- |
| 1x2 | 23 | 2 | 第 1 关 |
| 1x3 | 5 | 3 | 第 1 关 |
| 2x2 | 4 | 4 | 第 1 关 |
| 2x3 | 3 | 6 | 第 1 关 |
流程:
```text
主题词 / 场地底图主题词 / 用户底图 -> 4 张 sheet 坐标规划 -> gpt-image-2 生成素材工作表 -> 按 4x6 裁切 1x1 -> 合成最终 atlas -> atlas 与卡牌切片持久化 -> OSS / asset_object / bind -> session draft 回写
```
中央场地底图的 prompt 来源固定为:若用户填写 `boardBackgroundPrompt`AI 生成底图只读取该字段;若该字段为空,才回退读取 `themePrompt`。用户直接上传底图资产时不再用主题词重写该资产,只执行平台资产持久化与换签。中央场地底图在运行态不是普通棋盘衬底,而是玩家逐渐消除卡牌后露出的主题目标图;生成请求使用与中央棋盘一致的 1:1 正方形尺寸prompt 必须强调探索、揭开全貌、追求完成目标、精致主题主视觉和强主题表现,不写“画面干净”或“适合作为卡牌棋盘底图”。
### 素材工作表风险与切片验证
风险4x6 工作表 prompt 仍需要告诉 provider 编号布局;如果模型把布局理解成 UI 海报、说明图或卡牌模板,可能画出文字、编号、边框、切分线、贴纸外框或重复主体。若 provider 无法严格按布局输出,切片后可能出现跨格、主体贴边、重复图案、文字或图案错位。
验证策略:
- 生图 prompt 明确要求照片式构图 / 绘本式渲染的主题微场景拼图卡,每个 256x256 单元本身就是一张完整的单场景照片裁片,单元内部只能有一个连续画面,禁止出现两张照片、两个不同场景、拼接线、分割线、内部竖切、内部横切、左右 / 上下两块不同背景,场景变化只能发生在 256 单元边界上。
- 同编号连续格表示同一视觉家族,不是随机独立小图;同组格子要共享同一场景锚点、主色和道具语言,像同一套连拍或同一场景的不同局部,彼此能看出是同一个故事或场景家族。
- 同一张 sheet 内不同编号必须发散成不同视觉概念;以水果为例,应扩展为果园、集市摊位、野餐布、果汁杯、厨房案板、甜品盘、篮筐、玻璃罐、窗边餐桌、花园背景等微场景,禁止同品种主体换角度、换大小或换姿势后重复出现。
- 每个 256x256 小卡切片独立查看时也要有可辨识的背景纹理、桌面、草地、天空、建筑、布料、器皿、叶片、阴影或装饰元素,避免“孤立主体 + 纯色背景”导致运行态难区分。
- 生图 prompt 明确禁止文字、水印、UI、边框标签、切分线、网格线、裁切参考线、纯色背景、白底商品图、孤立主体、同品种重复和同一物体多角度。
- 复合图案组本身不画任何可见分割辅助线,但 prompt 必须说明每个 `1x2``1x3``2x2``2x3` 图案都能被服务端按等大的 1x1 方形单元切分;纵向 `1x2` 按横向切线分成两个 1x1横向 `1x2` 按纵向切线分成两个 1x1其他形状同理。图案组可以在语义上成组但不能把一张大图的照片边界或拼贴边界落在单个 1x1 单元内部。
- 服务端保留 `PuzzleClearPatternGroup` 坐标清单,切片前校验每个 sheet 正式编号出现次数等于领域图案组 `width * height`,并要求同编号区域是完整连续矩形;`FILL` 补位格不参与校验、切片、atlas 合成和运行态。
- 每张 sheet 生成后、正式切片前执行像素级质量门禁:非空格必须达到最低前景占比,空白格前景占比不得超阈值,单格内部明显人工拼贴式分割需要硬失败;内部强边缘检测必须同时满足“贯穿大部分高度或宽度”和“两侧近似低纹理平铺色块”,避免把照片式微场景里的窗框、桌沿、地平线等自然结构误杀。非同组边界前景贴边仅记录为质量提示,不作为硬失败,避免把模型正常铺满主体的图集误杀。
- 每张 sheet 生成最多尝试 4 次除质量门禁失败外VectorEngine 返回 `retryable=true``502``504``429` 或请求超时也应消耗下一次 sheet attempt避免上游 nginx 偶发 502 或单次拼贴式坏图直接把草稿置为 failed。
- 前端拼消消创作 action 的请求等待窗口为 40 分钟,用于覆盖 VectorEngine 单张图偶发 10 分钟以上的慢返回;这只是本地验收稳定性兜底,后续若继续优化体验,应把素材生成迁到后台任务 / 轮询进度链路。
- sheet 多次生成仍未通过硬质量门禁时,生成任务进入 `failed` 并写入错误原因;不得把明显空白格污染或主体缺失的工作表切成正式卡牌资产。
- 首版若当前 provider 无法稳定产出可切 atlas生成任务进入 `failed`,错误写入审计;不得退回前端假素材或绕过平台资产底座。
- 草稿编译和作品发布都必须拒绝缺失 atlas、缺失卡牌切片、空 `assetObjectId` / `imageObjectKey``placeholder` 占位资产;`spacetime-client` 不再为编译请求合成默认 atlas / card assets。
- 技术回退需要用户确认后才能改成更多 sheet、降低切片规格或改为逐图生成当前需求固定为 4 张 `1024x1536` sheet 与最终 `2560x2560` atlas。
## 领域规则
`module-puzzle-clear` 已固定以下规则:
- 关卡配置:单关 `6x6/35`600 秒。
- 图案组配比:`1x2=23``1x3=5``2x2=4``2x3=3`
- 开局随机铺满并保证至少一步可解。
- 补牌按列重力下落;补牌后仍保证至少一步可解。
- 完整图案组消除并清空对应格。
- 半锁定拼接组只由玩家主动交换 / 撞入打散,补牌不破坏。
- 超时失败只作用于当前单关,可重试;完成 35 次消除目标并清空棋盘后整局完成。
## API 命名空间
- `POST /api/creation/puzzle-clear/sessions`
- `GET /api/creation/puzzle-clear/sessions/{sessionId}`
- `POST /api/creation/puzzle-clear/sessions/{sessionId}/actions`
- `GET /api/creation/puzzle-clear/works`
- `GET /api/creation/puzzle-clear/works/{profileId}`
- `POST /api/creation/puzzle-clear/works/{profileId}/publish`
- `GET /api/runtime/puzzle-clear/works/{profileId}`
- `POST /api/runtime/puzzle-clear/runs`
- `POST /api/runtime/puzzle-clear/runs/{runId}/swap`
- `POST /api/runtime/puzzle-clear/runs/{runId}/retry-level`
- `POST /api/runtime/puzzle-clear/runs/{runId}/next-level`
- `POST /api/runtime/puzzle-clear/runs/{runId}/time-up`
api-server 路由熔断使用 SpacetimeDB 创作入口配置 `puzzle-clear`,不得新增前端硬编码事实源。
## Runtime 事件与统计载荷
正式 `published` run 记录开局、全局完成、当前关失败、耗时和消除统计。runtime action 返回的终态事件包括:
- `run-finished`:第 1 关完成并结束整局,结果 JSON 至少包含 `status``level``clears``clearDelta``elapsedMs`
- `level-failed`:当前关超时失败,结果 JSON 至少包含 `status``level``clears``clearDelta``elapsedMs`
草稿试玩只消费同一份 snapshot/action 结果做表现,不写正式统计。
## 前端阶段
新增阶段:
- `puzzle-clear-workspace` -> `/creation/puzzle-clear`
- `puzzle-clear-generating` -> `/creation/puzzle-clear/generating`
- `puzzle-clear-result` -> `/creation/puzzle-clear/result`
- `puzzle-clear-runtime` -> `/runtime/puzzle-clear`
runtime 移动端优先,首屏结构为顶部倒计时 / 单关铭牌、顶部列准备区、棋盘、失败 / 完成弹层。棋盘主网格、半锁定组覆盖层和消除 / 掉落覆盖层统一使用 1.5px 格间距。动画包括开场翻转、局部正确拼合高光、完整消除放大淡出和列补牌延迟下落,不再有下一关切换。消除和补牌动画只能作为当前后端 snapshot 的表现层覆盖;已有场上卡片因重力下沉后的最终格不得被旧消除坐标或掉落覆盖层隐藏,避免出现“下方空位但上方卡片未下落”的视觉假象;新补入卡牌应等完整消除淡出进入尾段后再播放下落反馈。
## 验证计划
- `cargo test -p module-puzzle-clear --manifest-path server-rs/Cargo.toml`
- `cargo test -p api-server puzzle_clear --manifest-path server-rs/Cargo.toml -- --nocapture`
- `cargo test -p spacetime-client --manifest-path server-rs/Cargo.toml puzzle_clear_compile_requires_real_atlas_assets_from_api_server`
- `npm run test -- src/services/puzzle-clear/puzzleClearLocalRuntime.test.ts`
- `npm run test -- src/components/puzzle-clear-result/PuzzleClearResultView.test.tsx src/components/puzzle-clear-runtime/PuzzleClearRuntimeShell.test.tsx`
- `npm run test -- src/routing/appPageRoutes.test.ts src/services/publicWorkCode.test.ts`
- `npm run check:encoding`
- `npm run typecheck`
- 接入 SpacetimeDB schema 后:`npm run spacetime:generate``npm run check:spacetime-runtime-access``npm run check:spacetime-schema``npm run check:server-rs-ddd`

View File

@@ -632,6 +632,45 @@ npm run check:server-rs-ddd
- Rust 结构体:`PuzzleWorkProfileRow`
- 源码:`server-rs/crates/spacetime-module/src/puzzle.rs`
### `puzzle_clear_agent_session`
- Rust 结构体:`PuzzleClearAgentSessionRow`
- 源码:`server-rs/crates/spacetime-module/src/puzzle_clear/tables.rs`
- 说明:拼消消创作会话表,保存轻表单草稿、生成状态、已发布 profile 关联和更新时间;只由拼消消 procedure 读写。
### `puzzle_clear_work_profile`
- Rust 结构体:`PuzzleClearWorkProfileRow`
- 源码:`server-rs/crates/spacetime-module/src/puzzle_clear/tables.rs`
- 说明:拼消消作品 profile 表保存中央底图资产、4 张素材工作表切片后合成的最终 atlas、35 个复合图案组、95 个 1x1 卡牌切片、卡背占位图、发布状态、可见性和基础 play count公开列表 / 详情只通过 read model 消费,不让前端直接订阅源表。
- 字段变更:`visible` 控制是否进入公开列表 / 详情,默认 `true`;旧迁移数据由 `migration.rs` 补默认值。
### `puzzle_clear_runtime_run`
- Rust 结构体:`PuzzleClearRuntimeRunRow`
- 源码:`server-rs/crates/spacetime-module/src/puzzle_clear/tables.rs`
- 说明:拼消消正式 runtime run 表,保存当前关卡、已消除次数、棋盘 snapshot、开始 / 完成时间和 run 状态;正式胜负、重试、完成、超时和交换结果以后端 procedure 裁决为准。
### `puzzle_clear_event`
- Rust 结构体:`PuzzleClearEventRow`
- 源码:`server-rs/crates/spacetime-module/src/puzzle_clear/tables.rs`
- 说明:拼消消基础 runtime 事件表,记录 published run 的开局、关卡完成、全局完成、失败、超时和消除统计来源;首版不做排行榜。
### SpacetimeDB view`puzzle_clear_gallery_view`
- Rust view`puzzle_clear_gallery_view`
- 返回类型:`Vec<PuzzleClearGalleryViewRow>`
- 源码:`server-rs/crates/spacetime-module/src/puzzle_clear.rs`
- 说明:拼消消公开详情 source 投影,只暴露 `publication_status = published``visible = true` 的作品,包含 atlas、底图、图案组和卡牌切片等详情级字段统一公开详情主路径通过 `public_work_detail_entry` 消费该 view只保留平台详情页展示摘要。
### SpacetimeDB view`puzzle_clear_gallery_card_view`
- Rust view`puzzle_clear_gallery_card_view`
- 返回类型:`Vec<PuzzleClearGalleryCardViewRow>`
- 源码:`server-rs/crates/spacetime-module/src/puzzle_clear.rs`
- 说明:拼消消公开列表 source 投影,只暴露平台卡片需要的公开字段;统一公开列表主路径通过 `public_work_gallery_entry` 消费该 view`/api/runtime/puzzle-clear/gallery` 保留玩法专属 HTTP shape。
### SpacetimeDB view`puzzle_gallery_view`
- Rust view`puzzle_gallery_view`
@@ -661,6 +700,7 @@ npm run check:server-rs-ddd
- `SELECT * FROM public_work_detail_entry`
- `SELECT * FROM bark_battle_gallery_view`
- `SELECT * FROM puzzle_gallery_card_view`
- `SELECT * FROM puzzle_clear_gallery_card_view`
- `SELECT * FROM jump_hop_gallery_card_view`
- `SELECT * FROM wooden_fish_gallery_card_view`
- `SELECT * FROM custom_world_gallery_entry`
@@ -677,6 +717,7 @@ npm run check:server-rs-ddd
- `SELECT * FROM public_work_play_daily_stat WHERE source_type = 'visual-novel'`
- `SELECT * FROM public_work_play_daily_stat WHERE source_type = 'big-fish'`
- `SELECT * FROM public_work_play_daily_stat WHERE source_type = 'bark-battle'`
- `SELECT * FROM public_work_play_daily_stat WHERE source_type = 'puzzle-clear'`
- `SELECT * FROM creation_entry_config`
- `SELECT * FROM creation_entry_type_config`
- `SELECT * FROM asset_object`

View File

@@ -1,4 +1,4 @@
# 本地开发验证与生产运维
# 本地开发验证与生产运维
更新时间:`2026-06-05`
@@ -51,6 +51,8 @@ Linux 本机多用户并发开发时,`npm run dev` 和 `npm run dev:*` 单模
开发态 `npm run dev``npm run dev:api-server` 会默认注入 `GENARRATIVE_DEV_PASSWORD_ENTRY_AUTO_REGISTER_ENABLED=true`,因此密码登录在本地开发环境可直接注册未知手机号账号;生产环境仍按 `api-server` 配置默认关闭该开关。
本地只做账号/UI smoke 且需要短信登录时,`SMS_AUTH_PROVIDER` 应显式设为 `mock`,并把 `SMS_AUTH_MOCK_VERIFY_CODE` 设为固定值(当前常用 `123456`),再重启 `npm run dev``npm run dev:api-server`。如果 `.env.local` 还保留 `SMS_AUTH_PROVIDER=aliyun``POST /api/auth/phone/login` 用 mock 验证码会稳定报“验证码错误”,不是前端表单问题。真实短信联调再切回 `aliyun` 并重启。
微信小程序虚拟支付使用 `WECHAT_MINI_PROGRAM_VIRTUAL_PAYMENT_OFFER_ID``WECHAT_MINI_PROGRAM_VIRTUAL_PAYMENT_APP_KEY``WECHAT_MINI_PROGRAM_VIRTUAL_PAYMENT_SANDBOX_APP_KEY``WECHAT_MINI_PROGRAM_VIRTUAL_PAYMENT_ENV` 配置。小程序充值统一走 `wechat_mp_virtual` / `wx.requestVirtualPayment`:泥点属于代币(`coin``buyQuantity` 按当前充值商品快照里的 `points_amount` 传;会员和后台新增道具类商品走 `short_series_goods``productId` 对应微信后台道具 ID。旧登录快照若缺 `session_key`,需要用户在小程序内重新登录后再支付;客户端成功回调不是最终到账,仍以后端通知或查询确认订单为准。详细口径见 `docs/【技术方案】微信虚拟支付接入-2026-05-26.md`
如果本地 `GET /api/creation-entry/config` 返回 `No such procedure`,或 `api-server` 日志出现 `no such table: puzzle_gallery_card_view` / `no such table: wooden_fish_gallery_card_view` 这类公开 view 缺失,通常是 `.env.local` 指向的 SpacetimeDB 库还没有发布当前 `spacetime-module`,或当前 CLI 身份无权发布该库。debug 构建的 `api-server` 会临时使用后端默认入口配置兜底,避免创作作品架整块消失;正式修复仍应切换到拥有目标库权限的 SpacetimeDB 身份后重新运行 `npm run dev` 完成发布,或用 gitignored 的 `spacetime.local.json` 指向可发布的本地库。

View File

@@ -195,6 +195,34 @@ RPG / 拼图等运行态存档仍以 `/api/profile/save-archives` 的后端列
平台首页推荐、精选、最新、公开详情、搜索、已玩作品和公开试玩统一按 `sourceType='wooden-fish'``WF-*` 公开作品号识别敲木鱼作品;公开列表应走 `wooden_fish_gallery_card_view` 订阅缓存,公开详情或运行态启动时卡片摘要不足则补读完整 work profile。
## 拼消消
对外名称:拼消消。工程域与 `playId``puzzle-clear`。公开作品码前缀:`PC-`。当前按新增玩法 SOP 接入完整公开闭环,不复用拼图运行态规则本体。
链路为:
```text
创作入口 -> 轻表单工作台 -> 生成过程页 -> 结果页 -> 试玩 -> 发布 -> 统一作品详情 -> 正式运行态
```
工作台字段固定为作品标题、简介、主题词、场地底图主题词 `boardBackgroundPrompt`、中央场地底图槽位、是否 AI 生成底图。中央场地底图必须复用 `CreativeImageInputPanel`,支持上传、历史图和 AI 重绘;若用户填写 `boardBackgroundPrompt`AI 生成底图只读取该字段,字段为空时才回退读取 `themePrompt`;用户上传底图时不再用主题词重写该资产。中央场地底图的字段名保留平台口径,但实际语义是玩家逐步消除清空棋盘后露出的主题目标图,生成尺寸必须与中央棋盘一致,按 1:1 正方形出图prompt 必须强绑定主题、画面精致、强表现力并一眼体现主题,不再要求“画面干净”或“适合作为卡牌棋盘底图”。运行态必须把中央场地底图作为棋盘内部静态底图使用,不能降级成整页氛围背景;卡牌消除后产生的空位和拖拽源位应露出该棋盘底图。卡面背面背景 v1 使用默认占位图,不作为创作者配置项。规则参数不开放编辑:单关 `6x6`、每局 10 分钟、35 次目标消除、形状解锁、防死局发牌和半锁定规则均由后端规则集固定。
素材生成使用拼消消专用编排,但必须复用 `platform-image`、VectorEngine `gpt-image-2`、OSS、`asset_object`、换签和失败审计。素材目标是 4 张 `1024x1536` 竖版工作表,每张后台按 `4 列 x 6 行` 裁切,每格 `256x256`;服务端从工作表切出总计 95 个 1x1 卡牌碎片,再合成一张 `10x10 / 2560x2560` 最终 atlas。复合图案组总数固定为 35形状配比固定为 `1x2=23``1x3=5``2x2=4``2x3=3`。服务端先预排每个复合图案组的 sheet 布局、最终 atlas 坐标和形状,再按坐标切成 1x1 卡牌碎片作为运行态素材sheet 生图 prompt 只能要求复合图案组可按后台 4x6 均等切成 1x1 方形小份,不能让模型在小图案上绘制切分线、边框、网格线、编号或裁切参考线。当前只有单关,同关内复合图案不重复。草稿编译和发布都必须使用 api-server 已持久化的真实 atlas / card assets拒绝缺失、空对象键或 `placeholder` 占位素材,不允许 `spacetime-client` 或 SpacetimeDB 侧合成临时素材绕过平台图片底座。
运行态规则:
1. 单关固定为 `6x6 / 35次消除`
2. 每局固定 10 分钟;超时只判当前关失败,可重试当前关。
3. 当前关直接出现 `1x2``1x3``2x2``2x3`
4. 开局棋盘随机铺满并保证至少一步可解;补牌后也必须由后端保证至少一步可解。
5. 顶部卡牌准备区按纵列补位,某列有空格时该列卡牌从顶部下落。
6. 非 2 格消除时,补牌不得破坏已完成局部;只有玩家主动交换或撞入才允许打散半锁定拼接组。
7. 正式 runtime 只消费后端 snapshot 与 action 结果;前端负责开局翻转、拖拽、掉落、消除和弹层动画。
拖拽手感必须对齐拼图模板:开局小卡片只翻转一次,交换落位不得重新翻牌;按住后可见卡片立即跟随鼠标或手指,源位置即时留出空槽;放下时被替换卡片要快速飞向对应空位;已完成局部拼接组要以连续整体呈现并可作为整组拖起。拖拽浮层必须挂到页面级 `document.body` portal避免平台壳层 transform 让 `position: fixed``clientX/clientY` 坐标系错位。
8. 正式 `published` run 的终态事件使用 `run-finished``level-failed`,事件结果 JSON 至少包含 `status``level``clears``clearDelta``elapsedMs`,供基础统计与排障回读。
新增阶段为 `puzzle-clear-workspace``puzzle-clear-generating``puzzle-clear-result``puzzle-clear-runtime`;路由为 `/creation/puzzle-clear``/creation/puzzle-clear/generating``/creation/puzzle-clear/result``/runtime/puzzle-clear`。API 命名空间为 `/api/creation/puzzle-clear/*``/api/runtime/puzzle-clear/*`。验证命令见 `docs/prd/【玩法创作】拼消消玩法模板PRD-2026-05-30.md``docs/technical/【玩法创作】拼消消玩法模板技术方案-2026-05-30.md`
## 抓大鹅 Match3D
对外名称:`抓大鹅`。工程域:`match3d`

View File

@@ -3,6 +3,7 @@ export type * from './creationAudio';
export type * from './hyper3d';
export type * from './jumpHop';
export type * from './puzzleCreativeTemplate';
export type * from './puzzleClear';
export type * from './publicWork';
export type * from './visualNovel';
export type * from './barkBattle';

View File

@@ -0,0 +1,226 @@
export type PuzzleClearGenerationStatus = 'draft' | 'generating' | 'ready' | 'failed';
export type PuzzleClearShapeKind = '1x2' | '1x3' | '2x2' | '2x3';
export type PuzzleClearOrientation = 'horizontal' | 'vertical';
export type PuzzleClearRunStatus =
| 'playing'
| 'level_failed'
| 'level_cleared'
| 'finished';
export type PuzzleClearActionType =
| 'compile-draft'
| 'regenerate-atlas'
| 'update-work-meta'
| 'update-board-background';
export interface PuzzleClearImageAsset {
assetId: string;
imageSrc: string;
imageObjectKey: string;
assetObjectId: string;
generationProvider: string;
prompt: string;
width: number;
height: number;
}
export interface PuzzleClearPatternGroup {
groupId: string;
shape: PuzzleClearShapeKind;
width: number;
height: number;
atlasX: number;
atlasY: number;
atlasWidth: number;
atlasHeight: number;
}
export interface PuzzleClearCardAsset {
cardId: string;
groupId: string;
shape: PuzzleClearShapeKind;
orientation: PuzzleClearOrientation;
partX: number;
partY: number;
imageSrc: string;
imageObjectKey: string;
assetObjectId: string;
sourceAtlasCell: string;
}
export interface PuzzleClearWorkspaceCreateRequest {
templateId: 'puzzle-clear' | string;
workTitle: string;
workDescription: string;
themePrompt: string;
boardBackgroundPrompt: string;
generateBoardBackground: boolean;
boardBackgroundAsset?: PuzzleClearImageAsset | null;
}
export interface PuzzleClearActionRequest {
actionType: PuzzleClearActionType;
profileId?: string | null;
workTitle?: string | null;
workDescription?: string | null;
themePrompt?: string | null;
boardBackgroundPrompt?: string | null;
generateBoardBackground?: boolean | null;
boardBackgroundAsset?: PuzzleClearImageAsset | null;
atlasAsset?: PuzzleClearImageAsset | null;
patternGroups?: PuzzleClearPatternGroup[] | null;
cardAssets?: PuzzleClearCardAsset[] | null;
}
export interface PuzzleClearDraftResponse {
templateId: string;
templateName: string;
profileId: string | null;
workTitle: string;
workDescription: string;
themePrompt: string;
boardBackgroundPrompt: string;
generateBoardBackground: boolean;
boardBackgroundAsset: PuzzleClearImageAsset | null;
cardBackImageSrc: string | null;
atlasAsset: PuzzleClearImageAsset | null;
patternGroups: PuzzleClearPatternGroup[];
cardAssets: PuzzleClearCardAsset[];
generationStatus: PuzzleClearGenerationStatus;
}
export interface PuzzleClearSessionSnapshotResponse {
sessionId: string;
ownerUserId: string;
status: PuzzleClearGenerationStatus;
draft: PuzzleClearDraftResponse | null;
createdAt: string;
updatedAt: string;
}
export interface PuzzleClearSessionResponse {
session: PuzzleClearSessionSnapshotResponse;
}
export interface PuzzleClearActionResponse {
actionType: PuzzleClearActionType;
session: PuzzleClearSessionSnapshotResponse;
work: PuzzleClearWorkProfileResponse | null;
}
export interface PuzzleClearWorkSummaryResponse {
runtimeKind: 'puzzle-clear';
workId: string;
profileId: string;
ownerUserId: string;
sourceSessionId: string | null;
workTitle: string;
workDescription: string;
themePrompt: string;
coverImageSrc: string | null;
publicationStatus: string;
playCount: number;
updatedAt: string;
publishedAt: string | null;
publishReady: boolean;
generationStatus: PuzzleClearGenerationStatus;
}
export interface PuzzleClearWorkProfileResponse {
summary: PuzzleClearWorkSummaryResponse;
draft: PuzzleClearDraftResponse;
boardBackgroundAsset: PuzzleClearImageAsset | null;
atlasAsset: PuzzleClearImageAsset;
patternGroups: PuzzleClearPatternGroup[];
cardAssets: PuzzleClearCardAsset[];
}
export interface PuzzleClearWorksResponse {
items: PuzzleClearWorkSummaryResponse[];
}
export interface PuzzleClearWorkDetailResponse {
item: PuzzleClearWorkProfileResponse;
}
export interface PuzzleClearWorkMutationResponse {
item: PuzzleClearWorkProfileResponse;
}
export interface PuzzleClearGalleryCardResponse
extends PuzzleClearWorkSummaryResponse {
publicWorkCode?: string;
authorDisplayName?: string;
recentPlayCount7d?: number;
}
export interface PuzzleClearGalleryResponse {
items: PuzzleClearGalleryCardResponse[];
hasMore: boolean;
nextCursor: string | null;
}
export interface PuzzleClearGalleryDetailResponse {
item: PuzzleClearWorkProfileResponse;
}
export interface PuzzleClearBoardCell {
row: number;
col: number;
card: PuzzleClearCardAsset | null;
lockedGroupId: string | null;
}
export interface PuzzleClearBoardSnapshot {
rows: number;
cols: number;
cells: PuzzleClearBoardCell[];
}
export interface PuzzleClearRuntimeSnapshotResponse {
runId: string;
profileId: string;
ownerUserId: string;
runtimeMode?: 'draft' | 'published';
status: PuzzleClearRunStatus;
levelIndex: number;
clearsDone: number;
targetClears: number;
levelDurationSeconds: number;
levelStartedAtMs: number;
board: PuzzleClearBoardSnapshot;
readyColumns: PuzzleClearCardAsset[][];
startedAtMs: number;
finishedAtMs: number | null;
}
export interface PuzzleClearRunResponse {
run: PuzzleClearRuntimeSnapshotResponse;
}
export interface PuzzleClearStartRunRequest {
profileId: string;
}
export interface PuzzleClearSwapRequest {
fromRow: number;
fromCol: number;
toRow: number;
toCol: number;
clientActionId: string;
}
export interface PuzzleClearRetryLevelRequest {
clientActionId: string;
}
export interface PuzzleClearNextLevelRequest {
clientActionId: string;
}
export interface PuzzleClearTimeUpRequest {
clientActionId: string;
}

View File

@@ -477,8 +477,14 @@ function getChangedFiles(baseRef) {
const diffOutput = tryGit(['diff', '--name-only', '-z', baseRef, '--']) ?? '';
const untrackedOutput =
tryGit(['ls-files', '--others', '--exclude-standard', '-z', moduleSrcRoot]) ?? '';
const untrackedBindingsOutput =
tryGit(['ls-files', '--others', '--exclude-standard', '-z', bindingsRoot]) ?? '';
return new Set(
[...diffOutput.split(/\u0000/u), ...untrackedOutput.split(/\u0000/u)]
[
...diffOutput.split(/\u0000/u),
...untrackedOutput.split(/\u0000/u),
...untrackedBindingsOutput.split(/\u0000/u),
]
.map(normalizePath)
.filter(Boolean),
);

View File

@@ -88,6 +88,29 @@ describe('dev utils env merge', () => {
);
});
test('本地短信 smoke 可以用 mock 验证码覆盖真实短信 provider 口径', () => {
withTempEnvFiles(
{
'.env.local': [
'SMS_AUTH_ENABLED=true',
'SMS_AUTH_PROVIDER=mock',
'SMS_AUTH_MOCK_VERIFY_CODE=123456',
].join('\n'),
},
(_env, tempDir) => {
const env = mergeApiServerEnv(tempDir, {
SMS_AUTH_ENABLED: 'true',
SMS_AUTH_PROVIDER: 'aliyun',
SMS_AUTH_MOCK_VERIFY_CODE: '654321',
});
expect(env.SMS_AUTH_ENABLED).toBe('true');
expect(env.SMS_AUTH_PROVIDER).toBe('mock');
expect(env.SMS_AUTH_MOCK_VERIFY_CODE).toBe('123456');
},
);
});
test('空外层 shell 变量不会遮蔽本地私密配置', () => {
withTempEnvFiles(
{

View File

@@ -5,6 +5,7 @@ import {
existsSync,
readdirSync,
readFileSync,
realpathSync,
statSync,
watch,
writeFileSync,
@@ -2046,6 +2047,36 @@ function normalizePath(path) {
return path.replace(/\\/gu, '/');
}
function normalizeDirectExecutionPath(path) {
return normalizePath(path).replace(/^\/([A-Za-z]:\/)/u, '$1');
}
function safeRealpath(pathValue) {
try {
return realpathSync(pathValue);
} catch {
return resolve(pathValue);
}
}
function isDirectModuleExecution(argv1, moduleUrl, resolvePath = safeRealpath) {
if (!argv1) {
return false;
}
try {
return (
normalizeDirectExecutionPath(resolvePath(argv1)) ===
normalizeDirectExecutionPath(resolvePath(fileURLToPath(moduleUrl)))
);
} catch {
return (
normalizeDirectExecutionPath(resolve(argv1)) ===
normalizeDirectExecutionPath(fileURLToPath(moduleUrl))
);
}
}
function buildSpacetimePublishArgs({database, server, preserveDatabase}) {
const args = [
'publish',
@@ -2098,6 +2129,7 @@ export {
createDevServerSpawnOptions,
createWatchConfigs,
isSpacetimePublishPermissionError,
isDirectModuleExecution,
parseSpacetimeToolVersion,
parseArgs,
resolveDevStackStatePath,
@@ -2129,6 +2161,6 @@ async function main() {
}
}
if (process.argv[1] && resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
if (isDirectModuleExecution(process.argv[1], import.meta.url)) {
void main();
}

View File

@@ -13,6 +13,7 @@ import {
buildSpacetimePublishArgs,
createDevServerSpawnOptions,
createWatchConfigs,
isDirectModuleExecution,
isSpacetimePublishPermissionError,
parseSpacetimeToolVersion,
parseArgs,
@@ -39,6 +40,19 @@ function workspaceSpacetimeVersionForTest() {
describe('dev scheduler argument routing', () => {
const linuxTest = process.platform === 'linux' ? test : test.skip;
test('Windows junction 路径下的直接执行入口也能识别为当前模块', () => {
const moduleUrl =
'file:///F:/DevWorktrees/codex/worktrees/f584/Genarrative/scripts/dev.mjs';
const argv1 =
'C:\\Users\\wuxiangwanzi\\.codex\\worktrees\\f584\\Genarrative\\scripts\\dev.mjs';
const resolvePath = (value) =>
value.startsWith('C:\\Users\\')
? 'F:\\DevWorktrees\\codex\\worktrees\\f584\\Genarrative\\scripts\\dev.mjs'
: value;
expect(isDirectModuleExecution(argv1, moduleUrl, resolvePath)).toBe(true);
});
test('完整 dev 栈覆盖前端代理到本次解析出的 api-server 地址', () => {
const {command, explicitOptions, options} = parseArgs([], {
GENARRATIVE_API_PORT: '8090',

12
server-rs/Cargo.lock generated
View File

@@ -113,6 +113,7 @@ dependencies = [
"module-match3d",
"module-npc",
"module-puzzle",
"module-puzzle-clear",
"module-runtime",
"module-runtime-item",
"module-runtime-story",
@@ -1971,6 +1972,15 @@ dependencies = [
"spacetimedb",
]
[[package]]
name = "module-puzzle-clear"
version = "0.1.0"
dependencies = [
"serde",
"shared-kernel",
"spacetimedb",
]
[[package]]
name = "module-quest"
version = "0.1.0"
@@ -3416,6 +3426,7 @@ dependencies = [
"module-match3d",
"module-npc",
"module-puzzle",
"module-puzzle-clear",
"module-runtime",
"module-runtime-item",
"module-runtime-story",
@@ -3451,6 +3462,7 @@ dependencies = [
"module-npc",
"module-progression",
"module-puzzle",
"module-puzzle-clear",
"module-quest",
"module-runtime",
"module-runtime-item",

View File

@@ -22,6 +22,7 @@ members = [
"crates/module-match3d",
"crates/module-npc",
"crates/module-puzzle",
"crates/module-puzzle-clear",
"crates/module-progression",
"crates/module-quest",
"crates/module-runtime",
@@ -68,6 +69,7 @@ module-match3d = { path = "crates/module-match3d", default-features = false }
module-npc = { path = "crates/module-npc", default-features = false }
module-progression = { path = "crates/module-progression", default-features = false }
module-puzzle = { path = "crates/module-puzzle", default-features = false }
module-puzzle-clear = { path = "crates/module-puzzle-clear", default-features = false }
module-quest = { path = "crates/module-quest", default-features = false }
module-runtime = { path = "crates/module-runtime", default-features = false }
module-runtime-item = { path = "crates/module-runtime-item", default-features = false }

View File

@@ -29,6 +29,7 @@ module-inventory = { workspace = true }
module-match3d = { workspace = true }
module-npc = { workspace = true }
module-puzzle = { workspace = true }
module-puzzle-clear = { workspace = true }
module-runtime = { workspace = true }
module-runtime-story = { workspace = true }
module-runtime-item = { workspace = true }

View File

@@ -67,6 +67,7 @@ pub fn build_router(state: AppState) -> Router {
.merge(modules::jump_hop::router(state.clone()))
.merge(modules::wooden_fish::router(state.clone()))
.merge(modules::public_work::router(state.clone()))
.merge(modules::puzzle_clear::router(state.clone()))
.merge(modules::puzzle::router(state.clone()))
.merge(visual_novel_router(state.clone()))
.route(
@@ -4135,8 +4136,7 @@ mod tests {
.await
.expect("banners body should collect")
.to_bytes();
let payload: Value =
serde_json::from_slice(&body).expect("banners payload should be json");
let payload: Value = serde_json::from_slice(&body).expect("banners payload should be json");
assert_eq!(payload["eventBanners"][0]["title"], "后台表单公告");
assert_eq!(payload["eventBanners"][0]["renderMode"], "html");

View File

@@ -77,17 +77,13 @@ pub fn resolve_creation_entry_route_id(path: &str) -> Option<&'static str> {
{
return Some("puzzle");
}
if normalized.starts_with("/api/runtime/puzzle/gallery/")
&& normalized.ends_with("/remix")
{
if normalized.starts_with("/api/runtime/puzzle/gallery/") && normalized.ends_with("/remix") {
return Some("puzzle");
}
if normalized == "/api/runtime/big-fish/agent/sessions" {
return Some("big-fish");
}
if normalized.starts_with("/api/runtime/big-fish/gallery/")
&& normalized.ends_with("/remix")
{
if normalized.starts_with("/api/runtime/big-fish/gallery/") && normalized.ends_with("/remix") {
return Some("big-fish");
}
if normalized == "/api/runtime/custom-world/agent/sessions"
@@ -115,6 +111,9 @@ pub fn resolve_creation_entry_route_id(path: &str) -> Option<&'static str> {
if normalized == "/api/creation/jump-hop/sessions" {
return Some("jump-hop");
}
if normalized == "/api/creation/puzzle-clear/sessions" {
return Some("puzzle-clear");
}
if normalized == "/api/creation/visual-novel/sessions" {
return Some("visual-novel");
}
@@ -178,6 +177,10 @@ mod tests {
resolve_creation_entry_route_id("/api/runtime/puzzle/agent/sessions"),
Some("puzzle"),
);
assert_eq!(
resolve_creation_entry_route_id("/api/creation/puzzle-clear/sessions"),
Some("puzzle-clear"),
);
assert_eq!(
resolve_creation_entry_route_id("/api/runtime/puzzle/gallery/profile-1/remix"),
Some("puzzle"),
@@ -236,6 +239,10 @@ mod tests {
resolve_creation_entry_route_id("/api/runtime/wooden-fish/runs/run-1"),
None,
);
assert_eq!(
resolve_creation_entry_route_id("/api/runtime/puzzle-clear/runs/run-1"),
None,
);
assert_eq!(
resolve_creation_entry_route_id("/api/creation/wooden-fish/sessions"),
Some("wooden-fish"),

View File

@@ -42,6 +42,10 @@ impl AppError {
&self.message
}
pub fn details(&self) -> Option<&Value> {
self.details.as_ref()
}
pub fn body_text(&self) -> String {
// 批处理任务不能只读 HTTP 状态文案,否则 DashScope 返回的真实失败原因会被压成“上游服务请求失败”。
self.details

View File

@@ -64,6 +64,7 @@ mod prompt;
mod public_work;
mod puzzle;
mod puzzle_agent_turn;
mod puzzle_clear;
mod puzzle_gallery_cache;
mod refresh_session;
mod registration_reward;

View File

@@ -13,6 +13,7 @@ pub mod platform;
pub mod profile;
pub mod public_work;
pub mod puzzle;
pub mod puzzle_clear;
pub mod square_hole;
pub mod story;
pub mod wooden_fish;

View File

@@ -0,0 +1,116 @@
use axum::{
Router, middleware,
routing::{get, post},
};
use crate::{
auth::{require_bearer_auth, require_runtime_principal_auth},
puzzle_clear::{
advance_puzzle_clear_next_level, create_puzzle_clear_session, execute_puzzle_clear_action,
get_puzzle_clear_gallery_detail, get_puzzle_clear_run, get_puzzle_clear_runtime_work,
get_puzzle_clear_session, get_puzzle_clear_work, list_puzzle_clear_gallery,
list_puzzle_clear_works, mark_puzzle_clear_level_time_up, publish_puzzle_clear_work,
retry_puzzle_clear_level, start_puzzle_clear_run, swap_puzzle_clear_cards,
},
state::AppState,
};
pub fn router(state: AppState) -> Router<AppState> {
Router::new()
.route(
"/api/creation/puzzle-clear/sessions",
post(create_puzzle_clear_session).route_layer(middleware::from_fn_with_state(
state.clone(),
require_bearer_auth,
)),
)
.route(
"/api/creation/puzzle-clear/sessions/{session_id}",
get(get_puzzle_clear_session).route_layer(middleware::from_fn_with_state(
state.clone(),
require_bearer_auth,
)),
)
.route(
"/api/creation/puzzle-clear/sessions/{session_id}/actions",
post(execute_puzzle_clear_action).route_layer(middleware::from_fn_with_state(
state.clone(),
require_bearer_auth,
)),
)
.route(
"/api/creation/puzzle-clear/works",
get(list_puzzle_clear_works).route_layer(middleware::from_fn_with_state(
state.clone(),
require_bearer_auth,
)),
)
.route(
"/api/creation/puzzle-clear/works/{profile_id}",
get(get_puzzle_clear_work).route_layer(middleware::from_fn_with_state(
state.clone(),
require_bearer_auth,
)),
)
.route(
"/api/creation/puzzle-clear/works/{profile_id}/publish",
post(publish_puzzle_clear_work).route_layer(middleware::from_fn_with_state(
state.clone(),
require_bearer_auth,
)),
)
.route(
"/api/runtime/puzzle-clear/works/{profile_id}",
get(get_puzzle_clear_runtime_work),
)
.route(
"/api/runtime/puzzle-clear/runs",
post(start_puzzle_clear_run).route_layer(middleware::from_fn_with_state(
state.clone(),
require_runtime_principal_auth,
)),
)
.route(
"/api/runtime/puzzle-clear/runs/{run_id}",
get(get_puzzle_clear_run).route_layer(middleware::from_fn_with_state(
state.clone(),
require_runtime_principal_auth,
)),
)
.route(
"/api/runtime/puzzle-clear/runs/{run_id}/swap",
post(swap_puzzle_clear_cards).route_layer(middleware::from_fn_with_state(
state.clone(),
require_runtime_principal_auth,
)),
)
.route(
"/api/runtime/puzzle-clear/runs/{run_id}/retry-level",
post(retry_puzzle_clear_level).route_layer(middleware::from_fn_with_state(
state.clone(),
require_runtime_principal_auth,
)),
)
.route(
"/api/runtime/puzzle-clear/runs/{run_id}/next-level",
post(advance_puzzle_clear_next_level).route_layer(middleware::from_fn_with_state(
state.clone(),
require_runtime_principal_auth,
)),
)
.route(
"/api/runtime/puzzle-clear/runs/{run_id}/time-up",
post(mark_puzzle_clear_level_time_up).route_layer(middleware::from_fn_with_state(
state.clone(),
require_runtime_principal_auth,
)),
)
.route(
"/api/runtime/puzzle-clear/gallery",
get(list_puzzle_clear_gallery),
)
.route(
"/api/runtime/puzzle-clear/gallery/{public_work_code}",
get(get_puzzle_clear_gallery_detail),
)
}

File diff suppressed because it is too large Load Diff

View File

@@ -1391,6 +1391,7 @@ fn current_utc_micros() -> i64 {
#[cfg(test)]
mod tests {
use super::*;
use crate::AppConfig;
use base64::{Engine as _, engine::general_purpose::STANDARD as BASE64_STANDARD};
#[test]
@@ -1562,8 +1563,7 @@ mod tests {
#[tokio::test]
async fn wooden_fish_draft_uses_default_hit_sound_asset_and_ignores_prompt() {
let state = crate::state::AppState::new(crate::config::AppConfig::default())
.expect("state should build");
let state = AppState::new(AppConfig::default()).expect("state should build");
let payload = WoodenFishWorkspaceCreateRequest {
template_id: WOODEN_FISH_TEMPLATE_ID.to_string(),
work_title: "今日敲木鱼".to_string(),

View File

@@ -1051,6 +1051,36 @@ impl InMemoryAuthStore {
.map_err(RefreshSessionError::Store)
}
fn resolve_phone_user_locked(
state: &mut InMemoryAuthStoreState,
phone_number: &str,
) -> Option<StoredPasswordUser> {
if let Some(user_id) = state.phone_to_user_id.get(phone_number).cloned() {
if let Some(stored_user) = state
.users_by_username
.values()
.find(|stored_user| stored_user.user.id == user_id)
.cloned()
{
return Some(stored_user);
}
state.phone_to_user_id.remove(phone_number);
}
let Some(stored_user) = state
.users_by_username
.values()
.find(|stored_user| stored_user.phone_number.as_deref() == Some(phone_number))
.cloned()
else {
return None;
};
state
.phone_to_user_id
.insert(phone_number.to_string(), stored_user.user.id.clone());
Some(stored_user)
}
fn find_by_user_id(
&self,
user_id: &str,
@@ -1148,36 +1178,24 @@ impl InMemoryAuthStore {
&self,
phone_number: &str,
) -> Result<Option<StoredPasswordUser>, PhoneAuthError> {
let state = self
let mut state = self
.inner
.lock()
.map_err(|_| PhoneAuthError::Store("用户仓储锁已中毒".to_string()))?;
let Some(user_id) = state.phone_to_user_id.get(phone_number) else {
return Ok(None);
};
Ok(state
.users_by_username
.values()
.find(|stored_user| stored_user.user.id == *user_id)
.map(|stored_user| hydrate_private_auth_fields(&state, stored_user)))
Ok(Self::resolve_phone_user_locked(&mut state, phone_number)
.map(|stored_user| hydrate_private_auth_fields(&state, &stored_user)))
}
fn find_by_phone_number_for_password(
&self,
phone_number: &str,
) -> Result<Option<StoredPasswordUser>, PasswordEntryError> {
let state = self
let mut state = self
.inner
.lock()
.map_err(|_| PasswordEntryError::Store("用户仓储锁已中毒".to_string()))?;
let Some(user_id) = state.phone_to_user_id.get(phone_number) else {
return Ok(None);
};
Ok(state
.users_by_username
.values()
.find(|stored_user| stored_user.user.id == *user_id)
.map(|stored_user| hydrate_private_auth_fields(&state, stored_user)))
Ok(Self::resolve_phone_user_locked(&mut state, phone_number)
.map(|stored_user| hydrate_private_auth_fields(&state, &stored_user)))
}
fn update_user_profile(
@@ -1220,17 +1238,10 @@ impl InMemoryAuthStore {
.inner
.lock()
.map_err(|_| PhoneAuthError::Store("用户仓储锁已中毒".to_string()))?;
if let Some(existing_user_id) = state.phone_to_user_id.get(&phone_number.e164).cloned() {
let existing_user_exists = state
.users_by_username
.values()
.any(|stored_user| stored_user.user.id == existing_user_id);
if existing_user_exists {
return Err(PhoneAuthError::Store(
"手机号已存在,无法重复创建账号".to_string(),
));
}
state.phone_to_user_id.remove(&phone_number.e164);
if Self::resolve_phone_user_locked(&mut state, &phone_number.e164).is_some() {
return Err(PhoneAuthError::Store(
"手机号已存在,无法重复创建账号".to_string(),
));
}
let created_at = format_rfc3339(OffsetDateTime::now_utc()).map_err(|message| {
@@ -1284,15 +1295,8 @@ impl InMemoryAuthStore {
.inner
.lock()
.map_err(|_| PasswordEntryError::Store("用户仓储锁已中毒".to_string()))?;
if let Some(existing_user_id) = state.phone_to_user_id.get(&phone_number.e164).cloned() {
let existing_user_exists = state
.users_by_username
.values()
.any(|stored_user| stored_user.user.id == existing_user_id);
if existing_user_exists {
return Err(PasswordEntryError::InvalidCredentials);
}
state.phone_to_user_id.remove(&phone_number.e164);
if Self::resolve_phone_user_locked(&mut state, &phone_number.e164).is_some() {
return Err(PasswordEntryError::InvalidCredentials);
}
let created_at = format_rfc3339(OffsetDateTime::now_utc()).map_err(|message| {
@@ -1760,7 +1764,9 @@ impl InMemoryAuthStore {
.lock()
.map_err(|_| PhoneAuthError::Store("用户仓储锁已中毒".to_string()))?;
let existing_phone_user_id = state.phone_to_user_id.get(&phone_number.e164).cloned();
let existing_phone_user_id =
Self::resolve_phone_user_locked(&mut state, &phone_number.e164)
.map(|stored_user| stored_user.user.id);
if let Some(target_user_id) = existing_phone_user_id
&& target_user_id != pending_user_id
{
@@ -2170,10 +2176,8 @@ impl InMemoryAuthStore {
.inner
.lock()
.map_err(|_| PhoneAuthError::Store("用户仓储锁已中毒".to_string()))?;
let user_id = state
.phone_to_user_id
.get(phone_number)
.cloned()
let user_id = Self::resolve_phone_user_locked(&mut state, phone_number)
.map(|stored_user| stored_user.user.id)
.ok_or(PhoneAuthError::UserNotFound)?;
for stored_user in state.users_by_username.values_mut() {
@@ -2694,6 +2698,90 @@ mod tests {
assert_eq!(error, PhoneAuthError::UserNotFound);
}
#[tokio::test]
async fn dev_password_registration_ignores_orphan_phone_index() {
let snapshot = PersistentAuthStoreSnapshot {
next_user_id: 7,
users_by_username: HashMap::new(),
phone_to_user_id: HashMap::from([(
"+8613800138004".to_string(),
"user_deleted".to_string(),
)]),
sessions_by_id: HashMap::new(),
session_id_by_refresh_token_hash: HashMap::new(),
wechat_identity_by_provider_uid: HashMap::new(),
user_id_by_provider_union_id: HashMap::new(),
};
let snapshot_json =
serde_json::to_string(&snapshot).expect("snapshot json should serialize");
let service = build_password_service(
InMemoryAuthStore::from_snapshot_json(&snapshot_json).expect("snapshot should restore"),
);
let created = service
.execute_with_dev_registration(PasswordEntryInput {
phone_number: "13800138004".to_string(),
password: "secret123".to_string(),
})
.await
.expect("orphan phone index should not block dev registration");
assert!(created.created);
assert_eq!(
created.user.phone_number_masked.as_deref(),
Some("138****8004")
);
}
#[tokio::test]
async fn phone_login_ignores_orphan_phone_index_after_code_verification() {
let snapshot = PersistentAuthStoreSnapshot {
next_user_id: 8,
users_by_username: HashMap::new(),
phone_to_user_id: HashMap::from([(
"+8613800138005".to_string(),
"user_deleted".to_string(),
)]),
sessions_by_id: HashMap::new(),
session_id_by_refresh_token_hash: HashMap::new(),
wechat_identity_by_provider_uid: HashMap::new(),
user_id_by_provider_union_id: HashMap::new(),
};
let snapshot_json =
serde_json::to_string(&snapshot).expect("snapshot json should serialize");
let phone_service = build_phone_service(
InMemoryAuthStore::from_snapshot_json(&snapshot_json).expect("snapshot should restore"),
);
let now = OffsetDateTime::now_utc();
phone_service
.send_code(
SendPhoneCodeInput {
phone_number: "13800138005".to_string(),
scene: PhoneAuthScene::Login,
},
now,
)
.await
.expect("phone code should send");
let created = phone_service
.login(
PhoneLoginInput {
phone_number: "13800138005".to_string(),
verify_code: "123456".to_string(),
},
now + Duration::seconds(1),
)
.await
.expect("orphan phone index should not turn login into duplicate create");
assert!(created.created);
assert_eq!(
created.user.phone_number_masked.as_deref(),
Some("138****8005")
);
}
#[tokio::test]
async fn snapshot_json_restores_user_and_refresh_session_after_roundtrip() {
let store = InMemoryAuthStore::default();

View File

@@ -0,0 +1,14 @@
[package]
name = "module-puzzle-clear"
edition.workspace = true
version.workspace = true
license.workspace = true
[features]
default = []
spacetime-types = ["dep:spacetimedb"]
[dependencies]
serde = { workspace = true }
shared-kernel = { workspace = true }
spacetimedb = { workspace = true, optional = true }

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,25 @@
use shared_kernel::normalize_required_string;
use crate::{PuzzleClearOrientation, PuzzleClearShapeKind};
pub fn parse_puzzle_clear_shape_kind(value: &str) -> PuzzleClearShapeKind {
match value.trim().to_ascii_lowercase().as_str() {
"1x3" | "one-by-three" => PuzzleClearShapeKind::OneByThree,
"2x2" | "two-by-two" => PuzzleClearShapeKind::TwoByTwo,
"2x3" | "two-by-three" => PuzzleClearShapeKind::TwoByThree,
_ => PuzzleClearShapeKind::OneByTwo,
}
}
pub fn parse_puzzle_clear_orientation(value: &str) -> PuzzleClearOrientation {
match value.trim().to_ascii_lowercase().as_str() {
"vertical" | "纵向" => PuzzleClearOrientation::Vertical,
_ => PuzzleClearOrientation::Horizontal,
}
}
pub fn normalize_puzzle_clear_seed(seed: &str, fallback: &str) -> String {
normalize_required_string(seed)
.or_else(|| normalize_required_string(fallback))
.unwrap_or_else(|| "puzzle-clear".to_string())
}

View File

@@ -0,0 +1,191 @@
use serde::{Deserialize, Serialize};
#[cfg(feature = "spacetime-types")]
use spacetimedb::SpacetimeType;
pub const PUZZLE_CLEAR_PLAY_ID: &str = "puzzle-clear";
pub const PUZZLE_CLEAR_PUBLIC_WORK_CODE_PREFIX: &str = "PC-";
pub const PUZZLE_CLEAR_SESSION_ID_PREFIX: &str = "puzzle-clear-session-";
pub const PUZZLE_CLEAR_PROFILE_ID_PREFIX: &str = "puzzle-clear-profile-";
pub const PUZZLE_CLEAR_WORK_ID_PREFIX: &str = "puzzle-clear-work-";
pub const PUZZLE_CLEAR_RUN_ID_PREFIX: &str = "puzzle-clear-run-";
pub const PUZZLE_CLEAR_LEVEL_DURATION_SECONDS: u32 = 600;
#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub enum PuzzleClearShapeKind {
OneByTwo,
OneByThree,
TwoByTwo,
TwoByThree,
}
#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub enum PuzzleClearOrientation {
Horizontal,
Vertical,
}
#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub enum PuzzleClearRunStatus {
Playing,
LevelFailed,
LevelCleared,
Finished,
}
#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))]
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct PuzzleClearLevelConfig {
pub level_index: u32,
pub board_size: u32,
pub target_clears: u32,
pub duration_seconds: u32,
pub unlocked_shapes: Vec<PuzzleClearShapeKind>,
}
#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))]
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct PuzzleClearShapeQuota {
pub shape: PuzzleClearShapeKind,
pub count: u32,
}
#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))]
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct PuzzleClearPatternGroup {
pub group_id: String,
pub shape: PuzzleClearShapeKind,
pub width: u32,
pub height: u32,
pub atlas_x: u32,
pub atlas_y: u32,
pub atlas_width: u32,
pub atlas_height: u32,
}
#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))]
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct PuzzleClearCard {
pub card_id: String,
pub group_id: String,
pub shape: PuzzleClearShapeKind,
pub orientation: PuzzleClearOrientation,
pub part_x: u32,
pub part_y: u32,
pub image_src: String,
pub image_object_key: String,
pub asset_object_id: String,
pub source_atlas_cell: String,
}
#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))]
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct PuzzleClearCell {
pub row: u32,
pub col: u32,
pub card: Option<PuzzleClearCard>,
pub locked_group_id: Option<String>,
}
#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))]
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct PuzzleClearBoard {
pub rows: u32,
pub cols: u32,
pub cells: Vec<PuzzleClearCell>,
}
#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))]
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct PuzzleClearDeck {
pub ready_columns: Vec<Vec<PuzzleClearCard>>,
}
#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))]
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct PuzzleClearMove {
pub from_row: u32,
pub from_col: u32,
pub to_row: u32,
pub to_col: u32,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct PuzzleClearElimination {
pub group_id: String,
pub positions: Vec<(u32, u32)>,
}
#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))]
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct PuzzleClearRunSnapshot {
pub run_id: String,
pub owner_user_id: String,
pub profile_id: String,
pub status: PuzzleClearRunStatus,
pub level_index: u32,
pub clears_done: u32,
pub board: PuzzleClearBoard,
pub deck: PuzzleClearDeck,
pub started_at_ms: u64,
pub level_started_at_ms: u64,
pub finished_at_ms: Option<u64>,
}
impl PuzzleClearShapeKind {
pub fn as_str(self) -> &'static str {
match self {
Self::OneByTwo => "1x2",
Self::OneByThree => "1x3",
Self::TwoByTwo => "2x2",
Self::TwoByThree => "2x3",
}
}
pub fn base_dimensions(self) -> (u32, u32) {
match self {
Self::OneByTwo => (2, 1),
Self::OneByThree => (3, 1),
Self::TwoByTwo => (2, 2),
Self::TwoByThree => (3, 2),
}
}
pub fn dimensions(self, orientation: PuzzleClearOrientation) -> (u32, u32) {
let (width, height) = self.base_dimensions();
if matches!(orientation, PuzzleClearOrientation::Vertical)
&& matches!(
self,
PuzzleClearShapeKind::OneByTwo
| PuzzleClearShapeKind::OneByThree
| PuzzleClearShapeKind::TwoByThree
)
{
(height, width)
} else {
(width, height)
}
}
}
impl PuzzleClearOrientation {
pub fn as_str(self) -> &'static str {
match self {
Self::Horizontal => "horizontal",
Self::Vertical => "vertical",
}
}
}
impl PuzzleClearRunStatus {
pub fn as_str(self) -> &'static str {
match self {
Self::Playing => "playing",
Self::LevelFailed => "level_failed",
Self::LevelCleared => "level_cleared",
Self::Finished => "finished",
}
}
}

View File

@@ -0,0 +1,37 @@
use std::fmt;
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum PuzzleClearError {
MissingRunId,
MissingOwnerUserId,
MissingProfileId,
InvalidLevel,
InvalidBoard,
InvalidPosition,
EmptyDeck,
NoPlayableMove,
RunNotPlaying,
LevelExpired,
MissingCard,
}
impl fmt::Display for PuzzleClearError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let message = match self {
Self::MissingRunId => "puzzle-clear run_id 不能为空",
Self::MissingOwnerUserId => "puzzle-clear owner_user_id 不能为空",
Self::MissingProfileId => "puzzle-clear profile_id 不能为空",
Self::InvalidLevel => "puzzle-clear 关卡配置无效",
Self::InvalidBoard => "puzzle-clear 棋盘状态无效",
Self::InvalidPosition => "puzzle-clear 坐标无效",
Self::EmptyDeck => "puzzle-clear 发牌池为空",
Self::NoPlayableMove => "puzzle-clear 棋盘没有可解拼接",
Self::RunNotPlaying => "puzzle-clear 当前 run 不在 playing 状态",
Self::LevelExpired => "puzzle-clear 当前关卡已经超时",
Self::MissingCard => "puzzle-clear 目标格子没有卡牌",
};
f.write_str(message)
}
}
impl std::error::Error for PuzzleClearError {}

View File

@@ -0,0 +1,31 @@
//! 拼消消领域事件。
//!
//! 事件只表达已经发生的领域事实,持久化、统计投影和前端通知由
//! SpacetimeDB adapter 与 BFF 编排层决定。
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum PuzzleClearDomainEvent {
DraftCompiled {
profile_id: String,
owner_user_id: String,
occurred_at_micros: i64,
},
WorkPublished {
profile_id: String,
owner_user_id: String,
occurred_at_micros: i64,
},
LevelCleared {
run_id: String,
owner_user_id: String,
level_index: u32,
clears_done: u32,
occurred_at_micros: i64,
},
RunSettled {
run_id: String,
owner_user_id: String,
status: String,
occurred_at_micros: i64,
},
}

View File

@@ -0,0 +1,11 @@
mod application;
mod commands;
mod domain;
mod errors;
mod events;
pub use application::*;
pub use commands::*;
pub use domain::*;
pub use errors::*;
pub use events::*;

View File

@@ -415,6 +415,20 @@ pub fn default_creation_entry_type_snapshots(
20,
updated_at_micros,
),
build_default_creation_entry_type_snapshot(
"puzzle-clear",
"拼消消",
"拼接消除玩法",
"可创建",
"/creation-type-references/puzzle.webp",
true,
true,
46,
"recommended",
"热门推荐",
20,
updated_at_micros,
),
build_default_creation_entry_type_snapshot(
"wooden-fish",
"敲木鱼",

View File

@@ -446,6 +446,22 @@ mod tests {
assert_eq!(wooden_fish.image_src, "/wooden-fish/default-hit-object.png");
}
#[test]
fn default_creation_entry_types_include_puzzle_clear() {
let configs = default_creation_entry_type_snapshots(1);
let puzzle_clear = configs
.iter()
.find(|item| item.id == "puzzle-clear")
.expect("puzzle-clear creation entry should be seeded");
assert_eq!(puzzle_clear.title, "拼消消");
assert!(puzzle_clear.visible);
assert!(puzzle_clear.open);
assert_eq!(puzzle_clear.badge, "可创建");
assert_eq!(puzzle_clear.sort_order, 46);
assert_eq!(puzzle_clear.category_id, "recommended");
}
#[test]
fn default_creation_entry_types_include_jump_hop_theme_only_entry() {
let configs = default_creation_entry_type_snapshots(1);

View File

@@ -22,7 +22,7 @@ const OSS_V4_SERVICE: &str = "oss";
const OSS_UNSIGNED_PAYLOAD: &str = "UNSIGNED-PAYLOAD";
const OSS_PROVIDER: &str = "aliyun-oss";
pub const LEGACY_PUBLIC_PREFIXES: [&str; 13] = [
pub const LEGACY_PUBLIC_PREFIXES: [&str; 14] = [
"generated-character-drafts",
"generated-characters",
"generated-animations",
@@ -31,6 +31,7 @@ pub const LEGACY_PUBLIC_PREFIXES: [&str; 13] = [
"generated-wooden-fish-assets",
"generated-match3d-assets",
"generated-puzzle-assets",
"generated-puzzle-clear-assets",
"generated-jump-hop-assets",
"generated-custom-world-scenes",
"generated-custom-world-covers",
@@ -55,6 +56,7 @@ pub enum LegacyAssetPrefix {
WoodenFishAssets,
Match3DAssets,
PuzzleAssets,
PuzzleClearAssets,
JumpHopAssets,
CustomWorldScenes,
CustomWorldCovers,
@@ -245,6 +247,7 @@ impl LegacyAssetPrefix {
"generated-wooden-fish-assets" => Some(Self::WoodenFishAssets),
"generated-match3d-assets" => Some(Self::Match3DAssets),
"generated-puzzle-assets" => Some(Self::PuzzleAssets),
"generated-puzzle-clear-assets" => Some(Self::PuzzleClearAssets),
"generated-jump-hop-assets" => Some(Self::JumpHopAssets),
"generated-custom-world-scenes" => Some(Self::CustomWorldScenes),
"generated-custom-world-covers" => Some(Self::CustomWorldCovers),
@@ -264,6 +267,7 @@ impl LegacyAssetPrefix {
Self::WoodenFishAssets => "generated-wooden-fish-assets",
Self::Match3DAssets => "generated-match3d-assets",
Self::PuzzleAssets => "generated-puzzle-assets",
Self::PuzzleClearAssets => "generated-puzzle-clear-assets",
Self::JumpHopAssets => "generated-jump-hop-assets",
Self::CustomWorldScenes => "generated-custom-world-scenes",
Self::CustomWorldCovers => "generated-custom-world-covers",

View File

@@ -19,6 +19,7 @@ pub mod match3d_runtime;
pub mod match3d_works;
pub mod public_work;
pub mod puzzle_agent;
pub mod puzzle_clear;
pub mod puzzle_creative_template;
pub mod puzzle_gallery;
pub mod puzzle_runtime;

View File

@@ -0,0 +1,313 @@
use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum PuzzleClearGenerationStatus {
Draft,
Generating,
Ready,
Failed,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub enum PuzzleClearActionType {
CompileDraft,
RegenerateAtlas,
UpdateWorkMeta,
UpdateBoardBackground,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum PuzzleClearRunStatus {
Playing,
LevelFailed,
LevelCleared,
Finished,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PuzzleClearImageAsset {
pub asset_id: String,
pub image_src: String,
pub image_object_key: String,
pub asset_object_id: String,
pub generation_provider: String,
pub prompt: String,
pub width: u32,
pub height: u32,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PuzzleClearPatternGroup {
pub group_id: String,
pub shape: String,
pub width: u32,
pub height: u32,
pub atlas_x: u32,
pub atlas_y: u32,
pub atlas_width: u32,
pub atlas_height: u32,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PuzzleClearCardAsset {
pub card_id: String,
pub group_id: String,
pub shape: String,
pub orientation: String,
pub part_x: u32,
pub part_y: u32,
pub image_src: String,
pub image_object_key: String,
pub asset_object_id: String,
pub source_atlas_cell: String,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PuzzleClearWorkspaceCreateRequest {
pub template_id: String,
pub work_title: String,
pub work_description: String,
pub theme_prompt: String,
#[serde(default)]
pub board_background_prompt: String,
pub generate_board_background: bool,
pub board_background_asset: Option<PuzzleClearImageAsset>,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PuzzleClearActionRequest {
pub action_type: PuzzleClearActionType,
pub profile_id: Option<String>,
pub work_title: Option<String>,
pub work_description: Option<String>,
pub theme_prompt: Option<String>,
#[serde(default)]
pub board_background_prompt: Option<String>,
pub generate_board_background: Option<bool>,
pub board_background_asset: Option<PuzzleClearImageAsset>,
pub atlas_asset: Option<PuzzleClearImageAsset>,
pub pattern_groups: Option<Vec<PuzzleClearPatternGroup>>,
pub card_assets: Option<Vec<PuzzleClearCardAsset>>,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PuzzleClearDraftResponse {
pub template_id: String,
pub template_name: String,
pub profile_id: Option<String>,
pub work_title: String,
pub work_description: String,
pub theme_prompt: String,
#[serde(default)]
pub board_background_prompt: String,
pub generate_board_background: bool,
pub board_background_asset: Option<PuzzleClearImageAsset>,
pub card_back_image_src: Option<String>,
pub atlas_asset: Option<PuzzleClearImageAsset>,
pub pattern_groups: Vec<PuzzleClearPatternGroup>,
pub card_assets: Vec<PuzzleClearCardAsset>,
pub generation_status: PuzzleClearGenerationStatus,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PuzzleClearSessionSnapshotResponse {
pub session_id: String,
pub owner_user_id: String,
pub status: PuzzleClearGenerationStatus,
pub draft: Option<PuzzleClearDraftResponse>,
pub created_at: String,
pub updated_at: String,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PuzzleClearSessionResponse {
pub session: PuzzleClearSessionSnapshotResponse,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PuzzleClearActionResponse {
pub action_type: PuzzleClearActionType,
pub session: PuzzleClearSessionSnapshotResponse,
pub work: Option<PuzzleClearWorkProfileResponse>,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PuzzleClearWorkSummaryResponse {
pub runtime_kind: String,
pub work_id: String,
pub profile_id: String,
pub owner_user_id: String,
pub source_session_id: Option<String>,
pub work_title: String,
pub work_description: String,
pub theme_prompt: String,
pub cover_image_src: Option<String>,
pub publication_status: String,
pub play_count: u32,
pub updated_at: String,
pub published_at: Option<String>,
pub publish_ready: bool,
pub generation_status: PuzzleClearGenerationStatus,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PuzzleClearWorkProfileResponse {
pub summary: PuzzleClearWorkSummaryResponse,
pub draft: PuzzleClearDraftResponse,
pub board_background_asset: Option<PuzzleClearImageAsset>,
pub atlas_asset: PuzzleClearImageAsset,
pub pattern_groups: Vec<PuzzleClearPatternGroup>,
pub card_assets: Vec<PuzzleClearCardAsset>,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PuzzleClearWorksResponse {
pub items: Vec<PuzzleClearWorkSummaryResponse>,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PuzzleClearWorkDetailResponse {
pub item: PuzzleClearWorkProfileResponse,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PuzzleClearWorkMutationResponse {
pub item: PuzzleClearWorkProfileResponse,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PuzzleClearBoardCell {
pub row: u32,
pub col: u32,
pub card: Option<PuzzleClearCardAsset>,
pub locked_group_id: Option<String>,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PuzzleClearBoardSnapshot {
pub rows: u32,
pub cols: u32,
pub cells: Vec<PuzzleClearBoardCell>,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PuzzleClearRuntimeSnapshotResponse {
pub run_id: String,
pub profile_id: String,
pub owner_user_id: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub runtime_mode: Option<String>,
pub status: PuzzleClearRunStatus,
pub level_index: u32,
pub clears_done: u32,
pub target_clears: u32,
pub level_duration_seconds: u32,
pub level_started_at_ms: u64,
pub board: PuzzleClearBoardSnapshot,
pub ready_columns: Vec<Vec<PuzzleClearCardAsset>>,
pub started_at_ms: u64,
pub finished_at_ms: Option<u64>,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PuzzleClearRunResponse {
pub run: PuzzleClearRuntimeSnapshotResponse,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PuzzleClearStartRunRequest {
pub profile_id: String,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PuzzleClearSwapRequest {
pub from_row: u32,
pub from_col: u32,
pub to_row: u32,
pub to_col: u32,
pub client_action_id: String,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PuzzleClearRetryLevelRequest {
pub client_action_id: String,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PuzzleClearNextLevelRequest {
pub client_action_id: String,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PuzzleClearTimeUpRequest {
pub client_action_id: String,
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
#[test]
fn workspace_create_request_uses_camel_case() {
let payload = PuzzleClearWorkspaceCreateRequest {
template_id: "puzzle-clear".to_string(),
work_title: "花园拼消消".to_string(),
work_description: "轻松消除".to_string(),
theme_prompt: "春日花园".to_string(),
board_background_prompt: "樱花庭院".to_string(),
generate_board_background: true,
board_background_asset: None,
};
let value = serde_json::to_value(payload).expect("request should serialize");
assert_eq!(value["templateId"], json!("puzzle-clear"));
assert_eq!(value["themePrompt"], json!("春日花园"));
assert_eq!(value["boardBackgroundPrompt"], json!("樱花庭院"));
assert_eq!(value["generateBoardBackground"], json!(true));
}
#[test]
fn runtime_swap_request_uses_camel_case() {
let payload = PuzzleClearSwapRequest {
from_row: 1,
from_col: 2,
to_row: 1,
to_col: 3,
client_action_id: "swap-1".to_string(),
};
let value = serde_json::to_value(payload).expect("request should serialize");
assert_eq!(value["fromRow"], json!(1));
assert_eq!(value["toCol"], json!(3));
assert_eq!(value["clientActionId"], json!("swap-1"));
}
}

View File

@@ -16,6 +16,7 @@ module-wooden-fish = { workspace = true }
module-match3d = { workspace = true }
module-npc = { workspace = true }
module-puzzle = { workspace = true }
module-puzzle-clear = { workspace = true }
module-runtime = { workspace = true }
module-runtime-story = { workspace = true }
module-runtime-item = { workspace = true }

View File

@@ -52,16 +52,24 @@ pub use mapper::{
PuzzleAgentMessageSubmitRecordInput, PuzzleAgentSessionCreateRecordInput,
PuzzleAgentSessionRecord, PuzzleAgentSuggestedActionRecord, PuzzleAnchorItemRecord,
PuzzleAnchorPackRecord, PuzzleAudioAssetRecord, PuzzleBoardRecord, PuzzleCellPositionRecord,
PuzzleCreatorIntentRecord, PuzzleDraftCompileFailureRecordInput, PuzzleDraftLevelRecord,
PuzzleFormDraftRecord, PuzzleFormDraftSaveRecordInput, PuzzleGalleryCardRecord,
PuzzleGeneratedImageCandidateRecord, PuzzleGeneratedImagesSaveRecordInput,
PuzzleLeaderboardEntryRecord, PuzzleLeaderboardSubmitRecordInput, PuzzleMergedGroupRecord,
PuzzlePieceStateRecord, PuzzlePublishRecordInput, PuzzleRecommendedNextWorkRecord,
PuzzleResultDraftRecord, PuzzleResultPreviewBlockerRecord, PuzzleResultPreviewFindingRecord,
PuzzleResultPreviewRecord, PuzzleRunDragRecordInput, PuzzleRunNextLevelRecordInput,
PuzzleRunPauseRecordInput, PuzzleRunPropRecordInput, PuzzleRunRecord,
PuzzleRunStartRecordInput, PuzzleRunSwapRecordInput, PuzzleRuntimeLevelRecord,
PuzzleSelectCoverImageRecordInput, PuzzleUiBackgroundSaveRecordInput,
PuzzleClearActionRequest, PuzzleClearActionResponse, PuzzleClearActionType,
PuzzleClearBoardCell, PuzzleClearBoardSnapshot, PuzzleClearCardAsset, PuzzleClearDraftResponse,
PuzzleClearGenerationStatus, PuzzleClearImageAsset, PuzzleClearNextLevelRequest,
PuzzleClearPatternGroup, PuzzleClearRetryLevelRequest, PuzzleClearRunResponse,
PuzzleClearRunStatus, PuzzleClearRuntimeSnapshotResponse, PuzzleClearSessionResponse,
PuzzleClearSessionSnapshotResponse, PuzzleClearStartRunRequest, PuzzleClearSwapRequest,
PuzzleClearTimeUpRequest, PuzzleClearWorkDetailResponse, PuzzleClearWorkMutationResponse,
PuzzleClearWorkProfileResponse, PuzzleClearWorkSummaryResponse, PuzzleClearWorksResponse,
PuzzleClearWorkspaceCreateRequest, PuzzleCreatorIntentRecord,
PuzzleDraftCompileFailureRecordInput, PuzzleDraftLevelRecord, PuzzleFormDraftRecord,
PuzzleFormDraftSaveRecordInput, PuzzleGalleryCardRecord, PuzzleGeneratedImageCandidateRecord,
PuzzleGeneratedImagesSaveRecordInput, PuzzleLeaderboardEntryRecord,
PuzzleLeaderboardSubmitRecordInput, PuzzleMergedGroupRecord, PuzzlePieceStateRecord,
PuzzlePublishRecordInput, PuzzleRecommendedNextWorkRecord, PuzzleResultDraftRecord,
PuzzleResultPreviewBlockerRecord, PuzzleResultPreviewFindingRecord, PuzzleResultPreviewRecord,
PuzzleRunDragRecordInput, PuzzleRunNextLevelRecordInput, PuzzleRunPauseRecordInput,
PuzzleRunPropRecordInput, PuzzleRunRecord, PuzzleRunStartRecordInput, PuzzleRunSwapRecordInput,
PuzzleRuntimeLevelRecord, PuzzleSelectCoverImageRecordInput, PuzzleUiBackgroundSaveRecordInput,
PuzzleWorkLikeReportRecordInput, PuzzleWorkPointIncentiveClaimRecordInput,
PuzzleWorkProfileRecord, PuzzleWorkRemixRecordInput, PuzzleWorkUpsertRecordInput,
ResolveCombatActionRecord, ResolveNpcBattleInteractionInput,
@@ -109,6 +117,7 @@ pub mod match3d;
pub mod npc;
pub mod public_work;
pub mod puzzle;
pub mod puzzle_clear;
pub mod runtime;
pub mod square_hole;
pub mod story;
@@ -575,6 +584,7 @@ impl SpacetimeClient {
"SELECT * FROM public_work_detail_entry",
"SELECT * FROM bark_battle_gallery_view",
"SELECT * FROM puzzle_gallery_card_view",
"SELECT * FROM puzzle_clear_gallery_card_view",
"SELECT * FROM jump_hop_gallery_card_view",
"SELECT * FROM wooden_fish_gallery_card_view",
"SELECT * FROM custom_world_gallery_entry",
@@ -591,6 +601,7 @@ impl SpacetimeClient {
for query in [
"SELECT * FROM public_work_play_daily_stat WHERE source_type = 'puzzle'",
"SELECT * FROM public_work_play_daily_stat WHERE source_type = 'puzzle-clear'",
"SELECT * FROM public_work_play_daily_stat WHERE source_type = 'jump-hop'",
"SELECT * FROM public_work_play_daily_stat WHERE source_type = 'wooden-fish'",
"SELECT * FROM public_work_play_daily_stat WHERE source_type = 'custom-world'",

View File

@@ -14,6 +14,7 @@ mod match3d;
mod npc;
mod public_work;
mod puzzle;
mod puzzle_clear;
mod runtime;
mod runtime_profile;
mod square_hole;
@@ -114,6 +115,17 @@ pub use self::puzzle::{
PuzzleWorkLikeReportRecordInput, PuzzleWorkPointIncentiveClaimRecordInput,
PuzzleWorkProfileRecord, PuzzleWorkRemixRecordInput, PuzzleWorkUpsertRecordInput,
};
pub use self::puzzle_clear::{
PuzzleClearActionRequest, PuzzleClearActionResponse, PuzzleClearActionType,
PuzzleClearBoardCell, PuzzleClearBoardSnapshot, PuzzleClearCardAsset, PuzzleClearDraftResponse,
PuzzleClearGenerationStatus, PuzzleClearImageAsset, PuzzleClearNextLevelRequest,
PuzzleClearPatternGroup, PuzzleClearRetryLevelRequest, PuzzleClearRunResponse,
PuzzleClearRunStatus, PuzzleClearRuntimeSnapshotResponse, PuzzleClearSessionResponse,
PuzzleClearSessionSnapshotResponse, PuzzleClearStartRunRequest, PuzzleClearSwapRequest,
PuzzleClearTimeUpRequest, PuzzleClearWorkDetailResponse, PuzzleClearWorkMutationResponse,
PuzzleClearWorkProfileResponse, PuzzleClearWorkSummaryResponse, PuzzleClearWorksResponse,
PuzzleClearWorkspaceCreateRequest,
};
pub use self::runtime::{
AdminWorkVisibilityRecord, BigFishGameDraftRecord, BigFishRuntimeEntityRecord,
BigFishRuntimeParamsRecord, BigFishRuntimeRunRecord, CreationEntryConfigRecord,
@@ -192,6 +204,11 @@ pub(crate) use self::puzzle::{
map_puzzle_works_procedure_result, map_runtime_profile_wallet_ledger_source_type_back,
parse_puzzle_agent_stage_record,
};
pub(crate) use self::puzzle_clear::{
map_puzzle_clear_agent_session_procedure_result, map_puzzle_clear_gallery_card_view_row,
map_puzzle_clear_run_procedure_result, map_puzzle_clear_work_procedure_result,
map_puzzle_clear_works_procedure_result,
};
pub(crate) use self::runtime::{
build_admin_work_visibility_list_input, build_admin_work_visibility_update_input,
build_creation_entry_config_record_from_rows, map_admin_work_visibility_list_procedure_result,

View File

@@ -0,0 +1,289 @@
use super::*;
pub use shared_contracts::puzzle_clear::{
PuzzleClearActionRequest, PuzzleClearActionResponse, PuzzleClearActionType,
PuzzleClearBoardCell, PuzzleClearBoardSnapshot, PuzzleClearCardAsset, PuzzleClearDraftResponse,
PuzzleClearGenerationStatus, PuzzleClearImageAsset, PuzzleClearNextLevelRequest,
PuzzleClearPatternGroup, PuzzleClearRetryLevelRequest, PuzzleClearRunResponse,
PuzzleClearRunStatus, PuzzleClearRuntimeSnapshotResponse, PuzzleClearSessionResponse,
PuzzleClearSessionSnapshotResponse, PuzzleClearStartRunRequest, PuzzleClearSwapRequest,
PuzzleClearTimeUpRequest, PuzzleClearWorkDetailResponse, PuzzleClearWorkMutationResponse,
PuzzleClearWorkProfileResponse, PuzzleClearWorkSummaryResponse, PuzzleClearWorksResponse,
PuzzleClearWorkspaceCreateRequest,
};
pub(crate) fn map_puzzle_clear_agent_session_procedure_result(
result: PuzzleClearAgentSessionProcedureResult,
) -> Result<PuzzleClearSessionSnapshotResponse, SpacetimeClientError> {
if !result.ok {
return Err(SpacetimeClientError::procedure_failed(result.error_message));
}
let session = result
.session
.ok_or_else(|| SpacetimeClientError::missing_snapshot("puzzle clear agent session 快照"))?;
Ok(map_puzzle_clear_session_snapshot(session))
}
pub(crate) fn map_puzzle_clear_work_procedure_result(
result: PuzzleClearWorkProcedureResult,
) -> Result<PuzzleClearWorkProfileResponse, SpacetimeClientError> {
if !result.ok {
return Err(SpacetimeClientError::procedure_failed(result.error_message));
}
let work = result
.work
.ok_or_else(|| SpacetimeClientError::missing_snapshot("puzzle clear work 快照"))?;
Ok(map_puzzle_clear_work_snapshot(work))
}
pub(crate) fn map_puzzle_clear_works_procedure_result(
result: PuzzleClearWorksProcedureResult,
) -> Result<Vec<PuzzleClearWorkProfileResponse>, SpacetimeClientError> {
if !result.ok {
return Err(SpacetimeClientError::procedure_failed(result.error_message));
}
Ok(result
.items
.into_iter()
.map(map_puzzle_clear_work_snapshot)
.collect())
}
pub(crate) fn map_puzzle_clear_run_procedure_result(
result: PuzzleClearRunProcedureResult,
) -> Result<PuzzleClearRuntimeSnapshotResponse, SpacetimeClientError> {
if !result.ok {
return Err(SpacetimeClientError::procedure_failed(result.error_message));
}
let run = result
.run
.ok_or_else(|| SpacetimeClientError::missing_snapshot("puzzle clear run 快照"))?;
Ok(map_puzzle_clear_run_snapshot(run))
}
pub(crate) fn map_puzzle_clear_gallery_card_view_row(
row: PuzzleClearGalleryCardViewRow,
) -> PuzzleClearWorkSummaryResponse {
PuzzleClearWorkSummaryResponse {
runtime_kind: "puzzle-clear".to_string(),
work_id: row.work_id,
profile_id: row.profile_id,
owner_user_id: row.owner_user_id,
source_session_id: None,
work_title: row.work_title,
work_description: row.work_description,
theme_prompt: row.theme_prompt,
cover_image_src: row.cover_image_src,
publication_status: normalize_publication_status(&row.publication_status).to_string(),
play_count: row.play_count,
updated_at: format_timestamp_micros(row.updated_at_micros),
published_at: row.published_at_micros.map(format_timestamp_micros),
publish_ready: true,
generation_status: parse_generation_status(&row.generation_status),
}
}
fn map_puzzle_clear_session_snapshot(
snapshot: PuzzleClearAgentSessionSnapshot,
) -> PuzzleClearSessionSnapshotResponse {
PuzzleClearSessionSnapshotResponse {
session_id: snapshot.session_id,
owner_user_id: snapshot.owner_user_id,
status: parse_generation_status(&snapshot.status),
draft: snapshot.draft.map(map_puzzle_clear_draft_snapshot),
created_at: format_timestamp_micros(snapshot.created_at_micros),
updated_at: format_timestamp_micros(snapshot.updated_at_micros),
}
}
fn map_puzzle_clear_work_snapshot(
snapshot: PuzzleClearWorkSnapshot,
) -> PuzzleClearWorkProfileResponse {
let atlas_asset = map_image_asset(snapshot.atlas_asset.clone());
let pattern_groups = snapshot
.pattern_groups
.clone()
.into_iter()
.map(map_pattern_group)
.collect::<Vec<_>>();
let card_assets = snapshot
.card_assets
.clone()
.into_iter()
.map(map_card_asset)
.collect::<Vec<_>>();
let board_background_asset = snapshot.board_background_asset.clone().map(map_image_asset);
let draft = PuzzleClearDraftResponse {
template_id: "puzzle-clear".to_string(),
template_name: "拼消消".to_string(),
profile_id: Some(snapshot.profile_id.clone()),
work_title: snapshot.work_title.clone(),
work_description: snapshot.work_description.clone(),
theme_prompt: snapshot.theme_prompt.clone(),
board_background_prompt: snapshot.board_background_prompt.clone(),
generate_board_background: snapshot.generate_board_background,
board_background_asset: board_background_asset.clone(),
card_back_image_src: snapshot.card_back_image_src.clone(),
atlas_asset: Some(atlas_asset.clone()),
pattern_groups: pattern_groups.clone(),
card_assets: card_assets.clone(),
generation_status: parse_generation_status(&snapshot.generation_status),
};
PuzzleClearWorkProfileResponse {
summary: PuzzleClearWorkSummaryResponse {
runtime_kind: "puzzle-clear".to_string(),
work_id: snapshot.work_id,
profile_id: snapshot.profile_id,
owner_user_id: snapshot.owner_user_id,
source_session_id: empty_string_to_none(snapshot.source_session_id),
work_title: snapshot.work_title,
work_description: snapshot.work_description,
theme_prompt: snapshot.theme_prompt,
cover_image_src: snapshot.cover_image_src,
publication_status: normalize_publication_status(&snapshot.publication_status)
.to_string(),
play_count: snapshot.play_count,
updated_at: format_timestamp_micros(snapshot.updated_at_micros),
published_at: snapshot.published_at_micros.map(format_timestamp_micros),
publish_ready: snapshot.publish_ready,
generation_status: parse_generation_status(&snapshot.generation_status),
},
draft,
board_background_asset,
atlas_asset,
pattern_groups,
card_assets,
}
}
fn map_puzzle_clear_draft_snapshot(snapshot: PuzzleClearDraftSnapshot) -> PuzzleClearDraftResponse {
PuzzleClearDraftResponse {
template_id: snapshot.template_id,
template_name: snapshot.template_name,
profile_id: snapshot.profile_id,
work_title: snapshot.work_title,
work_description: snapshot.work_description,
theme_prompt: snapshot.theme_prompt,
board_background_prompt: snapshot.board_background_prompt,
generate_board_background: snapshot.generate_board_background,
board_background_asset: snapshot.board_background_asset.map(map_image_asset),
card_back_image_src: snapshot.card_back_image_src,
atlas_asset: snapshot.atlas_asset.map(map_image_asset),
pattern_groups: snapshot
.pattern_groups
.into_iter()
.map(map_pattern_group)
.collect(),
card_assets: snapshot
.card_assets
.into_iter()
.map(map_card_asset)
.collect(),
generation_status: parse_generation_status(&snapshot.generation_status),
}
}
fn map_image_asset(snapshot: PuzzleClearImageAssetSnapshot) -> PuzzleClearImageAsset {
PuzzleClearImageAsset {
asset_id: snapshot.asset_id,
image_src: snapshot.image_src,
image_object_key: snapshot.image_object_key,
asset_object_id: snapshot.asset_object_id,
generation_provider: snapshot.generation_provider,
prompt: snapshot.prompt,
width: snapshot.width,
height: snapshot.height,
}
}
fn map_pattern_group(snapshot: PuzzleClearPatternGroupSnapshot) -> PuzzleClearPatternGroup {
PuzzleClearPatternGroup {
group_id: snapshot.group_id,
shape: snapshot.shape,
width: snapshot.width,
height: snapshot.height,
atlas_x: snapshot.atlas_x,
atlas_y: snapshot.atlas_y,
atlas_width: snapshot.atlas_width,
atlas_height: snapshot.atlas_height,
}
}
fn map_card_asset(snapshot: PuzzleClearCardAssetSnapshot) -> PuzzleClearCardAsset {
PuzzleClearCardAsset {
card_id: snapshot.card_id,
group_id: snapshot.group_id,
shape: snapshot.shape,
orientation: snapshot.orientation,
part_x: snapshot.part_x,
part_y: snapshot.part_y,
image_src: snapshot.image_src,
image_object_key: snapshot.image_object_key,
asset_object_id: snapshot.asset_object_id,
source_atlas_cell: snapshot.source_atlas_cell,
}
}
fn map_puzzle_clear_run_snapshot(
snapshot: PuzzleClearRuntimeSnapshot,
) -> PuzzleClearRuntimeSnapshotResponse {
PuzzleClearRuntimeSnapshotResponse {
run_id: snapshot.run_id,
profile_id: snapshot.profile_id,
owner_user_id: snapshot.owner_user_id,
runtime_mode: None,
status: parse_run_status(&snapshot.status),
level_index: snapshot.level_index,
clears_done: snapshot.clears_done,
target_clears: snapshot.target_clears,
level_duration_seconds: snapshot.level_duration_seconds,
level_started_at_ms: snapshot.level_started_at_ms,
board: PuzzleClearBoardSnapshot {
rows: snapshot.board.rows,
cols: snapshot.board.cols,
cells: snapshot
.board
.cells
.into_iter()
.map(|cell| PuzzleClearBoardCell {
row: cell.row,
col: cell.col,
card: cell.card.map(map_card_asset),
locked_group_id: cell.locked_group_id,
})
.collect(),
},
ready_columns: snapshot
.ready_columns
.into_iter()
.map(|column| column.into_iter().map(map_card_asset).collect())
.collect(),
started_at_ms: snapshot.started_at_ms,
finished_at_ms: snapshot.finished_at_ms,
}
}
fn parse_generation_status(value: &str) -> PuzzleClearGenerationStatus {
match value {
"generating" => PuzzleClearGenerationStatus::Generating,
"ready" => PuzzleClearGenerationStatus::Ready,
"failed" => PuzzleClearGenerationStatus::Failed,
_ => PuzzleClearGenerationStatus::Draft,
}
}
fn parse_run_status(value: &str) -> PuzzleClearRunStatus {
match value {
"level_failed" => PuzzleClearRunStatus::LevelFailed,
"level_cleared" => PuzzleClearRunStatus::LevelCleared,
"finished" => PuzzleClearRunStatus::Finished,
_ => PuzzleClearRunStatus::Playing,
}
}
fn normalize_publication_status(value: &str) -> &str {
match value {
"Published" | "published" => "published",
_ => "draft",
}
}

View File

@@ -25,6 +25,7 @@ pub mod admin_work_visibility_list_procedure_result_type;
pub mod admin_work_visibility_procedure_result_type;
pub mod admin_work_visibility_snapshot_type;
pub mod admin_work_visibility_update_input_type;
pub mod advance_puzzle_clear_next_level_procedure;
pub mod advance_puzzle_next_level_procedure;
pub mod ai_result_reference_input_type;
pub mod ai_result_reference_kind_type;
@@ -213,6 +214,7 @@ pub mod compile_custom_world_published_profile_procedure;
pub mod compile_jump_hop_draft_procedure;
pub mod compile_match_3_d_draft_procedure;
pub mod compile_puzzle_agent_draft_procedure;
pub mod compile_puzzle_clear_draft_procedure;
pub mod compile_square_hole_draft_procedure;
pub mod compile_visual_novel_work_profile_procedure;
pub mod compile_wooden_fish_draft_procedure;
@@ -235,6 +237,7 @@ pub mod create_jump_hop_agent_session_procedure;
pub mod create_match_3_d_agent_session_procedure;
pub mod create_profile_recharge_order_and_return_procedure;
pub mod create_puzzle_agent_session_procedure;
pub mod create_puzzle_clear_agent_session_procedure;
pub mod create_square_hole_agent_session_procedure;
pub mod create_visual_novel_agent_session_procedure;
pub mod create_wooden_fish_agent_session_procedure;
@@ -384,6 +387,9 @@ pub mod get_profile_recharge_order_and_return_procedure;
pub mod get_profile_referral_invite_center_procedure;
pub mod get_profile_task_center_procedure;
pub mod get_puzzle_agent_session_procedure;
pub mod get_puzzle_clear_agent_session_procedure;
pub mod get_puzzle_clear_runtime_run_procedure;
pub mod get_puzzle_clear_work_profile_procedure;
pub mod get_puzzle_gallery_detail_procedure;
pub mod get_puzzle_run_procedure;
pub mod get_puzzle_work_detail_procedure;
@@ -478,6 +484,7 @@ pub mod list_match_3_d_works_procedure;
pub mod list_platform_browse_history_procedure;
pub mod list_profile_save_archives_procedure;
pub mod list_profile_wallet_ledger_procedure;
pub mod list_puzzle_clear_works_procedure;
pub mod list_puzzle_gallery_procedure;
pub mod list_puzzle_works_procedure;
pub mod list_square_hole_works_procedure;
@@ -485,6 +492,7 @@ pub mod list_visual_novel_runtime_history_procedure;
pub mod list_visual_novel_works_procedure;
pub mod list_wooden_fish_works_procedure;
pub mod mark_profile_recharge_order_paid_and_return_procedure;
pub mod mark_puzzle_clear_level_time_up_procedure;
pub mod mark_puzzle_draft_generation_failed_procedure;
pub mod match_3_d_agent_message_finalize_input_type;
pub mod match_3_d_agent_message_row_type;
@@ -592,6 +600,7 @@ pub mod publish_custom_world_profile_reducer;
pub mod publish_custom_world_world_procedure;
pub mod publish_jump_hop_work_procedure;
pub mod publish_match_3_d_work_procedure;
pub mod publish_puzzle_clear_work_procedure;
pub mod publish_puzzle_work_procedure;
pub mod publish_square_hole_work_procedure;
pub mod publish_visual_novel_work_procedure;
@@ -618,6 +627,44 @@ pub mod puzzle_anchor_status_type;
pub mod puzzle_audio_asset_type;
pub mod puzzle_board_snapshot_type;
pub mod puzzle_cell_position_type;
pub mod puzzle_clear_agent_session_create_input_type;
pub mod puzzle_clear_agent_session_get_input_type;
pub mod puzzle_clear_agent_session_procedure_result_type;
pub mod puzzle_clear_agent_session_row_type;
pub mod puzzle_clear_agent_session_snapshot_type;
pub mod puzzle_clear_agent_session_table;
pub mod puzzle_clear_board_cell_snapshot_type;
pub mod puzzle_clear_board_snapshot_type;
pub mod puzzle_clear_card_asset_snapshot_type;
pub mod puzzle_clear_draft_compile_input_type;
pub mod puzzle_clear_draft_snapshot_type;
pub mod puzzle_clear_event_row_type;
pub mod puzzle_clear_event_table;
pub mod puzzle_clear_gallery_card_view_row_type;
pub mod puzzle_clear_gallery_card_view_table;
pub mod puzzle_clear_gallery_view_row_type;
pub mod puzzle_clear_gallery_view_table;
pub mod puzzle_clear_image_asset_snapshot_type;
pub mod puzzle_clear_pattern_group_snapshot_type;
pub mod puzzle_clear_run_get_input_type;
pub mod puzzle_clear_run_next_level_input_type;
pub mod puzzle_clear_run_procedure_result_type;
pub mod puzzle_clear_run_retry_level_input_type;
pub mod puzzle_clear_run_start_input_type;
pub mod puzzle_clear_run_swap_input_type;
pub mod puzzle_clear_run_time_up_input_type;
pub mod puzzle_clear_runtime_run_row_type;
pub mod puzzle_clear_runtime_run_table;
pub mod puzzle_clear_runtime_snapshot_type;
pub mod puzzle_clear_work_get_input_type;
pub mod puzzle_clear_work_procedure_result_type;
pub mod puzzle_clear_work_profile_row_type;
pub mod puzzle_clear_work_profile_table;
pub mod puzzle_clear_work_publish_input_type;
pub mod puzzle_clear_work_snapshot_type;
pub mod puzzle_clear_work_update_input_type;
pub mod puzzle_clear_works_list_input_type;
pub mod puzzle_clear_works_procedure_result_type;
pub mod puzzle_creator_intent_type;
pub mod puzzle_draft_compile_failure_input_type;
pub mod puzzle_draft_compile_input_type;
@@ -738,6 +785,7 @@ pub mod restart_jump_hop_run_procedure;
pub mod restart_match_3_d_run_procedure;
pub mod restart_square_hole_run_procedure;
pub mod resume_profile_save_archive_and_return_procedure;
pub mod retry_puzzle_clear_level_run_procedure;
pub mod revoke_database_migration_operator_procedure;
pub mod rpg_agent_draft_card_kind_type;
pub mod rpg_agent_draft_card_status_type;
@@ -908,6 +956,7 @@ pub mod start_bark_battle_run_procedure;
pub mod start_big_fish_run_procedure;
pub mod start_jump_hop_run_procedure;
pub mod start_match_3_d_run_procedure;
pub mod start_puzzle_clear_runtime_run_procedure;
pub mod start_puzzle_run_procedure;
pub mod start_square_hole_run_procedure;
pub mod start_visual_novel_run_procedure;
@@ -936,6 +985,7 @@ pub mod submit_puzzle_agent_message_procedure;
pub mod submit_puzzle_leaderboard_entry_procedure;
pub mod submit_square_hole_agent_message_procedure;
pub mod submit_visual_novel_agent_message_procedure;
pub mod swap_puzzle_clear_cards_procedure;
pub mod swap_puzzle_pieces_procedure;
pub mod tracking_daily_stat_table;
pub mod tracking_daily_stat_type;
@@ -954,6 +1004,7 @@ pub mod unpublish_custom_world_profile_reducer;
pub mod update_bark_battle_draft_config_procedure;
pub mod update_jump_hop_work_procedure;
pub mod update_match_3_d_work_procedure;
pub mod update_puzzle_clear_work_procedure;
pub mod update_puzzle_run_pause_procedure;
pub mod update_puzzle_work_procedure;
pub mod update_square_hole_work_procedure;
@@ -1078,6 +1129,7 @@ pub use admin_work_visibility_list_procedure_result_type::AdminWorkVisibilityLis
pub use admin_work_visibility_procedure_result_type::AdminWorkVisibilityProcedureResult;
pub use admin_work_visibility_snapshot_type::AdminWorkVisibilitySnapshot;
pub use admin_work_visibility_update_input_type::AdminWorkVisibilityUpdateInput;
pub use advance_puzzle_clear_next_level_procedure::advance_puzzle_clear_next_level;
pub use advance_puzzle_next_level_procedure::advance_puzzle_next_level;
pub use ai_result_reference_input_type::AiResultReferenceInput;
pub use ai_result_reference_kind_type::AiResultReferenceKind;
@@ -1266,6 +1318,7 @@ pub use compile_custom_world_published_profile_procedure::compile_custom_world_p
pub use compile_jump_hop_draft_procedure::compile_jump_hop_draft;
pub use compile_match_3_d_draft_procedure::compile_match_3_d_draft;
pub use compile_puzzle_agent_draft_procedure::compile_puzzle_agent_draft;
pub use compile_puzzle_clear_draft_procedure::compile_puzzle_clear_draft;
pub use compile_square_hole_draft_procedure::compile_square_hole_draft;
pub use compile_visual_novel_work_profile_procedure::compile_visual_novel_work_profile;
pub use compile_wooden_fish_draft_procedure::compile_wooden_fish_draft;
@@ -1288,6 +1341,7 @@ pub use create_jump_hop_agent_session_procedure::create_jump_hop_agent_session;
pub use create_match_3_d_agent_session_procedure::create_match_3_d_agent_session;
pub use create_profile_recharge_order_and_return_procedure::create_profile_recharge_order_and_return;
pub use create_puzzle_agent_session_procedure::create_puzzle_agent_session;
pub use create_puzzle_clear_agent_session_procedure::create_puzzle_clear_agent_session;
pub use create_square_hole_agent_session_procedure::create_square_hole_agent_session;
pub use create_visual_novel_agent_session_procedure::create_visual_novel_agent_session;
pub use create_wooden_fish_agent_session_procedure::create_wooden_fish_agent_session;
@@ -1437,6 +1491,9 @@ pub use get_profile_recharge_order_and_return_procedure::get_profile_recharge_or
pub use get_profile_referral_invite_center_procedure::get_profile_referral_invite_center;
pub use get_profile_task_center_procedure::get_profile_task_center;
pub use get_puzzle_agent_session_procedure::get_puzzle_agent_session;
pub use get_puzzle_clear_agent_session_procedure::get_puzzle_clear_agent_session;
pub use get_puzzle_clear_runtime_run_procedure::get_puzzle_clear_runtime_run;
pub use get_puzzle_clear_work_profile_procedure::get_puzzle_clear_work_profile;
pub use get_puzzle_gallery_detail_procedure::get_puzzle_gallery_detail;
pub use get_puzzle_run_procedure::get_puzzle_run;
pub use get_puzzle_work_detail_procedure::get_puzzle_work_detail;
@@ -1531,6 +1588,7 @@ pub use list_match_3_d_works_procedure::list_match_3_d_works;
pub use list_platform_browse_history_procedure::list_platform_browse_history;
pub use list_profile_save_archives_procedure::list_profile_save_archives;
pub use list_profile_wallet_ledger_procedure::list_profile_wallet_ledger;
pub use list_puzzle_clear_works_procedure::list_puzzle_clear_works;
pub use list_puzzle_gallery_procedure::list_puzzle_gallery;
pub use list_puzzle_works_procedure::list_puzzle_works;
pub use list_square_hole_works_procedure::list_square_hole_works;
@@ -1538,6 +1596,7 @@ pub use list_visual_novel_runtime_history_procedure::list_visual_novel_runtime_h
pub use list_visual_novel_works_procedure::list_visual_novel_works;
pub use list_wooden_fish_works_procedure::list_wooden_fish_works;
pub use mark_profile_recharge_order_paid_and_return_procedure::mark_profile_recharge_order_paid_and_return;
pub use mark_puzzle_clear_level_time_up_procedure::mark_puzzle_clear_level_time_up;
pub use mark_puzzle_draft_generation_failed_procedure::mark_puzzle_draft_generation_failed;
pub use match_3_d_agent_message_finalize_input_type::Match3DAgentMessageFinalizeInput;
pub use match_3_d_agent_message_row_type::Match3DAgentMessageRow;
@@ -1645,6 +1704,7 @@ pub use publish_custom_world_profile_reducer::publish_custom_world_profile;
pub use publish_custom_world_world_procedure::publish_custom_world_world;
pub use publish_jump_hop_work_procedure::publish_jump_hop_work;
pub use publish_match_3_d_work_procedure::publish_match_3_d_work;
pub use publish_puzzle_clear_work_procedure::publish_puzzle_clear_work;
pub use publish_puzzle_work_procedure::publish_puzzle_work;
pub use publish_square_hole_work_procedure::publish_square_hole_work;
pub use publish_visual_novel_work_procedure::publish_visual_novel_work;
@@ -1671,6 +1731,44 @@ pub use puzzle_anchor_status_type::PuzzleAnchorStatus;
pub use puzzle_audio_asset_type::PuzzleAudioAsset;
pub use puzzle_board_snapshot_type::PuzzleBoardSnapshot;
pub use puzzle_cell_position_type::PuzzleCellPosition;
pub use puzzle_clear_agent_session_create_input_type::PuzzleClearAgentSessionCreateInput;
pub use puzzle_clear_agent_session_get_input_type::PuzzleClearAgentSessionGetInput;
pub use puzzle_clear_agent_session_procedure_result_type::PuzzleClearAgentSessionProcedureResult;
pub use puzzle_clear_agent_session_row_type::PuzzleClearAgentSessionRow;
pub use puzzle_clear_agent_session_snapshot_type::PuzzleClearAgentSessionSnapshot;
pub use puzzle_clear_agent_session_table::*;
pub use puzzle_clear_board_cell_snapshot_type::PuzzleClearBoardCellSnapshot;
pub use puzzle_clear_board_snapshot_type::PuzzleClearBoardSnapshot;
pub use puzzle_clear_card_asset_snapshot_type::PuzzleClearCardAssetSnapshot;
pub use puzzle_clear_draft_compile_input_type::PuzzleClearDraftCompileInput;
pub use puzzle_clear_draft_snapshot_type::PuzzleClearDraftSnapshot;
pub use puzzle_clear_event_row_type::PuzzleClearEventRow;
pub use puzzle_clear_event_table::*;
pub use puzzle_clear_gallery_card_view_row_type::PuzzleClearGalleryCardViewRow;
pub use puzzle_clear_gallery_card_view_table::*;
pub use puzzle_clear_gallery_view_row_type::PuzzleClearGalleryViewRow;
pub use puzzle_clear_gallery_view_table::*;
pub use puzzle_clear_image_asset_snapshot_type::PuzzleClearImageAssetSnapshot;
pub use puzzle_clear_pattern_group_snapshot_type::PuzzleClearPatternGroupSnapshot;
pub use puzzle_clear_run_get_input_type::PuzzleClearRunGetInput;
pub use puzzle_clear_run_next_level_input_type::PuzzleClearRunNextLevelInput;
pub use puzzle_clear_run_procedure_result_type::PuzzleClearRunProcedureResult;
pub use puzzle_clear_run_retry_level_input_type::PuzzleClearRunRetryLevelInput;
pub use puzzle_clear_run_start_input_type::PuzzleClearRunStartInput;
pub use puzzle_clear_run_swap_input_type::PuzzleClearRunSwapInput;
pub use puzzle_clear_run_time_up_input_type::PuzzleClearRunTimeUpInput;
pub use puzzle_clear_runtime_run_row_type::PuzzleClearRuntimeRunRow;
pub use puzzle_clear_runtime_run_table::*;
pub use puzzle_clear_runtime_snapshot_type::PuzzleClearRuntimeSnapshot;
pub use puzzle_clear_work_get_input_type::PuzzleClearWorkGetInput;
pub use puzzle_clear_work_procedure_result_type::PuzzleClearWorkProcedureResult;
pub use puzzle_clear_work_profile_row_type::PuzzleClearWorkProfileRow;
pub use puzzle_clear_work_profile_table::*;
pub use puzzle_clear_work_publish_input_type::PuzzleClearWorkPublishInput;
pub use puzzle_clear_work_snapshot_type::PuzzleClearWorkSnapshot;
pub use puzzle_clear_work_update_input_type::PuzzleClearWorkUpdateInput;
pub use puzzle_clear_works_list_input_type::PuzzleClearWorksListInput;
pub use puzzle_clear_works_procedure_result_type::PuzzleClearWorksProcedureResult;
pub use puzzle_creator_intent_type::PuzzleCreatorIntent;
pub use puzzle_draft_compile_failure_input_type::PuzzleDraftCompileFailureInput;
pub use puzzle_draft_compile_input_type::PuzzleDraftCompileInput;
@@ -1791,6 +1889,7 @@ pub use restart_jump_hop_run_procedure::restart_jump_hop_run;
pub use restart_match_3_d_run_procedure::restart_match_3_d_run;
pub use restart_square_hole_run_procedure::restart_square_hole_run;
pub use resume_profile_save_archive_and_return_procedure::resume_profile_save_archive_and_return;
pub use retry_puzzle_clear_level_run_procedure::retry_puzzle_clear_level_run;
pub use revoke_database_migration_operator_procedure::revoke_database_migration_operator;
pub use rpg_agent_draft_card_kind_type::RpgAgentDraftCardKind;
pub use rpg_agent_draft_card_status_type::RpgAgentDraftCardStatus;
@@ -1961,6 +2060,7 @@ pub use start_bark_battle_run_procedure::start_bark_battle_run;
pub use start_big_fish_run_procedure::start_big_fish_run;
pub use start_jump_hop_run_procedure::start_jump_hop_run;
pub use start_match_3_d_run_procedure::start_match_3_d_run;
pub use start_puzzle_clear_runtime_run_procedure::start_puzzle_clear_runtime_run;
pub use start_puzzle_run_procedure::start_puzzle_run;
pub use start_square_hole_run_procedure::start_square_hole_run;
pub use start_visual_novel_run_procedure::start_visual_novel_run;
@@ -1989,6 +2089,7 @@ pub use submit_puzzle_agent_message_procedure::submit_puzzle_agent_message;
pub use submit_puzzle_leaderboard_entry_procedure::submit_puzzle_leaderboard_entry;
pub use submit_square_hole_agent_message_procedure::submit_square_hole_agent_message;
pub use submit_visual_novel_agent_message_procedure::submit_visual_novel_agent_message;
pub use swap_puzzle_clear_cards_procedure::swap_puzzle_clear_cards;
pub use swap_puzzle_pieces_procedure::swap_puzzle_pieces;
pub use tracking_daily_stat_table::*;
pub use tracking_daily_stat_type::TrackingDailyStat;
@@ -2007,6 +2108,7 @@ pub use unpublish_custom_world_profile_reducer::unpublish_custom_world_profile;
pub use update_bark_battle_draft_config_procedure::update_bark_battle_draft_config;
pub use update_jump_hop_work_procedure::update_jump_hop_work;
pub use update_match_3_d_work_procedure::update_match_3_d_work;
pub use update_puzzle_clear_work_procedure::update_puzzle_clear_work;
pub use update_puzzle_run_pause_procedure::update_puzzle_run_pause;
pub use update_puzzle_work_procedure::update_puzzle_work;
pub use update_square_hole_work_procedure::update_square_hole_work;
@@ -2459,6 +2561,12 @@ pub struct DbUpdate {
public_work_play_daily_stat: __sdk::TableUpdate<PublicWorkPlayDailyStat>,
puzzle_agent_message: __sdk::TableUpdate<PuzzleAgentMessageRow>,
puzzle_agent_session: __sdk::TableUpdate<PuzzleAgentSessionRow>,
puzzle_clear_agent_session: __sdk::TableUpdate<PuzzleClearAgentSessionRow>,
puzzle_clear_event: __sdk::TableUpdate<PuzzleClearEventRow>,
puzzle_clear_gallery_card_view: __sdk::TableUpdate<PuzzleClearGalleryCardViewRow>,
puzzle_clear_gallery_view: __sdk::TableUpdate<PuzzleClearGalleryViewRow>,
puzzle_clear_runtime_run: __sdk::TableUpdate<PuzzleClearRuntimeRunRow>,
puzzle_clear_work_profile: __sdk::TableUpdate<PuzzleClearWorkProfileRow>,
puzzle_event: __sdk::TableUpdate<PuzzleEvent>,
puzzle_gallery_card_view: __sdk::TableUpdate<PuzzleGalleryCardViewRow>,
puzzle_gallery_view: __sdk::TableUpdate<PuzzleWorkProfile>,
@@ -2738,6 +2846,26 @@ impl TryFrom<__ws::v2::TransactionUpdate> for DbUpdate {
"puzzle_agent_session" => db_update.puzzle_agent_session.append(
puzzle_agent_session_table::parse_table_update(table_update)?,
),
"puzzle_clear_agent_session" => db_update.puzzle_clear_agent_session.append(
puzzle_clear_agent_session_table::parse_table_update(table_update)?,
),
"puzzle_clear_event" => db_update
.puzzle_clear_event
.append(puzzle_clear_event_table::parse_table_update(table_update)?),
"puzzle_clear_gallery_card_view" => {
db_update.puzzle_clear_gallery_card_view.append(
puzzle_clear_gallery_card_view_table::parse_table_update(table_update)?,
)
}
"puzzle_clear_gallery_view" => db_update.puzzle_clear_gallery_view.append(
puzzle_clear_gallery_view_table::parse_table_update(table_update)?,
),
"puzzle_clear_runtime_run" => db_update.puzzle_clear_runtime_run.append(
puzzle_clear_runtime_run_table::parse_table_update(table_update)?,
),
"puzzle_clear_work_profile" => db_update.puzzle_clear_work_profile.append(
puzzle_clear_work_profile_table::parse_table_update(table_update)?,
),
"puzzle_event" => db_update
.puzzle_event
.append(puzzle_event_table::parse_table_update(table_update)?),
@@ -3237,6 +3365,30 @@ impl __sdk::DbUpdate for DbUpdate {
&self.puzzle_agent_session,
)
.with_updates_by_pk(|row| &row.session_id);
diff.puzzle_clear_agent_session = cache
.apply_diff_to_table::<PuzzleClearAgentSessionRow>(
"puzzle_clear_agent_session",
&self.puzzle_clear_agent_session,
)
.with_updates_by_pk(|row| &row.session_id);
diff.puzzle_clear_event = cache
.apply_diff_to_table::<PuzzleClearEventRow>(
"puzzle_clear_event",
&self.puzzle_clear_event,
)
.with_updates_by_pk(|row| &row.event_id);
diff.puzzle_clear_runtime_run = cache
.apply_diff_to_table::<PuzzleClearRuntimeRunRow>(
"puzzle_clear_runtime_run",
&self.puzzle_clear_runtime_run,
)
.with_updates_by_pk(|row| &row.run_id);
diff.puzzle_clear_work_profile = cache
.apply_diff_to_table::<PuzzleClearWorkProfileRow>(
"puzzle_clear_work_profile",
&self.puzzle_clear_work_profile,
)
.with_updates_by_pk(|row| &row.profile_id);
diff.puzzle_event = self.puzzle_event.into_event_diff();
diff.puzzle_leaderboard_entry = cache
.apply_diff_to_table::<PuzzleLeaderboardEntryRow>(
@@ -3402,6 +3554,15 @@ impl __sdk::DbUpdate for DbUpdate {
"public_work_gallery_entry",
&self.public_work_gallery_entry,
);
diff.puzzle_clear_gallery_card_view = cache
.apply_diff_to_table::<PuzzleClearGalleryCardViewRow>(
"puzzle_clear_gallery_card_view",
&self.puzzle_clear_gallery_card_view,
);
diff.puzzle_clear_gallery_view = cache.apply_diff_to_table::<PuzzleClearGalleryViewRow>(
"puzzle_clear_gallery_view",
&self.puzzle_clear_gallery_view,
);
diff.puzzle_gallery_card_view = cache.apply_diff_to_table::<PuzzleGalleryCardViewRow>(
"puzzle_gallery_card_view",
&self.puzzle_gallery_card_view,
@@ -3659,6 +3820,24 @@ impl __sdk::DbUpdate for DbUpdate {
"puzzle_agent_session" => db_update
.puzzle_agent_session
.append(__sdk::parse_row_list_as_inserts(table_rows.rows)?),
"puzzle_clear_agent_session" => db_update
.puzzle_clear_agent_session
.append(__sdk::parse_row_list_as_inserts(table_rows.rows)?),
"puzzle_clear_event" => db_update
.puzzle_clear_event
.append(__sdk::parse_row_list_as_inserts(table_rows.rows)?),
"puzzle_clear_gallery_card_view" => db_update
.puzzle_clear_gallery_card_view
.append(__sdk::parse_row_list_as_inserts(table_rows.rows)?),
"puzzle_clear_gallery_view" => db_update
.puzzle_clear_gallery_view
.append(__sdk::parse_row_list_as_inserts(table_rows.rows)?),
"puzzle_clear_runtime_run" => db_update
.puzzle_clear_runtime_run
.append(__sdk::parse_row_list_as_inserts(table_rows.rows)?),
"puzzle_clear_work_profile" => db_update
.puzzle_clear_work_profile
.append(__sdk::parse_row_list_as_inserts(table_rows.rows)?),
"puzzle_event" => db_update
.puzzle_event
.append(__sdk::parse_row_list_as_inserts(table_rows.rows)?),
@@ -4005,6 +4184,24 @@ impl __sdk::DbUpdate for DbUpdate {
"puzzle_agent_session" => db_update
.puzzle_agent_session
.append(__sdk::parse_row_list_as_deletes(table_rows.rows)?),
"puzzle_clear_agent_session" => db_update
.puzzle_clear_agent_session
.append(__sdk::parse_row_list_as_deletes(table_rows.rows)?),
"puzzle_clear_event" => db_update
.puzzle_clear_event
.append(__sdk::parse_row_list_as_deletes(table_rows.rows)?),
"puzzle_clear_gallery_card_view" => db_update
.puzzle_clear_gallery_card_view
.append(__sdk::parse_row_list_as_deletes(table_rows.rows)?),
"puzzle_clear_gallery_view" => db_update
.puzzle_clear_gallery_view
.append(__sdk::parse_row_list_as_deletes(table_rows.rows)?),
"puzzle_clear_runtime_run" => db_update
.puzzle_clear_runtime_run
.append(__sdk::parse_row_list_as_deletes(table_rows.rows)?),
"puzzle_clear_work_profile" => db_update
.puzzle_clear_work_profile
.append(__sdk::parse_row_list_as_deletes(table_rows.rows)?),
"puzzle_event" => db_update
.puzzle_event
.append(__sdk::parse_row_list_as_deletes(table_rows.rows)?),
@@ -4205,6 +4402,12 @@ pub struct AppliedDiff<'r> {
public_work_play_daily_stat: __sdk::TableAppliedDiff<'r, PublicWorkPlayDailyStat>,
puzzle_agent_message: __sdk::TableAppliedDiff<'r, PuzzleAgentMessageRow>,
puzzle_agent_session: __sdk::TableAppliedDiff<'r, PuzzleAgentSessionRow>,
puzzle_clear_agent_session: __sdk::TableAppliedDiff<'r, PuzzleClearAgentSessionRow>,
puzzle_clear_event: __sdk::TableAppliedDiff<'r, PuzzleClearEventRow>,
puzzle_clear_gallery_card_view: __sdk::TableAppliedDiff<'r, PuzzleClearGalleryCardViewRow>,
puzzle_clear_gallery_view: __sdk::TableAppliedDiff<'r, PuzzleClearGalleryViewRow>,
puzzle_clear_runtime_run: __sdk::TableAppliedDiff<'r, PuzzleClearRuntimeRunRow>,
puzzle_clear_work_profile: __sdk::TableAppliedDiff<'r, PuzzleClearWorkProfileRow>,
puzzle_event: __sdk::TableAppliedDiff<'r, PuzzleEvent>,
puzzle_gallery_card_view: __sdk::TableAppliedDiff<'r, PuzzleGalleryCardViewRow>,
puzzle_gallery_view: __sdk::TableAppliedDiff<'r, PuzzleWorkProfile>,
@@ -4618,6 +4821,36 @@ impl<'r> __sdk::AppliedDiff<'r> for AppliedDiff<'r> {
&self.puzzle_agent_session,
event,
);
callbacks.invoke_table_row_callbacks::<PuzzleClearAgentSessionRow>(
"puzzle_clear_agent_session",
&self.puzzle_clear_agent_session,
event,
);
callbacks.invoke_table_row_callbacks::<PuzzleClearEventRow>(
"puzzle_clear_event",
&self.puzzle_clear_event,
event,
);
callbacks.invoke_table_row_callbacks::<PuzzleClearGalleryCardViewRow>(
"puzzle_clear_gallery_card_view",
&self.puzzle_clear_gallery_card_view,
event,
);
callbacks.invoke_table_row_callbacks::<PuzzleClearGalleryViewRow>(
"puzzle_clear_gallery_view",
&self.puzzle_clear_gallery_view,
event,
);
callbacks.invoke_table_row_callbacks::<PuzzleClearRuntimeRunRow>(
"puzzle_clear_runtime_run",
&self.puzzle_clear_runtime_run,
event,
);
callbacks.invoke_table_row_callbacks::<PuzzleClearWorkProfileRow>(
"puzzle_clear_work_profile",
&self.puzzle_clear_work_profile,
event,
);
callbacks.invoke_table_row_callbacks::<PuzzleEvent>(
"puzzle_event",
&self.puzzle_event,
@@ -5525,6 +5758,12 @@ impl __sdk::SpacetimeModule for RemoteModule {
public_work_play_daily_stat_table::register_table(client_cache);
puzzle_agent_message_table::register_table(client_cache);
puzzle_agent_session_table::register_table(client_cache);
puzzle_clear_agent_session_table::register_table(client_cache);
puzzle_clear_event_table::register_table(client_cache);
puzzle_clear_gallery_card_view_table::register_table(client_cache);
puzzle_clear_gallery_view_table::register_table(client_cache);
puzzle_clear_runtime_run_table::register_table(client_cache);
puzzle_clear_work_profile_table::register_table(client_cache);
puzzle_event_table::register_table(client_cache);
puzzle_gallery_card_view_table::register_table(client_cache);
puzzle_gallery_view_table::register_table(client_cache);
@@ -5638,6 +5877,12 @@ impl __sdk::SpacetimeModule for RemoteModule {
"public_work_play_daily_stat",
"puzzle_agent_message",
"puzzle_agent_session",
"puzzle_clear_agent_session",
"puzzle_clear_event",
"puzzle_clear_gallery_card_view",
"puzzle_clear_gallery_view",
"puzzle_clear_runtime_run",
"puzzle_clear_work_profile",
"puzzle_event",
"puzzle_gallery_card_view",
"puzzle_gallery_view",

View File

@@ -0,0 +1,59 @@
// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
#![allow(unused, clippy::all)]
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
use super::puzzle_clear_run_next_level_input_type::PuzzleClearRunNextLevelInput;
use super::puzzle_clear_run_procedure_result_type::PuzzleClearRunProcedureResult;
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
#[sats(crate = __lib)]
struct AdvancePuzzleClearNextLevelArgs {
pub input: PuzzleClearRunNextLevelInput,
}
impl __sdk::InModule for AdvancePuzzleClearNextLevelArgs {
type Module = super::RemoteModule;
}
#[allow(non_camel_case_types)]
/// Extension trait for access to the procedure `advance_puzzle_clear_next_level`.
///
/// Implemented for [`super::RemoteProcedures`].
pub trait advance_puzzle_clear_next_level {
fn advance_puzzle_clear_next_level(&self, input: PuzzleClearRunNextLevelInput) {
self.advance_puzzle_clear_next_level_then(input, |_, _| {});
}
fn advance_puzzle_clear_next_level_then(
&self,
input: PuzzleClearRunNextLevelInput,
__callback: impl FnOnce(
&super::ProcedureEventContext,
Result<PuzzleClearRunProcedureResult, __sdk::InternalError>,
) + Send
+ 'static,
);
}
impl advance_puzzle_clear_next_level for super::RemoteProcedures {
fn advance_puzzle_clear_next_level_then(
&self,
input: PuzzleClearRunNextLevelInput,
__callback: impl FnOnce(
&super::ProcedureEventContext,
Result<PuzzleClearRunProcedureResult, __sdk::InternalError>,
) + Send
+ 'static,
) {
self.imp
.invoke_procedure_with_callback::<_, PuzzleClearRunProcedureResult>(
"advance_puzzle_clear_next_level",
AdvancePuzzleClearNextLevelArgs { input },
__callback,
);
}
}

View File

@@ -0,0 +1,59 @@
// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
#![allow(unused, clippy::all)]
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
use super::puzzle_clear_agent_session_procedure_result_type::PuzzleClearAgentSessionProcedureResult;
use super::puzzle_clear_draft_compile_input_type::PuzzleClearDraftCompileInput;
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
#[sats(crate = __lib)]
struct CompilePuzzleClearDraftArgs {
pub input: PuzzleClearDraftCompileInput,
}
impl __sdk::InModule for CompilePuzzleClearDraftArgs {
type Module = super::RemoteModule;
}
#[allow(non_camel_case_types)]
/// Extension trait for access to the procedure `compile_puzzle_clear_draft`.
///
/// Implemented for [`super::RemoteProcedures`].
pub trait compile_puzzle_clear_draft {
fn compile_puzzle_clear_draft(&self, input: PuzzleClearDraftCompileInput) {
self.compile_puzzle_clear_draft_then(input, |_, _| {});
}
fn compile_puzzle_clear_draft_then(
&self,
input: PuzzleClearDraftCompileInput,
__callback: impl FnOnce(
&super::ProcedureEventContext,
Result<PuzzleClearAgentSessionProcedureResult, __sdk::InternalError>,
) + Send
+ 'static,
);
}
impl compile_puzzle_clear_draft for super::RemoteProcedures {
fn compile_puzzle_clear_draft_then(
&self,
input: PuzzleClearDraftCompileInput,
__callback: impl FnOnce(
&super::ProcedureEventContext,
Result<PuzzleClearAgentSessionProcedureResult, __sdk::InternalError>,
) + Send
+ 'static,
) {
self.imp
.invoke_procedure_with_callback::<_, PuzzleClearAgentSessionProcedureResult>(
"compile_puzzle_clear_draft",
CompilePuzzleClearDraftArgs { input },
__callback,
);
}
}

View File

@@ -0,0 +1,59 @@
// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
#![allow(unused, clippy::all)]
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
use super::puzzle_clear_agent_session_create_input_type::PuzzleClearAgentSessionCreateInput;
use super::puzzle_clear_agent_session_procedure_result_type::PuzzleClearAgentSessionProcedureResult;
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
#[sats(crate = __lib)]
struct CreatePuzzleClearAgentSessionArgs {
pub input: PuzzleClearAgentSessionCreateInput,
}
impl __sdk::InModule for CreatePuzzleClearAgentSessionArgs {
type Module = super::RemoteModule;
}
#[allow(non_camel_case_types)]
/// Extension trait for access to the procedure `create_puzzle_clear_agent_session`.
///
/// Implemented for [`super::RemoteProcedures`].
pub trait create_puzzle_clear_agent_session {
fn create_puzzle_clear_agent_session(&self, input: PuzzleClearAgentSessionCreateInput) {
self.create_puzzle_clear_agent_session_then(input, |_, _| {});
}
fn create_puzzle_clear_agent_session_then(
&self,
input: PuzzleClearAgentSessionCreateInput,
__callback: impl FnOnce(
&super::ProcedureEventContext,
Result<PuzzleClearAgentSessionProcedureResult, __sdk::InternalError>,
) + Send
+ 'static,
);
}
impl create_puzzle_clear_agent_session for super::RemoteProcedures {
fn create_puzzle_clear_agent_session_then(
&self,
input: PuzzleClearAgentSessionCreateInput,
__callback: impl FnOnce(
&super::ProcedureEventContext,
Result<PuzzleClearAgentSessionProcedureResult, __sdk::InternalError>,
) + Send
+ 'static,
) {
self.imp
.invoke_procedure_with_callback::<_, PuzzleClearAgentSessionProcedureResult>(
"create_puzzle_clear_agent_session",
CreatePuzzleClearAgentSessionArgs { input },
__callback,
);
}
}

View File

@@ -0,0 +1,59 @@
// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
#![allow(unused, clippy::all)]
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
use super::puzzle_clear_agent_session_get_input_type::PuzzleClearAgentSessionGetInput;
use super::puzzle_clear_agent_session_procedure_result_type::PuzzleClearAgentSessionProcedureResult;
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
#[sats(crate = __lib)]
struct GetPuzzleClearAgentSessionArgs {
pub input: PuzzleClearAgentSessionGetInput,
}
impl __sdk::InModule for GetPuzzleClearAgentSessionArgs {
type Module = super::RemoteModule;
}
#[allow(non_camel_case_types)]
/// Extension trait for access to the procedure `get_puzzle_clear_agent_session`.
///
/// Implemented for [`super::RemoteProcedures`].
pub trait get_puzzle_clear_agent_session {
fn get_puzzle_clear_agent_session(&self, input: PuzzleClearAgentSessionGetInput) {
self.get_puzzle_clear_agent_session_then(input, |_, _| {});
}
fn get_puzzle_clear_agent_session_then(
&self,
input: PuzzleClearAgentSessionGetInput,
__callback: impl FnOnce(
&super::ProcedureEventContext,
Result<PuzzleClearAgentSessionProcedureResult, __sdk::InternalError>,
) + Send
+ 'static,
);
}
impl get_puzzle_clear_agent_session for super::RemoteProcedures {
fn get_puzzle_clear_agent_session_then(
&self,
input: PuzzleClearAgentSessionGetInput,
__callback: impl FnOnce(
&super::ProcedureEventContext,
Result<PuzzleClearAgentSessionProcedureResult, __sdk::InternalError>,
) + Send
+ 'static,
) {
self.imp
.invoke_procedure_with_callback::<_, PuzzleClearAgentSessionProcedureResult>(
"get_puzzle_clear_agent_session",
GetPuzzleClearAgentSessionArgs { input },
__callback,
);
}
}

View File

@@ -0,0 +1,59 @@
// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
#![allow(unused, clippy::all)]
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
use super::puzzle_clear_run_get_input_type::PuzzleClearRunGetInput;
use super::puzzle_clear_run_procedure_result_type::PuzzleClearRunProcedureResult;
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
#[sats(crate = __lib)]
struct GetPuzzleClearRuntimeRunArgs {
pub input: PuzzleClearRunGetInput,
}
impl __sdk::InModule for GetPuzzleClearRuntimeRunArgs {
type Module = super::RemoteModule;
}
#[allow(non_camel_case_types)]
/// Extension trait for access to the procedure `get_puzzle_clear_runtime_run`.
///
/// Implemented for [`super::RemoteProcedures`].
pub trait get_puzzle_clear_runtime_run {
fn get_puzzle_clear_runtime_run(&self, input: PuzzleClearRunGetInput) {
self.get_puzzle_clear_runtime_run_then(input, |_, _| {});
}
fn get_puzzle_clear_runtime_run_then(
&self,
input: PuzzleClearRunGetInput,
__callback: impl FnOnce(
&super::ProcedureEventContext,
Result<PuzzleClearRunProcedureResult, __sdk::InternalError>,
) + Send
+ 'static,
);
}
impl get_puzzle_clear_runtime_run for super::RemoteProcedures {
fn get_puzzle_clear_runtime_run_then(
&self,
input: PuzzleClearRunGetInput,
__callback: impl FnOnce(
&super::ProcedureEventContext,
Result<PuzzleClearRunProcedureResult, __sdk::InternalError>,
) + Send
+ 'static,
) {
self.imp
.invoke_procedure_with_callback::<_, PuzzleClearRunProcedureResult>(
"get_puzzle_clear_runtime_run",
GetPuzzleClearRuntimeRunArgs { input },
__callback,
);
}
}

View File

@@ -0,0 +1,59 @@
// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
#![allow(unused, clippy::all)]
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
use super::puzzle_clear_work_get_input_type::PuzzleClearWorkGetInput;
use super::puzzle_clear_work_procedure_result_type::PuzzleClearWorkProcedureResult;
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
#[sats(crate = __lib)]
struct GetPuzzleClearWorkProfileArgs {
pub input: PuzzleClearWorkGetInput,
}
impl __sdk::InModule for GetPuzzleClearWorkProfileArgs {
type Module = super::RemoteModule;
}
#[allow(non_camel_case_types)]
/// Extension trait for access to the procedure `get_puzzle_clear_work_profile`.
///
/// Implemented for [`super::RemoteProcedures`].
pub trait get_puzzle_clear_work_profile {
fn get_puzzle_clear_work_profile(&self, input: PuzzleClearWorkGetInput) {
self.get_puzzle_clear_work_profile_then(input, |_, _| {});
}
fn get_puzzle_clear_work_profile_then(
&self,
input: PuzzleClearWorkGetInput,
__callback: impl FnOnce(
&super::ProcedureEventContext,
Result<PuzzleClearWorkProcedureResult, __sdk::InternalError>,
) + Send
+ 'static,
);
}
impl get_puzzle_clear_work_profile for super::RemoteProcedures {
fn get_puzzle_clear_work_profile_then(
&self,
input: PuzzleClearWorkGetInput,
__callback: impl FnOnce(
&super::ProcedureEventContext,
Result<PuzzleClearWorkProcedureResult, __sdk::InternalError>,
) + Send
+ 'static,
) {
self.imp
.invoke_procedure_with_callback::<_, PuzzleClearWorkProcedureResult>(
"get_puzzle_clear_work_profile",
GetPuzzleClearWorkProfileArgs { input },
__callback,
);
}
}

View File

@@ -0,0 +1,59 @@
// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
#![allow(unused, clippy::all)]
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
use super::puzzle_clear_works_list_input_type::PuzzleClearWorksListInput;
use super::puzzle_clear_works_procedure_result_type::PuzzleClearWorksProcedureResult;
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
#[sats(crate = __lib)]
struct ListPuzzleClearWorksArgs {
pub input: PuzzleClearWorksListInput,
}
impl __sdk::InModule for ListPuzzleClearWorksArgs {
type Module = super::RemoteModule;
}
#[allow(non_camel_case_types)]
/// Extension trait for access to the procedure `list_puzzle_clear_works`.
///
/// Implemented for [`super::RemoteProcedures`].
pub trait list_puzzle_clear_works {
fn list_puzzle_clear_works(&self, input: PuzzleClearWorksListInput) {
self.list_puzzle_clear_works_then(input, |_, _| {});
}
fn list_puzzle_clear_works_then(
&self,
input: PuzzleClearWorksListInput,
__callback: impl FnOnce(
&super::ProcedureEventContext,
Result<PuzzleClearWorksProcedureResult, __sdk::InternalError>,
) + Send
+ 'static,
);
}
impl list_puzzle_clear_works for super::RemoteProcedures {
fn list_puzzle_clear_works_then(
&self,
input: PuzzleClearWorksListInput,
__callback: impl FnOnce(
&super::ProcedureEventContext,
Result<PuzzleClearWorksProcedureResult, __sdk::InternalError>,
) + Send
+ 'static,
) {
self.imp
.invoke_procedure_with_callback::<_, PuzzleClearWorksProcedureResult>(
"list_puzzle_clear_works",
ListPuzzleClearWorksArgs { input },
__callback,
);
}
}

View File

@@ -0,0 +1,59 @@
// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
#![allow(unused, clippy::all)]
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
use super::puzzle_clear_run_procedure_result_type::PuzzleClearRunProcedureResult;
use super::puzzle_clear_run_time_up_input_type::PuzzleClearRunTimeUpInput;
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
#[sats(crate = __lib)]
struct MarkPuzzleClearLevelTimeUpArgs {
pub input: PuzzleClearRunTimeUpInput,
}
impl __sdk::InModule for MarkPuzzleClearLevelTimeUpArgs {
type Module = super::RemoteModule;
}
#[allow(non_camel_case_types)]
/// Extension trait for access to the procedure `mark_puzzle_clear_level_time_up`.
///
/// Implemented for [`super::RemoteProcedures`].
pub trait mark_puzzle_clear_level_time_up {
fn mark_puzzle_clear_level_time_up(&self, input: PuzzleClearRunTimeUpInput) {
self.mark_puzzle_clear_level_time_up_then(input, |_, _| {});
}
fn mark_puzzle_clear_level_time_up_then(
&self,
input: PuzzleClearRunTimeUpInput,
__callback: impl FnOnce(
&super::ProcedureEventContext,
Result<PuzzleClearRunProcedureResult, __sdk::InternalError>,
) + Send
+ 'static,
);
}
impl mark_puzzle_clear_level_time_up for super::RemoteProcedures {
fn mark_puzzle_clear_level_time_up_then(
&self,
input: PuzzleClearRunTimeUpInput,
__callback: impl FnOnce(
&super::ProcedureEventContext,
Result<PuzzleClearRunProcedureResult, __sdk::InternalError>,
) + Send
+ 'static,
) {
self.imp
.invoke_procedure_with_callback::<_, PuzzleClearRunProcedureResult>(
"mark_puzzle_clear_level_time_up",
MarkPuzzleClearLevelTimeUpArgs { input },
__callback,
);
}
}

View File

@@ -0,0 +1,59 @@
// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
#![allow(unused, clippy::all)]
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
use super::puzzle_clear_work_procedure_result_type::PuzzleClearWorkProcedureResult;
use super::puzzle_clear_work_publish_input_type::PuzzleClearWorkPublishInput;
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
#[sats(crate = __lib)]
struct PublishPuzzleClearWorkArgs {
pub input: PuzzleClearWorkPublishInput,
}
impl __sdk::InModule for PublishPuzzleClearWorkArgs {
type Module = super::RemoteModule;
}
#[allow(non_camel_case_types)]
/// Extension trait for access to the procedure `publish_puzzle_clear_work`.
///
/// Implemented for [`super::RemoteProcedures`].
pub trait publish_puzzle_clear_work {
fn publish_puzzle_clear_work(&self, input: PuzzleClearWorkPublishInput) {
self.publish_puzzle_clear_work_then(input, |_, _| {});
}
fn publish_puzzle_clear_work_then(
&self,
input: PuzzleClearWorkPublishInput,
__callback: impl FnOnce(
&super::ProcedureEventContext,
Result<PuzzleClearWorkProcedureResult, __sdk::InternalError>,
) + Send
+ 'static,
);
}
impl publish_puzzle_clear_work for super::RemoteProcedures {
fn publish_puzzle_clear_work_then(
&self,
input: PuzzleClearWorkPublishInput,
__callback: impl FnOnce(
&super::ProcedureEventContext,
Result<PuzzleClearWorkProcedureResult, __sdk::InternalError>,
) + Send
+ 'static,
) {
self.imp
.invoke_procedure_with_callback::<_, PuzzleClearWorkProcedureResult>(
"publish_puzzle_clear_work",
PublishPuzzleClearWorkArgs { input },
__callback,
);
}
}

View File

@@ -0,0 +1,23 @@
// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
#![allow(unused, clippy::all)]
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
#[sats(crate = __lib)]
pub struct PuzzleClearAgentSessionCreateInput {
pub session_id: String,
pub owner_user_id: String,
pub work_title: String,
pub work_description: String,
pub theme_prompt: String,
pub generate_board_background: bool,
pub board_background_asset_json: Option<String>,
pub board_background_prompt: String,
pub created_at_micros: i64,
}
impl __sdk::InModule for PuzzleClearAgentSessionCreateInput {
type Module = super::RemoteModule;
}

View File

@@ -0,0 +1,16 @@
// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
#![allow(unused, clippy::all)]
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
#[sats(crate = __lib)]
pub struct PuzzleClearAgentSessionGetInput {
pub session_id: String,
pub owner_user_id: String,
}
impl __sdk::InModule for PuzzleClearAgentSessionGetInput {
type Module = super::RemoteModule;
}

View File

@@ -0,0 +1,19 @@
// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
#![allow(unused, clippy::all)]
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
use super::puzzle_clear_agent_session_snapshot_type::PuzzleClearAgentSessionSnapshot;
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
#[sats(crate = __lib)]
pub struct PuzzleClearAgentSessionProcedureResult {
pub ok: bool,
pub session: Option<PuzzleClearAgentSessionSnapshot>,
pub error_message: Option<String>,
}
impl __sdk::InModule for PuzzleClearAgentSessionProcedureResult {
type Module = super::RemoteModule;
}

View File

@@ -0,0 +1,72 @@
// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
#![allow(unused, clippy::all)]
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
#[sats(crate = __lib)]
pub struct PuzzleClearAgentSessionRow {
pub session_id: String,
pub owner_user_id: String,
pub status: String,
pub draft_json: String,
pub published_profile_id: String,
pub created_at: __sdk::Timestamp,
pub updated_at: __sdk::Timestamp,
}
impl __sdk::InModule for PuzzleClearAgentSessionRow {
type Module = super::RemoteModule;
}
/// Column accessor struct for the table `PuzzleClearAgentSessionRow`.
///
/// Provides typed access to columns for query building.
pub struct PuzzleClearAgentSessionRowCols {
pub session_id: __sdk::__query_builder::Col<PuzzleClearAgentSessionRow, String>,
pub owner_user_id: __sdk::__query_builder::Col<PuzzleClearAgentSessionRow, String>,
pub status: __sdk::__query_builder::Col<PuzzleClearAgentSessionRow, String>,
pub draft_json: __sdk::__query_builder::Col<PuzzleClearAgentSessionRow, String>,
pub published_profile_id: __sdk::__query_builder::Col<PuzzleClearAgentSessionRow, String>,
pub created_at: __sdk::__query_builder::Col<PuzzleClearAgentSessionRow, __sdk::Timestamp>,
pub updated_at: __sdk::__query_builder::Col<PuzzleClearAgentSessionRow, __sdk::Timestamp>,
}
impl __sdk::__query_builder::HasCols for PuzzleClearAgentSessionRow {
type Cols = PuzzleClearAgentSessionRowCols;
fn cols(table_name: &'static str) -> Self::Cols {
PuzzleClearAgentSessionRowCols {
session_id: __sdk::__query_builder::Col::new(table_name, "session_id"),
owner_user_id: __sdk::__query_builder::Col::new(table_name, "owner_user_id"),
status: __sdk::__query_builder::Col::new(table_name, "status"),
draft_json: __sdk::__query_builder::Col::new(table_name, "draft_json"),
published_profile_id: __sdk::__query_builder::Col::new(
table_name,
"published_profile_id",
),
created_at: __sdk::__query_builder::Col::new(table_name, "created_at"),
updated_at: __sdk::__query_builder::Col::new(table_name, "updated_at"),
}
}
}
/// Indexed column accessor struct for the table `PuzzleClearAgentSessionRow`.
///
/// Provides typed access to indexed columns for query building.
pub struct PuzzleClearAgentSessionRowIxCols {
pub owner_user_id: __sdk::__query_builder::IxCol<PuzzleClearAgentSessionRow, String>,
pub session_id: __sdk::__query_builder::IxCol<PuzzleClearAgentSessionRow, String>,
}
impl __sdk::__query_builder::HasIxCols for PuzzleClearAgentSessionRow {
type IxCols = PuzzleClearAgentSessionRowIxCols;
fn ix_cols(table_name: &'static str) -> Self::IxCols {
PuzzleClearAgentSessionRowIxCols {
owner_user_id: __sdk::__query_builder::IxCol::new(table_name, "owner_user_id"),
session_id: __sdk::__query_builder::IxCol::new(table_name, "session_id"),
}
}
}
impl __sdk::__query_builder::CanBeLookupTable for PuzzleClearAgentSessionRow {}

View File

@@ -0,0 +1,23 @@
// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
#![allow(unused, clippy::all)]
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
use super::puzzle_clear_draft_snapshot_type::PuzzleClearDraftSnapshot;
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
#[sats(crate = __lib)]
pub struct PuzzleClearAgentSessionSnapshot {
pub session_id: String,
pub owner_user_id: String,
pub status: String,
pub draft: Option<PuzzleClearDraftSnapshot>,
pub published_profile_id: Option<String>,
pub created_at_micros: i64,
pub updated_at_micros: i64,
}
impl __sdk::InModule for PuzzleClearAgentSessionSnapshot {
type Module = super::RemoteModule;
}

View File

@@ -0,0 +1,166 @@
// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
#![allow(unused, clippy::all)]
use super::puzzle_clear_agent_session_row_type::PuzzleClearAgentSessionRow;
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
/// Table handle for the table `puzzle_clear_agent_session`.
///
/// Obtain a handle from the [`PuzzleClearAgentSessionTableAccess::puzzle_clear_agent_session`] method on [`super::RemoteTables`],
/// like `ctx.db.puzzle_clear_agent_session()`.
///
/// Users are encouraged not to explicitly reference this type,
/// but to directly chain method calls,
/// like `ctx.db.puzzle_clear_agent_session().on_insert(...)`.
pub struct PuzzleClearAgentSessionTableHandle<'ctx> {
imp: __sdk::TableHandle<PuzzleClearAgentSessionRow>,
ctx: std::marker::PhantomData<&'ctx super::RemoteTables>,
}
#[allow(non_camel_case_types)]
/// Extension trait for access to the table `puzzle_clear_agent_session`.
///
/// Implemented for [`super::RemoteTables`].
pub trait PuzzleClearAgentSessionTableAccess {
#[allow(non_snake_case)]
/// Obtain a [`PuzzleClearAgentSessionTableHandle`], which mediates access to the table `puzzle_clear_agent_session`.
fn puzzle_clear_agent_session(&self) -> PuzzleClearAgentSessionTableHandle<'_>;
}
impl PuzzleClearAgentSessionTableAccess for super::RemoteTables {
fn puzzle_clear_agent_session(&self) -> PuzzleClearAgentSessionTableHandle<'_> {
PuzzleClearAgentSessionTableHandle {
imp: self
.imp
.get_table::<PuzzleClearAgentSessionRow>("puzzle_clear_agent_session"),
ctx: std::marker::PhantomData,
}
}
}
pub struct PuzzleClearAgentSessionInsertCallbackId(__sdk::CallbackId);
pub struct PuzzleClearAgentSessionDeleteCallbackId(__sdk::CallbackId);
impl<'ctx> __sdk::Table for PuzzleClearAgentSessionTableHandle<'ctx> {
type Row = PuzzleClearAgentSessionRow;
type EventContext = super::EventContext;
fn count(&self) -> u64 {
self.imp.count()
}
fn iter(&self) -> impl Iterator<Item = PuzzleClearAgentSessionRow> + '_ {
self.imp.iter()
}
type InsertCallbackId = PuzzleClearAgentSessionInsertCallbackId;
fn on_insert(
&self,
callback: impl FnMut(&Self::EventContext, &Self::Row) + Send + 'static,
) -> PuzzleClearAgentSessionInsertCallbackId {
PuzzleClearAgentSessionInsertCallbackId(self.imp.on_insert(Box::new(callback)))
}
fn remove_on_insert(&self, callback: PuzzleClearAgentSessionInsertCallbackId) {
self.imp.remove_on_insert(callback.0)
}
type DeleteCallbackId = PuzzleClearAgentSessionDeleteCallbackId;
fn on_delete(
&self,
callback: impl FnMut(&Self::EventContext, &Self::Row) + Send + 'static,
) -> PuzzleClearAgentSessionDeleteCallbackId {
PuzzleClearAgentSessionDeleteCallbackId(self.imp.on_delete(Box::new(callback)))
}
fn remove_on_delete(&self, callback: PuzzleClearAgentSessionDeleteCallbackId) {
self.imp.remove_on_delete(callback.0)
}
}
pub struct PuzzleClearAgentSessionUpdateCallbackId(__sdk::CallbackId);
impl<'ctx> __sdk::TableWithPrimaryKey for PuzzleClearAgentSessionTableHandle<'ctx> {
type UpdateCallbackId = PuzzleClearAgentSessionUpdateCallbackId;
fn on_update(
&self,
callback: impl FnMut(&Self::EventContext, &Self::Row, &Self::Row) + Send + 'static,
) -> PuzzleClearAgentSessionUpdateCallbackId {
PuzzleClearAgentSessionUpdateCallbackId(self.imp.on_update(Box::new(callback)))
}
fn remove_on_update(&self, callback: PuzzleClearAgentSessionUpdateCallbackId) {
self.imp.remove_on_update(callback.0)
}
}
/// Access to the `session_id` unique index on the table `puzzle_clear_agent_session`,
/// which allows point queries on the field of the same name
/// via the [`PuzzleClearAgentSessionSessionIdUnique::find`] method.
///
/// Users are encouraged not to explicitly reference this type,
/// but to directly chain method calls,
/// like `ctx.db.puzzle_clear_agent_session().session_id().find(...)`.
pub struct PuzzleClearAgentSessionSessionIdUnique<'ctx> {
imp: __sdk::UniqueConstraintHandle<PuzzleClearAgentSessionRow, String>,
phantom: std::marker::PhantomData<&'ctx super::RemoteTables>,
}
impl<'ctx> PuzzleClearAgentSessionTableHandle<'ctx> {
/// Get a handle on the `session_id` unique index on the table `puzzle_clear_agent_session`.
pub fn session_id(&self) -> PuzzleClearAgentSessionSessionIdUnique<'ctx> {
PuzzleClearAgentSessionSessionIdUnique {
imp: self.imp.get_unique_constraint::<String>("session_id"),
phantom: std::marker::PhantomData,
}
}
}
impl<'ctx> PuzzleClearAgentSessionSessionIdUnique<'ctx> {
/// Find the subscribed row whose `session_id` column value is equal to `col_val`,
/// if such a row is present in the client cache.
pub fn find(&self, col_val: &String) -> Option<PuzzleClearAgentSessionRow> {
self.imp.find(col_val)
}
}
#[doc(hidden)]
pub(super) fn register_table(client_cache: &mut __sdk::ClientCache<super::RemoteModule>) {
let _table =
client_cache.get_or_make_table::<PuzzleClearAgentSessionRow>("puzzle_clear_agent_session");
_table.add_unique_constraint::<String>("session_id", |row| &row.session_id);
}
#[doc(hidden)]
pub(super) fn parse_table_update(
raw_updates: __ws::v2::TableUpdate,
) -> __sdk::Result<__sdk::TableUpdate<PuzzleClearAgentSessionRow>> {
__sdk::TableUpdate::parse_table_update(raw_updates).map_err(|e| {
__sdk::InternalError::failed_parse("TableUpdate<PuzzleClearAgentSessionRow>", "TableUpdate")
.with_cause(e)
.into()
})
}
#[allow(non_camel_case_types)]
/// Extension trait for query builder access to the table `PuzzleClearAgentSessionRow`.
///
/// Implemented for [`__sdk::QueryTableAccessor`].
pub trait puzzle_clear_agent_sessionQueryTableAccess {
#[allow(non_snake_case)]
/// Get a query builder for the table `PuzzleClearAgentSessionRow`.
fn puzzle_clear_agent_session(
&self,
) -> __sdk::__query_builder::Table<PuzzleClearAgentSessionRow>;
}
impl puzzle_clear_agent_sessionQueryTableAccess for __sdk::QueryTableAccessor {
fn puzzle_clear_agent_session(
&self,
) -> __sdk::__query_builder::Table<PuzzleClearAgentSessionRow> {
__sdk::__query_builder::Table::new("puzzle_clear_agent_session")
}
}

View File

@@ -0,0 +1,20 @@
// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
#![allow(unused, clippy::all)]
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
use super::puzzle_clear_card_asset_snapshot_type::PuzzleClearCardAssetSnapshot;
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
#[sats(crate = __lib)]
pub struct PuzzleClearBoardCellSnapshot {
pub row: u32,
pub col: u32,
pub card: Option<PuzzleClearCardAssetSnapshot>,
pub locked_group_id: Option<String>,
}
impl __sdk::InModule for PuzzleClearBoardCellSnapshot {
type Module = super::RemoteModule;
}

View File

@@ -0,0 +1,19 @@
// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
#![allow(unused, clippy::all)]
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
use super::puzzle_clear_board_cell_snapshot_type::PuzzleClearBoardCellSnapshot;
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
#[sats(crate = __lib)]
pub struct PuzzleClearBoardSnapshot {
pub rows: u32,
pub cols: u32,
pub cells: Vec<PuzzleClearBoardCellSnapshot>,
}
impl __sdk::InModule for PuzzleClearBoardSnapshot {
type Module = super::RemoteModule;
}

View File

@@ -0,0 +1,24 @@
// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
#![allow(unused, clippy::all)]
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
#[sats(crate = __lib)]
pub struct PuzzleClearCardAssetSnapshot {
pub card_id: String,
pub group_id: String,
pub shape: String,
pub orientation: String,
pub part_x: u32,
pub part_y: u32,
pub image_src: String,
pub image_object_key: String,
pub asset_object_id: String,
pub source_atlas_cell: String,
}
impl __sdk::InModule for PuzzleClearCardAssetSnapshot {
type Module = super::RemoteModule;
}

View File

@@ -0,0 +1,29 @@
// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
#![allow(unused, clippy::all)]
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
#[sats(crate = __lib)]
pub struct PuzzleClearDraftCompileInput {
pub session_id: String,
pub owner_user_id: String,
pub profile_id: String,
pub author_display_name: String,
pub work_title: String,
pub work_description: String,
pub theme_prompt: String,
pub generate_board_background: bool,
pub board_background_asset_json: Option<String>,
pub board_background_prompt: String,
pub atlas_asset_json: Option<String>,
pub pattern_groups_json: Option<String>,
pub card_assets_json: Option<String>,
pub generation_status: Option<String>,
pub compiled_at_micros: i64,
}
impl __sdk::InModule for PuzzleClearDraftCompileInput {
type Module = super::RemoteModule;
}

View File

@@ -0,0 +1,32 @@
// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
#![allow(unused, clippy::all)]
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
use super::puzzle_clear_card_asset_snapshot_type::PuzzleClearCardAssetSnapshot;
use super::puzzle_clear_image_asset_snapshot_type::PuzzleClearImageAssetSnapshot;
use super::puzzle_clear_pattern_group_snapshot_type::PuzzleClearPatternGroupSnapshot;
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
#[sats(crate = __lib)]
pub struct PuzzleClearDraftSnapshot {
pub template_id: String,
pub template_name: String,
pub profile_id: Option<String>,
pub work_title: String,
pub work_description: String,
pub theme_prompt: String,
pub generate_board_background: bool,
pub board_background_asset: Option<PuzzleClearImageAssetSnapshot>,
pub board_background_prompt: String,
pub card_back_image_src: Option<String>,
pub atlas_asset: Option<PuzzleClearImageAssetSnapshot>,
pub pattern_groups: Vec<PuzzleClearPatternGroupSnapshot>,
pub card_assets: Vec<PuzzleClearCardAssetSnapshot>,
pub generation_status: String,
}
impl __sdk::InModule for PuzzleClearDraftSnapshot {
type Module = super::RemoteModule;
}

View File

@@ -0,0 +1,71 @@
// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
#![allow(unused, clippy::all)]
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
#[sats(crate = __lib)]
pub struct PuzzleClearEventRow {
pub event_id: String,
pub owner_user_id: String,
pub profile_id: String,
pub run_id: String,
pub event_type: String,
pub result: String,
pub occurred_at: __sdk::Timestamp,
}
impl __sdk::InModule for PuzzleClearEventRow {
type Module = super::RemoteModule;
}
/// Column accessor struct for the table `PuzzleClearEventRow`.
///
/// Provides typed access to columns for query building.
pub struct PuzzleClearEventRowCols {
pub event_id: __sdk::__query_builder::Col<PuzzleClearEventRow, String>,
pub owner_user_id: __sdk::__query_builder::Col<PuzzleClearEventRow, String>,
pub profile_id: __sdk::__query_builder::Col<PuzzleClearEventRow, String>,
pub run_id: __sdk::__query_builder::Col<PuzzleClearEventRow, String>,
pub event_type: __sdk::__query_builder::Col<PuzzleClearEventRow, String>,
pub result: __sdk::__query_builder::Col<PuzzleClearEventRow, String>,
pub occurred_at: __sdk::__query_builder::Col<PuzzleClearEventRow, __sdk::Timestamp>,
}
impl __sdk::__query_builder::HasCols for PuzzleClearEventRow {
type Cols = PuzzleClearEventRowCols;
fn cols(table_name: &'static str) -> Self::Cols {
PuzzleClearEventRowCols {
event_id: __sdk::__query_builder::Col::new(table_name, "event_id"),
owner_user_id: __sdk::__query_builder::Col::new(table_name, "owner_user_id"),
profile_id: __sdk::__query_builder::Col::new(table_name, "profile_id"),
run_id: __sdk::__query_builder::Col::new(table_name, "run_id"),
event_type: __sdk::__query_builder::Col::new(table_name, "event_type"),
result: __sdk::__query_builder::Col::new(table_name, "result"),
occurred_at: __sdk::__query_builder::Col::new(table_name, "occurred_at"),
}
}
}
/// Indexed column accessor struct for the table `PuzzleClearEventRow`.
///
/// Provides typed access to indexed columns for query building.
pub struct PuzzleClearEventRowIxCols {
pub event_id: __sdk::__query_builder::IxCol<PuzzleClearEventRow, String>,
pub profile_id: __sdk::__query_builder::IxCol<PuzzleClearEventRow, String>,
pub run_id: __sdk::__query_builder::IxCol<PuzzleClearEventRow, String>,
}
impl __sdk::__query_builder::HasIxCols for PuzzleClearEventRow {
type IxCols = PuzzleClearEventRowIxCols;
fn ix_cols(table_name: &'static str) -> Self::IxCols {
PuzzleClearEventRowIxCols {
event_id: __sdk::__query_builder::IxCol::new(table_name, "event_id"),
profile_id: __sdk::__query_builder::IxCol::new(table_name, "profile_id"),
run_id: __sdk::__query_builder::IxCol::new(table_name, "run_id"),
}
}
}
impl __sdk::__query_builder::CanBeLookupTable for PuzzleClearEventRow {}

View File

@@ -0,0 +1,161 @@
// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
#![allow(unused, clippy::all)]
use super::puzzle_clear_event_row_type::PuzzleClearEventRow;
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
/// Table handle for the table `puzzle_clear_event`.
///
/// Obtain a handle from the [`PuzzleClearEventTableAccess::puzzle_clear_event`] method on [`super::RemoteTables`],
/// like `ctx.db.puzzle_clear_event()`.
///
/// Users are encouraged not to explicitly reference this type,
/// but to directly chain method calls,
/// like `ctx.db.puzzle_clear_event().on_insert(...)`.
pub struct PuzzleClearEventTableHandle<'ctx> {
imp: __sdk::TableHandle<PuzzleClearEventRow>,
ctx: std::marker::PhantomData<&'ctx super::RemoteTables>,
}
#[allow(non_camel_case_types)]
/// Extension trait for access to the table `puzzle_clear_event`.
///
/// Implemented for [`super::RemoteTables`].
pub trait PuzzleClearEventTableAccess {
#[allow(non_snake_case)]
/// Obtain a [`PuzzleClearEventTableHandle`], which mediates access to the table `puzzle_clear_event`.
fn puzzle_clear_event(&self) -> PuzzleClearEventTableHandle<'_>;
}
impl PuzzleClearEventTableAccess for super::RemoteTables {
fn puzzle_clear_event(&self) -> PuzzleClearEventTableHandle<'_> {
PuzzleClearEventTableHandle {
imp: self
.imp
.get_table::<PuzzleClearEventRow>("puzzle_clear_event"),
ctx: std::marker::PhantomData,
}
}
}
pub struct PuzzleClearEventInsertCallbackId(__sdk::CallbackId);
pub struct PuzzleClearEventDeleteCallbackId(__sdk::CallbackId);
impl<'ctx> __sdk::Table for PuzzleClearEventTableHandle<'ctx> {
type Row = PuzzleClearEventRow;
type EventContext = super::EventContext;
fn count(&self) -> u64 {
self.imp.count()
}
fn iter(&self) -> impl Iterator<Item = PuzzleClearEventRow> + '_ {
self.imp.iter()
}
type InsertCallbackId = PuzzleClearEventInsertCallbackId;
fn on_insert(
&self,
callback: impl FnMut(&Self::EventContext, &Self::Row) + Send + 'static,
) -> PuzzleClearEventInsertCallbackId {
PuzzleClearEventInsertCallbackId(self.imp.on_insert(Box::new(callback)))
}
fn remove_on_insert(&self, callback: PuzzleClearEventInsertCallbackId) {
self.imp.remove_on_insert(callback.0)
}
type DeleteCallbackId = PuzzleClearEventDeleteCallbackId;
fn on_delete(
&self,
callback: impl FnMut(&Self::EventContext, &Self::Row) + Send + 'static,
) -> PuzzleClearEventDeleteCallbackId {
PuzzleClearEventDeleteCallbackId(self.imp.on_delete(Box::new(callback)))
}
fn remove_on_delete(&self, callback: PuzzleClearEventDeleteCallbackId) {
self.imp.remove_on_delete(callback.0)
}
}
pub struct PuzzleClearEventUpdateCallbackId(__sdk::CallbackId);
impl<'ctx> __sdk::TableWithPrimaryKey for PuzzleClearEventTableHandle<'ctx> {
type UpdateCallbackId = PuzzleClearEventUpdateCallbackId;
fn on_update(
&self,
callback: impl FnMut(&Self::EventContext, &Self::Row, &Self::Row) + Send + 'static,
) -> PuzzleClearEventUpdateCallbackId {
PuzzleClearEventUpdateCallbackId(self.imp.on_update(Box::new(callback)))
}
fn remove_on_update(&self, callback: PuzzleClearEventUpdateCallbackId) {
self.imp.remove_on_update(callback.0)
}
}
/// Access to the `event_id` unique index on the table `puzzle_clear_event`,
/// which allows point queries on the field of the same name
/// via the [`PuzzleClearEventEventIdUnique::find`] method.
///
/// Users are encouraged not to explicitly reference this type,
/// but to directly chain method calls,
/// like `ctx.db.puzzle_clear_event().event_id().find(...)`.
pub struct PuzzleClearEventEventIdUnique<'ctx> {
imp: __sdk::UniqueConstraintHandle<PuzzleClearEventRow, String>,
phantom: std::marker::PhantomData<&'ctx super::RemoteTables>,
}
impl<'ctx> PuzzleClearEventTableHandle<'ctx> {
/// Get a handle on the `event_id` unique index on the table `puzzle_clear_event`.
pub fn event_id(&self) -> PuzzleClearEventEventIdUnique<'ctx> {
PuzzleClearEventEventIdUnique {
imp: self.imp.get_unique_constraint::<String>("event_id"),
phantom: std::marker::PhantomData,
}
}
}
impl<'ctx> PuzzleClearEventEventIdUnique<'ctx> {
/// Find the subscribed row whose `event_id` column value is equal to `col_val`,
/// if such a row is present in the client cache.
pub fn find(&self, col_val: &String) -> Option<PuzzleClearEventRow> {
self.imp.find(col_val)
}
}
#[doc(hidden)]
pub(super) fn register_table(client_cache: &mut __sdk::ClientCache<super::RemoteModule>) {
let _table = client_cache.get_or_make_table::<PuzzleClearEventRow>("puzzle_clear_event");
_table.add_unique_constraint::<String>("event_id", |row| &row.event_id);
}
#[doc(hidden)]
pub(super) fn parse_table_update(
raw_updates: __ws::v2::TableUpdate,
) -> __sdk::Result<__sdk::TableUpdate<PuzzleClearEventRow>> {
__sdk::TableUpdate::parse_table_update(raw_updates).map_err(|e| {
__sdk::InternalError::failed_parse("TableUpdate<PuzzleClearEventRow>", "TableUpdate")
.with_cause(e)
.into()
})
}
#[allow(non_camel_case_types)]
/// Extension trait for query builder access to the table `PuzzleClearEventRow`.
///
/// Implemented for [`__sdk::QueryTableAccessor`].
pub trait puzzle_clear_eventQueryTableAccess {
#[allow(non_snake_case)]
/// Get a query builder for the table `PuzzleClearEventRow`.
fn puzzle_clear_event(&self) -> __sdk::__query_builder::Table<PuzzleClearEventRow>;
}
impl puzzle_clear_eventQueryTableAccess for __sdk::QueryTableAccessor {
fn puzzle_clear_event(&self) -> __sdk::__query_builder::Table<PuzzleClearEventRow> {
__sdk::__query_builder::Table::new("puzzle_clear_event")
}
}

View File

@@ -0,0 +1,77 @@
// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
#![allow(unused, clippy::all)]
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
#[sats(crate = __lib)]
pub struct PuzzleClearGalleryCardViewRow {
pub public_work_code: String,
pub work_id: String,
pub profile_id: String,
pub owner_user_id: String,
pub author_display_name: String,
pub work_title: String,
pub work_description: String,
pub theme_prompt: String,
pub cover_image_src: Option<String>,
pub publication_status: String,
pub play_count: u32,
pub updated_at_micros: i64,
pub published_at_micros: Option<i64>,
pub generation_status: String,
}
impl __sdk::InModule for PuzzleClearGalleryCardViewRow {
type Module = super::RemoteModule;
}
/// Column accessor struct for the table `PuzzleClearGalleryCardViewRow`.
///
/// Provides typed access to columns for query building.
pub struct PuzzleClearGalleryCardViewRowCols {
pub public_work_code: __sdk::__query_builder::Col<PuzzleClearGalleryCardViewRow, String>,
pub work_id: __sdk::__query_builder::Col<PuzzleClearGalleryCardViewRow, String>,
pub profile_id: __sdk::__query_builder::Col<PuzzleClearGalleryCardViewRow, String>,
pub owner_user_id: __sdk::__query_builder::Col<PuzzleClearGalleryCardViewRow, String>,
pub author_display_name: __sdk::__query_builder::Col<PuzzleClearGalleryCardViewRow, String>,
pub work_title: __sdk::__query_builder::Col<PuzzleClearGalleryCardViewRow, String>,
pub work_description: __sdk::__query_builder::Col<PuzzleClearGalleryCardViewRow, String>,
pub theme_prompt: __sdk::__query_builder::Col<PuzzleClearGalleryCardViewRow, String>,
pub cover_image_src: __sdk::__query_builder::Col<PuzzleClearGalleryCardViewRow, Option<String>>,
pub publication_status: __sdk::__query_builder::Col<PuzzleClearGalleryCardViewRow, String>,
pub play_count: __sdk::__query_builder::Col<PuzzleClearGalleryCardViewRow, u32>,
pub updated_at_micros: __sdk::__query_builder::Col<PuzzleClearGalleryCardViewRow, i64>,
pub published_at_micros:
__sdk::__query_builder::Col<PuzzleClearGalleryCardViewRow, Option<i64>>,
pub generation_status: __sdk::__query_builder::Col<PuzzleClearGalleryCardViewRow, String>,
}
impl __sdk::__query_builder::HasCols for PuzzleClearGalleryCardViewRow {
type Cols = PuzzleClearGalleryCardViewRowCols;
fn cols(table_name: &'static str) -> Self::Cols {
PuzzleClearGalleryCardViewRowCols {
public_work_code: __sdk::__query_builder::Col::new(table_name, "public_work_code"),
work_id: __sdk::__query_builder::Col::new(table_name, "work_id"),
profile_id: __sdk::__query_builder::Col::new(table_name, "profile_id"),
owner_user_id: __sdk::__query_builder::Col::new(table_name, "owner_user_id"),
author_display_name: __sdk::__query_builder::Col::new(
table_name,
"author_display_name",
),
work_title: __sdk::__query_builder::Col::new(table_name, "work_title"),
work_description: __sdk::__query_builder::Col::new(table_name, "work_description"),
theme_prompt: __sdk::__query_builder::Col::new(table_name, "theme_prompt"),
cover_image_src: __sdk::__query_builder::Col::new(table_name, "cover_image_src"),
publication_status: __sdk::__query_builder::Col::new(table_name, "publication_status"),
play_count: __sdk::__query_builder::Col::new(table_name, "play_count"),
updated_at_micros: __sdk::__query_builder::Col::new(table_name, "updated_at_micros"),
published_at_micros: __sdk::__query_builder::Col::new(
table_name,
"published_at_micros",
),
generation_status: __sdk::__query_builder::Col::new(table_name, "generation_status"),
}
}
}

View File

@@ -0,0 +1,121 @@
// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
#![allow(unused, clippy::all)]
use super::puzzle_clear_gallery_card_view_row_type::PuzzleClearGalleryCardViewRow;
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
/// Table handle for the table `puzzle_clear_gallery_card_view`.
///
/// Obtain a handle from the [`PuzzleClearGalleryCardViewTableAccess::puzzle_clear_gallery_card_view`] method on [`super::RemoteTables`],
/// like `ctx.db.puzzle_clear_gallery_card_view()`.
///
/// Users are encouraged not to explicitly reference this type,
/// but to directly chain method calls,
/// like `ctx.db.puzzle_clear_gallery_card_view().on_insert(...)`.
pub struct PuzzleClearGalleryCardViewTableHandle<'ctx> {
imp: __sdk::TableHandle<PuzzleClearGalleryCardViewRow>,
ctx: std::marker::PhantomData<&'ctx super::RemoteTables>,
}
#[allow(non_camel_case_types)]
/// Extension trait for access to the table `puzzle_clear_gallery_card_view`.
///
/// Implemented for [`super::RemoteTables`].
pub trait PuzzleClearGalleryCardViewTableAccess {
#[allow(non_snake_case)]
/// Obtain a [`PuzzleClearGalleryCardViewTableHandle`], which mediates access to the table `puzzle_clear_gallery_card_view`.
fn puzzle_clear_gallery_card_view(&self) -> PuzzleClearGalleryCardViewTableHandle<'_>;
}
impl PuzzleClearGalleryCardViewTableAccess for super::RemoteTables {
fn puzzle_clear_gallery_card_view(&self) -> PuzzleClearGalleryCardViewTableHandle<'_> {
PuzzleClearGalleryCardViewTableHandle {
imp: self
.imp
.get_table::<PuzzleClearGalleryCardViewRow>("puzzle_clear_gallery_card_view"),
ctx: std::marker::PhantomData,
}
}
}
pub struct PuzzleClearGalleryCardViewInsertCallbackId(__sdk::CallbackId);
pub struct PuzzleClearGalleryCardViewDeleteCallbackId(__sdk::CallbackId);
impl<'ctx> __sdk::Table for PuzzleClearGalleryCardViewTableHandle<'ctx> {
type Row = PuzzleClearGalleryCardViewRow;
type EventContext = super::EventContext;
fn count(&self) -> u64 {
self.imp.count()
}
fn iter(&self) -> impl Iterator<Item = PuzzleClearGalleryCardViewRow> + '_ {
self.imp.iter()
}
type InsertCallbackId = PuzzleClearGalleryCardViewInsertCallbackId;
fn on_insert(
&self,
callback: impl FnMut(&Self::EventContext, &Self::Row) + Send + 'static,
) -> PuzzleClearGalleryCardViewInsertCallbackId {
PuzzleClearGalleryCardViewInsertCallbackId(self.imp.on_insert(Box::new(callback)))
}
fn remove_on_insert(&self, callback: PuzzleClearGalleryCardViewInsertCallbackId) {
self.imp.remove_on_insert(callback.0)
}
type DeleteCallbackId = PuzzleClearGalleryCardViewDeleteCallbackId;
fn on_delete(
&self,
callback: impl FnMut(&Self::EventContext, &Self::Row) + Send + 'static,
) -> PuzzleClearGalleryCardViewDeleteCallbackId {
PuzzleClearGalleryCardViewDeleteCallbackId(self.imp.on_delete(Box::new(callback)))
}
fn remove_on_delete(&self, callback: PuzzleClearGalleryCardViewDeleteCallbackId) {
self.imp.remove_on_delete(callback.0)
}
}
#[doc(hidden)]
pub(super) fn register_table(client_cache: &mut __sdk::ClientCache<super::RemoteModule>) {
let _table = client_cache
.get_or_make_table::<PuzzleClearGalleryCardViewRow>("puzzle_clear_gallery_card_view");
}
#[doc(hidden)]
pub(super) fn parse_table_update(
raw_updates: __ws::v2::TableUpdate,
) -> __sdk::Result<__sdk::TableUpdate<PuzzleClearGalleryCardViewRow>> {
__sdk::TableUpdate::parse_table_update(raw_updates).map_err(|e| {
__sdk::InternalError::failed_parse(
"TableUpdate<PuzzleClearGalleryCardViewRow>",
"TableUpdate",
)
.with_cause(e)
.into()
})
}
#[allow(non_camel_case_types)]
/// Extension trait for query builder access to the table `PuzzleClearGalleryCardViewRow`.
///
/// Implemented for [`__sdk::QueryTableAccessor`].
pub trait puzzle_clear_gallery_card_viewQueryTableAccess {
#[allow(non_snake_case)]
/// Get a query builder for the table `PuzzleClearGalleryCardViewRow`.
fn puzzle_clear_gallery_card_view(
&self,
) -> __sdk::__query_builder::Table<PuzzleClearGalleryCardViewRow>;
}
impl puzzle_clear_gallery_card_viewQueryTableAccess for __sdk::QueryTableAccessor {
fn puzzle_clear_gallery_card_view(
&self,
) -> __sdk::__query_builder::Table<PuzzleClearGalleryCardViewRow> {
__sdk::__query_builder::Table::new("puzzle_clear_gallery_card_view")
}
}

View File

@@ -0,0 +1,124 @@
// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
#![allow(unused, clippy::all)]
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
use super::puzzle_clear_card_asset_snapshot_type::PuzzleClearCardAssetSnapshot;
use super::puzzle_clear_image_asset_snapshot_type::PuzzleClearImageAssetSnapshot;
use super::puzzle_clear_pattern_group_snapshot_type::PuzzleClearPatternGroupSnapshot;
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
#[sats(crate = __lib)]
pub struct PuzzleClearGalleryViewRow {
pub work_id: String,
pub profile_id: String,
pub owner_user_id: String,
pub source_session_id: String,
pub author_display_name: String,
pub work_title: String,
pub work_description: String,
pub theme_prompt: String,
pub generate_board_background: bool,
pub board_background_asset: Option<PuzzleClearImageAssetSnapshot>,
pub board_background_prompt: String,
pub card_back_image_src: Option<String>,
pub atlas_asset: PuzzleClearImageAssetSnapshot,
pub pattern_groups: Vec<PuzzleClearPatternGroupSnapshot>,
pub card_assets: Vec<PuzzleClearCardAssetSnapshot>,
pub cover_image_src: Option<String>,
pub publication_status: String,
pub publish_ready: bool,
pub play_count: u32,
pub generation_status: String,
pub updated_at_micros: i64,
pub published_at_micros: Option<i64>,
}
impl __sdk::InModule for PuzzleClearGalleryViewRow {
type Module = super::RemoteModule;
}
/// Column accessor struct for the table `PuzzleClearGalleryViewRow`.
///
/// Provides typed access to columns for query building.
pub struct PuzzleClearGalleryViewRowCols {
pub work_id: __sdk::__query_builder::Col<PuzzleClearGalleryViewRow, String>,
pub profile_id: __sdk::__query_builder::Col<PuzzleClearGalleryViewRow, String>,
pub owner_user_id: __sdk::__query_builder::Col<PuzzleClearGalleryViewRow, String>,
pub source_session_id: __sdk::__query_builder::Col<PuzzleClearGalleryViewRow, String>,
pub author_display_name: __sdk::__query_builder::Col<PuzzleClearGalleryViewRow, String>,
pub work_title: __sdk::__query_builder::Col<PuzzleClearGalleryViewRow, String>,
pub work_description: __sdk::__query_builder::Col<PuzzleClearGalleryViewRow, String>,
pub theme_prompt: __sdk::__query_builder::Col<PuzzleClearGalleryViewRow, String>,
pub generate_board_background: __sdk::__query_builder::Col<PuzzleClearGalleryViewRow, bool>,
pub board_background_asset: __sdk::__query_builder::Col<
PuzzleClearGalleryViewRow,
Option<PuzzleClearImageAssetSnapshot>,
>,
pub board_background_prompt: __sdk::__query_builder::Col<PuzzleClearGalleryViewRow, String>,
pub card_back_image_src: __sdk::__query_builder::Col<PuzzleClearGalleryViewRow, Option<String>>,
pub atlas_asset:
__sdk::__query_builder::Col<PuzzleClearGalleryViewRow, PuzzleClearImageAssetSnapshot>,
pub pattern_groups: __sdk::__query_builder::Col<
PuzzleClearGalleryViewRow,
Vec<PuzzleClearPatternGroupSnapshot>,
>,
pub card_assets:
__sdk::__query_builder::Col<PuzzleClearGalleryViewRow, Vec<PuzzleClearCardAssetSnapshot>>,
pub cover_image_src: __sdk::__query_builder::Col<PuzzleClearGalleryViewRow, Option<String>>,
pub publication_status: __sdk::__query_builder::Col<PuzzleClearGalleryViewRow, String>,
pub publish_ready: __sdk::__query_builder::Col<PuzzleClearGalleryViewRow, bool>,
pub play_count: __sdk::__query_builder::Col<PuzzleClearGalleryViewRow, u32>,
pub generation_status: __sdk::__query_builder::Col<PuzzleClearGalleryViewRow, String>,
pub updated_at_micros: __sdk::__query_builder::Col<PuzzleClearGalleryViewRow, i64>,
pub published_at_micros: __sdk::__query_builder::Col<PuzzleClearGalleryViewRow, Option<i64>>,
}
impl __sdk::__query_builder::HasCols for PuzzleClearGalleryViewRow {
type Cols = PuzzleClearGalleryViewRowCols;
fn cols(table_name: &'static str) -> Self::Cols {
PuzzleClearGalleryViewRowCols {
work_id: __sdk::__query_builder::Col::new(table_name, "work_id"),
profile_id: __sdk::__query_builder::Col::new(table_name, "profile_id"),
owner_user_id: __sdk::__query_builder::Col::new(table_name, "owner_user_id"),
source_session_id: __sdk::__query_builder::Col::new(table_name, "source_session_id"),
author_display_name: __sdk::__query_builder::Col::new(
table_name,
"author_display_name",
),
work_title: __sdk::__query_builder::Col::new(table_name, "work_title"),
work_description: __sdk::__query_builder::Col::new(table_name, "work_description"),
theme_prompt: __sdk::__query_builder::Col::new(table_name, "theme_prompt"),
generate_board_background: __sdk::__query_builder::Col::new(
table_name,
"generate_board_background",
),
board_background_asset: __sdk::__query_builder::Col::new(
table_name,
"board_background_asset",
),
board_background_prompt: __sdk::__query_builder::Col::new(
table_name,
"board_background_prompt",
),
card_back_image_src: __sdk::__query_builder::Col::new(
table_name,
"card_back_image_src",
),
atlas_asset: __sdk::__query_builder::Col::new(table_name, "atlas_asset"),
pattern_groups: __sdk::__query_builder::Col::new(table_name, "pattern_groups"),
card_assets: __sdk::__query_builder::Col::new(table_name, "card_assets"),
cover_image_src: __sdk::__query_builder::Col::new(table_name, "cover_image_src"),
publication_status: __sdk::__query_builder::Col::new(table_name, "publication_status"),
publish_ready: __sdk::__query_builder::Col::new(table_name, "publish_ready"),
play_count: __sdk::__query_builder::Col::new(table_name, "play_count"),
generation_status: __sdk::__query_builder::Col::new(table_name, "generation_status"),
updated_at_micros: __sdk::__query_builder::Col::new(table_name, "updated_at_micros"),
published_at_micros: __sdk::__query_builder::Col::new(
table_name,
"published_at_micros",
),
}
}
}

View File

@@ -0,0 +1,120 @@
// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
#![allow(unused, clippy::all)]
use super::puzzle_clear_card_asset_snapshot_type::PuzzleClearCardAssetSnapshot;
use super::puzzle_clear_gallery_view_row_type::PuzzleClearGalleryViewRow;
use super::puzzle_clear_image_asset_snapshot_type::PuzzleClearImageAssetSnapshot;
use super::puzzle_clear_pattern_group_snapshot_type::PuzzleClearPatternGroupSnapshot;
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
/// Table handle for the table `puzzle_clear_gallery_view`.
///
/// Obtain a handle from the [`PuzzleClearGalleryViewTableAccess::puzzle_clear_gallery_view`] method on [`super::RemoteTables`],
/// like `ctx.db.puzzle_clear_gallery_view()`.
///
/// Users are encouraged not to explicitly reference this type,
/// but to directly chain method calls,
/// like `ctx.db.puzzle_clear_gallery_view().on_insert(...)`.
pub struct PuzzleClearGalleryViewTableHandle<'ctx> {
imp: __sdk::TableHandle<PuzzleClearGalleryViewRow>,
ctx: std::marker::PhantomData<&'ctx super::RemoteTables>,
}
#[allow(non_camel_case_types)]
/// Extension trait for access to the table `puzzle_clear_gallery_view`.
///
/// Implemented for [`super::RemoteTables`].
pub trait PuzzleClearGalleryViewTableAccess {
#[allow(non_snake_case)]
/// Obtain a [`PuzzleClearGalleryViewTableHandle`], which mediates access to the table `puzzle_clear_gallery_view`.
fn puzzle_clear_gallery_view(&self) -> PuzzleClearGalleryViewTableHandle<'_>;
}
impl PuzzleClearGalleryViewTableAccess for super::RemoteTables {
fn puzzle_clear_gallery_view(&self) -> PuzzleClearGalleryViewTableHandle<'_> {
PuzzleClearGalleryViewTableHandle {
imp: self
.imp
.get_table::<PuzzleClearGalleryViewRow>("puzzle_clear_gallery_view"),
ctx: std::marker::PhantomData,
}
}
}
pub struct PuzzleClearGalleryViewInsertCallbackId(__sdk::CallbackId);
pub struct PuzzleClearGalleryViewDeleteCallbackId(__sdk::CallbackId);
impl<'ctx> __sdk::Table for PuzzleClearGalleryViewTableHandle<'ctx> {
type Row = PuzzleClearGalleryViewRow;
type EventContext = super::EventContext;
fn count(&self) -> u64 {
self.imp.count()
}
fn iter(&self) -> impl Iterator<Item = PuzzleClearGalleryViewRow> + '_ {
self.imp.iter()
}
type InsertCallbackId = PuzzleClearGalleryViewInsertCallbackId;
fn on_insert(
&self,
callback: impl FnMut(&Self::EventContext, &Self::Row) + Send + 'static,
) -> PuzzleClearGalleryViewInsertCallbackId {
PuzzleClearGalleryViewInsertCallbackId(self.imp.on_insert(Box::new(callback)))
}
fn remove_on_insert(&self, callback: PuzzleClearGalleryViewInsertCallbackId) {
self.imp.remove_on_insert(callback.0)
}
type DeleteCallbackId = PuzzleClearGalleryViewDeleteCallbackId;
fn on_delete(
&self,
callback: impl FnMut(&Self::EventContext, &Self::Row) + Send + 'static,
) -> PuzzleClearGalleryViewDeleteCallbackId {
PuzzleClearGalleryViewDeleteCallbackId(self.imp.on_delete(Box::new(callback)))
}
fn remove_on_delete(&self, callback: PuzzleClearGalleryViewDeleteCallbackId) {
self.imp.remove_on_delete(callback.0)
}
}
#[doc(hidden)]
pub(super) fn register_table(client_cache: &mut __sdk::ClientCache<super::RemoteModule>) {
let _table =
client_cache.get_or_make_table::<PuzzleClearGalleryViewRow>("puzzle_clear_gallery_view");
}
#[doc(hidden)]
pub(super) fn parse_table_update(
raw_updates: __ws::v2::TableUpdate,
) -> __sdk::Result<__sdk::TableUpdate<PuzzleClearGalleryViewRow>> {
__sdk::TableUpdate::parse_table_update(raw_updates).map_err(|e| {
__sdk::InternalError::failed_parse("TableUpdate<PuzzleClearGalleryViewRow>", "TableUpdate")
.with_cause(e)
.into()
})
}
#[allow(non_camel_case_types)]
/// Extension trait for query builder access to the table `PuzzleClearGalleryViewRow`.
///
/// Implemented for [`__sdk::QueryTableAccessor`].
pub trait puzzle_clear_gallery_viewQueryTableAccess {
#[allow(non_snake_case)]
/// Get a query builder for the table `PuzzleClearGalleryViewRow`.
fn puzzle_clear_gallery_view(&self)
-> __sdk::__query_builder::Table<PuzzleClearGalleryViewRow>;
}
impl puzzle_clear_gallery_viewQueryTableAccess for __sdk::QueryTableAccessor {
fn puzzle_clear_gallery_view(
&self,
) -> __sdk::__query_builder::Table<PuzzleClearGalleryViewRow> {
__sdk::__query_builder::Table::new("puzzle_clear_gallery_view")
}
}

View File

@@ -0,0 +1,22 @@
// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
#![allow(unused, clippy::all)]
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
#[sats(crate = __lib)]
pub struct PuzzleClearImageAssetSnapshot {
pub asset_id: String,
pub image_src: String,
pub image_object_key: String,
pub asset_object_id: String,
pub generation_provider: String,
pub prompt: String,
pub width: u32,
pub height: u32,
}
impl __sdk::InModule for PuzzleClearImageAssetSnapshot {
type Module = super::RemoteModule;
}

View File

@@ -0,0 +1,22 @@
// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
#![allow(unused, clippy::all)]
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
#[sats(crate = __lib)]
pub struct PuzzleClearPatternGroupSnapshot {
pub group_id: String,
pub shape: String,
pub width: u32,
pub height: u32,
pub atlas_x: u32,
pub atlas_y: u32,
pub atlas_width: u32,
pub atlas_height: u32,
}
impl __sdk::InModule for PuzzleClearPatternGroupSnapshot {
type Module = super::RemoteModule;
}

View File

@@ -0,0 +1,16 @@
// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
#![allow(unused, clippy::all)]
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
#[sats(crate = __lib)]
pub struct PuzzleClearRunGetInput {
pub run_id: String,
pub owner_user_id: String,
}
impl __sdk::InModule for PuzzleClearRunGetInput {
type Module = super::RemoteModule;
}

View File

@@ -0,0 +1,18 @@
// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
#![allow(unused, clippy::all)]
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
#[sats(crate = __lib)]
pub struct PuzzleClearRunNextLevelInput {
pub run_id: String,
pub owner_user_id: String,
pub client_action_id: String,
pub started_at_ms: i64,
}
impl __sdk::InModule for PuzzleClearRunNextLevelInput {
type Module = super::RemoteModule;
}

View File

@@ -0,0 +1,19 @@
// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
#![allow(unused, clippy::all)]
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
use super::puzzle_clear_runtime_snapshot_type::PuzzleClearRuntimeSnapshot;
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
#[sats(crate = __lib)]
pub struct PuzzleClearRunProcedureResult {
pub ok: bool,
pub run: Option<PuzzleClearRuntimeSnapshot>,
pub error_message: Option<String>,
}
impl __sdk::InModule for PuzzleClearRunProcedureResult {
type Module = super::RemoteModule;
}

View File

@@ -0,0 +1,18 @@
// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
#![allow(unused, clippy::all)]
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
#[sats(crate = __lib)]
pub struct PuzzleClearRunRetryLevelInput {
pub run_id: String,
pub owner_user_id: String,
pub client_action_id: String,
pub restarted_at_ms: i64,
}
impl __sdk::InModule for PuzzleClearRunRetryLevelInput {
type Module = super::RemoteModule;
}

View File

@@ -0,0 +1,19 @@
// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
#![allow(unused, clippy::all)]
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
#[sats(crate = __lib)]
pub struct PuzzleClearRunStartInput {
pub run_id: String,
pub owner_user_id: String,
pub profile_id: String,
pub client_event_id: String,
pub started_at_ms: i64,
}
impl __sdk::InModule for PuzzleClearRunStartInput {
type Module = super::RemoteModule;
}

View File

@@ -0,0 +1,22 @@
// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
#![allow(unused, clippy::all)]
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
#[sats(crate = __lib)]
pub struct PuzzleClearRunSwapInput {
pub run_id: String,
pub owner_user_id: String,
pub from_row: u32,
pub from_col: u32,
pub to_row: u32,
pub to_col: u32,
pub client_action_id: String,
pub swapped_at_ms: i64,
}
impl __sdk::InModule for PuzzleClearRunSwapInput {
type Module = super::RemoteModule;
}

View File

@@ -0,0 +1,18 @@
// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
#![allow(unused, clippy::all)]
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
#[sats(crate = __lib)]
pub struct PuzzleClearRunTimeUpInput {
pub run_id: String,
pub owner_user_id: String,
pub client_action_id: String,
pub occurred_at_ms: i64,
}
impl __sdk::InModule for PuzzleClearRunTimeUpInput {
type Module = super::RemoteModule;
}

View File

@@ -0,0 +1,83 @@
// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
#![allow(unused, clippy::all)]
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
#[sats(crate = __lib)]
pub struct PuzzleClearRuntimeRunRow {
pub run_id: String,
pub owner_user_id: String,
pub profile_id: String,
pub status: String,
pub level_index: u32,
pub clears_done: u32,
pub snapshot_json: String,
pub started_at_ms: i64,
pub finished_at_ms: i64,
pub created_at: __sdk::Timestamp,
pub updated_at: __sdk::Timestamp,
}
impl __sdk::InModule for PuzzleClearRuntimeRunRow {
type Module = super::RemoteModule;
}
/// Column accessor struct for the table `PuzzleClearRuntimeRunRow`.
///
/// Provides typed access to columns for query building.
pub struct PuzzleClearRuntimeRunRowCols {
pub run_id: __sdk::__query_builder::Col<PuzzleClearRuntimeRunRow, String>,
pub owner_user_id: __sdk::__query_builder::Col<PuzzleClearRuntimeRunRow, String>,
pub profile_id: __sdk::__query_builder::Col<PuzzleClearRuntimeRunRow, String>,
pub status: __sdk::__query_builder::Col<PuzzleClearRuntimeRunRow, String>,
pub level_index: __sdk::__query_builder::Col<PuzzleClearRuntimeRunRow, u32>,
pub clears_done: __sdk::__query_builder::Col<PuzzleClearRuntimeRunRow, u32>,
pub snapshot_json: __sdk::__query_builder::Col<PuzzleClearRuntimeRunRow, String>,
pub started_at_ms: __sdk::__query_builder::Col<PuzzleClearRuntimeRunRow, i64>,
pub finished_at_ms: __sdk::__query_builder::Col<PuzzleClearRuntimeRunRow, i64>,
pub created_at: __sdk::__query_builder::Col<PuzzleClearRuntimeRunRow, __sdk::Timestamp>,
pub updated_at: __sdk::__query_builder::Col<PuzzleClearRuntimeRunRow, __sdk::Timestamp>,
}
impl __sdk::__query_builder::HasCols for PuzzleClearRuntimeRunRow {
type Cols = PuzzleClearRuntimeRunRowCols;
fn cols(table_name: &'static str) -> Self::Cols {
PuzzleClearRuntimeRunRowCols {
run_id: __sdk::__query_builder::Col::new(table_name, "run_id"),
owner_user_id: __sdk::__query_builder::Col::new(table_name, "owner_user_id"),
profile_id: __sdk::__query_builder::Col::new(table_name, "profile_id"),
status: __sdk::__query_builder::Col::new(table_name, "status"),
level_index: __sdk::__query_builder::Col::new(table_name, "level_index"),
clears_done: __sdk::__query_builder::Col::new(table_name, "clears_done"),
snapshot_json: __sdk::__query_builder::Col::new(table_name, "snapshot_json"),
started_at_ms: __sdk::__query_builder::Col::new(table_name, "started_at_ms"),
finished_at_ms: __sdk::__query_builder::Col::new(table_name, "finished_at_ms"),
created_at: __sdk::__query_builder::Col::new(table_name, "created_at"),
updated_at: __sdk::__query_builder::Col::new(table_name, "updated_at"),
}
}
}
/// Indexed column accessor struct for the table `PuzzleClearRuntimeRunRow`.
///
/// Provides typed access to indexed columns for query building.
pub struct PuzzleClearRuntimeRunRowIxCols {
pub owner_user_id: __sdk::__query_builder::IxCol<PuzzleClearRuntimeRunRow, String>,
pub profile_id: __sdk::__query_builder::IxCol<PuzzleClearRuntimeRunRow, String>,
pub run_id: __sdk::__query_builder::IxCol<PuzzleClearRuntimeRunRow, String>,
}
impl __sdk::__query_builder::HasIxCols for PuzzleClearRuntimeRunRow {
type IxCols = PuzzleClearRuntimeRunRowIxCols;
fn ix_cols(table_name: &'static str) -> Self::IxCols {
PuzzleClearRuntimeRunRowIxCols {
owner_user_id: __sdk::__query_builder::IxCol::new(table_name, "owner_user_id"),
profile_id: __sdk::__query_builder::IxCol::new(table_name, "profile_id"),
run_id: __sdk::__query_builder::IxCol::new(table_name, "run_id"),
}
}
}
impl __sdk::__query_builder::CanBeLookupTable for PuzzleClearRuntimeRunRow {}

View File

@@ -0,0 +1,162 @@
// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
#![allow(unused, clippy::all)]
use super::puzzle_clear_runtime_run_row_type::PuzzleClearRuntimeRunRow;
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
/// Table handle for the table `puzzle_clear_runtime_run`.
///
/// Obtain a handle from the [`PuzzleClearRuntimeRunTableAccess::puzzle_clear_runtime_run`] method on [`super::RemoteTables`],
/// like `ctx.db.puzzle_clear_runtime_run()`.
///
/// Users are encouraged not to explicitly reference this type,
/// but to directly chain method calls,
/// like `ctx.db.puzzle_clear_runtime_run().on_insert(...)`.
pub struct PuzzleClearRuntimeRunTableHandle<'ctx> {
imp: __sdk::TableHandle<PuzzleClearRuntimeRunRow>,
ctx: std::marker::PhantomData<&'ctx super::RemoteTables>,
}
#[allow(non_camel_case_types)]
/// Extension trait for access to the table `puzzle_clear_runtime_run`.
///
/// Implemented for [`super::RemoteTables`].
pub trait PuzzleClearRuntimeRunTableAccess {
#[allow(non_snake_case)]
/// Obtain a [`PuzzleClearRuntimeRunTableHandle`], which mediates access to the table `puzzle_clear_runtime_run`.
fn puzzle_clear_runtime_run(&self) -> PuzzleClearRuntimeRunTableHandle<'_>;
}
impl PuzzleClearRuntimeRunTableAccess for super::RemoteTables {
fn puzzle_clear_runtime_run(&self) -> PuzzleClearRuntimeRunTableHandle<'_> {
PuzzleClearRuntimeRunTableHandle {
imp: self
.imp
.get_table::<PuzzleClearRuntimeRunRow>("puzzle_clear_runtime_run"),
ctx: std::marker::PhantomData,
}
}
}
pub struct PuzzleClearRuntimeRunInsertCallbackId(__sdk::CallbackId);
pub struct PuzzleClearRuntimeRunDeleteCallbackId(__sdk::CallbackId);
impl<'ctx> __sdk::Table for PuzzleClearRuntimeRunTableHandle<'ctx> {
type Row = PuzzleClearRuntimeRunRow;
type EventContext = super::EventContext;
fn count(&self) -> u64 {
self.imp.count()
}
fn iter(&self) -> impl Iterator<Item = PuzzleClearRuntimeRunRow> + '_ {
self.imp.iter()
}
type InsertCallbackId = PuzzleClearRuntimeRunInsertCallbackId;
fn on_insert(
&self,
callback: impl FnMut(&Self::EventContext, &Self::Row) + Send + 'static,
) -> PuzzleClearRuntimeRunInsertCallbackId {
PuzzleClearRuntimeRunInsertCallbackId(self.imp.on_insert(Box::new(callback)))
}
fn remove_on_insert(&self, callback: PuzzleClearRuntimeRunInsertCallbackId) {
self.imp.remove_on_insert(callback.0)
}
type DeleteCallbackId = PuzzleClearRuntimeRunDeleteCallbackId;
fn on_delete(
&self,
callback: impl FnMut(&Self::EventContext, &Self::Row) + Send + 'static,
) -> PuzzleClearRuntimeRunDeleteCallbackId {
PuzzleClearRuntimeRunDeleteCallbackId(self.imp.on_delete(Box::new(callback)))
}
fn remove_on_delete(&self, callback: PuzzleClearRuntimeRunDeleteCallbackId) {
self.imp.remove_on_delete(callback.0)
}
}
pub struct PuzzleClearRuntimeRunUpdateCallbackId(__sdk::CallbackId);
impl<'ctx> __sdk::TableWithPrimaryKey for PuzzleClearRuntimeRunTableHandle<'ctx> {
type UpdateCallbackId = PuzzleClearRuntimeRunUpdateCallbackId;
fn on_update(
&self,
callback: impl FnMut(&Self::EventContext, &Self::Row, &Self::Row) + Send + 'static,
) -> PuzzleClearRuntimeRunUpdateCallbackId {
PuzzleClearRuntimeRunUpdateCallbackId(self.imp.on_update(Box::new(callback)))
}
fn remove_on_update(&self, callback: PuzzleClearRuntimeRunUpdateCallbackId) {
self.imp.remove_on_update(callback.0)
}
}
/// Access to the `run_id` unique index on the table `puzzle_clear_runtime_run`,
/// which allows point queries on the field of the same name
/// via the [`PuzzleClearRuntimeRunRunIdUnique::find`] method.
///
/// Users are encouraged not to explicitly reference this type,
/// but to directly chain method calls,
/// like `ctx.db.puzzle_clear_runtime_run().run_id().find(...)`.
pub struct PuzzleClearRuntimeRunRunIdUnique<'ctx> {
imp: __sdk::UniqueConstraintHandle<PuzzleClearRuntimeRunRow, String>,
phantom: std::marker::PhantomData<&'ctx super::RemoteTables>,
}
impl<'ctx> PuzzleClearRuntimeRunTableHandle<'ctx> {
/// Get a handle on the `run_id` unique index on the table `puzzle_clear_runtime_run`.
pub fn run_id(&self) -> PuzzleClearRuntimeRunRunIdUnique<'ctx> {
PuzzleClearRuntimeRunRunIdUnique {
imp: self.imp.get_unique_constraint::<String>("run_id"),
phantom: std::marker::PhantomData,
}
}
}
impl<'ctx> PuzzleClearRuntimeRunRunIdUnique<'ctx> {
/// Find the subscribed row whose `run_id` column value is equal to `col_val`,
/// if such a row is present in the client cache.
pub fn find(&self, col_val: &String) -> Option<PuzzleClearRuntimeRunRow> {
self.imp.find(col_val)
}
}
#[doc(hidden)]
pub(super) fn register_table(client_cache: &mut __sdk::ClientCache<super::RemoteModule>) {
let _table =
client_cache.get_or_make_table::<PuzzleClearRuntimeRunRow>("puzzle_clear_runtime_run");
_table.add_unique_constraint::<String>("run_id", |row| &row.run_id);
}
#[doc(hidden)]
pub(super) fn parse_table_update(
raw_updates: __ws::v2::TableUpdate,
) -> __sdk::Result<__sdk::TableUpdate<PuzzleClearRuntimeRunRow>> {
__sdk::TableUpdate::parse_table_update(raw_updates).map_err(|e| {
__sdk::InternalError::failed_parse("TableUpdate<PuzzleClearRuntimeRunRow>", "TableUpdate")
.with_cause(e)
.into()
})
}
#[allow(non_camel_case_types)]
/// Extension trait for query builder access to the table `PuzzleClearRuntimeRunRow`.
///
/// Implemented for [`__sdk::QueryTableAccessor`].
pub trait puzzle_clear_runtime_runQueryTableAccess {
#[allow(non_snake_case)]
/// Get a query builder for the table `PuzzleClearRuntimeRunRow`.
fn puzzle_clear_runtime_run(&self) -> __sdk::__query_builder::Table<PuzzleClearRuntimeRunRow>;
}
impl puzzle_clear_runtime_runQueryTableAccess for __sdk::QueryTableAccessor {
fn puzzle_clear_runtime_run(&self) -> __sdk::__query_builder::Table<PuzzleClearRuntimeRunRow> {
__sdk::__query_builder::Table::new("puzzle_clear_runtime_run")
}
}

View File

@@ -0,0 +1,30 @@
// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
#![allow(unused, clippy::all)]
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
use super::puzzle_clear_board_snapshot_type::PuzzleClearBoardSnapshot;
use super::puzzle_clear_card_asset_snapshot_type::PuzzleClearCardAssetSnapshot;
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
#[sats(crate = __lib)]
pub struct PuzzleClearRuntimeSnapshot {
pub run_id: String,
pub profile_id: String,
pub owner_user_id: String,
pub status: String,
pub level_index: u32,
pub clears_done: u32,
pub target_clears: u32,
pub level_duration_seconds: u32,
pub level_started_at_ms: u64,
pub board: PuzzleClearBoardSnapshot,
pub ready_columns: Vec<Vec<PuzzleClearCardAssetSnapshot>>,
pub started_at_ms: u64,
pub finished_at_ms: Option<u64>,
}
impl __sdk::InModule for PuzzleClearRuntimeSnapshot {
type Module = super::RemoteModule;
}

View File

@@ -0,0 +1,16 @@
// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
#![allow(unused, clippy::all)]
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
#[sats(crate = __lib)]
pub struct PuzzleClearWorkGetInput {
pub profile_id: String,
pub owner_user_id: String,
}
impl __sdk::InModule for PuzzleClearWorkGetInput {
type Module = super::RemoteModule;
}

View File

@@ -0,0 +1,19 @@
// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
#![allow(unused, clippy::all)]
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
use super::puzzle_clear_work_snapshot_type::PuzzleClearWorkSnapshot;
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
#[sats(crate = __lib)]
pub struct PuzzleClearWorkProcedureResult {
pub ok: bool,
pub work: Option<PuzzleClearWorkSnapshot>,
pub error_message: Option<String>,
}
impl __sdk::InModule for PuzzleClearWorkProcedureResult {
type Module = super::RemoteModule;
}

View File

@@ -0,0 +1,139 @@
// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
#![allow(unused, clippy::all)]
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
#[sats(crate = __lib)]
pub struct PuzzleClearWorkProfileRow {
pub profile_id: String,
pub work_id: String,
pub owner_user_id: String,
pub source_session_id: String,
pub author_display_name: String,
pub work_title: String,
pub work_description: String,
pub theme_prompt: String,
pub generate_board_background: bool,
pub board_background_asset_json: String,
pub board_background_prompt: Option<String>,
pub card_back_image_src: String,
pub atlas_asset_json: String,
pub pattern_groups_json: String,
pub card_assets_json: String,
pub cover_image_src: String,
pub generation_status: String,
pub publication_status: String,
pub play_count: u32,
pub updated_at: __sdk::Timestamp,
pub published_at: Option<__sdk::Timestamp>,
pub visible: bool,
}
impl __sdk::InModule for PuzzleClearWorkProfileRow {
type Module = super::RemoteModule;
}
/// Column accessor struct for the table `PuzzleClearWorkProfileRow`.
///
/// Provides typed access to columns for query building.
pub struct PuzzleClearWorkProfileRowCols {
pub profile_id: __sdk::__query_builder::Col<PuzzleClearWorkProfileRow, String>,
pub work_id: __sdk::__query_builder::Col<PuzzleClearWorkProfileRow, String>,
pub owner_user_id: __sdk::__query_builder::Col<PuzzleClearWorkProfileRow, String>,
pub source_session_id: __sdk::__query_builder::Col<PuzzleClearWorkProfileRow, String>,
pub author_display_name: __sdk::__query_builder::Col<PuzzleClearWorkProfileRow, String>,
pub work_title: __sdk::__query_builder::Col<PuzzleClearWorkProfileRow, String>,
pub work_description: __sdk::__query_builder::Col<PuzzleClearWorkProfileRow, String>,
pub theme_prompt: __sdk::__query_builder::Col<PuzzleClearWorkProfileRow, String>,
pub generate_board_background: __sdk::__query_builder::Col<PuzzleClearWorkProfileRow, bool>,
pub board_background_asset_json: __sdk::__query_builder::Col<PuzzleClearWorkProfileRow, String>,
pub board_background_prompt:
__sdk::__query_builder::Col<PuzzleClearWorkProfileRow, Option<String>>,
pub card_back_image_src: __sdk::__query_builder::Col<PuzzleClearWorkProfileRow, String>,
pub atlas_asset_json: __sdk::__query_builder::Col<PuzzleClearWorkProfileRow, String>,
pub pattern_groups_json: __sdk::__query_builder::Col<PuzzleClearWorkProfileRow, String>,
pub card_assets_json: __sdk::__query_builder::Col<PuzzleClearWorkProfileRow, String>,
pub cover_image_src: __sdk::__query_builder::Col<PuzzleClearWorkProfileRow, String>,
pub generation_status: __sdk::__query_builder::Col<PuzzleClearWorkProfileRow, String>,
pub publication_status: __sdk::__query_builder::Col<PuzzleClearWorkProfileRow, String>,
pub play_count: __sdk::__query_builder::Col<PuzzleClearWorkProfileRow, u32>,
pub updated_at: __sdk::__query_builder::Col<PuzzleClearWorkProfileRow, __sdk::Timestamp>,
pub published_at:
__sdk::__query_builder::Col<PuzzleClearWorkProfileRow, Option<__sdk::Timestamp>>,
pub visible: __sdk::__query_builder::Col<PuzzleClearWorkProfileRow, bool>,
}
impl __sdk::__query_builder::HasCols for PuzzleClearWorkProfileRow {
type Cols = PuzzleClearWorkProfileRowCols;
fn cols(table_name: &'static str) -> Self::Cols {
PuzzleClearWorkProfileRowCols {
profile_id: __sdk::__query_builder::Col::new(table_name, "profile_id"),
work_id: __sdk::__query_builder::Col::new(table_name, "work_id"),
owner_user_id: __sdk::__query_builder::Col::new(table_name, "owner_user_id"),
source_session_id: __sdk::__query_builder::Col::new(table_name, "source_session_id"),
author_display_name: __sdk::__query_builder::Col::new(
table_name,
"author_display_name",
),
work_title: __sdk::__query_builder::Col::new(table_name, "work_title"),
work_description: __sdk::__query_builder::Col::new(table_name, "work_description"),
theme_prompt: __sdk::__query_builder::Col::new(table_name, "theme_prompt"),
generate_board_background: __sdk::__query_builder::Col::new(
table_name,
"generate_board_background",
),
board_background_asset_json: __sdk::__query_builder::Col::new(
table_name,
"board_background_asset_json",
),
board_background_prompt: __sdk::__query_builder::Col::new(
table_name,
"board_background_prompt",
),
card_back_image_src: __sdk::__query_builder::Col::new(
table_name,
"card_back_image_src",
),
atlas_asset_json: __sdk::__query_builder::Col::new(table_name, "atlas_asset_json"),
pattern_groups_json: __sdk::__query_builder::Col::new(
table_name,
"pattern_groups_json",
),
card_assets_json: __sdk::__query_builder::Col::new(table_name, "card_assets_json"),
cover_image_src: __sdk::__query_builder::Col::new(table_name, "cover_image_src"),
generation_status: __sdk::__query_builder::Col::new(table_name, "generation_status"),
publication_status: __sdk::__query_builder::Col::new(table_name, "publication_status"),
play_count: __sdk::__query_builder::Col::new(table_name, "play_count"),
updated_at: __sdk::__query_builder::Col::new(table_name, "updated_at"),
published_at: __sdk::__query_builder::Col::new(table_name, "published_at"),
visible: __sdk::__query_builder::Col::new(table_name, "visible"),
}
}
}
/// Indexed column accessor struct for the table `PuzzleClearWorkProfileRow`.
///
/// Provides typed access to indexed columns for query building.
pub struct PuzzleClearWorkProfileRowIxCols {
pub owner_user_id: __sdk::__query_builder::IxCol<PuzzleClearWorkProfileRow, String>,
pub profile_id: __sdk::__query_builder::IxCol<PuzzleClearWorkProfileRow, String>,
pub publication_status: __sdk::__query_builder::IxCol<PuzzleClearWorkProfileRow, String>,
}
impl __sdk::__query_builder::HasIxCols for PuzzleClearWorkProfileRow {
type IxCols = PuzzleClearWorkProfileRowIxCols;
fn ix_cols(table_name: &'static str) -> Self::IxCols {
PuzzleClearWorkProfileRowIxCols {
owner_user_id: __sdk::__query_builder::IxCol::new(table_name, "owner_user_id"),
profile_id: __sdk::__query_builder::IxCol::new(table_name, "profile_id"),
publication_status: __sdk::__query_builder::IxCol::new(
table_name,
"publication_status",
),
}
}
}
impl __sdk::__query_builder::CanBeLookupTable for PuzzleClearWorkProfileRow {}

View File

@@ -0,0 +1,165 @@
// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
#![allow(unused, clippy::all)]
use super::puzzle_clear_work_profile_row_type::PuzzleClearWorkProfileRow;
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
/// Table handle for the table `puzzle_clear_work_profile`.
///
/// Obtain a handle from the [`PuzzleClearWorkProfileTableAccess::puzzle_clear_work_profile`] method on [`super::RemoteTables`],
/// like `ctx.db.puzzle_clear_work_profile()`.
///
/// Users are encouraged not to explicitly reference this type,
/// but to directly chain method calls,
/// like `ctx.db.puzzle_clear_work_profile().on_insert(...)`.
pub struct PuzzleClearWorkProfileTableHandle<'ctx> {
imp: __sdk::TableHandle<PuzzleClearWorkProfileRow>,
ctx: std::marker::PhantomData<&'ctx super::RemoteTables>,
}
#[allow(non_camel_case_types)]
/// Extension trait for access to the table `puzzle_clear_work_profile`.
///
/// Implemented for [`super::RemoteTables`].
pub trait PuzzleClearWorkProfileTableAccess {
#[allow(non_snake_case)]
/// Obtain a [`PuzzleClearWorkProfileTableHandle`], which mediates access to the table `puzzle_clear_work_profile`.
fn puzzle_clear_work_profile(&self) -> PuzzleClearWorkProfileTableHandle<'_>;
}
impl PuzzleClearWorkProfileTableAccess for super::RemoteTables {
fn puzzle_clear_work_profile(&self) -> PuzzleClearWorkProfileTableHandle<'_> {
PuzzleClearWorkProfileTableHandle {
imp: self
.imp
.get_table::<PuzzleClearWorkProfileRow>("puzzle_clear_work_profile"),
ctx: std::marker::PhantomData,
}
}
}
pub struct PuzzleClearWorkProfileInsertCallbackId(__sdk::CallbackId);
pub struct PuzzleClearWorkProfileDeleteCallbackId(__sdk::CallbackId);
impl<'ctx> __sdk::Table for PuzzleClearWorkProfileTableHandle<'ctx> {
type Row = PuzzleClearWorkProfileRow;
type EventContext = super::EventContext;
fn count(&self) -> u64 {
self.imp.count()
}
fn iter(&self) -> impl Iterator<Item = PuzzleClearWorkProfileRow> + '_ {
self.imp.iter()
}
type InsertCallbackId = PuzzleClearWorkProfileInsertCallbackId;
fn on_insert(
&self,
callback: impl FnMut(&Self::EventContext, &Self::Row) + Send + 'static,
) -> PuzzleClearWorkProfileInsertCallbackId {
PuzzleClearWorkProfileInsertCallbackId(self.imp.on_insert(Box::new(callback)))
}
fn remove_on_insert(&self, callback: PuzzleClearWorkProfileInsertCallbackId) {
self.imp.remove_on_insert(callback.0)
}
type DeleteCallbackId = PuzzleClearWorkProfileDeleteCallbackId;
fn on_delete(
&self,
callback: impl FnMut(&Self::EventContext, &Self::Row) + Send + 'static,
) -> PuzzleClearWorkProfileDeleteCallbackId {
PuzzleClearWorkProfileDeleteCallbackId(self.imp.on_delete(Box::new(callback)))
}
fn remove_on_delete(&self, callback: PuzzleClearWorkProfileDeleteCallbackId) {
self.imp.remove_on_delete(callback.0)
}
}
pub struct PuzzleClearWorkProfileUpdateCallbackId(__sdk::CallbackId);
impl<'ctx> __sdk::TableWithPrimaryKey for PuzzleClearWorkProfileTableHandle<'ctx> {
type UpdateCallbackId = PuzzleClearWorkProfileUpdateCallbackId;
fn on_update(
&self,
callback: impl FnMut(&Self::EventContext, &Self::Row, &Self::Row) + Send + 'static,
) -> PuzzleClearWorkProfileUpdateCallbackId {
PuzzleClearWorkProfileUpdateCallbackId(self.imp.on_update(Box::new(callback)))
}
fn remove_on_update(&self, callback: PuzzleClearWorkProfileUpdateCallbackId) {
self.imp.remove_on_update(callback.0)
}
}
/// Access to the `profile_id` unique index on the table `puzzle_clear_work_profile`,
/// which allows point queries on the field of the same name
/// via the [`PuzzleClearWorkProfileProfileIdUnique::find`] method.
///
/// Users are encouraged not to explicitly reference this type,
/// but to directly chain method calls,
/// like `ctx.db.puzzle_clear_work_profile().profile_id().find(...)`.
pub struct PuzzleClearWorkProfileProfileIdUnique<'ctx> {
imp: __sdk::UniqueConstraintHandle<PuzzleClearWorkProfileRow, String>,
phantom: std::marker::PhantomData<&'ctx super::RemoteTables>,
}
impl<'ctx> PuzzleClearWorkProfileTableHandle<'ctx> {
/// Get a handle on the `profile_id` unique index on the table `puzzle_clear_work_profile`.
pub fn profile_id(&self) -> PuzzleClearWorkProfileProfileIdUnique<'ctx> {
PuzzleClearWorkProfileProfileIdUnique {
imp: self.imp.get_unique_constraint::<String>("profile_id"),
phantom: std::marker::PhantomData,
}
}
}
impl<'ctx> PuzzleClearWorkProfileProfileIdUnique<'ctx> {
/// Find the subscribed row whose `profile_id` column value is equal to `col_val`,
/// if such a row is present in the client cache.
pub fn find(&self, col_val: &String) -> Option<PuzzleClearWorkProfileRow> {
self.imp.find(col_val)
}
}
#[doc(hidden)]
pub(super) fn register_table(client_cache: &mut __sdk::ClientCache<super::RemoteModule>) {
let _table =
client_cache.get_or_make_table::<PuzzleClearWorkProfileRow>("puzzle_clear_work_profile");
_table.add_unique_constraint::<String>("profile_id", |row| &row.profile_id);
}
#[doc(hidden)]
pub(super) fn parse_table_update(
raw_updates: __ws::v2::TableUpdate,
) -> __sdk::Result<__sdk::TableUpdate<PuzzleClearWorkProfileRow>> {
__sdk::TableUpdate::parse_table_update(raw_updates).map_err(|e| {
__sdk::InternalError::failed_parse("TableUpdate<PuzzleClearWorkProfileRow>", "TableUpdate")
.with_cause(e)
.into()
})
}
#[allow(non_camel_case_types)]
/// Extension trait for query builder access to the table `PuzzleClearWorkProfileRow`.
///
/// Implemented for [`__sdk::QueryTableAccessor`].
pub trait puzzle_clear_work_profileQueryTableAccess {
#[allow(non_snake_case)]
/// Get a query builder for the table `PuzzleClearWorkProfileRow`.
fn puzzle_clear_work_profile(&self)
-> __sdk::__query_builder::Table<PuzzleClearWorkProfileRow>;
}
impl puzzle_clear_work_profileQueryTableAccess for __sdk::QueryTableAccessor {
fn puzzle_clear_work_profile(
&self,
) -> __sdk::__query_builder::Table<PuzzleClearWorkProfileRow> {
__sdk::__query_builder::Table::new("puzzle_clear_work_profile")
}
}

View File

@@ -0,0 +1,17 @@
// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
#![allow(unused, clippy::all)]
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
#[sats(crate = __lib)]
pub struct PuzzleClearWorkPublishInput {
pub profile_id: String,
pub owner_user_id: String,
pub published_at_micros: i64,
}
impl __sdk::InModule for PuzzleClearWorkPublishInput {
type Module = super::RemoteModule;
}

View File

@@ -0,0 +1,40 @@
// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
#![allow(unused, clippy::all)]
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
use super::puzzle_clear_card_asset_snapshot_type::PuzzleClearCardAssetSnapshot;
use super::puzzle_clear_image_asset_snapshot_type::PuzzleClearImageAssetSnapshot;
use super::puzzle_clear_pattern_group_snapshot_type::PuzzleClearPatternGroupSnapshot;
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
#[sats(crate = __lib)]
pub struct PuzzleClearWorkSnapshot {
pub work_id: String,
pub profile_id: String,
pub owner_user_id: String,
pub source_session_id: String,
pub author_display_name: String,
pub work_title: String,
pub work_description: String,
pub theme_prompt: String,
pub generate_board_background: bool,
pub board_background_asset: Option<PuzzleClearImageAssetSnapshot>,
pub board_background_prompt: String,
pub card_back_image_src: Option<String>,
pub atlas_asset: PuzzleClearImageAssetSnapshot,
pub pattern_groups: Vec<PuzzleClearPatternGroupSnapshot>,
pub card_assets: Vec<PuzzleClearCardAssetSnapshot>,
pub cover_image_src: Option<String>,
pub publication_status: String,
pub publish_ready: bool,
pub play_count: u32,
pub generation_status: String,
pub updated_at_micros: i64,
pub published_at_micros: Option<i64>,
}
impl __sdk::InModule for PuzzleClearWorkSnapshot {
type Module = super::RemoteModule;
}

View File

@@ -0,0 +1,23 @@
// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
#![allow(unused, clippy::all)]
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
#[sats(crate = __lib)]
pub struct PuzzleClearWorkUpdateInput {
pub profile_id: String,
pub owner_user_id: String,
pub work_title: String,
pub work_description: String,
pub theme_prompt: String,
pub generate_board_background: bool,
pub board_background_asset_json: Option<String>,
pub board_background_prompt: String,
pub updated_at_micros: i64,
}
impl __sdk::InModule for PuzzleClearWorkUpdateInput {
type Module = super::RemoteModule;
}

View File

@@ -0,0 +1,16 @@
// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
#![allow(unused, clippy::all)]
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
#[sats(crate = __lib)]
pub struct PuzzleClearWorksListInput {
pub owner_user_id: String,
pub published_only: bool,
}
impl __sdk::InModule for PuzzleClearWorksListInput {
type Module = super::RemoteModule;
}

View File

@@ -0,0 +1,19 @@
// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
#![allow(unused, clippy::all)]
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
use super::puzzle_clear_work_snapshot_type::PuzzleClearWorkSnapshot;
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
#[sats(crate = __lib)]
pub struct PuzzleClearWorksProcedureResult {
pub ok: bool,
pub items: Vec<PuzzleClearWorkSnapshot>,
pub error_message: Option<String>,
}
impl __sdk::InModule for PuzzleClearWorksProcedureResult {
type Module = super::RemoteModule;
}

View File

@@ -0,0 +1,59 @@
// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
#![allow(unused, clippy::all)]
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
use super::puzzle_clear_run_procedure_result_type::PuzzleClearRunProcedureResult;
use super::puzzle_clear_run_retry_level_input_type::PuzzleClearRunRetryLevelInput;
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
#[sats(crate = __lib)]
struct RetryPuzzleClearLevelRunArgs {
pub input: PuzzleClearRunRetryLevelInput,
}
impl __sdk::InModule for RetryPuzzleClearLevelRunArgs {
type Module = super::RemoteModule;
}
#[allow(non_camel_case_types)]
/// Extension trait for access to the procedure `retry_puzzle_clear_level_run`.
///
/// Implemented for [`super::RemoteProcedures`].
pub trait retry_puzzle_clear_level_run {
fn retry_puzzle_clear_level_run(&self, input: PuzzleClearRunRetryLevelInput) {
self.retry_puzzle_clear_level_run_then(input, |_, _| {});
}
fn retry_puzzle_clear_level_run_then(
&self,
input: PuzzleClearRunRetryLevelInput,
__callback: impl FnOnce(
&super::ProcedureEventContext,
Result<PuzzleClearRunProcedureResult, __sdk::InternalError>,
) + Send
+ 'static,
);
}
impl retry_puzzle_clear_level_run for super::RemoteProcedures {
fn retry_puzzle_clear_level_run_then(
&self,
input: PuzzleClearRunRetryLevelInput,
__callback: impl FnOnce(
&super::ProcedureEventContext,
Result<PuzzleClearRunProcedureResult, __sdk::InternalError>,
) + Send
+ 'static,
) {
self.imp
.invoke_procedure_with_callback::<_, PuzzleClearRunProcedureResult>(
"retry_puzzle_clear_level_run",
RetryPuzzleClearLevelRunArgs { input },
__callback,
);
}
}

View File

@@ -0,0 +1,59 @@
// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
#![allow(unused, clippy::all)]
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
use super::puzzle_clear_run_procedure_result_type::PuzzleClearRunProcedureResult;
use super::puzzle_clear_run_start_input_type::PuzzleClearRunStartInput;
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
#[sats(crate = __lib)]
struct StartPuzzleClearRuntimeRunArgs {
pub input: PuzzleClearRunStartInput,
}
impl __sdk::InModule for StartPuzzleClearRuntimeRunArgs {
type Module = super::RemoteModule;
}
#[allow(non_camel_case_types)]
/// Extension trait for access to the procedure `start_puzzle_clear_runtime_run`.
///
/// Implemented for [`super::RemoteProcedures`].
pub trait start_puzzle_clear_runtime_run {
fn start_puzzle_clear_runtime_run(&self, input: PuzzleClearRunStartInput) {
self.start_puzzle_clear_runtime_run_then(input, |_, _| {});
}
fn start_puzzle_clear_runtime_run_then(
&self,
input: PuzzleClearRunStartInput,
__callback: impl FnOnce(
&super::ProcedureEventContext,
Result<PuzzleClearRunProcedureResult, __sdk::InternalError>,
) + Send
+ 'static,
);
}
impl start_puzzle_clear_runtime_run for super::RemoteProcedures {
fn start_puzzle_clear_runtime_run_then(
&self,
input: PuzzleClearRunStartInput,
__callback: impl FnOnce(
&super::ProcedureEventContext,
Result<PuzzleClearRunProcedureResult, __sdk::InternalError>,
) + Send
+ 'static,
) {
self.imp
.invoke_procedure_with_callback::<_, PuzzleClearRunProcedureResult>(
"start_puzzle_clear_runtime_run",
StartPuzzleClearRuntimeRunArgs { input },
__callback,
);
}
}

View File

@@ -0,0 +1,59 @@
// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
#![allow(unused, clippy::all)]
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
use super::puzzle_clear_run_procedure_result_type::PuzzleClearRunProcedureResult;
use super::puzzle_clear_run_swap_input_type::PuzzleClearRunSwapInput;
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
#[sats(crate = __lib)]
struct SwapPuzzleClearCardsArgs {
pub input: PuzzleClearRunSwapInput,
}
impl __sdk::InModule for SwapPuzzleClearCardsArgs {
type Module = super::RemoteModule;
}
#[allow(non_camel_case_types)]
/// Extension trait for access to the procedure `swap_puzzle_clear_cards`.
///
/// Implemented for [`super::RemoteProcedures`].
pub trait swap_puzzle_clear_cards {
fn swap_puzzle_clear_cards(&self, input: PuzzleClearRunSwapInput) {
self.swap_puzzle_clear_cards_then(input, |_, _| {});
}
fn swap_puzzle_clear_cards_then(
&self,
input: PuzzleClearRunSwapInput,
__callback: impl FnOnce(
&super::ProcedureEventContext,
Result<PuzzleClearRunProcedureResult, __sdk::InternalError>,
) + Send
+ 'static,
);
}
impl swap_puzzle_clear_cards for super::RemoteProcedures {
fn swap_puzzle_clear_cards_then(
&self,
input: PuzzleClearRunSwapInput,
__callback: impl FnOnce(
&super::ProcedureEventContext,
Result<PuzzleClearRunProcedureResult, __sdk::InternalError>,
) + Send
+ 'static,
) {
self.imp
.invoke_procedure_with_callback::<_, PuzzleClearRunProcedureResult>(
"swap_puzzle_clear_cards",
SwapPuzzleClearCardsArgs { input },
__callback,
);
}
}

View File

@@ -0,0 +1,59 @@
// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
#![allow(unused, clippy::all)]
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
use super::puzzle_clear_work_procedure_result_type::PuzzleClearWorkProcedureResult;
use super::puzzle_clear_work_update_input_type::PuzzleClearWorkUpdateInput;
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
#[sats(crate = __lib)]
struct UpdatePuzzleClearWorkArgs {
pub input: PuzzleClearWorkUpdateInput,
}
impl __sdk::InModule for UpdatePuzzleClearWorkArgs {
type Module = super::RemoteModule;
}
#[allow(non_camel_case_types)]
/// Extension trait for access to the procedure `update_puzzle_clear_work`.
///
/// Implemented for [`super::RemoteProcedures`].
pub trait update_puzzle_clear_work {
fn update_puzzle_clear_work(&self, input: PuzzleClearWorkUpdateInput) {
self.update_puzzle_clear_work_then(input, |_, _| {});
}
fn update_puzzle_clear_work_then(
&self,
input: PuzzleClearWorkUpdateInput,
__callback: impl FnOnce(
&super::ProcedureEventContext,
Result<PuzzleClearWorkProcedureResult, __sdk::InternalError>,
) + Send
+ 'static,
);
}
impl update_puzzle_clear_work for super::RemoteProcedures {
fn update_puzzle_clear_work_then(
&self,
input: PuzzleClearWorkUpdateInput,
__callback: impl FnOnce(
&super::ProcedureEventContext,
Result<PuzzleClearWorkProcedureResult, __sdk::InternalError>,
) + Send
+ 'static,
) {
self.imp
.invoke_procedure_with_callback::<_, PuzzleClearWorkProcedureResult>(
"update_puzzle_clear_work",
UpdatePuzzleClearWorkArgs { input },
__callback,
);
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -24,6 +24,7 @@ module-wooden-fish = { workspace = true, features = ["spacetime-types"] }
module-match3d = { workspace = true }
module-npc = { workspace = true, features = ["spacetime-types"] }
module-puzzle = { workspace = true, features = ["spacetime-types"] }
module-puzzle-clear = { workspace = true, features = ["spacetime-types"] }
module-progression = { workspace = true, features = ["spacetime-types"] }
module-quest = { workspace = true, features = ["spacetime-types"] }
module-runtime = { workspace = true, features = ["spacetime-types"] }

View File

@@ -454,13 +454,29 @@ fn export_auth_store_snapshot_from_tables_tx(
.meta_id()
.find(&AUTH_STORE_PROJECTION_META_ID.to_string())
.map(|row| row.updated_at.to_micros_since_unix_epoch());
let snapshot = build_auth_store_snapshot_from_rows(users, identities, sessions)?;
if let Some(updated_at_micros) = updated_at_micros {
upsert_auth_store_snapshot_rows(ctx, &snapshot, updated_at_micros)?;
}
let snapshot_json = serde_json::to_string_pretty(&snapshot)
.map_err(|error| format!("序列化认证快照失败:{error}"))?;
Ok(AuthStoreSnapshotRecord {
snapshot_json: Some(snapshot_json),
updated_at_micros,
})
}
fn build_auth_store_snapshot_from_rows(
users: Vec<UserAccount>,
identities: Vec<AuthIdentity>,
sessions: Vec<RefreshSession>,
) -> Result<PersistentAuthStoreSnapshot, String> {
let valid_user_ids = users
.iter()
.map(|user| user.user_id.clone())
.collect::<std::collections::HashSet<_>>();
let mut phone_identity_by_user_id = std::collections::HashMap::new();
let mut phone_to_user_id = std::collections::HashMap::new();
let mut wechat_identity_by_provider_uid = std::collections::HashMap::new();
let mut user_id_by_provider_union_id = std::collections::HashMap::new();
@@ -475,7 +491,6 @@ fn export_auth_store_snapshot_from_tables_tx(
.phone_e164
.clone()
.unwrap_or_else(|| identity.provider_uid.clone());
phone_to_user_id.insert(phone_number.clone(), identity.user_id.clone());
phone_identity_by_user_id.insert(identity.user_id, phone_number);
}
"wechat" => {
@@ -498,6 +513,7 @@ fn export_auth_store_snapshot_from_tables_tx(
}
let mut next_user_id = 1_u64;
let mut phone_to_user_id = std::collections::HashMap::new();
let mut users_by_username = std::collections::HashMap::new();
for user in users {
if let Some(numeric_id) = user
@@ -507,6 +523,13 @@ fn export_auth_store_snapshot_from_tables_tx(
{
next_user_id = next_user_id.max(numeric_id.saturating_add(1));
}
let phone_number = user
.phone_number_e164
.clone()
.or_else(|| phone_identity_by_user_id.remove(&user.user_id));
if let Some(phone_number) = phone_number.clone() {
phone_to_user_id.insert(phone_number, user.user_id.clone());
}
let auth_user = AuthUserSnapshot {
id: user.user_id.clone(),
public_user_code: user.public_user_code,
@@ -527,9 +550,7 @@ fn export_auth_store_snapshot_from_tables_tx(
user: auth_user,
password_hash: user.password_hash,
password_login_enabled: user.password_login_enabled,
phone_number: user
.phone_number_e164
.or_else(|| phone_identity_by_user_id.remove(&user.user_id)),
phone_number,
},
);
}
@@ -566,7 +587,7 @@ fn export_auth_store_snapshot_from_tables_tx(
);
}
let snapshot = PersistentAuthStoreSnapshot {
Ok(PersistentAuthStoreSnapshot {
next_user_id,
users_by_username,
phone_to_user_id,
@@ -574,16 +595,6 @@ fn export_auth_store_snapshot_from_tables_tx(
session_id_by_refresh_token_hash,
wechat_identity_by_provider_uid,
user_id_by_provider_union_id,
};
if let Some(updated_at_micros) = updated_at_micros {
upsert_auth_store_snapshot_rows(ctx, &snapshot, updated_at_micros)?;
}
let snapshot_json = serde_json::to_string_pretty(&snapshot)
.map_err(|error| format!("序列化认证快照失败:{error}"))?;
Ok(AuthStoreSnapshotRecord {
snapshot_json: Some(snapshot_json),
updated_at_micros,
})
}
@@ -721,4 +732,47 @@ mod tests {
auth_store_snapshot_row_ids(&after)
);
}
#[test]
fn auth_export_ignores_phone_identity_without_user_account() {
let live_user = UserAccount {
user_id: "user_live".to_string(),
public_user_code: "SY-00000001".to_string(),
username: "phone_live".to_string(),
display_name: "测试玩家".to_string(),
avatar_url: None,
phone_number_masked: Some("138****8000".to_string()),
phone_number_e164: Some("+8613800008000".to_string()),
login_method: "phone".to_string(),
binding_status: "active".to_string(),
wechat_bound: false,
password_hash: "hash-live".to_string(),
password_login_enabled: true,
token_version: 1,
user_tags: Some(vec![]),
};
let orphan_identity = AuthIdentity {
identity_id: "authi_phone_orphan".to_string(),
user_id: "user_deleted".to_string(),
provider: "phone".to_string(),
provider_uid: "+8613900009999".to_string(),
provider_union_id: None,
phone_e164: Some("+8613900009999".to_string()),
display_name: None,
avatar_url: None,
};
let snapshot =
build_auth_store_snapshot_from_rows(vec![live_user], vec![orphan_identity], vec![])
.expect("auth rows should export");
assert_eq!(
snapshot.phone_to_user_id,
std::collections::HashMap::from([(
"+8613800008000".to_string(),
"user_live".to_string()
)])
);
assert!(!snapshot.phone_to_user_id.contains_key("+8613900009999"));
}
}

View File

@@ -11,6 +11,7 @@ pub use module_inventory::*;
pub use module_jump_hop::*;
pub use module_npc::*;
pub use module_progression::*;
pub use module_puzzle_clear::*;
pub use module_quest::*;
pub use module_runtime::*;
pub use module_runtime_item::*;
@@ -36,6 +37,7 @@ mod match3d;
mod migration;
mod public_work;
mod puzzle;
mod puzzle_clear;
mod runtime;
mod square_hole;
mod visual_novel;
@@ -54,6 +56,7 @@ pub use jump_hop::*;
pub use match3d::*;
pub use migration::*;
pub use public_work::*;
pub use puzzle_clear::*;
pub use runtime::*;
pub use square_hole::*;
pub use visual_novel::*;

View File

@@ -23,6 +23,10 @@ use crate::puzzle::{
puzzle_agent_message, puzzle_agent_session, puzzle_event, puzzle_leaderboard_entry,
puzzle_runtime_run, puzzle_work_profile,
};
use crate::puzzle_clear::tables::{
puzzle_clear_agent_session, puzzle_clear_event, puzzle_clear_runtime_run,
puzzle_clear_work_profile,
};
use crate::square_hole::tables::{
square_hole_agent_message, square_hole_agent_session, square_hole_runtime_run,
square_hole_work_profile,
@@ -230,6 +234,10 @@ macro_rules! migration_tables {
puzzle_event,
puzzle_runtime_run,
puzzle_leaderboard_entry,
puzzle_clear_agent_session,
puzzle_clear_work_profile,
puzzle_clear_runtime_run,
puzzle_clear_event,
bark_battle_draft_config,
bark_battle_published_config,
bark_battle_runtime_run,
@@ -1321,6 +1329,7 @@ fn normalize_migration_row(table_name: &str, value: &serde_json::Value) -> serde
if matches!(
table_name,
"jump_hop_work_profile"
| "puzzle_clear_work_profile"
| "square_hole_work_profile"
| "visual_novel_work_profile"
| "bark_battle_published_config"
@@ -1330,6 +1339,12 @@ fn normalize_migration_row(table_name: &str, value: &serde_json::Value) -> serde
object
.entry("visible".to_string())
.or_insert_with(|| serde_json::Value::Bool(true));
if table_name == "puzzle_clear_work_profile" {
// 中文注释:拼消消底图提示词字段晚于作品表加入,旧迁移包按空提示词兼容。
object
.entry("board_background_prompt".to_string())
.or_insert(serde_json::Value::Null);
}
if table_name == "jump_hop_work_profile" {
// 中文注释:跳一跳主题返回按钮资产晚于首版作品表加入,旧迁移包按未生成按钮兼容。
object

Some files were not shown because too many files have changed in this diff Show More