Merge remote-tracking branch 'origin/codex/wooden-fish-template'

This commit is contained in:
kdletters
2026-05-22 08:09:58 +08:00
617 changed files with 31612 additions and 237 deletions

View File

@@ -19,6 +19,7 @@ module-combat = { workspace = true, features = ["spacetime-types"] }
module-inventory = { workspace = true, features = ["spacetime-types"] }
module-custom-world = { workspace = true, features = ["spacetime-types"] }
module-jump-hop = { workspace = true, features = ["spacetime-types"] }
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"] }

View File

@@ -1,4 +1,4 @@
// 中文注释SpacetimeDB 绑定生成依赖根模块继续公开 re-export 各领域类型;
// 中文注释SpacetimeDB 绑定生成依赖根模块继续公开 re-export 各领域类型;
// 少数领域 helper 同名只影响 value namespace 导出,不影响 table / reducer 类型。
#![allow(ambiguous_glob_reexports)]
@@ -15,6 +15,7 @@ pub use module_quest::*;
pub use module_runtime::*;
pub use module_runtime_item::*;
pub use module_story::*;
pub use module_wooden_fish::*;
pub(crate) use serde_json::{Map as JsonMap, Value as JsonValue, json};
pub(crate) use shared_kernel::format_timestamp_micros;
@@ -37,6 +38,7 @@ mod puzzle;
mod runtime;
mod square_hole;
mod visual_novel;
mod wooden_fish;
pub use ai::*;
pub use asset_metadata::*;
@@ -53,3 +55,4 @@ pub use migration::*;
pub use runtime::*;
pub use square_hole::*;
pub use visual_novel::*;
pub use wooden_fish::*;

View File

@@ -26,6 +26,9 @@ use crate::square_hole::tables::{
square_hole_agent_message, square_hole_agent_session, square_hole_runtime_run,
square_hole_work_profile,
};
use crate::wooden_fish::tables::{
wooden_fish_agent_session, wooden_fish_event, wooden_fish_runtime_run, wooden_fish_work_profile,
};
use crate::{
visual_novel_agent_message, visual_novel_agent_session, visual_novel_runtime_event,
visual_novel_runtime_history_entry, visual_novel_runtime_run, visual_novel_work_profile,
@@ -241,6 +244,10 @@ macro_rules! migration_tables {
jump_hop_work_profile,
jump_hop_runtime_run,
jump_hop_event,
wooden_fish_agent_session,
wooden_fish_work_profile,
wooden_fish_runtime_run,
wooden_fish_event,
square_hole_agent_session,
square_hole_agent_message,
square_hole_work_profile,
@@ -1258,6 +1265,14 @@ fn normalize_migration_row(table_name: &str, value: &serde_json::Value) -> serde
.or_insert(serde_json::Value::Null);
}
}
if table_name == "wooden_fish_work_profile" {
if let Some(object) = next_value.as_object_mut() {
// 中文注释:敲木鱼背景环境图晚于首版作品表加入,旧迁移包按未生成背景兼容。
object
.entry("background_asset_json".to_string())
.or_insert(serde_json::Value::Null);
}
}
next_value
}

View File

@@ -182,17 +182,8 @@ fn seed_creation_entry_config_if_missing(ctx: &ReducerContext) {
migrate_rpg_entry_from_old_hidden_default(ctx, now);
migrate_visual_novel_entry_from_old_visible_default(ctx, now);
migrate_bark_battle_entry_to_open_default(ctx, now);
migrate_coming_soon_entry_from_old_open_default(
ctx,
now,
ComingSoonEntryDefault {
id: "baby-object-match",
title: "宝贝识物",
subtitle: "亲子识物分类",
image_src: "/child-motion-demo/picture-book-grass-stage.png",
sort_order: 90,
},
);
migrate_baby_object_match_entry_from_old_coming_soon_default(ctx, now);
migrate_wooden_fish_entry_from_old_puzzle_image_default(ctx, now);
}
fn migrate_rpg_entry_from_old_hidden_default(ctx: &ReducerContext, now: Timestamp) {
@@ -284,33 +275,24 @@ fn migrate_visual_novel_entry_from_old_visible_default(ctx: &ReducerContext, now
});
}
struct ComingSoonEntryDefault {
id: &'static str,
title: &'static str,
subtitle: &'static str,
image_src: &'static str,
sort_order: i32,
}
fn migrate_coming_soon_entry_from_old_open_default(
fn migrate_baby_object_match_entry_from_old_coming_soon_default(
ctx: &ReducerContext,
now: Timestamp,
target: ComingSoonEntryDefault,
) {
let id = target.id.to_string();
let id = "baby-object-match".to_string();
let Some(row) = ctx.db.creation_entry_type_config().id().find(&id) else {
return;
};
// 中文注释:只把旧默认开放种子纠偏为敬请期待,不覆盖后台手动维护过的入口配置
let still_old_open_default = row.title == target.title
&& row.subtitle == target.subtitle
&& row.badge == "可创建"
&& row.image_src == target.image_src
// 中文注释:宝贝识物已接入完整创作发布链路,只纠偏历史默认敬请期待种子
let still_old_coming_soon_default = row.title == "宝贝识物"
&& row.subtitle == "亲子识物分类"
&& row.badge == "敬请期待"
&& row.image_src == "/child-motion-demo/picture-book-grass-stage.png"
&& row.visible
&& row.open
&& row.sort_order == target.sort_order;
if !still_old_open_default {
&& !row.open
&& row.sort_order == 90;
if !still_old_coming_soon_default {
return;
}
@@ -318,8 +300,36 @@ fn migrate_coming_soon_entry_from_old_open_default(
.creation_entry_type_config()
.id()
.update(CreationEntryTypeConfig {
badge: "敬请期待".to_string(),
open: false,
badge: "可创建".to_string(),
open: true,
updated_at: now,
..row
});
}
fn migrate_wooden_fish_entry_from_old_puzzle_image_default(ctx: &ReducerContext, now: Timestamp) {
let id = "wooden-fish".to_string();
let Some(row) = ctx.db.creation_entry_type_config().id().find(&id) else {
return;
};
// 中文注释:只替换敲木鱼旧默认入口图,不覆盖后台手动设置过的其它展示信息。
let still_old_puzzle_image_default = row.title == "敲木鱼"
&& row.subtitle == "点击祈福轻玩法"
&& row.badge == "可创建"
&& row.image_src == "/creation-type-references/puzzle.webp"
&& row.visible
&& row.open
&& row.sort_order == 47;
if !still_old_puzzle_image_default {
return;
}
ctx.db
.creation_entry_type_config()
.id()
.update(CreationEntryTypeConfig {
image_src: "/wooden-fish/default-hit-object.png".to_string(),
updated_at: now,
..row
});

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,87 @@
use crate::*;
#[spacetimedb::table(
accessor = wooden_fish_agent_session,
index(accessor = by_wooden_fish_agent_session_owner_user_id, btree(columns = [owner_user_id]))
)]
pub struct WoodenFishAgentSessionRow {
#[primary_key]
pub(crate) session_id: String,
pub(crate) owner_user_id: String,
pub(crate) current_turn: u32,
pub(crate) progress_percent: u32,
pub(crate) stage: String,
pub(crate) config_json: String,
pub(crate) draft_json: String,
pub(crate) published_profile_id: String,
pub(crate) created_at: Timestamp,
pub(crate) updated_at: Timestamp,
}
#[spacetimedb::table(
accessor = wooden_fish_work_profile,
index(accessor = by_wooden_fish_work_owner_user_id, btree(columns = [owner_user_id])),
index(accessor = by_wooden_fish_work_publication_status, btree(columns = [publication_status]))
)]
pub struct WoodenFishWorkProfileRow {
#[primary_key]
pub(crate) profile_id: String,
pub(crate) work_id: String,
pub(crate) owner_user_id: String,
pub(crate) source_session_id: String,
pub(crate) author_display_name: String,
pub(crate) work_title: String,
pub(crate) work_description: String,
pub(crate) theme_tags_json: String,
pub(crate) hit_object_prompt: String,
pub(crate) hit_object_reference_image_src: String,
pub(crate) hit_sound_prompt: String,
pub(crate) hit_object_asset_json: String,
pub(crate) hit_sound_asset_json: String,
pub(crate) floating_words_json: String,
pub(crate) cover_image_src: String,
pub(crate) generation_status: String,
pub(crate) publication_status: String,
pub(crate) play_count: u32,
pub(crate) updated_at: Timestamp,
pub(crate) published_at: Option<Timestamp>,
#[default(None::<String>)]
pub(crate) background_asset_json: Option<String>,
}
#[spacetimedb::table(
accessor = wooden_fish_runtime_run,
index(accessor = by_wooden_fish_run_owner_user_id, btree(columns = [owner_user_id])),
index(accessor = by_wooden_fish_run_profile_id, btree(columns = [profile_id]))
)]
pub struct WoodenFishRuntimeRunRow {
#[primary_key]
pub(crate) run_id: String,
pub(crate) owner_user_id: String,
pub(crate) profile_id: String,
pub(crate) status: String,
pub(crate) total_tap_count: u32,
pub(crate) word_counters_json: String,
pub(crate) started_at_ms: i64,
pub(crate) updated_at_ms: i64,
pub(crate) finished_at_ms: i64,
pub(crate) snapshot_json: String,
pub(crate) created_at: Timestamp,
pub(crate) updated_at: Timestamp,
}
#[spacetimedb::table(
accessor = wooden_fish_event,
index(accessor = by_wooden_fish_event_profile_id, btree(columns = [profile_id])),
index(accessor = by_wooden_fish_event_run_id, btree(columns = [run_id]))
)]
pub struct WoodenFishEventRow {
#[primary_key]
pub(crate) event_id: String,
pub(crate) owner_user_id: String,
pub(crate) profile_id: String,
pub(crate) run_id: String,
pub(crate) event_type: String,
pub(crate) result: String,
pub(crate) occurred_at: Timestamp,
}

View File

@@ -0,0 +1,258 @@
use crate::*;
use serde::{Deserialize, Serialize};
pub const WOODEN_FISH_TEMPLATE_ID: &str = "wooden-fish";
pub const WOODEN_FISH_TEMPLATE_NAME: &str = "敲木鱼";
pub const WOODEN_FISH_STAGE_COLLECTING: &str = "Collecting";
pub const WOODEN_FISH_STAGE_DRAFT_COMPILED: &str = "DraftCompiled";
pub const WOODEN_FISH_STAGE_PUBLISHED: &str = "Published";
pub const WOODEN_FISH_PUBLICATION_DRAFT: &str = "Draft";
pub const WOODEN_FISH_PUBLICATION_PUBLISHED: &str = "Published";
pub const WOODEN_FISH_GENERATION_DRAFT: &str = "draft";
pub const WOODEN_FISH_GENERATION_READY: &str = "ready";
pub const WOODEN_FISH_EVENT_RUN_STARTED: &str = "run-started";
pub const WOODEN_FISH_EVENT_RUN_CHECKPOINT: &str = "checkpoint";
pub const WOODEN_FISH_EVENT_RUN_FINISHED: &str = "finish";
#[derive(Clone, Debug, PartialEq, Eq, SpacetimeType)]
pub struct WoodenFishAgentSessionCreateInput {
pub session_id: String,
pub owner_user_id: String,
pub work_title: String,
pub work_description: String,
pub theme_tags_json: Option<String>,
pub config_json: Option<String>,
pub draft_json: Option<String>,
pub created_at_micros: i64,
}
#[derive(Clone, Debug, PartialEq, Eq, SpacetimeType)]
pub struct WoodenFishAgentSessionGetInput {
pub session_id: String,
pub owner_user_id: String,
}
#[derive(Clone, Debug, PartialEq, Eq, SpacetimeType)]
pub struct WoodenFishDraftCompileInput {
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_tags_json: Option<String>,
pub hit_object_prompt: String,
pub hit_object_reference_image_src: Option<String>,
pub hit_sound_prompt: Option<String>,
pub hit_object_asset_json: Option<String>,
pub background_asset_json: Option<String>,
pub hit_sound_asset_json: Option<String>,
pub floating_words_json: Option<String>,
pub cover_image_src: Option<String>,
pub generation_status: Option<String>,
pub compiled_at_micros: i64,
}
#[derive(Clone, Debug, PartialEq, Eq, SpacetimeType)]
pub struct WoodenFishWorkUpdateInput {
pub profile_id: String,
pub owner_user_id: String,
pub work_title: String,
pub work_description: String,
pub theme_tags_json: String,
pub hit_object_prompt: Option<String>,
pub hit_object_reference_image_src: Option<String>,
pub hit_sound_prompt: Option<String>,
pub hit_object_asset_json: Option<String>,
pub background_asset_json: Option<String>,
pub hit_sound_asset_json: Option<String>,
pub floating_words_json: Option<String>,
pub cover_image_src: Option<String>,
pub generation_status: Option<String>,
pub updated_at_micros: i64,
}
#[derive(Clone, Debug, PartialEq, Eq, SpacetimeType)]
pub struct WoodenFishWorkPublishInput {
pub profile_id: String,
pub owner_user_id: String,
pub published_at_micros: i64,
}
#[derive(Clone, Debug, PartialEq, Eq, SpacetimeType)]
pub struct WoodenFishWorksListInput {
pub owner_user_id: String,
pub published_only: bool,
}
#[derive(Clone, Debug, PartialEq, Eq, SpacetimeType)]
pub struct WoodenFishWorkGetInput {
pub profile_id: String,
pub owner_user_id: String,
}
#[derive(Clone, Debug, PartialEq, Eq, SpacetimeType)]
pub struct WoodenFishRunStartInput {
pub run_id: String,
pub owner_user_id: String,
pub profile_id: String,
pub client_event_id: String,
pub started_at_ms: i64,
}
#[derive(Clone, Debug, PartialEq, Eq, SpacetimeType)]
pub struct WoodenFishRunGetInput {
pub run_id: String,
pub owner_user_id: String,
}
#[derive(Clone, Debug, PartialEq, Eq, SpacetimeType)]
pub struct WoodenFishRunCheckpointInput {
pub run_id: String,
pub owner_user_id: String,
pub total_tap_count: u32,
pub word_counters_json: String,
pub client_event_id: String,
pub checkpoint_at_ms: i64,
}
#[derive(Clone, Debug, PartialEq, Eq, SpacetimeType)]
pub struct WoodenFishRunFinishInput {
pub run_id: String,
pub owner_user_id: String,
pub total_tap_count: u32,
pub word_counters_json: String,
pub client_event_id: String,
pub finished_at_ms: i64,
}
#[derive(Clone, Debug, PartialEq, SpacetimeType)]
pub struct WoodenFishAgentSessionProcedureResult {
pub ok: bool,
pub session: Option<WoodenFishAgentSessionSnapshot>,
pub error_message: Option<String>,
}
#[derive(Clone, Debug, PartialEq, SpacetimeType)]
pub struct WoodenFishWorkProcedureResult {
pub ok: bool,
pub work: Option<WoodenFishWorkSnapshot>,
pub error_message: Option<String>,
}
#[derive(Clone, Debug, PartialEq, SpacetimeType)]
pub struct WoodenFishWorksProcedureResult {
pub ok: bool,
pub items: Vec<WoodenFishWorkSnapshot>,
pub error_message: Option<String>,
}
#[derive(Clone, Debug, PartialEq, SpacetimeType)]
pub struct WoodenFishRunProcedureResult {
pub ok: bool,
pub run: Option<module_wooden_fish::WoodenFishRunSnapshot>,
pub error_message: Option<String>,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, SpacetimeType)]
#[serde(rename_all = "camelCase")]
pub struct WoodenFishCreatorConfigSnapshot {
pub work_title: String,
pub work_description: String,
pub theme_tags: Vec<String>,
pub hit_object_prompt: String,
#[serde(default)]
pub hit_object_reference_image_src: Option<String>,
#[serde(default)]
pub hit_sound_prompt: Option<String>,
pub floating_words: Vec<String>,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, SpacetimeType)]
#[serde(rename_all = "camelCase")]
pub struct WoodenFishImageAssetSnapshot {
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, SpacetimeType)]
#[serde(rename_all = "camelCase")]
pub struct WoodenFishAudioAssetSnapshot {
pub asset_id: String,
pub audio_src: String,
pub audio_object_key: String,
pub asset_object_id: String,
pub source: String,
#[serde(default)]
pub prompt: Option<String>,
#[serde(default)]
pub duration_ms: Option<u32>,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, SpacetimeType)]
#[serde(rename_all = "camelCase")]
pub struct WoodenFishDraftSnapshot {
pub template_id: String,
pub template_name: String,
pub profile_id: Option<String>,
pub work_title: String,
pub work_description: String,
pub theme_tags: Vec<String>,
pub hit_object_prompt: String,
pub hit_object_reference_image_src: Option<String>,
pub hit_sound_prompt: Option<String>,
pub floating_words: Vec<String>,
pub hit_object_asset: Option<WoodenFishImageAssetSnapshot>,
pub background_asset: Option<WoodenFishImageAssetSnapshot>,
pub hit_sound_asset: Option<WoodenFishAudioAssetSnapshot>,
pub cover_image_src: Option<String>,
pub generation_status: String,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, SpacetimeType)]
#[serde(rename_all = "camelCase")]
pub struct WoodenFishAgentSessionSnapshot {
pub session_id: String,
pub owner_user_id: String,
pub current_turn: u32,
pub progress_percent: u32,
pub stage: String,
pub config: WoodenFishCreatorConfigSnapshot,
pub draft: Option<WoodenFishDraftSnapshot>,
pub published_profile_id: Option<String>,
pub created_at_micros: i64,
pub updated_at_micros: i64,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, SpacetimeType)]
#[serde(rename_all = "camelCase")]
pub struct WoodenFishWorkSnapshot {
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_tags: Vec<String>,
pub hit_object_prompt: String,
pub hit_object_reference_image_src: Option<String>,
pub hit_sound_prompt: Option<String>,
pub hit_object_asset: Option<WoodenFishImageAssetSnapshot>,
pub background_asset: Option<WoodenFishImageAssetSnapshot>,
pub hit_sound_asset: Option<WoodenFishAudioAssetSnapshot>,
pub floating_words: Vec<String>,
pub cover_image_src: 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>,
}