From 7ab0933f6d60ae225d1daca90520098c2a04cdef Mon Sep 17 00:00:00 2001 From: kdletters Date: Thu, 30 Apr 2026 13:39:06 +0800 Subject: [PATCH] Integrate unfinished server-rs refactor worklists --- docs/technical/README.md | 23 + ...G1_CONTRACT_AND_ROUTE_MATRIX_2026-04-29.md | 26 +- ...CT_AND_ROUTE_MATRIX_PROGRESS_2026-04-29.md | 10 + ...VER_RS_DDD_PARALLEL_TASKLIST_2026-04-29.md | 1157 ++- ..._WP_AI_INTERNAL_MODULE_SPLIT_2026-04-29.md | 94 + ...S_DDD_WP_AI_TASK_BFF_CLOSURE_2026-04-29.md | 73 + ..._AS_ASSET_OBJECT_TYPE_REHOME_2026-04-29.md | 58 + ...DOMAIN_VALUE_OBJECT_REFACTOR_2026-04-29.md | 121 + ..._WP_BF_RUNTIME_BACKEND_TRUTH_2026-04-29.md | 84 + ...DDD_WP_CW_DOMAIN_ENUM_REHOME_2026-04-29.md | 41 + ...G_RUNTIME_SHELL_TEST_FIXTURE_2026-04-29.md | 32 + ...NTIME_STORY_HOOKS_PROJECTION_2026-04-29.md | 32 + ...NTIME_STORY_CLIENT_MIGRATION_2026-04-29.md | 57 + ...LATFORM_ERROR_CLASSIFICATION_2026-04-29.md | 69 + ...DDD_WP_PZ_DOMAIN_ENUM_REHOME_2026-04-29.md | 43 + ...ER_RS_DDD_WP_PZ_DOMAIN_SPLIT_2026-04-29.md | 33 + ...PG_COMBAT_DOMAIN_ENUM_REHOME_2026-04-29.md | 41 + ...DD_WP_RPG_STORY_DOMAIN_SPLIT_2026-04-29.md | 38 + ...D_WP_RS_COMPAT_RESIDUE_AUDIT_2026-04-29.md | 56 + ...DD_WP_RT_ADAPTER_API_CLOSURE_2026-04-29.md | 130 + ..._APPLICATION_RECORD_REFACTOR_2026-04-29.md | 47 + ..._DDD_WP_RT_COMMANDS_REFACTOR_2026-04-29.md | 50 + ...AIN_SNAPSHOT_RECORD_REFACTOR_2026-04-29.md | 52 + ...D_WP_RT_ERROR_LAYER_REFACTOR_2026-04-29.md | 48 + ...IME_SETTINGS_DOMAIN_REFACTOR_2026-04-29.md | 52 + ...SC_SPACETIME_CLIENT_REFACTOR_2026-04-29.md | 42 + ...DDD_WP_ST_AUTH_ADAPTER_SPLIT_2026-04-29.md | 56 + ...P_ST_CUSTOM_WORLD_ROOT_SPLIT_2026-04-29.md | 30 + ...DD_WP_ST_GAMEPLAY_ROOT_SPLIT_2026-04-29.md | 31 + docs/technical/SPACETIMEDB_TABLE_CATALOG.md | 2 +- packages/shared/src/contracts/bigFish.ts | 4 + packages/shared/src/contracts/story.ts | 104 + packages/shared/src/index.ts | 2 + server-rs/Cargo.lock | 1 + server-rs/crates/api-server/src/ai_tasks.rs | 124 + server-rs/crates/api-server/src/app.rs | 106 +- server-rs/crates/api-server/src/assets.rs | 16 +- server-rs/crates/api-server/src/big_fish.rs | 178 +- .../src/character_animation_assets.rs | 19 +- .../api-server/src/character_visual_assets.rs | 15 +- .../crates/api-server/src/custom_world_ai.rs | 15 +- server-rs/crates/api-server/src/http_error.rs | 5 + .../api-server/src/legacy_generated_assets.rs | 16 +- server-rs/crates/api-server/src/llm.rs | 37 +- server-rs/crates/api-server/src/main.rs | 1 + server-rs/crates/api-server/src/phone_auth.rs | 10 +- .../crates/api-server/src/platform_errors.rs | 133 + server-rs/crates/api-server/src/puzzle.rs | 6 +- .../api-server/src/runtime_browse_history.rs | 64 +- .../crates/api-server/src/runtime_profile.rs | 147 +- .../crates/api-server/src/runtime_save.rs | 296 +- server-rs/crates/api-server/src/state.rs | 4 +- .../crates/api-server/src/story_battles.rs | 219 +- .../crates/api-server/src/story_sessions.rs | 107 +- .../crates/api-server/src/wechat_auth.rs | 49 +- .../crates/api-server/src/wechat_provider.rs | 306 +- server-rs/crates/module-ai/README.md | 13 +- server-rs/crates/module-ai/src/application.rs | 395 +- .../module-ai/src/application/result.rs | 14 + .../module-ai/src/application/service.rs | 250 + .../crates/module-ai/src/application/store.rs | 138 + server-rs/crates/module-ai/src/commands.rs | 130 +- .../crates/module-ai/src/commands/inputs.rs | 87 + .../module-ai/src/commands/validation.rs | 40 + server-rs/crates/module-ai/src/domain.rs | 252 +- server-rs/crates/module-ai/src/domain/ids.rs | 41 + .../crates/module-ai/src/domain/stages.rs | 77 + .../crates/module-ai/src/domain/types.rs | 124 + server-rs/crates/module-ai/src/lib.rs | 223 +- server-rs/crates/module-ai/src/tests.rs | 220 + server-rs/crates/module-assets/README.md | 2 + .../crates/module-assets/src/application.rs | 45 +- .../module-assets/src/asset_object_core.rs | 243 +- .../crates/module-assets/src/commands.rs | 104 +- server-rs/crates/module-assets/src/domain.rs | 139 +- server-rs/crates/module-assets/src/errors.rs | 35 +- server-rs/crates/module-assets/src/lib.rs | 11 +- server-rs/crates/module-auth/README.md | 9 +- .../crates/module-auth/src/application.rs | 111 +- server-rs/crates/module-auth/src/commands.rs | 91 +- server-rs/crates/module-auth/src/domain.rs | 254 +- server-rs/crates/module-auth/src/errors.rs | 178 +- server-rs/crates/module-auth/src/events.rs | 26 +- server-rs/crates/module-auth/src/lib.rs | 601 +- .../crates/module-big-fish/src/application.rs | 592 +- .../crates/module-big-fish/src/commands.rs | 23 +- .../crates/module-big-fish/src/domain.rs | 63 + .../crates/module-big-fish/src/errors.rs | 4 + .../crates/module-big-fish/src/events.rs | 13 + server-rs/crates/module-big-fish/src/lib.rs | 86 +- server-rs/crates/module-combat/Cargo.toml | 2 +- server-rs/crates/module-combat/README.md | 13 +- server-rs/crates/module-combat/src/domain.rs | 81 + server-rs/crates/module-combat/src/lib.rs | 71 +- .../crates/module-custom-world/README.md | 13 +- .../crates/module-custom-world/src/domain.rs | 302 + .../crates/module-custom-world/src/lib.rs | 300 +- server-rs/crates/module-puzzle/README.md | 51 + .../crates/module-puzzle/src/application.rs | 1870 ++++- .../crates/module-puzzle/src/commands.rs | 182 +- server-rs/crates/module-puzzle/src/domain.rs | 350 + server-rs/crates/module-puzzle/src/errors.rs | 40 +- server-rs/crates/module-puzzle/src/events.rs | 24 + server-rs/crates/module-puzzle/src/lib.rs | 2421 +----- .../crates/module-runtime-story/README.md | 4 +- .../crates/module-runtime-story/src/battle.rs | 4 +- .../module-runtime-story/src/commands.rs | 4 +- .../crates/module-runtime-story/src/core.rs | 2 +- .../crates/module-runtime-story/src/domain.rs | 4 +- .../crates/module-runtime-story/src/errors.rs | 4 +- .../crates/module-runtime-story/src/events.rs | 4 +- .../crates/module-runtime-story/src/forge.rs | 2 +- .../module-runtime-story/src/forge_actions.rs | 2 +- .../module-runtime-story/src/game_state.rs | 4 +- .../module-runtime-story/src/npc_support.rs | 2 +- server-rs/crates/module-runtime/README.md | 18 +- .../crates/module-runtime/src/application.rs | 795 ++ .../crates/module-runtime/src/commands.rs | 469 ++ server-rs/crates/module-runtime/src/domain.rs | 1015 ++- server-rs/crates/module-runtime/src/errors.rs | 104 + server-rs/crates/module-runtime/src/lib.rs | 1958 +---- server-rs/crates/module-story/README.md | 37 +- .../crates/module-story/src/application.rs | 168 +- server-rs/crates/module-story/src/commands.rs | 149 +- server-rs/crates/module-story/src/domain.rs | 57 +- server-rs/crates/module-story/src/errors.rs | 37 +- server-rs/crates/module-story/src/events.rs | 60 +- server-rs/crates/module-story/src/lib.rs | 423 +- server-rs/crates/platform-auth/Cargo.toml | 1 + server-rs/crates/platform-auth/src/lib.rs | 504 ++ server-rs/crates/platform-llm/src/lib.rs | 47 + server-rs/crates/platform-oss/src/lib.rs | 40 + .../crates/shared-contracts/src/big_fish.rs | 48 + .../crates/spacetime-client/src/big_fish.rs | 76 + server-rs/crates/spacetime-client/src/lib.rs | 8 +- .../crates/spacetime-client/src/mapper.rs | 241 +- .../module_bindings/accept_quest_reducer.rs | 22 +- .../acknowledge_quest_completion_reducer.rs | 22 +- ...n_disable_profile_redeem_code_procedure.rs | 39 +- ...in_upsert_profile_redeem_code_procedure.rs | 39 +- .../advance_puzzle_next_level_procedure.rs | 39 +- .../ai_result_reference_input_type.rs | 11 +- .../ai_result_reference_kind_type.rs | 11 +- .../ai_result_reference_snapshot_type.rs | 11 +- .../ai_result_reference_type.rs | 26 +- .../ai_stage_completion_input_type.rs | 15 +- .../ai_task_cancel_input_type.rs | 10 +- .../ai_task_create_input_type.rs | 15 +- .../ai_task_event_kind_type.rs | 35 + .../module_bindings/ai_task_event_table.rs | 105 + .../src/module_bindings/ai_task_event_type.rs | 89 + .../ai_task_failure_input_type.rs | 10 +- .../ai_task_finish_input_type.rs | 10 +- .../src/module_bindings/ai_task_kind_type.rs | 11 +- .../ai_task_procedure_result_type.rs | 15 +- .../module_bindings/ai_task_snapshot_type.rs | 31 +- .../ai_task_stage_blueprint_type.rs | 9 +- .../ai_task_stage_kind_type.rs | 11 +- .../ai_task_stage_snapshot_type.rs | 19 +- .../ai_task_stage_start_input_type.rs | 9 +- .../ai_task_stage_status_type.rs | 11 +- .../src/module_bindings/ai_task_stage_type.rs | 37 +- .../ai_task_start_input_type.rs | 10 +- .../module_bindings/ai_task_status_type.rs | 11 +- .../src/module_bindings/ai_task_type.rs | 50 +- .../ai_text_chunk_append_input_type.rs | 9 +- .../ai_text_chunk_snapshot_type.rs | 9 +- .../src/module_bindings/ai_text_chunk_type.rs | 12 +- ...pend_ai_text_chunk_and_return_procedure.rs | 41 +- ...ssion_ledger_entry_and_return_procedure.rs | 42 +- ...hapter_progression_ledger_entry_reducer.rs | 27 +- .../apply_inventory_mutation_reducer.rs | 22 +- .../apply_quest_signal_reducer.rs | 22 +- .../asset_entity_binding_input_type.rs | 14 +- ...et_entity_binding_procedure_result_type.rs | 13 +- .../asset_entity_binding_snapshot_type.rs | 14 +- .../asset_entity_binding_type.rs | 21 +- .../asset_history_entry_snapshot_type.rs | 16 +- .../asset_history_list_input_type.rs | 10 +- .../asset_history_list_result_type.rs | 13 +- .../asset_object_access_policy_type.rs | 11 +- .../asset_object_procedure_result_type.rs | 13 +- .../src/module_bindings/asset_object_type.rs | 36 +- .../asset_object_upsert_input_type.rs | 21 +- .../asset_object_upsert_snapshot_type.rs | 21 +- ...i_result_reference_and_return_procedure.rs | 41 +- .../src/module_bindings/auth_identity_type.rs | 29 +- ...e_snapshot_import_procedure_result_type.rs | 13 +- .../auth_store_snapshot_import_record_type.rs | 10 +- ...th_store_snapshot_procedure_result_type.rs | 13 +- .../auth_store_snapshot_record_type.rs | 14 +- .../auth_store_snapshot_type.rs | 13 +- .../auth_store_snapshot_upsert_input_type.rs | 10 +- ...e_database_migration_operator_procedure.rs | 42 +- .../src/module_bindings/battle_mode_type.rs | 11 +- .../battle_state_input_type.rs | 13 +- .../battle_state_procedure_result_type.rs | 13 +- .../battle_state_query_input_type.rs | 10 +- .../battle_state_snapshot_type.rs | 21 +- .../src/module_bindings/battle_state_type.rs | 44 +- .../src/module_bindings/battle_status_type.rs | 11 +- ...egin_story_session_and_return_procedure.rs | 39 +- .../begin_story_session_reducer.rs | 22 +- .../big_fish_agent_message_kind_type.rs | 11 +- .../big_fish_agent_message_role_type.rs | 11 +- .../big_fish_agent_message_snapshot_type.rs | 11 +- .../big_fish_agent_message_type.rs | 14 +- .../big_fish_anchor_item_type.rs | 9 +- .../big_fish_anchor_pack_type.rs | 9 +- .../big_fish_anchor_status_type.rs | 11 +- .../big_fish_asset_coverage_type.rs | 12 +- .../big_fish_asset_generate_input_type.rs | 15 +- .../big_fish_asset_kind_type.rs | 11 +- .../big_fish_asset_slot_snapshot_type.rs | 15 +- .../big_fish_asset_slot_type.rs | 24 +- .../big_fish_asset_status_type.rs | 11 +- .../big_fish_background_blueprint_type.rs | 10 +- .../big_fish_creation_session_type.rs | 30 +- .../big_fish_creation_stage_type.rs | 11 +- .../big_fish_draft_compile_input_type.rs | 12 +- .../big_fish_event_kind_type.rs | 25 + .../module_bindings/big_fish_event_table.rs | 103 + .../module_bindings/big_fish_event_type.rs | 81 + .../big_fish_game_draft_type.rs | 13 +- .../big_fish_input_submit_input_type.rs | 27 + .../big_fish_level_blueprint_type.rs | 16 +- .../big_fish_message_finalize_input_type.rs | 15 +- .../big_fish_message_submit_input_type.rs | 10 +- .../big_fish_play_record_input_type.rs | 10 +- .../big_fish_publish_input_type.rs | 10 +- .../big_fish_run_get_input_type.rs | 24 + .../big_fish_run_procedure_result_type.rs | 25 + .../big_fish_run_start_input_type.rs | 26 + .../big_fish_run_status_type.rs | 29 + .../big_fish_runtime_params_type.rs | 14 +- .../big_fish_runtime_run_type.rs | 92 + .../big_fish_session_create_input_type.rs | 10 +- .../big_fish_session_get_input_type.rs | 10 +- .../big_fish_session_procedure_result_type.rs | 13 +- .../big_fish_session_snapshot_type.rs | 25 +- .../big_fish_work_delete_input_type.rs | 10 +- .../big_fish_works_list_input_type.rs | 10 +- .../big_fish_works_procedure_result_type.rs | 14 +- ...t_object_to_entity_and_return_procedure.rs | 39 +- .../bind_asset_object_to_entity_reducer.rs | 22 +- .../cancel_ai_task_and_return_procedure.rs | 41 +- .../module_bindings/chapter_pace_band_type.rs | 11 +- .../chapter_progression_get_input_type.rs | 10 +- .../chapter_progression_input_type.rs | 9 +- .../chapter_progression_ledger_input_type.rs | 12 +- ...apter_progression_procedure_result_type.rs | 13 +- .../chapter_progression_snapshot_type.rs | 11 +- .../chapter_progression_type.rs | 46 +- ...orm_browse_history_and_return_procedure.rs | 39 +- .../module_bindings/combat_outcome_type.rs | 11 +- .../compile_big_fish_draft_procedure.rs | 39 +- ...ustom_world_published_profile_procedure.rs | 42 +- .../compile_puzzle_agent_draft_procedure.rs | 41 +- .../complete_ai_stage_and_return_procedure.rs | 41 +- .../complete_ai_task_and_return_procedure.rs | 41 +- ...nfirm_asset_object_and_return_procedure.rs | 41 +- .../confirm_asset_object_reducer.rs | 22 +- .../consume_inventory_item_input_type.rs | 10 +- ...file_wallet_points_and_return_procedure.rs | 39 +- .../continue_story_and_return_procedure.rs | 41 +- .../module_bindings/continue_story_reducer.rs | 22 +- .../create_ai_task_and_return_procedure.rs | 41 +- .../module_bindings/create_ai_task_reducer.rs | 22 +- ...reate_battle_state_and_return_procedure.rs | 39 +- .../create_battle_state_reducer.rs | 22 +- .../create_big_fish_session_procedure.rs | 41 +- ...te_custom_world_agent_session_procedure.rs | 39 +- ...ile_recharge_order_and_return_procedure.rs | 44 +- .../create_puzzle_agent_session_procedure.rs | 41 +- ...m_world_agent_action_execute_input_type.rs | 12 +- ..._world_agent_action_execute_result_type.rs | 13 +- ..._world_agent_card_detail_get_input_type.rs | 10 +- ...world_agent_message_finalize_input_type.rs | 25 +- ...ustom_world_agent_message_snapshot_type.rs | 13 +- ...m_world_agent_message_submit_input_type.rs | 10 +- .../custom_world_agent_message_type.rs | 23 +- ...om_world_agent_operation_get_input_type.rs | 10 +- ...d_agent_operation_procedure_result_type.rs | 13 +- ...rld_agent_operation_progress_input_type.rs | 13 +- ...tom_world_agent_operation_snapshot_type.rs | 13 +- .../custom_world_agent_operation_type.rs | 21 +- ...m_world_agent_session_create_input_type.rs | 18 +- ...stom_world_agent_session_get_input_type.rs | 10 +- ...rld_agent_session_procedure_result_type.rs | 13 +- ...ustom_world_agent_session_snapshot_type.rs | 37 +- .../custom_world_agent_session_type.rs | 94 +- ...tom_world_draft_card_detail_result_type.rs | 13 +- ...draft_card_detail_section_snapshot_type.rs | 10 +- ...m_world_draft_card_detail_snapshot_type.rs | 19 +- .../custom_world_draft_card_snapshot_type.rs | 17 +- .../custom_world_draft_card_type.rs | 32 +- ...world_gallery_detail_by_code_input_type.rs | 10 +- .../custom_world_gallery_detail_input_type.rs | 10 +- ...ustom_world_gallery_entry_snapshot_type.rs | 11 +- .../custom_world_gallery_entry_table.rs | 113 +- .../custom_world_gallery_entry_type.rs | 26 +- .../custom_world_gallery_list_result_type.rs | 13 +- .../custom_world_generation_mode_type.rs | 11 +- .../custom_world_library_detail_input_type.rs | 10 +- ...stom_world_library_mutation_result_type.rs | 17 +- .../custom_world_profile_delete_input_type.rs | 10 +- .../custom_world_profile_list_input_type.rs | 10 +- .../custom_world_profile_list_result_type.rs | 13 +- ...custom_world_profile_publish_input_type.rs | 12 +- .../custom_world_profile_snapshot_type.rs | 23 +- .../custom_world_profile_type.rs | 69 +- ...stom_world_profile_unpublish_input_type.rs | 10 +- .../custom_world_profile_upsert_input_type.rs | 17 +- .../custom_world_publication_status_type.rs | 11 +- .../custom_world_publish_world_input_type.rs | 14 +- .../custom_world_publish_world_result_type.rs | 23 +- ...ld_published_profile_compile_input_type.rs | 12 +- ...d_published_profile_compile_result_type.rs | 13 +- ...published_profile_compile_snapshot_type.rs | 11 +- .../custom_world_role_asset_status_type.rs | 11 +- .../custom_world_session_status_type.rs | 11 +- .../custom_world_session_type.rs | 39 +- .../custom_world_theme_mode_type.rs | 11 +- ...custom_world_work_summary_snapshot_type.rs | 29 +- .../custom_world_works_list_input_type.rs | 10 +- .../custom_world_works_list_result_type.rs | 13 +- ...migration_authorize_operator_input_type.rs | 10 +- .../database_migration_export_input_type.rs | 12 +- .../database_migration_import_input_type.rs | 12 +- ...igration_operator_procedure_result_type.rs | 14 +- .../database_migration_operator_type.rs | 16 +- ...atabase_migration_procedure_result_type.rs | 15 +- ...se_migration_revoke_operator_input_type.rs | 10 +- .../database_migration_table_stat_type.rs | 10 +- .../delete_big_fish_work_procedure.rs | 39 +- ...te_custom_world_agent_session_procedure.rs | 39 +- ...stom_world_profile_and_return_procedure.rs | 39 +- .../delete_puzzle_work_procedure.rs | 39 +- ...e_runtime_snapshot_and_return_procedure.rs | 39 +- .../drag_puzzle_piece_or_group_procedure.rs | 41 +- .../equip_inventory_item_input_type.rs | 10 +- ...ute_custom_world_agent_action_procedure.rs | 39 +- ...th_store_snapshot_from_tables_procedure.rs | 43 +- ...rt_database_migration_to_file_procedure.rs | 39 +- .../fail_ai_task_and_return_procedure.rs | 41 +- ...e_big_fish_agent_message_turn_procedure.rs | 41 +- ...stom_world_agent_message_turn_procedure.rs | 42 +- ...ize_puzzle_agent_message_turn_procedure.rs | 41 +- .../generate_big_fish_asset_procedure.rs | 41 +- .../get_auth_store_snapshot_procedure.rs | 43 +- .../get_battle_state_procedure.rs | 39 +- .../get_big_fish_run_procedure.rs | 58 + .../get_big_fish_session_procedure.rs | 41 +- .../get_chapter_progression_procedure.rs | 41 +- ...ustom_world_agent_card_detail_procedure.rs | 39 +- ..._custom_world_agent_operation_procedure.rs | 41 +- ...et_custom_world_agent_session_procedure.rs | 41 +- ..._world_gallery_detail_by_code_procedure.rs | 41 +- ...t_custom_world_gallery_detail_procedure.rs | 39 +- ...t_custom_world_library_detail_procedure.rs | 41 +- ...player_progression_or_default_procedure.rs | 39 +- .../get_profile_dashboard_procedure.rs | 39 +- .../get_profile_play_stats_procedure.rs | 39 +- .../get_profile_recharge_center_procedure.rs | 41 +- ...rofile_referral_invite_center_procedure.rs | 39 +- .../get_puzzle_agent_session_procedure.rs | 41 +- .../get_puzzle_gallery_detail_procedure.rs | 39 +- .../get_puzzle_run_procedure.rs | 41 +- .../get_puzzle_work_detail_procedure.rs | 39 +- .../get_runtime_inventory_state_procedure.rs | 41 +- ...et_runtime_setting_or_default_procedure.rs | 39 +- .../get_runtime_snapshot_procedure.rs | 41 +- .../get_story_session_state_procedure.rs | 39 +- .../grant_inventory_item_input_type.rs | 9 +- ...ression_experience_and_return_procedure.rs | 41 +- ...t_player_progression_experience_reducer.rs | 25 +- .../import_auth_store_snapshot_procedure.rs | 43 +- ..._database_migration_from_file_procedure.rs | 41 +- ...gration_incremental_from_file_procedure.rs | 41 +- .../inventory_container_kind_type.rs | 11 +- .../inventory_equipment_slot_type.rs | 11 +- .../inventory_item_rarity_type.rs | 11 +- .../inventory_item_snapshot_type.rs | 19 +- .../inventory_item_source_kind_type.rs | 11 +- .../inventory_mutation_input_type.rs | 11 +- .../inventory_mutation_type.rs | 13 +- .../inventory_slot_snapshot_type.rs | 23 +- .../module_bindings/inventory_slot_type.rs | 47 +- ...list_asset_history_and_return_procedure.rs | 39 +- .../list_big_fish_works_procedure.rs | 41 +- ..._custom_world_gallery_entries_procedure.rs | 43 +- .../list_custom_world_profiles_procedure.rs | 41 +- .../list_custom_world_works_procedure.rs | 41 +- .../list_platform_browse_history_procedure.rs | 41 +- .../list_profile_save_archives_procedure.rs | 39 +- .../list_profile_wallet_ledger_procedure.rs | 39 +- .../list_puzzle_gallery_procedure.rs | 43 +- .../list_puzzle_works_procedure.rs | 41 +- .../src/module_bindings/mod.rs | 898 +-- ...attle_interaction_procedure_result_type.rs | 13 +- .../npc_battle_interaction_result_type.rs | 9 +- .../npc_interaction_battle_mode_type.rs | 11 +- .../npc_interaction_procedure_result_type.rs | 13 +- .../npc_interaction_result_type.rs | 17 +- .../npc_interaction_status_type.rs | 11 +- .../npc_relation_stance_type.rs | 11 +- .../npc_relation_state_type.rs | 9 +- .../npc_social_action_kind_type.rs | 11 +- .../npc_stance_profile_type.rs | 16 +- .../npc_state_procedure_result_type.rs | 13 +- .../npc_state_snapshot_type.rs | 17 +- .../src/module_bindings/npc_state_type.rs | 53 +- .../npc_state_upsert_input_type.rs | 19 +- .../player_progression_get_input_type.rs | 10 +- .../player_progression_grant_input_type.rs | 9 +- .../player_progression_grant_source_type.rs | 11 +- ...layer_progression_procedure_result_type.rs | 13 +- .../player_progression_snapshot_type.rs | 11 +- .../player_progression_type.rs | 22 +- .../profile_dashboard_state_type.rs | 13 +- .../profile_invite_code_type.rs | 13 +- .../profile_membership_type.rs | 12 +- .../profile_played_world_type.rs | 30 +- .../profile_recharge_order_type.rs | 27 +- .../profile_redeem_code_type.rs | 16 +- .../profile_redeem_code_usage_type.rs | 13 +- .../profile_referral_relation_type.rs | 23 +- .../profile_save_archive_type.rs | 33 +- .../profile_wallet_ledger_type.rs | 15 +- .../publish_big_fish_game_procedure.rs | 41 +- ...stom_world_profile_and_return_procedure.rs | 39 +- .../publish_custom_world_profile_reducer.rs | 25 +- .../publish_custom_world_world_procedure.rs | 39 +- .../publish_puzzle_work_procedure.rs | 41 +- ...uzzle_agent_message_finalize_input_type.rs | 15 +- .../puzzle_agent_message_kind_type.rs | 11 +- .../puzzle_agent_message_role_type.rs | 11 +- .../puzzle_agent_message_row_type.rs | 14 +- .../puzzle_agent_message_submit_input_type.rs | 10 +- .../puzzle_agent_session_create_input_type.rs | 10 +- .../puzzle_agent_session_get_input_type.rs | 10 +- ...zle_agent_session_procedure_result_type.rs | 14 +- .../puzzle_agent_session_row_type.rs | 34 +- .../puzzle_agent_stage_type.rs | 11 +- .../puzzle_draft_compile_input_type.rs | 10 +- .../module_bindings/puzzle_event_kind_type.rs | 25 + .../src/module_bindings/puzzle_event_table.rs | 103 + .../src/module_bindings/puzzle_event_type.rs | 81 + ...puzzle_generated_images_save_input_type.rs | 10 +- .../puzzle_leaderboard_entry_row_type.rs | 13 +- .../puzzle_leaderboard_submit_input_type.rs | 10 +- .../puzzle_publication_status_type.rs | 11 +- .../puzzle_publish_input_type.rs | 16 +- .../puzzle_run_drag_input_type.rs | 10 +- .../puzzle_run_get_input_type.rs | 10 +- .../puzzle_run_next_level_input_type.rs | 10 +- .../puzzle_run_procedure_result_type.rs | 14 +- .../puzzle_run_start_input_type.rs | 10 +- .../puzzle_run_swap_input_type.rs | 10 +- .../puzzle_runtime_run_row_type.rs | 33 +- .../puzzle_select_cover_image_input_type.rs | 10 +- .../puzzle_work_delete_input_type.rs | 10 +- .../puzzle_work_get_input_type.rs | 10 +- .../puzzle_work_procedure_result_type.rs | 14 +- .../puzzle_work_profile_row_type.rs | 44 +- .../puzzle_work_upsert_input_type.rs | 16 +- .../puzzle_works_list_input_type.rs | 10 +- .../puzzle_works_procedure_result_type.rs | 14 +- .../quest_completion_ack_input_type.rs | 10 +- .../quest_hostile_npc_defeated_signal_type.rs | 12 +- .../quest_item_delivered_signal_type.rs | 10 +- .../quest_log_event_kind_type.rs | 11 +- .../src/module_bindings/quest_log_type.rs | 39 +- .../quest_narrative_binding_snapshot_type.rs | 11 +- .../quest_narrative_origin_type.rs | 11 +- .../quest_narrative_type_type.rs | 11 +- .../quest_npc_spar_completed_signal_type.rs | 10 +- .../quest_npc_talk_completed_signal_type.rs | 10 +- .../quest_objective_kind_type.rs | 11 +- .../quest_objective_snapshot_type.rs | 17 +- .../quest_progress_signal_type.rs | 15 +- .../quest_record_input_type.rs | 37 +- .../src/module_bindings/quest_record_type.rs | 94 +- .../quest_reward_equipment_slot_type.rs | 11 +- .../quest_reward_intel_type.rs | 12 +- .../quest_reward_item_rarity_type.rs | 11 +- .../module_bindings/quest_reward_item_type.rs | 17 +- .../quest_reward_snapshot_type.rs | 19 +- .../quest_scene_reached_signal_type.rs | 10 +- .../quest_signal_apply_input_type.rs | 9 +- .../module_bindings/quest_signal_kind_type.rs | 11 +- .../src/module_bindings/quest_status_type.rs | 11 +- .../quest_step_snapshot_type.rs | 17 +- .../quest_treasure_inspected_signal_type.rs | 12 +- .../quest_turn_in_input_type.rs | 10 +- .../record_big_fish_play_procedure.rs | 41 +- ..._profile_referral_invite_code_procedure.rs | 39 +- .../redeem_profile_reward_code_procedure.rs | 39 +- .../module_bindings/refresh_session_type.rs | 22 +- ...file_wallet_points_and_return_procedure.rs | 39 +- ...olve_combat_action_and_return_procedure.rs | 39 +- .../resolve_combat_action_input_type.rs | 10 +- ...lve_combat_action_procedure_result_type.rs | 13 +- .../resolve_combat_action_reducer.rs | 22 +- .../resolve_combat_action_result_type.rs | 11 +- ...battle_interaction_and_return_procedure.rs | 41 +- ...solve_npc_battle_interaction_input_type.rs | 15 +- ...ve_npc_interaction_and_return_procedure.rs | 41 +- .../resolve_npc_interaction_input_type.rs | 12 +- .../resolve_npc_interaction_reducer.rs | 22 +- ..._npc_social_action_and_return_procedure.rs | 41 +- .../resolve_npc_social_action_input_type.rs | 13 +- .../resolve_npc_social_action_reducer.rs | 22 +- ...easure_interaction_and_return_procedure.rs | 41 +- .../resolve_treasure_interaction_reducer.rs | 22 +- ...ofile_save_archive_and_return_procedure.rs | 39 +- ...e_database_migration_operator_procedure.rs | 39 +- .../rpg_agent_draft_card_kind_type.rs | 11 +- .../rpg_agent_draft_card_status_type.rs | 11 +- .../rpg_agent_message_kind_type.rs | 11 +- .../rpg_agent_message_role_type.rs | 11 +- .../rpg_agent_operation_status_type.rs | 11 +- .../rpg_agent_operation_type_type.rs | 11 +- .../module_bindings/rpg_agent_stage_type.rs | 11 +- ...runtime_browse_history_clear_input_type.rs | 10 +- .../runtime_browse_history_list_input_type.rs | 10 +- ...me_browse_history_procedure_result_type.rs | 13 +- .../runtime_browse_history_snapshot_type.rs | 11 +- .../runtime_browse_history_sync_input_type.rs | 11 +- .../runtime_browse_history_theme_mode_type.rs | 11 +- ...runtime_browse_history_write_input_type.rs | 22 +- ...e_inventory_state_procedure_result_type.rs | 13 +- ...untime_inventory_state_query_input_type.rs | 10 +- .../runtime_inventory_state_snapshot_type.rs | 13 +- .../runtime_item_equipment_slot_type.rs | 11 +- .../runtime_item_reward_item_rarity_type.rs | 11 +- .../runtime_item_reward_item_snapshot_type.rs | 17 +- .../runtime_platform_theme_type.rs | 11 +- ...untime_profile_dashboard_get_input_type.rs | 10 +- ...profile_dashboard_procedure_result_type.rs | 13 +- ...runtime_profile_dashboard_snapshot_type.rs | 12 +- ...rofile_membership_benefit_snapshot_type.rs | 10 +- ...untime_profile_membership_snapshot_type.rs | 15 +- .../runtime_profile_membership_status_type.rs | 11 +- .../runtime_profile_membership_tier_type.rs | 11 +- ...ntime_profile_play_stats_get_input_type.rs | 10 +- ...rofile_play_stats_procedure_result_type.rs | 13 +- ...untime_profile_play_stats_snapshot_type.rs | 13 +- ...time_profile_played_world_snapshot_type.rs | 16 +- ..._profile_recharge_center_get_input_type.rs | 10 +- ...e_recharge_center_procedure_result_type.rs | 15 +- ...e_profile_recharge_center_snapshot_type.rs | 21 +- ...rofile_recharge_order_create_input_type.rs | 10 +- ...me_profile_recharge_order_snapshot_type.rs | 13 +- ...time_profile_recharge_order_status_type.rs | 11 +- ...time_profile_recharge_product_kind_type.rs | 11 +- ..._profile_recharge_product_snapshot_type.rs | 9 +- ...le_redeem_code_admin_disable_input_type.rs | 10 +- ...redeem_code_admin_procedure_result_type.rs | 13 +- ...ile_redeem_code_admin_upsert_input_type.rs | 13 +- .../runtime_profile_redeem_code_mode_type.rs | 11 +- ...ntime_profile_redeem_code_snapshot_type.rs | 11 +- ...e_profile_reward_code_redeem_input_type.rs | 10 +- ...eward_code_redeem_procedure_result_type.rs | 13 +- ...rofile_reward_code_redeem_snapshot_type.rs | 9 +- ...me_profile_save_archive_list_input_type.rs | 10 +- ...file_save_archive_procedure_result_type.rs | 19 +- ..._profile_save_archive_resume_input_type.rs | 10 +- ...time_profile_save_archive_snapshot_type.rs | 20 +- ...me_profile_wallet_adjustment_input_type.rs | 10 +- ...wallet_adjustment_procedure_result_type.rs | 13 +- ...ofile_wallet_ledger_entry_snapshot_type.rs | 9 +- ...e_profile_wallet_ledger_list_input_type.rs | 10 +- ...ile_wallet_ledger_procedure_result_type.rs | 13 +- ..._profile_wallet_ledger_source_type_type.rs | 11 +- ...e_referral_invite_center_get_input_type.rs | 10 +- ...ral_invite_center_procedure_result_type.rs | 13 +- ...me_referral_invite_center_snapshot_type.rs | 14 +- .../runtime_referral_redeem_input_type.rs | 10 +- ...e_referral_redeem_procedure_result_type.rs | 13 +- .../runtime_referral_redeem_snapshot_type.rs | 9 +- .../runtime_setting_get_input_type.rs | 10 +- .../runtime_setting_procedure_result_type.rs | 13 +- .../runtime_setting_snapshot_type.rs | 9 +- .../module_bindings/runtime_setting_type.rs | 12 +- .../runtime_setting_upsert_input_type.rs | 9 +- .../runtime_snapshot_delete_input_type.rs | 10 +- .../runtime_snapshot_get_input_type.rs | 10 +- .../runtime_snapshot_procedure_result_type.rs | 13 +- .../runtime_snapshot_row_type.rs | 17 +- .../module_bindings/runtime_snapshot_type.rs | 12 +- .../runtime_snapshot_upsert_input_type.rs | 12 +- .../save_puzzle_generated_images_procedure.rs | 39 +- .../select_puzzle_cover_image_procedure.rs | 39 +- .../module_bindings/start_ai_task_reducer.rs | 22 +- .../start_ai_task_stage_reducer.rs | 22 +- .../start_big_fish_run_procedure.rs | 58 + .../start_puzzle_run_procedure.rs | 39 +- .../story_continue_input_type.rs | 12 +- .../module_bindings/story_event_kind_type.rs | 11 +- .../story_event_snapshot_type.rs | 11 +- .../src/module_bindings/story_event_type.rs | 16 +- .../story_session_input_type.rs | 12 +- .../story_session_procedure_result_type.rs | 17 +- .../story_session_snapshot_type.rs | 13 +- .../story_session_state_input_type.rs | 10 +- ...ory_session_state_procedure_result_type.rs | 17 +- .../story_session_status_type.rs | 11 +- .../src/module_bindings/story_session_type.rs | 35 +- .../submit_big_fish_input_procedure.rs | 58 + .../submit_big_fish_message_procedure.rs | 41 +- ...it_custom_world_agent_message_procedure.rs | 41 +- .../submit_puzzle_agent_message_procedure.rs | 41 +- ...bmit_puzzle_leaderboard_entry_procedure.rs | 41 +- .../swap_puzzle_pieces_procedure.rs | 39 +- .../treasure_interaction_action_type.rs | 11 +- .../treasure_record_procedure_result_type.rs | 13 +- .../treasure_record_snapshot_type.rs | 17 +- .../module_bindings/treasure_record_type.rs | 39 +- .../treasure_resolve_input_type.rs | 17 +- .../module_bindings/turn_in_quest_reducer.rs | 22 +- .../unequip_inventory_item_input_type.rs | 10 +- ...stom_world_profile_and_return_procedure.rs | 39 +- .../unpublish_custom_world_profile_reducer.rs | 25 +- .../update_puzzle_work_procedure.rs | 39 +- .../upsert_auth_store_snapshot_procedure.rs | 39 +- ...hapter_progression_and_return_procedure.rs | 41 +- .../upsert_chapter_progression_reducer.rs | 22 +- ...orld_agent_operation_progress_procedure.rs | 42 +- ...stom_world_profile_and_return_procedure.rs | 39 +- .../upsert_custom_world_profile_reducer.rs | 25 +- .../upsert_npc_state_and_return_procedure.rs | 39 +- .../upsert_npc_state_reducer.rs | 22 +- ...orm_browse_history_and_return_procedure.rs | 39 +- ...rt_runtime_setting_and_return_procedure.rs | 39 +- ...t_runtime_snapshot_and_return_procedure.rs | 39 +- .../src/module_bindings/user_account_type.rs | 31 +- .../user_browse_history_type.rs | 21 +- .../spacetime-client/src/story_runtime.rs | 335 +- .../spacetime-module/src/auth/mapper.rs | 86 + .../crates/spacetime-module/src/auth/mod.rs | 6 + .../src/{auth.rs => auth/procedures.rs} | 175 +- .../spacetime-module/src/auth/tables.rs | 66 + .../spacetime-module/src/big_fish/events.rs | 5 +- .../spacetime-module/src/big_fish/mod.rs | 2 + .../spacetime-module/src/big_fish/runtime.rs | 231 + .../spacetime-module/src/big_fish/session.rs | 15 +- .../spacetime-module/src/big_fish/tables.rs | 19 + .../spacetime-module/src/custom_world/mod.rs | 2173 +++--- .../spacetime-module/src/gameplay/mod.rs | 82 + server-rs/crates/spacetime-module/src/lib.rs | 6634 +---------------- .../crates/spacetime-module/src/migration.rs | 2 + .../spacetime-module/src/runtime/profile.rs | 405 +- src/BigFishPlaygroundApp.tsx | 221 +- .../BigFishRuntimeShell.test.tsx | 21 +- .../PlatformEntryFlowShellImpl.tsx | 127 +- ...gEntryFlowShell.agent.interaction.test.tsx | 61 +- .../RpgRuntimeShell.test.tsx | 104 +- .../rpg-runtime-story/inventoryActions.ts | 6 +- .../rpgRuntimeStoryGateway.ts | 55 +- .../runtimeStoryCoordinator.test.ts | 320 +- src/hooks/useGameFlow.customWorld.test.tsx | 180 + src/persistence/runtimeSnapshot.test.ts | 1 + src/persistence/runtimeSnapshot.ts | 7 + src/persistence/runtimeSnapshotTypes.ts | 1 + .../big-fish-runtime/bigFishLocalRuntime.ts | 395 - .../big-fish-runtime/bigFishRuntimeClient.ts | 47 +- src/services/big-fish-runtime/index.ts | 9 +- .../customWorldAgentGenerationProgress.ts | 10 +- .../rpg-entry/rpgEntryClients.routing.test.ts | 24 +- .../rpg-entry/rpgProfileClient.test.ts | 24 +- src/services/rpg-runtime/index.ts | 8 + src/services/rpg-runtime/rpgRuntimeRequest.ts | 5 +- .../rpg-runtime/rpgRuntimeStoryClient.test.ts | 443 +- .../rpg-runtime/rpgRuntimeStoryClient.ts | 231 +- src/types/game.ts | 1 + 676 files changed, 24487 insertions(+), 21531 deletions(-) create mode 100644 docs/technical/SERVER_RS_DDD_WP_AI_INTERNAL_MODULE_SPLIT_2026-04-29.md create mode 100644 docs/technical/SERVER_RS_DDD_WP_AI_TASK_BFF_CLOSURE_2026-04-29.md create mode 100644 docs/technical/SERVER_RS_DDD_WP_AS_ASSET_OBJECT_TYPE_REHOME_2026-04-29.md create mode 100644 docs/technical/SERVER_RS_DDD_WP_A_AUTH_DOMAIN_VALUE_OBJECT_REFACTOR_2026-04-29.md create mode 100644 docs/technical/SERVER_RS_DDD_WP_BF_RUNTIME_BACKEND_TRUTH_2026-04-29.md create mode 100644 docs/technical/SERVER_RS_DDD_WP_CW_DOMAIN_ENUM_REHOME_2026-04-29.md create mode 100644 docs/technical/SERVER_RS_DDD_WP_FE_C_RPG_RUNTIME_SHELL_TEST_FIXTURE_2026-04-29.md create mode 100644 docs/technical/SERVER_RS_DDD_WP_FE_H_RPG_RUNTIME_STORY_HOOKS_PROJECTION_2026-04-29.md create mode 100644 docs/technical/SERVER_RS_DDD_WP_FE_S_RPG_RUNTIME_STORY_CLIENT_MIGRATION_2026-04-29.md create mode 100644 docs/technical/SERVER_RS_DDD_WP_PF_PLATFORM_ERROR_CLASSIFICATION_2026-04-29.md create mode 100644 docs/technical/SERVER_RS_DDD_WP_PZ_DOMAIN_ENUM_REHOME_2026-04-29.md create mode 100644 docs/technical/SERVER_RS_DDD_WP_PZ_DOMAIN_SPLIT_2026-04-29.md create mode 100644 docs/technical/SERVER_RS_DDD_WP_RPG_COMBAT_DOMAIN_ENUM_REHOME_2026-04-29.md create mode 100644 docs/technical/SERVER_RS_DDD_WP_RPG_STORY_DOMAIN_SPLIT_2026-04-29.md create mode 100644 docs/technical/SERVER_RS_DDD_WP_RS_COMPAT_RESIDUE_AUDIT_2026-04-29.md create mode 100644 docs/technical/SERVER_RS_DDD_WP_RT_ADAPTER_API_CLOSURE_2026-04-29.md create mode 100644 docs/technical/SERVER_RS_DDD_WP_RT_APPLICATION_RECORD_REFACTOR_2026-04-29.md create mode 100644 docs/technical/SERVER_RS_DDD_WP_RT_COMMANDS_REFACTOR_2026-04-29.md create mode 100644 docs/technical/SERVER_RS_DDD_WP_RT_DOMAIN_SNAPSHOT_RECORD_REFACTOR_2026-04-29.md create mode 100644 docs/technical/SERVER_RS_DDD_WP_RT_ERROR_LAYER_REFACTOR_2026-04-29.md create mode 100644 docs/technical/SERVER_RS_DDD_WP_RT_RUNTIME_SETTINGS_DOMAIN_REFACTOR_2026-04-29.md create mode 100644 docs/technical/SERVER_RS_DDD_WP_ST_AUTH_ADAPTER_SPLIT_2026-04-29.md create mode 100644 docs/technical/SERVER_RS_DDD_WP_ST_CUSTOM_WORLD_ROOT_SPLIT_2026-04-29.md create mode 100644 docs/technical/SERVER_RS_DDD_WP_ST_GAMEPLAY_ROOT_SPLIT_2026-04-29.md create mode 100644 packages/shared/src/contracts/story.ts create mode 100644 server-rs/crates/api-server/src/platform_errors.rs create mode 100644 server-rs/crates/module-ai/src/application/result.rs create mode 100644 server-rs/crates/module-ai/src/application/service.rs create mode 100644 server-rs/crates/module-ai/src/application/store.rs create mode 100644 server-rs/crates/module-ai/src/commands/inputs.rs create mode 100644 server-rs/crates/module-ai/src/commands/validation.rs create mode 100644 server-rs/crates/module-ai/src/domain/ids.rs create mode 100644 server-rs/crates/module-ai/src/domain/stages.rs create mode 100644 server-rs/crates/module-ai/src/domain/types.rs create mode 100644 server-rs/crates/module-ai/src/tests.rs create mode 100644 server-rs/crates/module-puzzle/README.md create mode 100644 server-rs/crates/spacetime-client/src/module_bindings/ai_task_event_kind_type.rs create mode 100644 server-rs/crates/spacetime-client/src/module_bindings/ai_task_event_table.rs create mode 100644 server-rs/crates/spacetime-client/src/module_bindings/ai_task_event_type.rs create mode 100644 server-rs/crates/spacetime-client/src/module_bindings/big_fish_event_kind_type.rs create mode 100644 server-rs/crates/spacetime-client/src/module_bindings/big_fish_event_table.rs create mode 100644 server-rs/crates/spacetime-client/src/module_bindings/big_fish_event_type.rs create mode 100644 server-rs/crates/spacetime-client/src/module_bindings/big_fish_input_submit_input_type.rs create mode 100644 server-rs/crates/spacetime-client/src/module_bindings/big_fish_run_get_input_type.rs create mode 100644 server-rs/crates/spacetime-client/src/module_bindings/big_fish_run_procedure_result_type.rs create mode 100644 server-rs/crates/spacetime-client/src/module_bindings/big_fish_run_start_input_type.rs create mode 100644 server-rs/crates/spacetime-client/src/module_bindings/big_fish_run_status_type.rs create mode 100644 server-rs/crates/spacetime-client/src/module_bindings/big_fish_runtime_run_type.rs create mode 100644 server-rs/crates/spacetime-client/src/module_bindings/get_big_fish_run_procedure.rs create mode 100644 server-rs/crates/spacetime-client/src/module_bindings/puzzle_event_kind_type.rs create mode 100644 server-rs/crates/spacetime-client/src/module_bindings/puzzle_event_table.rs create mode 100644 server-rs/crates/spacetime-client/src/module_bindings/puzzle_event_type.rs create mode 100644 server-rs/crates/spacetime-client/src/module_bindings/start_big_fish_run_procedure.rs create mode 100644 server-rs/crates/spacetime-client/src/module_bindings/submit_big_fish_input_procedure.rs create mode 100644 server-rs/crates/spacetime-module/src/auth/mapper.rs create mode 100644 server-rs/crates/spacetime-module/src/auth/mod.rs rename server-rs/crates/spacetime-module/src/{auth.rs => auth/procedures.rs} (74%) create mode 100644 server-rs/crates/spacetime-module/src/auth/tables.rs create mode 100644 server-rs/crates/spacetime-module/src/big_fish/runtime.rs delete mode 100644 src/services/big-fish-runtime/bigFishLocalRuntime.ts diff --git a/docs/technical/README.md b/docs/technical/README.md index 9acd89c3..a5ee9ba6 100644 --- a/docs/technical/README.md +++ b/docs/technical/README.md @@ -4,7 +4,30 @@ ## 文档列表 +- [SERVER_RS_DDD_WP_BF_RUNTIME_BACKEND_TRUTH_2026-04-29.md](./SERVER_RS_DDD_WP_BF_RUNTIME_BACKEND_TRUTH_2026-04-29.md):记录 `WP-BF Big Fish` 运行态从前端本地规则切到 Rust 领域真相源、SpacetimeDB run 表、API facade 和前端新接口接入的关闭口径。 +- [SERVER_RS_DDD_WP_PF_PLATFORM_ERROR_CLASSIFICATION_2026-04-29.md](./SERVER_RS_DDD_WP_PF_PLATFORM_ERROR_CLASSIFICATION_2026-04-29.md):记录 `WP-PF platform side effects` 平台副作用收口,统一 LLM、OSS、SMS、微信平台错误分类与 API 映射,并将微信 OAuth provider 下沉到 `platform-auth`。 +- [SERVER_RS_DDD_WP_RT_ADAPTER_API_CLOSURE_2026-04-29.md](./SERVER_RS_DDD_WP_RT_ADAPTER_API_CLOSURE_2026-04-29.md):记录 `WP-RT Runtime/Profile/Save` Adapter/API 收口,将 checkpoint、profile/save archive meta、充值/邀请/兑换/钱包等剩余纯规则迁入 `module-runtime`,移除 `/api/runtime/profile/*` 旧兼容挂载并对齐前端 `/api/profile/*` 请求路径。 - [SERVER_RS_DDD_WP_SC_SPACETIME_CLIENT_REFACTOR_2026-04-29.md](./SERVER_RS_DDD_WP_SC_SPACETIME_CLIENT_REFACTOR_2026-04-29.md):冻结 `WP-SC Spacetime Client` 本次基础设施重构边界,明确只收口 `spacetime-client` 的 typed facade、错误映射和 row snapshot mapper,不预判尚未由 `WP-ST` 稳定的表、reducer、procedure 或 row shape。 +- [SERVER_RS_DDD_WP_RT_APPLICATION_RECORD_REFACTOR_2026-04-29.md](./SERVER_RS_DDD_WP_RT_APPLICATION_RECORD_REFACTOR_2026-04-29.md):记录 `WP-RT Runtime/Profile/Save` 的应用记录投影拆分切片,将 settings、browse history、profile/save 等 `build_runtime_*_record` 迁入 `module-runtime/src/application.rs`,不改回包字段语义。 +- [SERVER_RS_DDD_WP_RT_COMMANDS_REFACTOR_2026-04-29.md](./SERVER_RS_DDD_WP_RT_COMMANDS_REFACTOR_2026-04-29.md):记录 `WP-RT Runtime/Profile/Save` 的命令构造拆分切片,将 settings、browse history、profile/save 等 `build_runtime_*_input` 和写入归一化函数迁入 `module-runtime/src/commands.rs`,不改校验语义。 +- [SERVER_RS_DDD_WP_AI_INTERNAL_MODULE_SPLIT_2026-04-29.md](./SERVER_RS_DDD_WP_AI_INTERNAL_MODULE_SPLIT_2026-04-29.md):记录 `WP-AI AI Task` 的 `module-ai` 内部子模块拆分,将 domain、commands、application 与行为测试继续拆到职责更细的子文件,同时保持 `module_ai::*` 公开导出、SpacetimeDB schema、BFF route 和前端契约不变。 +- [SERVER_RS_DDD_WP_AI_TASK_BFF_CLOSURE_2026-04-29.md](./SERVER_RS_DDD_WP_AI_TASK_BFF_CLOSURE_2026-04-29.md):记录 `WP-AI AI Task` BFF 收口与关闭口径,补齐 AI task mutation route 鉴权和 SpacetimeDB 未发布错误 envelope 的定向验证,不改表结构、LLM provider、SSE 或前端消费。 +- [SERVER_RS_DDD_WP_CW_DOMAIN_ENUM_REHOME_2026-04-29.md](./SERVER_RS_DDD_WP_CW_DOMAIN_ENUM_REHOME_2026-04-29.md):记录 `WP-CW Custom World` 基础领域枚举归位切片,将 Custom World / RPG Agent 基础枚举、进度常量和字符串口径迁入 `module-custom-world/src/domain.rs`,不改 SpacetimeDB、API 或前端行为。 +- [SERVER_RS_DDD_WP_RPG_STORY_DOMAIN_SPLIT_2026-04-29.md](./SERVER_RS_DDD_WP_RPG_STORY_DOMAIN_SPLIT_2026-04-29.md):记录 `WP-RPG Gameplay 域` 的 `module-story` 领域拆分收口,将 story session 领域模型、命令、事件、应用映射和错误层从 `lib.rs` 拆入 DDD 骨架文件,并修正 README 不再指向旧 `/api/runtime/story/*` 兼容链路。 +- [SERVER_RS_DDD_WP_PZ_DOMAIN_SPLIT_2026-04-29.md](./SERVER_RS_DDD_WP_PZ_DOMAIN_SPLIT_2026-04-29.md):记录 `WP-PZ Puzzle` 领域类型与规则拆分切片,将 Agent/作品/运行态领域类型、写入命令、应用规则、字段错误和最小领域事件归位到 `module-puzzle` 的 DDD 骨架文件,不改 SpacetimeDB、API 或前端行为。 +- [SERVER_RS_DDD_WP_PZ_DOMAIN_ENUM_REHOME_2026-04-29.md](./SERVER_RS_DDD_WP_PZ_DOMAIN_ENUM_REHOME_2026-04-29.md):记录 `WP-PZ Puzzle` 基础领域常量与枚举归位切片,将 Puzzle Agent、发布状态、运行态状态、ID 前缀、标签数量和洗牌次数口径迁入 `module-puzzle/src/domain.rs`,不改 SpacetimeDB、API 或前端行为。 +- [SERVER_RS_DDD_WP_RPG_COMBAT_DOMAIN_ENUM_REHOME_2026-04-29.md](./SERVER_RS_DDD_WP_RPG_COMBAT_DOMAIN_ENUM_REHOME_2026-04-29.md):记录 `WP-RPG Gameplay 域` 的 combat 基础领域常量与枚举归位切片,将战斗 ID 前缀、版本、伤害、切磋保底生命、旧攻击 function 列表和基础枚举迁入 `module-combat/src/domain.rs`,不改 SpacetimeDB、API 或前端行为。 +- [SERVER_RS_DDD_WP_ST_AUTH_ADAPTER_SPLIT_2026-04-29.md](./SERVER_RS_DDD_WP_ST_AUTH_ADAPTER_SPLIT_2026-04-29.md):记录 `WP-ST` Auth SpacetimeDB adapter 目录化切片,将认证表、procedure 和快照 JSON mapper 拆入 `auth/` 子模块,不改 schema、procedure 签名或绑定形状。 +- [SERVER_RS_DDD_WP_RS_COMPAT_RESIDUE_AUDIT_2026-04-29.md](./SERVER_RS_DDD_WP_RS_COMPAT_RESIDUE_AUDIT_2026-04-29.md):记录 `WP-RS Runtime Story 去兼容层` 的 compat 残留审计切片,清理 `module-runtime-story` 运行代码注释口径,并冻结仍需等待新写接口的前端和 contract 残留。 +- [SERVER_RS_DDD_WP_AS_ASSET_OBJECT_TYPE_REHOME_2026-04-29.md](./SERVER_RS_DDD_WP_AS_ASSET_OBJECT_TYPE_REHOME_2026-04-29.md):记录 `WP-AS Assets` 资产对象类型归位切片,将领域快照、命令 DTO、应用返回 DTO 和字段错误拆入 `module-assets` 的 DDD 骨架文件,不改 SpacetimeDB、API、OSS 或前端行为。 +- [SERVER_RS_DDD_WP_RT_ERROR_LAYER_REFACTOR_2026-04-29.md](./SERVER_RS_DDD_WP_RT_ERROR_LAYER_REFACTOR_2026-04-29.md):记录 `WP-RT Runtime/Profile/Save` 的错误层拆分切片,将 settings、browse history、profile/save 三组字段错误和中文错误文案迁入 `module-runtime/src/errors.rs`,不改校验语义。 +- [SERVER_RS_DDD_WP_RT_DOMAIN_SNAPSHOT_RECORD_REFACTOR_2026-04-29.md](./SERVER_RS_DDD_WP_RT_DOMAIN_SNAPSHOT_RECORD_REFACTOR_2026-04-29.md):记录 `WP-RT Runtime/Profile/Save` 的 snapshot、profile、wallet、played world 与 save archive 领域快照和记录类型拆分切片,只移动纯类型和枚举方法,不改 SpacetimeDB、API 或前端接线。 +- [SERVER_RS_DDD_WP_RT_RUNTIME_SETTINGS_DOMAIN_REFACTOR_2026-04-29.md](./SERVER_RS_DDD_WP_RT_RUNTIME_SETTINGS_DOMAIN_REFACTOR_2026-04-29.md):记录 `WP-RT Runtime/Profile/Save` 的 runtime settings 领域值对象拆分切片,将默认设置、平台主题和值对象迁入 `module-runtime/src/domain.rs`,不改 SpacetimeDB、API 或前端接线。 +- [SERVER_RS_DDD_WP_A_AUTH_DOMAIN_VALUE_OBJECT_REFACTOR_2026-04-29.md](./SERVER_RS_DDD_WP_A_AUTH_DOMAIN_VALUE_OBJECT_REFACTOR_2026-04-29.md):记录 `WP-A Auth` DDD 分层收口,将账号、会话、验证码、微信 state/绑定规则、命令输入、应用返回、领域错误和领域事件归位到 `module-auth` 骨架,并核查 API、platform 与 SpacetimeDB adapter 边界。 +- [SERVER_RS_DDD_WP_ST_CUSTOM_WORLD_ROOT_SPLIT_2026-04-29.md](./SERVER_RS_DDD_WP_ST_CUSTOM_WORLD_ROOT_SPLIT_2026-04-29.md):记录 `WP-ST` Custom World SpacetimeDB adapter 从根入口迁入 `custom_world/mod.rs` 的边界、无 schema 变更口径和验收命令。 +- [SERVER_RS_DDD_WP_FE_S_RPG_RUNTIME_STORY_CLIENT_MIGRATION_2026-04-29.md](./SERVER_RS_DDD_WP_FE_S_RPG_RUNTIME_STORY_CLIENT_MIGRATION_2026-04-29.md):记录 `WP-FE-S` RPG runtime story client 读取侧迁到 `storySessionId` scoped runtime projection,并补齐 story session 新主链 `begin/continue/state/projection` API client 的边界、旧写接口暂留原因和后续依赖。 +- [SERVER_RS_DDD_WP_FE_H_RPG_RUNTIME_STORY_HOOKS_PROJECTION_2026-04-29.md](./SERVER_RS_DDD_WP_FE_H_RPG_RUNTIME_STORY_HOOKS_PROJECTION_2026-04-29.md):记录 `WP-FE-H` RPG runtime story 读取侧 hooks 接线切片,将 option catalog 与继续游戏刷新显式接入 `getRpgStoryRuntimeProjection`,写接口和组件层仍等待后续收口。 +- [SERVER_RS_DDD_WP_FE_C_RPG_RUNTIME_SHELL_TEST_FIXTURE_2026-04-29.md](./SERVER_RS_DDD_WP_FE_C_RPG_RUNTIME_SHELL_TEST_FIXTURE_2026-04-29.md):记录 `WP-FE-C` RPG runtime shell 组件测试夹具接线切片,将组件测试 mock 对齐当前 hooks 暴露的 UI 对象形状,不触碰未稳定写接口。 - [SERVER_RS_DDD_G1_CONTRACT_AND_ROUTE_MATRIX_PROGRESS_2026-04-29.md](./SERVER_RS_DDD_G1_CONTRACT_AND_ROUTE_MATRIX_PROGRESS_2026-04-29.md):记录 `G1 契约与路由矩阵` 已完成的本地进度、验证结果、单 owner 边界和下一批并行任务入口。 - [SERVER_RS_DDD_G1_CONTRACT_AND_ROUTE_MATRIX_2026-04-29.md](./SERVER_RS_DDD_G1_CONTRACT_AND_ROUTE_MATRIX_2026-04-29.md):冻结 `server-rs` DDD G1 契约与路由矩阵,明确新旧 HTTP 路由去留、DTO 删除/保留/重命名、页面到 query/result DTO 映射、breaking change、API 错误 envelope 和共享契约单 owner 边界。 - [SERVER_RS_DDD_WP_API_BFF_START_2026-04-29.md](./SERVER_RS_DDD_WP_API_BFF_START_2026-04-29.md):记录 `WP-API api-server BFF` 启动切片,先收口旧 runtime story 兼容路由挂载、错误 envelope 回归和后续依赖,不越过 `spacetime-client` 接线边界。 diff --git a/docs/technical/SERVER_RS_DDD_G1_CONTRACT_AND_ROUTE_MATRIX_2026-04-29.md b/docs/technical/SERVER_RS_DDD_G1_CONTRACT_AND_ROUTE_MATRIX_2026-04-29.md index a174e36a..edd6e5ff 100644 --- a/docs/technical/SERVER_RS_DDD_G1_CONTRACT_AND_ROUTE_MATRIX_2026-04-29.md +++ b/docs/technical/SERVER_RS_DDD_G1_CONTRACT_AND_ROUTE_MATRIX_2026-04-29.md @@ -43,13 +43,13 @@ G1 单 owner 文件范围: | Runtime settings/save | `GET/PUT /api/runtime/settings`、`GET/PUT/DELETE /api/runtime/save/snapshot` | 保留 | `RuntimeSettingsResponse`、`PutRuntimeSettingsRequest`、`SavedGameSnapshotResponse`、`PutSavedGameSnapshotRequest` | WP-RT | | RPG 作品库 | `GET /api/runtime/custom-world-library`、`GET/PUT/DELETE /api/runtime/custom-world-library/{profile_id}`、`POST /publish`、`POST /unpublish`、`GET /api/runtime/custom-world-gallery`、`GET /api/runtime/custom-world-gallery/{owner_user_id}/{profile_id}`、`GET /api/runtime/custom-world-gallery/by-code/{code}` | 收敛 | 命名后续改为 RPG creation/work route family;删除 `custom-world` 旧泛名歧义 | WP-CW、WP-FE | | RPG Agent | `POST /api/runtime/custom-world/agent/sessions`、`GET/DELETE /sessions/{session_id}`、`GET /result-view`、`GET /works`、`GET /cards/{card_id}`、`POST /messages`、`POST /messages/stream`、`POST /actions`、`GET /operations/{operation_id}` | 收敛 | DTO 重命名为 `RpgAgent*`,Rust 当前 `CustomWorldAgent*` 后续物理重命名 | WP-CW、WP-FE、WP-DEL | -| Big Fish Agent/Works | `POST /api/runtime/big-fish/agent/sessions`、`GET /sessions/{session_id}`、`POST /messages`、`POST /messages/stream`、`POST /actions`、`GET /works`、`DELETE /works/{session_id}`、`GET /gallery`、`POST /sessions/{session_id}/play`、`POST /works/{session_id}/play` | 保留 | `BigFish*` DTO,`sessions/{id}/play` 与 `works/{id}/play` 后续二选一保留 | WP-BF | +| Big Fish Agent/Works/Runtime | `POST /api/runtime/big-fish/agent/sessions`、`GET /sessions/{session_id}`、`POST /messages`、`POST /messages/stream`、`POST /actions`、`GET /works`、`DELETE /works/{session_id}`、`GET /gallery`、`POST /sessions/{session_id}/play`、`POST /works/{session_id}/play`、`POST /sessions/{session_id}/runs`、`GET /runs/{run_id}`、`POST /runs/{run_id}/input` | 保留 | `BigFish*` DTO,运行态正式使用 `BigFishRunResponse` 和 `SubmitBigFishInputRequest`;`sessions/{id}/play` 与 `works/{id}/play` 后续二选一保留 | WP-BF | | Puzzle Agent/Works/Runtime | `POST /api/runtime/puzzle/agent/sessions`、`GET /sessions/{session_id}`、`POST /messages`、`POST /messages/stream`、`POST /actions`、`GET /works`、`GET/PUT/DELETE /works/{profile_id}`、`GET /gallery`、`GET /gallery/{profile_id}`、`POST /runs`、`POST /runs/local-next-level`、`GET /runs/{run_id}`、`POST /runs/{run_id}/swap`、`POST /runs/{run_id}/drag`、`POST /runs/{run_id}/next-level`、`POST /runs/{run_id}/leaderboard` | 保留 | `PuzzleAgent*`、`PuzzleWork*`、`PuzzleRun*` DTO | WP-PZ | | RPG profile/asset generation | `POST /api/runtime/custom-world/profile`、`POST /api/custom-world/entity`、`POST /api/runtime/custom-world/entity`、`POST /api/custom-world/scene-npc`、`POST /api/runtime/custom-world/scene-npc`、`POST /api/custom-world/scene-image`、`POST /api/custom-world/cover-image`、`POST /api/runtime/custom-world/cover-image`、`POST /api/custom-world/cover-upload`、`POST /api/runtime/custom-world/cover-upload` | 重命名 | 去掉非 runtime 前缀旧入口;统一到 RPG creation asset/profile route family | WP-CW、WP-AS、WP-DEL | -| Profile | `GET/POST/DELETE /api/runtime/profile/browse-history`、`GET/POST/DELETE /api/profile/browse-history`、`GET /dashboard`、`GET /wallet-ledger`、`GET /recharge-center`、`POST /recharge/orders`、`GET /referrals/invite-center`、`POST /referrals/redeem-code`、`POST /redeem-codes/redeem`、`GET /play-stats`、`GET /save-archives`、`POST /save-archives/{world_key}` | 重命名 | 保留 `/api/runtime/profile/*` 主链,删除 `/api/profile/*` 镜像入口 | WP-RT、WP-FE、WP-DEL | +| Profile | `GET/POST/DELETE /api/profile/browse-history`、`GET /api/profile/dashboard`、`GET /api/profile/wallet-ledger`、`GET /api/profile/recharge-center`、`POST /api/profile/recharge/orders`、`GET /api/profile/referrals/invite-center`、`POST /api/profile/referrals/redeem-code`、`POST /api/profile/redeem-codes/redeem`、`GET /api/profile/play-stats`、`GET /api/profile/save-archives`、`POST /api/profile/save-archives/{world_key}`;旧 `GET/POST/DELETE /api/runtime/profile/*` 已取消挂载 | 重命名 | 保留 `/api/profile/*` 主链,删除 `/api/runtime/profile/*` 旧兼容入口 | WP-RT、WP-FE、WP-DEL | | Runtime inventory | `GET /api/runtime/sessions/{runtime_session_id}/inventory` | 保留 | `RuntimeInventoryStateResponse` | WP-RPG、WP-RT | | Runtime story 旧层 | `POST /api/runtime/story/sessions`、`POST /api/runtime/story/state/resolve`、`GET /api/runtime/story/state/{session_id}`、`POST /api/runtime/story/actions/resolve`、`POST /api/runtime/story/initial`、`POST /api/runtime/story/continue` | 已删除 | 已从 `api-server` 取消挂载并删除 `api-server/src/runtime_story*` 兼容实现;后续前端迁移到 `GET/POST /api/story/*` 和 session scoped story/chat facade | WP-RS、WP-FE、WP-DEL | -| Story/Game facade | `POST /api/story/sessions`、`GET /api/story/sessions/{story_session_id}/state`、`POST /api/story/sessions/continue`、`POST /api/story/battles`、`GET /api/story/battles/{battle_state_id}`、`POST /api/story/npc/battle`、`POST /api/story/battles/resolve` | 保留 | `BeginStorySession*`、`ContinueStory*`、battle/npc command/result DTO 后续补齐到 `shared-contracts` | WP-RPG、WP-RS | +| Story/Game facade | `POST /api/story/sessions`、`GET /api/story/sessions/{story_session_id}/state`、`GET /api/story/sessions/{story_session_id}/runtime-projection`、`POST /api/story/sessions/continue`、`POST /api/story/battles`、`GET /api/story/battles/{battle_state_id}`、`POST /api/story/npc/battle`、`POST /api/story/battles/resolve` | 保留 | `BeginStorySession*`、`ContinueStory*`、`StoryRuntimeProjection*`、`CreateStoryBattle*`、`StoryBattleState*`、`ResolveStoryBattle*` DTO 已补齐到 `shared-contracts` | WP-RPG、WP-RS | ## 3. DTO 冻结清单 @@ -62,13 +62,15 @@ G1 单 owner 文件范围: | `shared-contracts/src/auth.rs` | `AuthLoginOptionsResponse`、`AuthUserPayload`、`PublicUserSummaryPayload`、`PublicUserSearchResponse`、`PasswordEntry*`、`PasswordChange*`、`PasswordReset*`、`AuthMeResponse`、`AuthSessionsResponse`、`RefreshSessionResponse`、`Logout*`、`Phone*`、`Wechat*` | | `shared-contracts/src/ai.rs` | `CreateAiTaskRequest`、`AppendAiTextChunkRequest`、`CompleteAiStageRequest`、`AttachAiResultReferenceRequest`、`FailAiTaskRequest`、`AiTask*Payload`、`AiTaskMutationResponse`、`AiTaskAcceptedResponse` | | `shared-contracts/src/assets.rs` | Direct upload、read url、asset object、asset binding、asset history、character visual/animation、workflow cache、role asset workflow 相关 DTO | -| `shared-contracts/src/big_fish*.rs` | `CreateBigFishSessionRequest`、`SendBigFishMessageRequest`、`ExecuteBigFishActionRequest`、`RecordBigFishPlayRequest`、`BigFish*Response`、`BigFishWorksResponse` | +| `shared-contracts/src/creation_agent_document_input.rs` | `ParseCreationAgentDocumentInputRequest/Response`、`CreationAgentDocumentInputPayload` | +| `shared-contracts/src/big_fish*.rs` | `CreateBigFishSessionRequest`、`SendBigFishMessageRequest`、`ExecuteBigFishActionRequest`、`RecordBigFishPlayRequest`、`SubmitBigFishInputRequest`、`BigFish*Response`、`BigFishRunResponse`、`BigFishWorksResponse` | | `shared-contracts/src/puzzle_*.rs` | `CreatePuzzleAgentSessionRequest`、`SendPuzzleAgentMessageRequest`、`ExecutePuzzleAgentActionRequest`、`PuzzleAgent*Response`、`PuzzleWork*`、`PuzzleRun*`、`PuzzleGallery*` | | `shared-contracts/src/runtime.rs` | runtime settings/save/profile/browse history/custom world library/agent/result view/inventory 现有 DTO 在迁移窗口保留 | -| `shared-contracts/src/story.rs` | `BeginStorySessionRequest`、`ContinueStoryRequest`、`StorySessionPayload`、`StoryEventPayload`、`StorySessionMutationResponse`、`StorySessionStateResponse` | +| `shared-contracts/src/story.rs` | `BeginStorySessionRequest`、`ContinueStoryRequest`、`StorySessionPayload`、`StoryEventPayload`、`StorySessionMutationResponse`、`StorySessionStateResponse`、`StoryRuntimeProjectionRequest/Response`、`CreateStoryBattleRequest`、`CreateStoryNpcBattleRequest`、`StoryBattleStateResponse`、`ResolveStoryBattleRequest/Response` | | `packages/shared/src/contracts/runtime.ts` | `RuntimeSettings`、`SavedGameSnapshot*`、profile、browse history、library/gallery DTO;迁移窗口继续作为前端消费主入口 | +| `packages/shared/src/contracts/creationAgentDocumentInput.ts` | `ParseCreationAgentDocumentInputRequest/Response`、`CreationAgentDocumentInputPayload` | | `packages/shared/src/contracts/rpgAgent*.ts` | RPG Agent、draft、anchors、result view、work summary DTO | -| `packages/shared/src/contracts/bigFish*.ts` | Big Fish Agent、runtime、本地作品列表 DTO | +| `packages/shared/src/contracts/bigFish*.ts` | Big Fish Agent、backend runtime run/input、本地作品列表 DTO | | `packages/shared/src/contracts/puzzle*.ts` | Puzzle Agent、work、gallery、runtime DTO | ### 3.2 重命名 @@ -80,7 +82,7 @@ G1 单 owner 文件范围: | Rust `GenerateCustomWorldProfile*` | `GenerateRpgCreationProfile*` | 去掉泛化 custom world 命名,明确 RPG 创作 profile。 | | TS `AuthEntry*` | `PasswordEntry*` 或统一后端 `AuthPasswordEntry*` | 需要在 WP-A 中二选一收口,避免 entry 与 phone/wechat 登录语义混杂。 | | `RuntimeStory*` view model | `RpgRuntimeStory*` 或拆到 `Story*`、`Battle*`、`Inventory*` | 旧聚合大 DTO 后续拆分为 story session、battle、inventory、npc interaction 投影。 | -| Profile 镜像 DTO | `RuntimeProfile*` | `/api/profile/*` 镜像删除后,契约命名跟随 `/api/runtime/profile/*`。 | +| Profile 旧兼容入口 DTO | `RuntimeProfile*` | `/api/runtime/profile/*` 旧兼容入口已删除,契约命名可继续保留 RuntimeProfile 领域语义,HTTP 主链固定为 `/api/profile/*`。 | ### 3.3 删除 @@ -93,7 +95,7 @@ G1 单 owner 文件范围: | Rust/TS `RuntimeStoryActionRequest/Response` 旧总入口形态 | `POST /api/runtime/story/actions/resolve` 删除后 | story/battle/npc/inventory 分命令 result DTO | | TS `StoryRequestPayload`、`PlainTextPromptRequest`、`PlainTextResponse` | runtime chat 不再由前端传 prompt 后 | 后端 session scoped chat/story command | | TS `CreateCustomWorldSessionRequest`、`AnswerCustomWorldSessionQuestionRequest`、`CustomWorldSessionRecord` 等旧问答生成 DTO | 确认无前端运行引用后 | RPG Agent session DTO | -| `/api/profile/*` 镜像 DTO 别名 | 前端全量迁到 `/api/runtime/profile/*` 后 | Runtime profile DTO | +| `/api/runtime/profile/*` 旧兼容 DTO 别名 | 前端全量迁到 `/api/profile/*` 后 | Runtime profile DTO | ## 4. 页面/功能到 query/result DTO 映射 @@ -111,23 +113,23 @@ G1 单 owner 文件范围: | RPG Agent 工作区 | route param `session_id` / `operation_id` | `CreateCustomWorldAgentSessionRequest`、`SendCustomWorldAgentMessageRequest`、`ExecuteCustomWorldAgentActionRequest` | `CustomWorldAgentSessionResponse`、`CustomWorldAgentOperationResponse`、`CustomWorldCreationResultViewResponse`,后续重命名 `RpgAgent*` | | RPG 结果页 | route param `session_id` | section patch/action request | `CustomWorldCreationResultViewResponse`、`CustomWorldAgentCardDetailResponse` | | RPG 资产工坊 | route param `character_id`、`GetReadUrlQuery` | `CharacterVisualGenerateRequest`、`CharacterAnimationGenerateRequest`、`CharacterWorkflowCacheSaveRequest`、`CharacterRoleAssetWorkflowResolveRequest` | `CharacterVisualGenerateResponse`、`CharacterAnimationGenerateResponse`、`CharacterWorkflowCacheGetResponse`、`CharacterRoleAssetWorkflowResponse` | -| Big Fish Agent | route param `session_id` | `CreateBigFishSessionRequest`、`SendBigFishMessageRequest`、`ExecuteBigFishActionRequest` | `BigFishSessionResponse`、`BigFishActionResponse` | +| Big Fish Agent/Runtime | route param `session_id` / `run_id` | `CreateBigFishSessionRequest`、`SendBigFishMessageRequest`、`ExecuteBigFishActionRequest`、`SubmitBigFishInputRequest` | `BigFishSessionResponse`、`BigFishActionResponse`、`BigFishRunResponse` | | Big Fish 广场/作品 | bearer token 或公开 gallery query | `RecordBigFishPlayRequest` | `BigFishWorksResponse`、`BigFishGalleryResponse`、`BigFishSessionResponse` | | Puzzle Agent | route param `session_id` | `CreatePuzzleAgentSessionRequest`、`SendPuzzleAgentMessageRequest`、`ExecutePuzzleAgentActionRequest` | `PuzzleAgentSessionResponse`、`PuzzleAgentActionResponse` | | Puzzle 作品/广场 | route param `profile_id` | `PutPuzzleWorkRequest` | `PuzzleWorksResponse`、`PuzzleWorkDetailResponse`、`PuzzleGalleryResponse`、`PuzzleGalleryDetailResponse` | | Puzzle 运行态 | route param `run_id` | `StartPuzzleRunRequest`、`AdvanceLocalPuzzleNextLevelRequest`、`SwapPuzzlePiecesRequest`、`DragPuzzlePieceRequest`、`SubmitPuzzleLeaderboardRequest` | `PuzzleRunResponse` | | Runtime 设置与存档 | bearer token | `PutRuntimeSettingsRequest`、`PutSavedGameSnapshotRequest`、`PutRuntimeSaveCheckpointRequest` | `RuntimeSettingsResponse`、`SavedGameSnapshotResponse`、`BasicOkResponse` | | 个人中心 | bearer token | `CreateProfileRechargeOrderRequest`、`RedeemProfileReferralInviteCodeRequest`、`RedeemProfileRewardCodeRequest`、`PlatformBrowseHistoryUpsertRequest` | `ProfileDashboardSummaryResponse`、`ProfileWalletLedgerResponse`、`ProfileRechargeCenterResponse`、`ProfileReferralInviteCenterResponse`、`ProfilePlayStatsResponse`、`ProfileSaveArchiveListResponse`、`PlatformBrowseHistoryResponse` | -| RPG Story 运行态 | route param `story_session_id`、`battle_state_id` | `BeginStorySessionRequest`、`ContinueStoryRequest`,battle/npc 命令 DTO 后续补齐 | `StorySessionMutationResponse`、`StorySessionStateResponse`、`RuntimeInventoryStateResponse` | +| RPG Story 运行态 | route param `story_session_id`、`battle_state_id` | `BeginStorySessionRequest`、`ContinueStoryRequest`、`CreateStoryBattleRequest`、`CreateStoryNpcBattleRequest`、`ResolveStoryBattleRequest` | `StorySessionMutationResponse`、`StorySessionStateResponse`、`StoryRuntimeProjectionResponse`、`StoryBattleStateResponse`、`ResolveStoryBattleResponse`、`RuntimeInventoryStateResponse` | | Runtime chat/NPC 私聊 | route param `runtime_session_id` 或 `story_session_id` | 后续新增 session scoped chat command | 后续新增 chat turn result;旧 `rpgRuntimeChat.ts` DTO 只作为迁移参考 | ## 5. Breaking change 清单 -1. 删除兼容层是本轮默认策略。旧 `/api/runtime/story/*`、`/_internal/auth/*`、`/generated-*` 和 `/api/profile/*` 镜像入口在对应前端迁移完成后物理删除。 +1. 删除兼容层是本轮默认策略。旧 `/api/runtime/story/*`、`/_internal/auth/*`、`/generated-*` 和 `/api/runtime/profile/*` 兼容入口在对应前端迁移完成后物理删除。 2. Runtime story/chat 不再接受前端拼装的 `worldType`、`character`、`monsters`、`history`、`context`、prompt 文本作为正式真相。正式命令必须以 `runtimeSessionId`、`storySessionId`、`battleStateId` 等后端 session id 为索引。 3. `CustomWorld*` 作为 RPG 主链命名将被重命名为 `RpgCreation*` 或 `RpgAgent*`。前端可同步修改,不保留旧命名适配层。 4. `/api/custom-world/*` 非 runtime 前缀旧入口删除,统一进入 RPG creation route family 或 asset route family。 -5. `/api/profile/*` 镜像入口删除,统一使用 `/api/runtime/profile/*`。 +5. `/api/runtime/profile/*` 兼容入口删除,统一使用 `/api/profile/*`。 6. 资产读取不再依赖 `/generated-*` 静态代理作为正式 contract,统一走 asset object、read url 或后端投影里的正式 URL 字段。 7. LLM 代理不得作为玩法 prompt 透传入口。玩法 prompt 由 `api-server`/`platform-llm` 内部编排,前端只提交用户动作和展示态输入。 8. API 错误体统一为 `ApiErrorEnvelope`。旧 `{ error, meta }` 只允许在已列入删除计划的旧接口中短期存在。 diff --git a/docs/technical/SERVER_RS_DDD_G1_CONTRACT_AND_ROUTE_MATRIX_PROGRESS_2026-04-29.md b/docs/technical/SERVER_RS_DDD_G1_CONTRACT_AND_ROUTE_MATRIX_PROGRESS_2026-04-29.md index 91c0e2f9..09f1f780 100644 --- a/docs/technical/SERVER_RS_DDD_G1_CONTRACT_AND_ROUTE_MATRIX_PROGRESS_2026-04-29.md +++ b/docs/technical/SERVER_RS_DDD_G1_CONTRACT_AND_ROUTE_MATRIX_PROGRESS_2026-04-29.md @@ -40,6 +40,16 @@ npm.cmd run check:encoding -- docs/technical/SERVER_RS_DDD_G1_CONTRACT_AND_ROUTE 结果:通过,4 个文档文件 UTF-8 编码检查正常。 +## 4.1 持续巡检记录 + +2026-04-29 本轮持续巡检已补齐迁移后漂移: + +1. `Profile` 路由矩阵从旧 `/api/runtime/profile/*` 主链修正为当前 `/api/profile/*` 主链,并明确 `/api/runtime/profile/*` 已作为旧兼容入口取消挂载。 +2. `Big Fish Agent/Works` 路由矩阵补齐后端真相源运行态接口:`POST /api/runtime/big-fish/sessions/{session_id}/runs`、`GET /api/runtime/big-fish/runs/{run_id}`、`POST /api/runtime/big-fish/runs/{run_id}/input`。 +3. `Story/Game facade` 补齐 `GET /api/story/sessions/{story_session_id}/runtime-projection`,并将 story runtime projection、story battle command/result DTO 标记为已进入 `shared-contracts/src/story.rs`。 +4. DTO 保留清单补齐 `shared-contracts/src/creation_agent_document_input.rs` 与 `packages/shared/src/contracts/creationAgentDocumentInput.ts`。 +5. `packages/shared/src/index.ts` 已导出 `creationAgentDocumentInput` 类型,避免前端继续绕过共享契约包入口深路径引用。 + ## 5. 后续入口 下一步可以按全局清单进入第 1 批领域规则并行任务: diff --git a/docs/technical/SERVER_RS_DDD_PARALLEL_TASKLIST_2026-04-29.md b/docs/technical/SERVER_RS_DDD_PARALLEL_TASKLIST_2026-04-29.md index 4ec33141..9dbe4742 100644 --- a/docs/technical/SERVER_RS_DDD_PARALLEL_TASKLIST_2026-04-29.md +++ b/docs/technical/SERVER_RS_DDD_PARALLEL_TASKLIST_2026-04-29.md @@ -152,29 +152,56 @@ LLM、OSS、SMS、微信等外部副作用可以独立准备,不等待 `WP-SC` ## 4. 工作包总表 -| 工作包 | 可并行条件 | 主要文件边界 | 禁止触碰 | 交付物 | 验收 | -| --- | --- | --- | --- | --- | --- | -| G0 文档、边界、冻结窗口 | 首个串行 | `PLAN.md`、`docs/technical/*DDD*`、`docs/planning/*` | 业务代码 | 全局任务清单、专项清单索引、阶段性交接模板 | 编码检查通过 | -| G1 契约与路由矩阵 | G0 后 | `shared-contracts`、`packages/shared/src/contracts/*`、API 路由索引 | 领域实现 | DTO 分组、breaking change 清单、前后端路由矩阵 | shared contract 测试通过 | -| G2 DDD 骨架与边界检查 | G0 后 | `module-*` 骨架、`scripts/check-server-rs-ddd-boundaries.mjs` | 业务重写 | 所有 `module-*` 具备 `domain/commands/application/events/errors`,检查脚本覆盖禁用依赖 | `npm.cmd run check:server-rs-ddd` | -| WP-A Auth | G1 后 | `module-auth`、`spacetime-module/src/auth*`、`api-server/src/auth*`、`platform-auth` | 其他玩法域 | 账号、会话、验证码、微信绑定领域化;真实短信/微信在 platform | `cargo test -p module-auth`,auth API 测试 | -| WP-AS Assets | G1 后 | `module-assets`、`spacetime-module/src/asset_metadata/*`、资产 API、OSS adapter | 玩法业务规则 | 资产对象与绑定规则纯化;OSS head/upload 移出领域核心 | `cargo test -p module-assets`,资产 facade 测试 | -| WP-AI AI Task | G1 后 | `module-ai`、`spacetime-module/src/ai/*`、AI task API | LLM prompt 业务规则 | AI task/stage/chunk/result 状态机领域化 | `cargo test -p module-ai`,AI task reducer/procedure smoke | -| WP-CW Custom World | G1 后 | `module-custom-world`、`spacetime-module/src/custom_world/*`、`api-server` custom world 路由、前端创作 client | Big Fish/Puzzle | profile、agent session、draft card、gallery、publish gate 领域化;LLM 留在 API/platform | `cargo test -p module-custom-world`,custom world 定向测试 | -| WP-BF Big Fish | G1 后 | `module-big-fish`、`spacetime-module/src/big_fish/*`、Big Fish API、Big Fish 前端 client | Puzzle/RPG | 会话、草稿、素材槽、运行态纯规则;草稿校验下沉 | `cargo test -p module-big-fish`,Big Fish API 测试 | -| WP-PZ Puzzle | G1 后 | `module-puzzle`、`spacetime-module/src/puzzle*`、Puzzle API、Puzzle 前端 client | Big Fish/RPG | Agent session、work profile、runtime run、排行榜规则领域化 | `cargo test -p module-puzzle`,Puzzle 定向测试 | -| WP-RT Runtime/Profile/Save | G1 后 | `module-runtime`、`spacetime-module/src/runtime/*`、runtime/save/profile API | RPG story 规则 | runtime setting、snapshot、wallet、played world、save archive 领域化 | `cargo test -p module-runtime`,runtime API 测试 | -| WP-RPG Gameplay 域 | G1 后 | `module-combat`、`module-inventory`、`module-npc`、`module-progression`、`module-quest`、`module-runtime-item`、`module-story` | 创作域 | 战斗、背包、NPC、成长、任务、宝箱、story session 纯规则与跨域事件 | 各 module 测试;跨域应用结果测试 | -| WP-RS Runtime Story 去兼容层 | G1 后 | `module-runtime-story`、`api-server/src/runtime_story/*`、`src/hooks/rpg-runtime-story/*` | 非 RPG 创作域 | 先将历史 `module-runtime-story-compat` 迁为新主链 crate,再删除 HTTP compat 层、接 session scoped 新接口、前端匹配新接口 | `cargo test -p module-runtime-story`,runtime story/API/前端定向测试 | -| WP-ST SpacetimeDB Adapter | 领域任务输出稳定后 | `spacetime-module/src/**`、`migration.rs`、表目录 | `api-server` 业务逻辑 | table/reducer/procedure/mapper/queries 按上下文接入领域函数;必要 event/projection table;`lib.rs/migration.rs/表目录` 单 owner 合流;已完成 AI task event、Big Fish readiness event、Asset row mapper、Puzzle publish event | `cargo check -p spacetime-module`,需要时 `spacetime build/generate` | -| WP-SC Spacetime Client | 对应 WP-ST facade 稳定后 | `spacetime-client/src/**`、绑定 mapper | 领域规则、未稳定 facade 的预判接线 | typed facade、错误映射、row snapshot mapper | `cargo check -p spacetime-client` | -| WP-PF platform side effects | G1 后可独立准备;接入 API 前与 WP-API 对齐错误模型 | `platform-*`、`api-server` platform 接线 | 领域状态机 | LLM、OSS、SMS、微信等副作用统一 adapter | platform crate 测试或 API smoke | -| WP-API api-server BFF | WP-SC facade 和 WP-PF 接口稳定后 | `api-server/src/**`,其中 `app.rs` 单 owner | SpacetimeDB table 定义、领域主规则、绕过 spacetime-client 的直连实现 | 路由、鉴权、SSE、请求响应映射、平台编排收口 | `cargo test -p api-server`,`cargo check -p api-server` | -| WP-FE-S Frontend API client 迁移 | G1 和 WP-API 契约稳定后 | `src/services/**`、必要 contract type import | hooks / components 大改 | API client、路径常量、请求体与响应解析对齐新 contract | `npm.cmd run test -- src/services` | -| WP-FE-H Frontend hooks 迁移 | WP-FE-S 完成且后端接口可用后 | `src/hooks/**`、必要 hook 测试 | components 大面积 UI 改版 | hooks 改为调用新 client,只保留 loading/error/transition 和 UI 临时态 | `npm.cmd run test -- src/hooks` | -| WP-FE-C Frontend components 接线 | WP-FE-H 完成后 | `src/components/**`、组件测试 | services / hooks contract 改动 | 组件接入新 hooks 和 DTO;不新增规则说明文案 | 相关组件 vitest / 交互测试 | -| WP-DEL 删除旧层与命名收口 | 新接口与前端迁移后 | 旧 compat、旧 facade、旧 contract、旧测试 | 新主链 | 物理删除旧入口、旧命名、旧 fixture 中非必要样本 | 搜索无运行代码引用旧层 | -| WP-V 全链验证与发布 smoke | 最后 | 文档、测试脚本、README | 新功能扩展 | 全链命令、Maincloud smoke、文档交接 | 第 8 节命令通过或记录非本轮阻塞 | +| 工作包 | 完成情况 | 认领状态 | 可并行条件 | 主要文件边界 | 禁止触碰 | 交付物 | 验收 | +| --- | --- | --- | --- | --- | --- | --- | --- | +| G0 文档、边界、冻结窗口 | 已完成;全局方案、边界和并行清单已落地,后续仅随合流维护 | 未认领 | 首个串行 | `PLAN.md`、`docs/technical/*DDD*`、`docs/planning/*` | 业务代码 | 全局任务清单、专项清单索引、阶段性交接模板 | 编码检查通过 | +| G1 契约与路由矩阵 | 已完成;本轮共享契约与路由矩阵持续巡检已关闭,已同步 profile、Big Fish run、story projection/battle 和文档输入契约索引漂移 | 未认领 | G0 后 | `shared-contracts`、`packages/shared/src/contracts/*`、API 路由索引 | 领域实现 | DTO 分组、breaking change 清单、前后端路由矩阵 | shared contract 测试通过 | +| G2 DDD 骨架与边界检查 | 已完成并持续执行;本轮 G2 骨架巡检已关闭,15 个 module crate 均通过边界门禁,后续每批继续跑门禁 | 未认领 | G0 后 | `module-*` 骨架、`scripts/check-server-rs-ddd-boundaries.mjs` | 业务重写 | 所有 `module-*` 具备 `domain/commands/application/events/errors`,检查脚本覆盖禁用依赖 | `npm.cmd run check:server-rs-ddd` | +| WP-A Auth | 已完成;module-auth 已完成 domain/commands/application/errors/events 分层归位,账号、会话、验证码、微信 state/绑定规则由 module-auth 承接,api-server/platform-auth/spacetime-module 边界已核查 | 未认领 | G1 后 | `module-auth`、`spacetime-module/src/auth*`、`api-server/src/auth*`、`platform-auth` | 其他玩法域 | 账号、会话、验证码、微信绑定领域化;真实短信/微信在 platform | `cargo test -p module-auth`,auth API 测试 | +| WP-AS Assets | 进行中;资产对象类型归位、资产领域测试和 SpacetimeDB row mapper 切片已完成,资产 API/OSS/facade 尚未全链收口 | 未认领 | G1 后 | `module-assets`、`spacetime-module/src/asset_metadata/*`、资产 API、OSS adapter | 玩法业务规则 | 资产对象与绑定规则纯化;OSS head/upload 移出领域核心 | `cargo test -p module-assets`,资产 facade 测试 | +| WP-AI AI Task | 已完成;领域层、SpacetimeDB AI adapter/event、spacetime-client facade、BFF 路由和定向测试已闭环,`module-ai` 内部子模块拆分已完成,真实 LLM/SSE/前端消费归后续 WP-PF/WP-API/WP-FE 承接 | 未认领 | G1 后 | `module-ai`、`spacetime-module/src/ai/*`、AI task API | LLM prompt 业务规则 | AI task/stage/chunk/result 状态机领域化 | `cargo test -p module-ai`,AI task reducer/procedure smoke | +| WP-CW Custom World | 进行中;基础领域枚举归位切片已关闭,profile/agent/draft/gallery/publish gate 全链仍待收口 | 未认领 | G1 后 | `module-custom-world`、`spacetime-module/src/custom_world/*`、`api-server` custom world 路由、前端创作 client | Big Fish/Puzzle | profile、agent session、draft card、gallery、publish gate 领域化;LLM 留在 API/platform | `cargo test -p module-custom-world`,custom world 定向测试 | +| WP-BF Big Fish | 已完成;运行态真相源已迁入 `module-big-fish`,并完成 SpacetimeDB run 表、spacetime-client facade、API 路由、前端 client 接入和本地运行态删除 | 未认领 | G1 后 | `module-big-fish`、`spacetime-module/src/big_fish/*`、Big Fish API、Big Fish 前端 client | Puzzle/RPG | 会话、草稿、素材槽、运行态纯规则;草稿校验下沉 | `cargo test -p module-big-fish`,Big Fish API 测试 | +| WP-PZ Puzzle | 进行中;领域类型与规则拆分切片已关闭,Puzzle API/前端消费仍未全链完成 | 未认领 | G1 后 | `module-puzzle`、`spacetime-module/src/puzzle*`、Puzzle API、Puzzle 前端 client | Big Fish/RPG | Agent session、work profile、runtime run、排行榜规则领域化 | `cargo test -p module-puzzle`,Puzzle 定向测试 | +| WP-RT Runtime/Profile/Save | 已完成;runtime settings、snapshot/profile/save archive 类型、错误层、命令构造、应用记录投影、Adapter/API/Frontend profile 路径和剩余纯规则均已收口 | 未认领 | G1 后 | `module-runtime`、`spacetime-module/src/runtime/*`、runtime/save/profile API | RPG story 规则 | runtime setting、snapshot、wallet、played world、save archive 领域化 | `cargo test -p module-runtime`,runtime API 测试 | +| WP-RPG Gameplay 域 | 进行中;combat 基础领域常量与枚举归位、`module-story` DDD 物理拆分和 README 漂移收口切片已关闭,inventory/npc/progression/quest/runtime-item 与跨域事件仍待收口 | 未认领 | G1 后 | `module-combat`、`module-inventory`、`module-npc`、`module-progression`、`module-quest`、`module-runtime-item`、`module-story` | 创作域 | 战斗、背包、NPC、成长、任务、宝箱、story session 纯规则与跨域事件 | 各 module 测试;跨域应用结果测试 | +| WP-RS Runtime Story 去兼容层 | 进行中;compat 残留审计切片已关闭,module-runtime-story 运行代码注释口径已清理,旧前端写 client 与旧 contract 残留已冻结到后续 WP-FE-S/WP-DEL | 未认领 | G1 后 | `module-runtime-story`、`api-server/src/runtime_story/*`、`src/hooks/rpg-runtime-story/*` | 非 RPG 创作域 | 先将历史 `module-runtime-story-compat` 迁为新主链 crate,再删除 HTTP compat 层、接 session scoped 新接口、前端匹配新接口 | `cargo test -p module-runtime-story`,runtime story/API/前端定向测试 | +| WP-ST SpacetimeDB Adapter | 进行中;已完成 AI task event、Big Fish readiness event、Asset row mapper、Puzzle publish event、Gameplay/Custom World 根入口瘦身和 Auth adapter 目录化切片,其他上下文和绑定生成仍待推进 | 未认领 | 领域任务输出稳定后 | `spacetime-module/src/**`、`migration.rs`、表目录 | `api-server` 业务逻辑 | table/reducer/procedure/mapper/queries 按上下文接入领域函数;必要 event/projection table;`lib.rs/migration.rs/表目录` 单 owner 合流;已完成 AI task event、Big Fish readiness event、Asset row mapper、Puzzle publish event、Gameplay 根入口瘦身、Custom World 根入口瘦身、Auth adapter 目录化 | `cargo check -p spacetime-module`,需要时 `spacetime build/generate` | +| WP-SC Spacetime Client | 进行中;story runtime projection inventory source 接线切片已关闭,读取投影已纳入稳定 runtime inventory facade;后续仅随 WP-ST 新 facade 稳定后继续 typed facade/row mapper | 未认领 | 对应 WP-ST facade 稳定后 | `spacetime-client/src/**`、绑定 mapper | 领域规则、未稳定 facade 的预判接线 | typed facade、错误映射、row snapshot mapper | `cargo check -p spacetime-client` | +| WP-PF platform side effects | 已完成;LLM/OSS/SMS/微信平台副作用错误分类与 API 接线错误模型已统一,微信 OAuth provider 已下沉到 `platform-auth` | 未认领 | G1 后可独立准备;接入 API 前与 WP-API 对齐错误模型 | `platform-*`、`api-server` platform 接线 | 领域状态机 | LLM、OSS、SMS、微信等副作用统一 adapter | platform crate 测试或 API smoke | +| WP-API api-server BFF | 进行中;story/game facade 当前阶段收口切片已关闭,runtime projection、story battle DTO 和 owner guard 已推进,更多 session scoped 写接口依赖 WP-ST/WP-SC | 未认领 | WP-SC facade 和 WP-PF 接口稳定后 | `api-server/src/**`,其中 `app.rs` 单 owner | SpacetimeDB table 定义、领域主规则、绕过 spacetime-client 的直连实现 | 路由、鉴权、SSE、请求响应映射、平台编排收口 | `cargo test -p api-server`,`cargo check -p api-server` | +| WP-FE-S Frontend API client 迁移 | 进行中;story session API client 收口切片已关闭,已补齐 begin/continue/state/projection 新主链请求封装与定向测试;开局快照和完整动作结算仍等待后端新写接口 | 未认领 | G1 和 WP-API 契约稳定后 | `src/services/**`、必要 contract type import | hooks / components 大改 | API client、路径常量、请求体与响应解析对齐新 contract | `npm.cmd run test -- src/services` | +| WP-FE-H Frontend hooks 迁移 | 进行中;RPG runtime story 读取侧 hooks 接线切片已关闭,完整开局/动作写接口 hooks 迁移与组件接线仍等待后端新接口 | 未认领 | WP-FE-S 完成且后端接口可用后 | `src/hooks/**`、必要 hook 测试 | components 大面积 UI 改版 | hooks 改为调用新 client,只保留 loading/error/transition 和 UI 临时态 | `npm.cmd run test -- src/hooks` | +| WP-FE-C Frontend components 接线 | 进行中;RPG runtime shell 组件测试夹具接线切片已关闭,组件测试 mock 已对齐当前 hooks UI 出口;完整组件迁移仍等待后端写接口和 hooks 写侧稳定 | 未认领 | WP-FE-H 完成后 | `src/components/**`、组件测试 | services / hooks contract 改动 | 组件接入新 hooks 和 DTO;不新增规则说明文案 | 相关组件 vitest / 交互测试 | +| WP-DEL 删除旧层与命名收口 | 暂不可执行;需等待后端新接口、前端迁移和旧引用替换完成 | 未认领 | 新接口与前端迁移后 | 旧 compat、旧 facade、旧 contract、旧测试 | 新主链 | 物理删除旧入口、旧命名、旧 fixture 中非必要样本 | 搜索无运行代码引用旧层 | +| WP-V 全链验证与发布 smoke | 暂不可执行;必须在 WP-DEL 后串行执行,当前仍处第 1/2/3 批交错推进 | 未认领 | 最后 | 文档、测试脚本、README | 新功能扩展 | 全链命令、Maincloud smoke、文档交接 | 第 8 节命令通过或记录非本轮阻塞 | + +## 4.1 未完整收口内容整合清单 + +本节合并“半截内容”巡检、全 crate 巡检和关键词 `过渡落位` 的搜索结果,用于后续继续认领工作包。这里的“未完整收口”分三类: + +1. 功能链路仍缺真实实现或新接口,必须继续编码。 +2. 领域 crate 已有首版真实逻辑,但 DDD 物理拆分、README 或边界说明仍停在迁移期口径。 +3. 已完成工作包仍有注释、命名或文档收尾,不直接回退完成状态,但必须纳入 `WP-DEL/WP-V` 前的清理门禁。 + +| 归属工作包 | 未完整收口项 | 当前证据 | 收口边界 | 依赖与优先级 | +| --- | --- | --- | --- | --- | +| `WP-RPG Gameplay 域` | 成长系统归属 RPG 域;`module-progression` 是真实首版,不是空占位,但仍未覆盖完整 RPG 成长闭环 | `server-rs/crates/module-progression/README.md` 已说明首版落地,同时列出 custom-world 章节蓝图、`repeatPenalty`、超预算衰减、完整章节偏差审计表仍未迁移;`src/domain.rs`、`commands.rs`、`application.rs`、`events.rs`、`errors.rs` 仍命中 `过渡落位` | 保持在 `WP-RPG` 内推进,不拆成通用 Runtime 能力;补齐章节蓝图输入、章节预算审计、quest/combat/npc 联动事件和应用结果测试 | P0;依赖 `module-custom-world` 章节蓝图 Rust 化边界稳定,随后接 `WP-ST/WP-SC/WP-API` | +| `WP-RPG Gameplay 域` | `module-story` README 与 DDD 物理拆分漂移已关闭;后续只剩完整 story action 写侧与跨域事件继续排队 | `server-rs/crates/module-story/README.md` 已改为当前真实边界;`src/domain.rs`、`commands.rs`、`application.rs`、`events.rs`、`errors.rs` 已承载真实类型、命令、应用映射、事件和错误;`cargo test -p module-story` 通过 | 保持 `module-story` 作为 story session 纯领域薄层;不恢复旧 `/api/runtime/story/*` compat route;完整动作结算继续跟随 `WP-RS/WP-ST/WP-SC/WP-API/WP-FE` | 已关闭本项漂移;剩余写侧依赖仍在 P0 主链 | +| `WP-RPG Gameplay 域` | `module-combat`、`module-inventory`、`module-npc`、`module-quest`、`module-runtime-item` 仍是首批领域化,跨域事件和完整应用用例未收口 | 上述 crate 的 `domain/commands/application/events/errors` 多数仍命中 `过渡落位`;README 中仍有“最小主链”“刻意未做”“后续衔接”等范围说明 | 以 RPG 子域为单位补聚合、命令、事件和应用结果测试;跨域副作用只输出领域事件,不在单个 crate 内互相直连 | P0;可按 combat、inventory、npc、quest、runtime-item 并行,但共享事件命名需先在 `WP-RPG` 冻结 | +| `WP-RS Runtime Story 去兼容层` | `module-runtime-story` 已从 compat 口径转新主链,但仍有 DDD 文件命中 `过渡落位`,旧写接口未完整替代 | `module-runtime-story/src/domain.rs`、`commands.rs`、`events.rs`、`errors.rs` 仍命中 `过渡落位`;文档记录旧前端写 client 与 `RuntimeStoryActionResponse` 等残留等待 `WP-FE-S/WP-DEL` | 补齐 session scoped 开局、动作结算、inventory action、NPC interaction、forge/battle/quest 写接口;前端只接新 `/api/story/*` 与新 contract | P0;依赖 `WP-ST/WP-SC` facade,完成后解锁 `WP-FE-S/WP-FE-H/WP-FE-C/WP-DEL` | +| `WP-CW Custom World` | Custom World agent 仍有多个动作接到最小兼容占位,未形成真实编排链 | `spacetime-module/src/custom_world/mod.rs` 中 `generate_characters`、`generate_landmarks`、`generate_role_assets`、`sync_role_assets`、`generate_scene_assets`、`sync_scene_assets`、`expand_long_tail` 仍调用 `execute_placeholder_custom_world_action`,并返回“最小兼容占位”消息 | 将动作拆入 `module-custom-world` 领域命令和应用结果,再由 `WP-ST` 接 reducer/procedure;LLM、资产生成和 OSS 只通过 `WP-API/WP-PF/WP-AS` 编排 | P0;依赖 `WP-AS` 资产对象链和 `WP-PF` LLM/OSS 能力 | +| `WP-CW Custom World` | `module-custom-world` 只完成首批类型契约和字段校验,profile、agent session、draft/gallery/publish gate 未全链收口 | README 写明“当前阶段明确不提前进入”;`src/domain.rs`、`commands.rs`、`application.rs`、`events.rs`、`errors.rs` 仍命中 `过渡落位` | 继续把 profile、agent session、草稿卡、画廊投影、发布门禁迁成纯领域规则;SpacetimeDB 表和 procedure 只在 `WP-ST` 落地 | P0;可与 Custom World action 真实编排并行,但共享 domain enum 只能单 owner 合流 | +| `WP-PZ Puzzle` | Puzzle 已完成领域类型和规则拆分切片,但 API、前端消费和运行态全链仍未完成 | 第 4 节仍标记 `WP-PZ` 进行中;`module-puzzle/README.md` 写明当前只固定 contract 与最小规则;`domain.rs`、`events.rs` 仍命中 `过渡落位` | 补齐 agent session、work profile、runtime run、排行榜、图片选择和结果页草稿的领域应用服务,再接 `WP-ST/WP-SC/WP-API/WP-FE` | P1;与 `WP-CW` 并行时避免共改创作 agent 通用文档 | +| `WP-AS Assets` | 资产对象类型、领域测试和 row mapper 已完成,资产 API、OSS adapter、facade 和 event table 尚未全链收口 | 第 4 节仍标记 `WP-AS` 进行中;`module-assets/README.md` 写明尚未进入完整资产状态建模;`module-assets/src/events.rs` 仍命中 `过渡落位` | 将 asset object、binding、manifest、history、OSS head/upload 和确认状态统一成领域应用结果,SpacetimeDB 与 API 只做 adapter | P1;依赖 `WP-PF` OSS 错误模型,反向支撑 `WP-CW/WP-PZ/WP-BF` | +| `WP-API api-server BFF` | LLM 流式代理、微信登录、手机号登录、角色动画资产仍有首版禁用或占位链 | `api-server/src/llm.rs` 流式请求返回 `501`;`wechat_auth.rs` 返回“微信登录暂未启用”;`phone_auth.rs`、`password_management.rs` 返回“手机号登录暂未启用”;`character_animation_assets.rs` 仍写 Stage 1 序列帧和视频占位链 | API 层只做 BFF 编排、鉴权、SSE 和 DTO 映射;真实副作用落 `platform-llm/platform-auth/platform-oss`,领域状态变化必须回写 SpacetimeDB facade | P1;依赖 `WP-PF` 能力与 `WP-ST/WP-SC` 写入 facade | +| `WP-FE-S/WP-FE-H/WP-FE-C` | 前端读取侧已推进,完整开局和动作写侧仍等后端新接口;不能继续补旧 compat client | 文档记录 `beginRuntimeStorySession`、`resolveRuntimeStoryAction` 暂未迁移;旧 `src/services/rpg-runtime/rpgRuntimeStoryClient.ts` 写侧和测试仍等待新接口 | `WP-FE-S` 先换 API client,`WP-FE-H` 再换 hooks,`WP-FE-C` 最后接组件;前端只做表现和临时 UI 状态,不重建规则 | P0;强依赖 `WP-RS/WP-ST/WP-SC/WP-API` | +| `WP-ST SpacetimeDB Adapter` | 多个上下文已根入口瘦身,但剩余 table/reducer/procedure、migration、表目录和绑定生成仍未整体闭环 | 第 4 节仍标记 `WP-ST` 进行中;Custom World 真实动作、RPG 写接口、Puzzle/Assets 完整链都需要继续接 `spacetime-module` | 所有 SpacetimeDB schema 变更由 `WP-ST` 单 owner 合流,并同步 `migration.rs`、表目录和生成绑定;禁止 API 直连生成绑定绕过 `spacetime-client` | P0;是 `WP-SC/WP-API/WP-FE/DEL` 的主依赖 | +| `WP-SC Spacetime Client` | 已接 runtime projection inventory source,但后续 typed facade、row mapper 和错误映射仍随 `WP-ST` 滚动补齐 | 第 4 节仍标记 `WP-SC` 进行中;文档记录“后续仅随 WP-ST 新 facade 稳定后继续 typed facade/row mapper” | 只在 reducer/procedure shape 稳定后补 typed facade;不在 client crate 内发明领域规则或预判 row shape | P0;跟随 `WP-ST` 分批执行 | +| `WP-DEL 删除旧层与命名收口` | 旧 compat、旧 contract、旧 facade、旧测试仍不能删除 | 第 4 节标记暂不可执行;`RuntimeStoryActionResponse`、旧 runtime story contract、旧前端写 client 等仍等待新写接口和前端迁移完成 | 所有新接口和前端迁移完成后再统一物理删除;删除前必须搜索运行代码无旧层引用 | P0;依赖 `WP-FE-S/WP-FE-H/WP-FE-C` 完成 | +| `WP-V 全链验证与发布 smoke` | 全链验证仍被旧层删除和 Maincloud smoke 阻塞 | 第 4 节标记暂不可执行;此前 `api-server:maincloud` 多次以常驻服务超时或端口占用形式结束,需要在最终阶段统一验证 | 串行执行 cargo、npm、encoding、DDD boundary、SpacetimeDB build/generate 和 Maincloud smoke;只记录非本轮阻塞,不新增功能 | P2;必须在 `WP-DEL` 后 | +| `G2/跨包清理` | `过渡落位` 注释横跨多个已完成或进行中 crate,说明 DDD 物理拆分仍有收尾清理 | 搜索命中 `module-big-fish` 5 处、`module-runtime` 4 处、`module-runtime-story` 4 处,以及 RPG、Custom World、Puzzle、Assets 多处 | 对进行中包,只有真实规则迁完后才能改注释;对已完成包如 `WP-BF/WP-RT`,若只剩注释口径,归入 `WP-DEL/WP-V` 前清理,不重开主功能包 | P2;不阻塞 P0 功能链,但阻塞最终“无迁移期口径”验收 | +| `tests-support` | 仍只是目录占位,不是 workspace crate | `server-rs/crates/tests-support/README.md` 写明“当前提交仅完成目录占位”;目录下无 `Cargo.toml` 和 Rust 源文件 | 到 `WP-V` 前若需要共享测试夹具,则补真实 crate;如果最终无使用场景,改成明确文档占位或删除 | P2;依赖最终测试策略 | ## 5. 工作包边界细则 @@ -401,6 +428,594 @@ spacetime describe --json ## 10. 本地进度记录 +### 2026-04-29 WP-FE-C RPG runtime shell 组件测试夹具接线切片 + +已完成: + +1. 认领 `WP-FE-C Frontend components 接线` 中可并行的 RPG runtime shell 组件测试夹具接线切片,完成后认领状态已更新为 `已关闭`。 +2. 新增 `SERVER_RS_DDD_WP_FE_C_RPG_RUNTIME_SHELL_TEST_FIXTURE_2026-04-29.md`,冻结本次组件测试夹具接线边界。 +3. `RpgRuntimeShell.test.tsx` 的 `StoryMoment` mock 已从旧 `storyText` 对齐到当前 `text/options`。 +4. `Character` mock 已移除旧 `motivation/combatStyle/role/imageSrc/initialItems` 等不属于当前 `Character` 类型的字段,并补齐当前角色类型所需的资产与属性字段。 +5. `npcUi/characterChatUi/inventoryUi/battleRewardUi/questUi/npcChatQuestOfferUi/goalUi` mock 已对齐当前 hooks 暴露的 UI 对象形状,不再使用旧布尔开关式组件夹具。 +6. 本次不改正式组件行为,只消除组件测试对旧 hooks/UI contract 的依赖。 + +边界说明: + +1. 本次未修改 `src/services/**`、`src/hooks/**`、后端接口或共享契约。 +2. 本次未迁移 `beginRuntimeStorySession`、`resolveRuntimeStoryAction` 等尚未稳定的新写接口。 +3. 本次不新增 UI 文案、不改组件视觉布局、不删除旧 runtime story contract 或旧 client alias;这些仍等待后续 `WP-FE-S/WP-FE-H/WP-DEL` 顺序收口。 + +验证: + +```powershell +npm.cmd run test -- src/components/rpg-runtime-shell/RpgRuntimeShell.test.tsx +npm.cmd run check:encoding -- docs/technical/SERVER_RS_DDD_WP_FE_C_RPG_RUNTIME_SHELL_TEST_FIXTURE_2026-04-29.md docs/technical/SERVER_RS_DDD_PARALLEL_TASKLIST_2026-04-29.md docs/technical/README.md src/components/rpg-runtime-shell/RpgRuntimeShell.test.tsx +git diff --check -- docs/technical/SERVER_RS_DDD_WP_FE_C_RPG_RUNTIME_SHELL_TEST_FIXTURE_2026-04-29.md docs/technical/SERVER_RS_DDD_PARALLEL_TASKLIST_2026-04-29.md docs/technical/README.md src/components/rpg-runtime-shell/RpgRuntimeShell.test.tsx +``` + +结果:通过。`RpgRuntimeShell.test.tsx` 2 个测试通过;编码检查通过 4 个文件;空白检查通过。 + +补充验证: + +```powershell +npm.cmd run typecheck -- --pretty false +``` + +结果:未全量通过,但已不再报 `src/components/rpg-runtime-shell/RpgRuntimeShell.test.tsx`。剩余阻塞来自既有非本切片文件:`src/data/sceneEncounterPreviews.ts` 的 `slot` 可能为 `undefined`,以及 `src/services/ai.ts` 缺失 `hasMixedNarrativeLanguage`。 + +### 2026-04-29 G1 契约与路由矩阵持续巡检切片 + +已完成: + +1. 认领 `G1 契约与路由矩阵` 中可并行的共享契约与路由矩阵持续巡检切片,完成后认领状态已更新为 `已关闭`。 +2. 修正 G1 冻结矩阵中 profile 主链方向:当前保留 `/api/profile/*`,旧 `/api/runtime/profile/*` 兼容入口已取消挂载。 +3. 补齐 Big Fish 后端真相源运行态路由:`POST /api/runtime/big-fish/sessions/{session_id}/runs`、`GET /api/runtime/big-fish/runs/{run_id}`、`POST /api/runtime/big-fish/runs/{run_id}/input`。 +4. 补齐 Story/Game facade 的 `GET /api/story/sessions/{story_session_id}/runtime-projection`,并将 story runtime projection 与 story battle command/result DTO 标记为已落入 `shared-contracts/src/story.rs`。 +5. 补齐 `creationAgentDocumentInput` 前后端共享契约清单,并在 `packages/shared/src/index.ts` 导出 `creationAgentDocumentInput` 类型,避免前端继续绕过共享契约包入口深路径引用。 +6. 更新 `SERVER_RS_DDD_G1_CONTRACT_AND_ROUTE_MATRIX_PROGRESS_2026-04-29.md`,记录本轮持续巡检漂移与修正。 + +边界说明: + +1. 本次只修正 G1 契约文档、进度文档和前端共享契约索引,不改业务实现。 +2. 本次不改 `api-server` 路由实现、不改 `shared-contracts` Rust DTO shape、不改前端 service/hook/component 行为。 +3. 本次不删除旧 runtime story DTO 或旧 profile 命名;这些仍等待 `WP-DEL` 串行收口。 + +验证: + +```powershell +cargo test -p shared-contracts --manifest-path server-rs/Cargo.toml +npm.cmd run test -- packages/shared/src/contracts/rpgContracts.test.ts packages/shared/src/contracts/rpgRuntimeContracts.test.ts +npm.cmd run check:encoding -- docs/technical/SERVER_RS_DDD_G1_CONTRACT_AND_ROUTE_MATRIX_2026-04-29.md docs/technical/SERVER_RS_DDD_G1_CONTRACT_AND_ROUTE_MATRIX_PROGRESS_2026-04-29.md docs/technical/SERVER_RS_DDD_PARALLEL_TASKLIST_2026-04-29.md packages/shared/src/index.ts +git diff --check -- docs/technical/SERVER_RS_DDD_G1_CONTRACT_AND_ROUTE_MATRIX_2026-04-29.md docs/technical/SERVER_RS_DDD_G1_CONTRACT_AND_ROUTE_MATRIX_PROGRESS_2026-04-29.md docs/technical/SERVER_RS_DDD_PARALLEL_TASKLIST_2026-04-29.md packages/shared/src/index.ts +``` + +结果:通过。`shared-contracts` 共 45 个测试通过;前端共享契约 2 个测试文件、11 个测试通过;编码检查通过 4 个文件。 + +### 2026-04-29 G2 DDD 骨架与边界检查本轮巡检 + +已完成: + +1. 认领 `G2 DDD 骨架与边界检查` 中可并行的持续门禁巡检切片,完成后认领状态已更新为 `已关闭`。 +2. 复核当前 15 个 `module-*` crate 均具备 `src/domain.rs`、`src/commands.rs`、`src/application.rs`、`src/events.rs`、`src/errors.rs`。 +3. 复核 `scripts/check-server-rs-ddd-boundaries.mjs` 已覆盖 DDD 落位文件、`lib.rs` 模块声明、`mapper.rs` 禁放、Axum/SpacetimeDB table/reducer/procedure 直依赖禁用,以及核心文件对 reqwest、platform adapter、spacetime-client、文件系统和 tokio 的禁用。 +4. 本次只做 G2 门禁复核与文档状态收口,不修改业务代码、不改 SpacetimeDB 表结构、不触碰 `migration.rs`。 + +验证: + +```powershell +npm.cmd run check:server-rs-ddd +npm.cmd run check:encoding -- docs/technical/SERVER_RS_DDD_PARALLEL_TASKLIST_2026-04-29.md scripts/check-server-rs-ddd-boundaries.mjs +``` + +结果:通过。`server-rs DDD boundary check passed for 15 module crate(s).`,编码检查通过 2 个文件。 + +### 2026-04-29 WP-RT Adapter/API/Frontend profile 路径收口切片 + +已完成: + +1. 继续认领 `WP-RT Runtime/Profile/Save` 剩余 Adapter/API 收口内容,完成后认领状态已更新为 `已关闭`。 +2. 新增 `SERVER_RS_DDD_WP_RT_ADAPTER_API_CLOSURE_2026-04-29.md`,冻结 checkpoint、profile/save archive meta、充值/邀请/兑换/钱包纯规则下沉和 profile 旧路径删除边界。 +3. 将 runtime save checkpoint 的 `sessionId` 校验、非持久运行态拒绝、服务端快照 `runtimeStats` 时间水位刷新迁入 `module-runtime`。 +4. 将 save archive/world meta 的 JSON 解析、内置世界标题、非负数读取和字符串字段读取迁入 `module-runtime`。 +5. 将 played world/save archive/recharge/redeem/ledger 等 ID 生成、首充奖励、会员续期、邀请码、邀请每日奖励窗口、兑换码模式校验和钱包余额计算迁入 `module-runtime`。 +6. `spacetime-module/src/runtime/profile.rs` 改为调用 `module-runtime` 纯规则,继续只保留 SpacetimeDB 表读写、事务编排和 row mapper。 +7. `api-server/src/runtime_save.rs` 改为调用领域 checkpoint 输入和更新构造,并保留 runtime-save HTTP 错误 envelope 映射。 +8. 删除 `/api/runtime/profile/*` 旧兼容路由挂载,只保留 `/api/profile/*` 新主路径,并新增旧路径返回 `404` 的路由测试。 +9. 前端 `requestRpgRuntimeJson` 对 `/profile/*` 发送到 `/api/profile/*`,并更新 profile client/routing 测试断言。 +10. 更新 `module-runtime/README.md` 与 `docs/technical/README.md`,补齐本次 WP-RT 收口文档入口。 + +边界说明: + +1. 本次不改 SpacetimeDB 表结构、reducer/procedure 对外签名或 generated binding。 +2. 本次不修改 `migration.rs`。 +3. 本次不处理 RPG story / runtime story 规则;相关内容仍归 `WP-RPG` 与 `WP-RS`。 +4. 本次删除的是 HTTP profile 兼容挂载,不再保留 `/api/runtime/profile/*` fallback。 +5. 本次仅格式化 WP-RT 相关 Rust 文件;未整理其他并行包文件。 + +验证: + +```powershell +cargo fmt -p module-runtime --manifest-path server-rs/Cargo.toml --check +cargo fmt -p module-runtime -p spacetime-module -p api-server --manifest-path server-rs/Cargo.toml --check +cargo test -p module-runtime --manifest-path server-rs/Cargo.toml +cargo check -p spacetime-module --manifest-path server-rs/Cargo.toml +cargo test -p api-server runtime_profile --manifest-path server-rs/Cargo.toml +cargo test -p api-server runtime_browse_history --manifest-path server-rs/Cargo.toml +cargo test -p api-server runtime_snapshot --manifest-path server-rs/Cargo.toml +cargo test -p api-server profile_save_archives --manifest-path server-rs/Cargo.toml +npm.cmd run test -- src/services/rpg-entry/rpgProfileClient.test.ts src/services/rpg-entry/rpgEntryClients.routing.test.ts +npm.cmd run check:server-rs-ddd +npm.cmd run check:encoding -- docs/technical/SERVER_RS_DDD_WP_RT_ADAPTER_API_CLOSURE_2026-04-29.md server-rs/crates/module-runtime/src/domain.rs server-rs/crates/module-runtime/src/errors.rs server-rs/crates/module-runtime/src/commands.rs server-rs/crates/module-runtime/src/application.rs server-rs/crates/module-runtime/src/lib.rs server-rs/crates/module-runtime/README.md server-rs/crates/spacetime-module/src/runtime/profile.rs server-rs/crates/api-server/src/app.rs server-rs/crates/api-server/src/runtime_save.rs server-rs/crates/api-server/src/runtime_profile.rs server-rs/crates/api-server/src/runtime_browse_history.rs src/services/rpg-runtime/rpgRuntimeRequest.ts src/services/rpg-entry/rpgProfileClient.test.ts src/services/rpg-entry/rpgEntryClients.routing.test.ts docs/technical/README.md docs/technical/SERVER_RS_DDD_PARALLEL_TASKLIST_2026-04-29.md +git diff --check -- docs/technical/SERVER_RS_DDD_WP_RT_ADAPTER_API_CLOSURE_2026-04-29.md server-rs/crates/module-runtime/src/domain.rs server-rs/crates/module-runtime/src/errors.rs server-rs/crates/module-runtime/src/commands.rs server-rs/crates/module-runtime/src/application.rs server-rs/crates/module-runtime/src/lib.rs server-rs/crates/module-runtime/README.md server-rs/crates/spacetime-module/src/runtime/profile.rs server-rs/crates/api-server/src/app.rs server-rs/crates/api-server/src/runtime_save.rs server-rs/crates/api-server/src/runtime_profile.rs server-rs/crates/api-server/src/runtime_browse_history.rs src/services/rpg-runtime/rpgRuntimeRequest.ts src/services/rpg-entry/rpgProfileClient.test.ts src/services/rpg-entry/rpgEntryClients.routing.test.ts docs/technical/README.md docs/technical/SERVER_RS_DDD_PARALLEL_TASKLIST_2026-04-29.md +``` + +结果:通过。`cargo test -p module-runtime` 共 18 个测试通过;`runtime_profile` 9 个测试通过并覆盖旧 profile route 未挂载;`runtime_browse_history` 3 个测试通过;`runtime_snapshot` 5 个测试通过;`profile_save_archives` 1 个测试通过;前端 profile client/routing 共 14 个测试通过;DDD 边界检查通过 15 个 module crate;编码检查通过 17 个文件。 + +后端启动: + +```powershell +npm.cmd run api-server:maincloud +``` + +结果:命令在 180 秒观察窗口内超时,因为 `cargo run` 前台常驻;随后探测 `http://127.0.0.1:3100/healthz` 返回 `200`,本仓库 `server-rs/target/debug/api-server.exe` 已在线。 + +### 2026-04-29 WP-FE-H RPG runtime story 读取侧 hooks 接线切片 + +已完成: + +1. 认领 `WP-FE-H Frontend hooks 迁移` 中可并行的 RPG runtime story 读取侧 hooks 接线切片,完成后认领状态已更新为 `已关闭`。 +2. 新增 `SERVER_RS_DDD_WP_FE_H_RPG_RUNTIME_STORY_HOOKS_PROJECTION_2026-04-29.md`,冻结本次 hooks 读取侧边界。 +3. 将 `src/hooks/rpg-runtime-story/rpgRuntimeStoryGateway.ts` 的 option catalog 拉取和继续游戏刷新显式接入 `getRpgStoryRuntimeProjection`。 +4. `loadServerRuntimeOptionCatalog` 继续只读取服务端 projection option,不上传本地 `GameState` 参与动作合法性解析。 +5. `resumeServerRuntimeStory` 继续保留本地已水合快照主体,只同步服务端 `runtimeSessionId / storySessionId / runtimeActionVersion` 和投影故事。 +6. 更新 `runtimeStoryCoordinator.test.ts` 的 mock 命名和断言,确保 hooks 读取侧明确走 story runtime projection client。 +7. 补齐 `src/hooks/useGameFlow.customWorld.test.tsx` 的 `beginRpgRuntimeStorySession` 测试桩,让 hooks 全量测试继续以服务端开局快照为真相源,不在 Node 环境直接 fetch 相对路径。 +8. 更新 `docs/technical/README.md`,补齐本次 WP-FE-H 文档入口。 + +边界说明: + +1. 本次不迁移 `beginRuntimeStorySession`,因为新开局接口尚未返回可直接进入游戏的完整 `GameState` 快照。 +2. 本次不迁移 `resolveRuntimeStoryAction`,因为完整动作结算的新 session scoped 写接口尚未稳定。 +3. 本次不改 components,不新增 UI 文案,不在组件层拼 API 请求。 +4. 本次不改 `api-server`、`spacetime-client`、`spacetime-module` 或共享契约。 +5. 本次不删除旧 runtime story contract、旧 client alias 或兼容测试;这些仍等待 `WP-DEL` 串行收口。 + +验证: + +```powershell +npm.cmd run test -- src/hooks/rpg-runtime-story/runtimeStoryCoordinator.test.ts +npm.cmd run test -- src/hooks/useGameFlow.customWorld.test.tsx +npm.cmd run test -- src/hooks +npm.cmd run check:encoding -- docs/technical/SERVER_RS_DDD_WP_FE_H_RPG_RUNTIME_STORY_HOOKS_PROJECTION_2026-04-29.md src/hooks/rpg-runtime-story/rpgRuntimeStoryGateway.ts src/hooks/rpg-runtime-story/inventoryActions.ts src/hooks/rpg-runtime-story/runtimeStoryCoordinator.test.ts src/hooks/useGameFlow.customWorld.test.tsx docs/technical/README.md docs/technical/SERVER_RS_DDD_PARALLEL_TASKLIST_2026-04-29.md +git diff --check -- docs/technical/SERVER_RS_DDD_WP_FE_H_RPG_RUNTIME_STORY_HOOKS_PROJECTION_2026-04-29.md src/hooks/rpg-runtime-story/rpgRuntimeStoryGateway.ts src/hooks/rpg-runtime-story/inventoryActions.ts src/hooks/rpg-runtime-story/runtimeStoryCoordinator.test.ts src/hooks/useGameFlow.customWorld.test.tsx docs/technical/README.md docs/technical/SERVER_RS_DDD_PARALLEL_TASKLIST_2026-04-29.md +``` + +结果:通过。`runtimeStoryCoordinator.test.ts` 共 8 个测试通过;`useGameFlow.customWorld.test.tsx` 共 2 个测试通过;`src/hooks` 全量 27 个测试文件、109 个测试通过;编码检查和 diff 空白检查通过。 + +### 2026-04-29 WP-RPG Combat 基础领域常量与枚举归位切片 + +已完成: + +1. 认领 `WP-RPG Gameplay 域` 中可并行的 `module-combat` 基础领域常量与枚举归位切片,完成后认领状态已更新为 `已关闭`。 +2. 新增 `SERVER_RS_DDD_WP_RPG_COMBAT_DOMAIN_ENUM_REHOME_2026-04-29.md`,冻结本次迁移边界。 +3. 将战斗 ID 前缀、初始版本、普通战斗反击比例、反击最低伤害、切磋保底生命和旧攻击 function id 白名单迁入 `module-combat/src/domain.rs`。 +4. 将 `BattleMode`、`BattleStatus`、`CombatOutcome` 及其 `as_str` 方法迁入 `domain.rs`,并补齐中文领域注释。 +5. `module-combat/src/lib.rs` 通过 `pub use domain::*` 保持既有 `module_combat::*` 公开 API。 +6. 更新 `module-combat/README.md` 与 `docs/technical/README.md`,补齐本次 WP-RPG 文档入口。 +7. 更新 `module-combat/Cargo.toml`,让 `spacetime-types` feature 同步启用 `module-runtime-item/spacetime-types`,保证 `RuntimeItemRewardItemSnapshot` 在 wasm 目标下可被 SpacetimeDB 类型派生使用。 + +边界说明: + +1. 本次不改 `spacetime-module`、`spacetime-client`、`api-server`、platform adapter 或前端接线。 +2. 本次不改表结构、reducer、procedure、row mapper 或 `migration.rs`。 +3. 本次不移动 `BattleStateInput`、`BattleStateSnapshot`、`ResolveCombatActionInput`、`CombatFieldError` 和战斗结算函数,后续仍按 `WP-RPG` 继续拆分。 +4. 本次不改变任何战斗数值、支持的 function id、枚举字符串值或中文错误文案。 + +验证: + +```powershell +cargo fmt -p module-combat --manifest-path server-rs/Cargo.toml --check +cargo test -p module-combat --manifest-path server-rs/Cargo.toml +cargo check -p module-combat --features spacetime-types --target wasm32-unknown-unknown --manifest-path server-rs/Cargo.toml +npm.cmd run check:server-rs-ddd +npm.cmd run check:encoding -- docs/technical/SERVER_RS_DDD_WP_RPG_COMBAT_DOMAIN_ENUM_REHOME_2026-04-29.md server-rs/crates/module-combat/Cargo.toml server-rs/crates/module-combat/src/domain.rs server-rs/crates/module-combat/src/lib.rs server-rs/crates/module-combat/README.md docs/technical/README.md docs/technical/SERVER_RS_DDD_PARALLEL_TASKLIST_2026-04-29.md +git diff --check -- docs/technical/SERVER_RS_DDD_WP_RPG_COMBAT_DOMAIN_ENUM_REHOME_2026-04-29.md server-rs/crates/module-combat/Cargo.toml server-rs/crates/module-combat/src/domain.rs server-rs/crates/module-combat/src/lib.rs server-rs/crates/module-combat/README.md docs/technical/README.md docs/technical/SERVER_RS_DDD_PARALLEL_TASKLIST_2026-04-29.md +``` + +结果:通过。`cargo test -p module-combat` 共 9 个测试通过;`spacetime-types` feature 在 `wasm32-unknown-unknown` 目标下编译通过;DDD 边界检查通过 15 个 module crate。 + +后端启动: + +```powershell +npm.cmd run api-server:maincloud +``` + +结果:未能启动到监听阶段。编译被其他并行工作包中的 `api-server/src/big_fish.rs` 缺失 `map_big_fish_run_response`,以及 `api-server/src/runtime_save.rs` 对 `actual_session_id` 的部分移动错误阻塞;本次 WP-RPG combat 切片未修改这些文件。随后探测 `http://127.0.0.1:3100/healthz` 返回无法连接,本地未发现 `api-server.exe` 运行进程。 + +### 2026-04-29 WP-PZ 基础领域常量与枚举归位切片 + +已完成: + +1. 认领 `WP-PZ Puzzle` 中可并行的基础领域常量与枚举归位切片,完成后认领状态已更新为 `已关闭`。 +2. 新增 `SERVER_RS_DDD_WP_PZ_DOMAIN_ENUM_REHOME_2026-04-29.md`,冻结本次迁移边界。 +3. 新增 `module-puzzle/README.md`,记录模块职责、当前落位和后续边界。 +4. 将 Puzzle ID 前缀、标签数量、洗牌次数常量迁入 `module-puzzle/src/domain.rs`。 +5. 将 `PuzzleAgentStage`、`PuzzleAnchorStatus`、`PuzzleAgentMessageRole`、`PuzzleAgentMessageKind`、`PuzzlePublicationStatus`、`PuzzleRuntimeLevelStatus` 及其 `as_str` 方法迁入 `domain.rs`。 +6. `module-puzzle/src/lib.rs` 通过 `pub use domain::*` 保持既有 `module_puzzle::*` 公开 API。 +7. 更新 `docs/technical/README.md`,补齐本次 WP-PZ 文档入口。 + +边界说明: + +1. 本次不改 `spacetime-module`、`spacetime-client`、`api-server`、platform adapter 或前端接线。 +2. 本次不改表结构、reducer、procedure 或 `migration.rs`。 +3. 本次不移动 Agent session、work profile、runtime run、leaderboard 的结构体和校验函数,后续仍按 `WP-PZ` 继续拆分。 +4. 本次不改变标签数量、洗牌规则、枚举字符串值或中文错误文案。 + +验证: + +```powershell +cargo fmt -p module-puzzle --manifest-path server-rs/Cargo.toml --check +cargo test -p module-puzzle --manifest-path server-rs/Cargo.toml +cargo check -p module-puzzle --features spacetime-types --target wasm32-unknown-unknown --manifest-path server-rs/Cargo.toml +npm.cmd run check:server-rs-ddd +npm.cmd run check:encoding -- docs/technical/SERVER_RS_DDD_WP_PZ_DOMAIN_ENUM_REHOME_2026-04-29.md server-rs/crates/module-puzzle/src/domain.rs server-rs/crates/module-puzzle/src/lib.rs server-rs/crates/module-puzzle/README.md docs/technical/README.md docs/technical/SERVER_RS_DDD_PARALLEL_TASKLIST_2026-04-29.md +git diff --check -- docs/technical/SERVER_RS_DDD_WP_PZ_DOMAIN_ENUM_REHOME_2026-04-29.md server-rs/crates/module-puzzle/src/domain.rs server-rs/crates/module-puzzle/src/lib.rs server-rs/crates/module-puzzle/README.md docs/technical/README.md docs/technical/SERVER_RS_DDD_PARALLEL_TASKLIST_2026-04-29.md +``` + +结果:通过。`cargo test -p module-puzzle` 共 13 个测试通过;`spacetime-types` feature 在 `wasm32-unknown-unknown` 目标下编译通过;DDD 边界检查通过 15 个 module crate。 + +后端启动: + +```powershell +npm.cmd run api-server:maincloud +``` + +结果:命令在 180 秒观察窗口内超时,因为 `cargo run` 前台常驻;随后探测 `http://127.0.0.1:3100/healthz` 返回 `200`,本地存在 `server-rs/target/debug/api-server.exe` 运行进程。 + +### 2026-04-29 WP-PZ 领域类型与规则拆分切片 + +已完成: + +1. 继续认领 `WP-PZ Puzzle` 中可并行的领域类型与规则拆分切片。 +2. 新增 `SERVER_RS_DDD_WP_PZ_DOMAIN_SPLIT_2026-04-29.md`,冻结本次迁移边界。 +3. `module-puzzle/src/lib.rs` 收口为 DDD 子模块声明和公开导出。 +4. `domain.rs` 承接 Agent、锚点包、草稿、作品 profile、运行态棋盘和排行榜等领域类型。 +5. `commands.rs` 承接 SpacetimeDB procedure/reducer 写入输入。 +6. `application.rs` 承接 procedure 返回包装和拼图纯规则函数。 +7. `errors.rs` 承接 `PuzzleFieldError` 与中文错误文案。 +8. `events.rs` 新增最小 `PuzzleDomainEvent`,先表达草稿变化、作品发布和运行态推进事实。 + +边界说明: + +1. 本次不改 `spacetime-module`、`spacetime-client`、`api-server`、platform adapter 或前端接线。 +2. 本次不改表结构、reducer、procedure 或 `migration.rs`。 +3. 本次不改变序列化字段、procedure 输入结构、procedure 返回 JSON 包装、标签数量、洗牌、移动/合并/拆分语义或中文错误文案。 +4. 本次新增领域事件只在 `module-puzzle` 落位,不接入 SpacetimeDB event table。 + +验证: + +```powershell +cargo fmt -p module-puzzle --manifest-path server-rs/Cargo.toml --check +cargo test -p module-puzzle --manifest-path server-rs/Cargo.toml +cargo check -p module-puzzle --features spacetime-types --target wasm32-unknown-unknown --manifest-path server-rs/Cargo.toml +npm.cmd run check:server-rs-ddd +npm.cmd run check:encoding -- docs/technical/SERVER_RS_DDD_WP_PZ_DOMAIN_SPLIT_2026-04-29.md server-rs/crates/module-puzzle/src/lib.rs server-rs/crates/module-puzzle/src/domain.rs server-rs/crates/module-puzzle/src/commands.rs server-rs/crates/module-puzzle/src/application.rs server-rs/crates/module-puzzle/src/events.rs server-rs/crates/module-puzzle/src/errors.rs server-rs/crates/module-puzzle/README.md docs/technical/README.md docs/technical/SERVER_RS_DDD_PARALLEL_TASKLIST_2026-04-29.md +``` + +结果:通过。`cargo test -p module-puzzle` 共 13 个测试通过;`spacetime-types` feature 在 `wasm32-unknown-unknown` 目标下编译通过;DDD 边界检查通过 15 个 module crate;编码检查通过 10 个文件。 + +补充验证: + +```powershell +git diff --check -- docs/technical/SERVER_RS_DDD_WP_PZ_DOMAIN_SPLIT_2026-04-29.md server-rs/crates/module-puzzle/src/lib.rs server-rs/crates/module-puzzle/src/domain.rs server-rs/crates/module-puzzle/src/commands.rs server-rs/crates/module-puzzle/src/application.rs server-rs/crates/module-puzzle/src/events.rs server-rs/crates/module-puzzle/src/errors.rs server-rs/crates/module-puzzle/README.md docs/technical/README.md docs/technical/SERVER_RS_DDD_PARALLEL_TASKLIST_2026-04-29.md +cargo check -p api-server --manifest-path server-rs/Cargo.toml +npm.cmd run api-server:maincloud +``` + +结果:通过。`git diff --check` 无空白问题;`api-server` 编译通过,仅保留既有 runtime prompt dead code warning。`npm.cmd run api-server:maincloud` 按前台服务启动后,`GET http://127.0.0.1:3100/healthz` 返回 `200`,本仓库 `server-rs/target/debug/api-server.exe` 已监听 `127.0.0.1:3100`。 + +### 2026-04-29 WP-RT 应用记录投影拆分切片 + +已完成: + +1. 继续认领 `WP-RT Runtime/Profile/Save` 中可并行的应用记录投影拆分切片。 +2. 新增 `SERVER_RS_DDD_WP_RT_APPLICATION_RECORD_REFACTOR_2026-04-29.md`,冻结本次应用层迁移边界。 +3. 将 settings、browse history、profile dashboard、wallet ledger、recharge center、membership、referral、reward code、redeem code、played world、play stats、runtime snapshot、save archive 的 `build_runtime_*_record` 迁入 `module-runtime/src/application.rs`。 +4. 将记录投影专用 `parse_optional_json_value` 迁入 `application.rs`。 +5. `module-runtime/src/lib.rs` 通过 `pub use application::*` 保持原公开 API。 +6. 更新 `module-runtime/README.md` 与 `docs/technical/README.md`,补齐本次 WP-RT 应用层文档入口。 + +边界说明: + +1. 本次不改变任何记录投影字段、时间格式、JSON 解析或错误语义。 +2. 本次不移动充值商品目录和商品查找函数,避免影响命令层订单校验。 +3. 本次不改 `spacetime-module`、`spacetime-client`、`api-server` 或前端接线。 +4. 本次不改表结构、reducer、procedure 或 `migration.rs`。 + +验证: + +```powershell +cargo fmt -p module-runtime --manifest-path server-rs/Cargo.toml --check +cargo test -p module-runtime --manifest-path server-rs/Cargo.toml +npm.cmd run check:server-rs-ddd +npm.cmd run check:encoding -- docs/technical/SERVER_RS_DDD_WP_RT_APPLICATION_RECORD_REFACTOR_2026-04-29.md server-rs/crates/module-runtime/src/application.rs server-rs/crates/module-runtime/src/lib.rs server-rs/crates/module-runtime/README.md docs/technical/README.md docs/technical/SERVER_RS_DDD_PARALLEL_TASKLIST_2026-04-29.md +git diff --check -- docs/technical/SERVER_RS_DDD_WP_RT_APPLICATION_RECORD_REFACTOR_2026-04-29.md server-rs/crates/module-runtime/src/application.rs server-rs/crates/module-runtime/src/lib.rs server-rs/crates/module-runtime/README.md docs/technical/README.md docs/technical/SERVER_RS_DDD_PARALLEL_TASKLIST_2026-04-29.md +``` + +结果:通过。`cargo test -p module-runtime` 共 13 个测试通过。 + +后端启动: + +```powershell +npm.cmd run api-server:maincloud +``` + +结果:编译通过,仅有既有 `api-server/src/prompt/rpg/runtime_chat.rs` 未使用 prompt helper warning;运行阶段因 `127.0.0.1:3100` 已被本仓库 `server-rs/target/debug/api-server.exe` 监听而退出,错误为 `AddrInUse / 10048`。随后探测 `http://127.0.0.1:3100/healthz` 返回 `200`,确认本地已有服务在线。 + +### 2026-04-29 WP-CW 基础领域枚举归位切片 + +已完成: + +1. 认领 `WP-CW Custom World` 中可并行的基础领域枚举归位切片,完成后认领状态已更新为 `已关闭`。 +2. 新增 `SERVER_RS_DDD_WP_CW_DOMAIN_ENUM_REHOME_2026-04-29.md`,冻结本次枚举归位边界。 +3. 将 `MAX_PROGRESS_PERCENT` 与 Custom World / RPG Agent 基础枚举迁入 `module-custom-world/src/domain.rs`。 +4. 将这些枚举的 `as_str` 方法和 `CustomWorldThemeMode::from_client_str` 一并迁入 `domain.rs`。 +5. `module-custom-world/src/lib.rs` 通过 `pub use domain::*` 保持既有 `module_custom_world::*` 公开 API。 +6. 更新 `module-custom-world/README.md` 与 `docs/technical/README.md`,补齐本次 WP-CW 文档入口。 + +边界说明: + +1. 本次不改 `spacetime-module`、`spacetime-client`、`api-server`、platform adapter 或前端接线。 +2. 本次不改表结构、reducer、procedure 或 `migration.rs`。 +3. 本次不移动 profile、agent session、draft card、gallery、publish gate 的结构体和校验函数,后续仍按 `WP-CW` 继续拆分。 + +验证: + +```powershell +cargo fmt -p module-custom-world --manifest-path server-rs/Cargo.toml --check +cargo test -p module-custom-world --manifest-path server-rs/Cargo.toml +cargo check -p module-custom-world --features spacetime-types --target wasm32-unknown-unknown --manifest-path server-rs/Cargo.toml +npm.cmd run check:encoding -- docs/technical/SERVER_RS_DDD_WP_CW_DOMAIN_ENUM_REHOME_2026-04-29.md server-rs/crates/module-custom-world/src/domain.rs server-rs/crates/module-custom-world/src/lib.rs server-rs/crates/module-custom-world/README.md docs/technical/README.md docs/technical/SERVER_RS_DDD_PARALLEL_TASKLIST_2026-04-29.md +``` + +结果:通过。`cargo test -p module-custom-world` 共 13 个测试通过;`spacetime-types` feature 在 `wasm32-unknown-unknown` 目标下编译通过。 + +补充验证: + +```powershell +npm.cmd run check:server-rs-ddd +``` + +结果:通过。`server-rs DDD boundary check passed for 15 module crate(s).` + +后端启动: + +```powershell +npm.cmd run api-server:maincloud +``` + +结果:命令在 180 秒观察窗口内超时,因为 `cargo run` 前台常驻;随后探测 `http://127.0.0.1:3100/healthz` 返回 `200`,本地存在 `server-rs/target/debug/api-server.exe` 运行进程。 + +### 2026-04-29 WP-RT 命令构造拆分切片 + +已完成: + +1. 继续认领 `WP-RT Runtime/Profile/Save` 中可并行的命令构造拆分切片。 +2. 新增 `SERVER_RS_DDD_WP_RT_COMMANDS_REFACTOR_2026-04-29.md`,冻结本次命令层迁移边界。 +3. 将 settings、browse history、profile dashboard、wallet、recharge、referral、reward code、redeem code、play stats、runtime snapshot、save archive 的 `build_runtime_*_input` 迁入 `module-runtime/src/commands.rs`。 +4. 将 `build_runtime_browse_history_sync_input`、`prepare_runtime_browse_history_entries`、`build_runtime_browse_history_id` 迁入 `commands.rs`。 +5. 将 `normalize_invite_code`、`normalize_redeem_code` 和命令构造私有 helper 迁入 `commands.rs`。 +6. `module-runtime/src/lib.rs` 通过 `pub use commands::*` 保持原公开 API。 +7. 更新 `module-runtime/README.md` 与 `docs/technical/README.md`,补齐本次 WP-RT 命令层文档入口。 + +边界说明: + +1. 本次不改变任何输入构造、字段归一化或浏览历史去重排序语义。 +2. 本次不移动记录投影 builder、充值商品目录、时间格式化或 JSON 读取 helper,后续可按 `application.rs` 切片迁移。 +3. 本次不改 `spacetime-module`、`spacetime-client`、`api-server` 或前端接线。 +4. 本次不改表结构、reducer、procedure 或 `migration.rs`。 + +验证: + +```powershell +cargo fmt -p module-runtime --manifest-path server-rs/Cargo.toml --check +cargo test -p module-runtime --manifest-path server-rs/Cargo.toml +npm.cmd run check:server-rs-ddd +npm.cmd run check:encoding -- docs/technical/SERVER_RS_DDD_WP_RT_COMMANDS_REFACTOR_2026-04-29.md server-rs/crates/module-runtime/src/commands.rs server-rs/crates/module-runtime/src/lib.rs server-rs/crates/module-runtime/README.md docs/technical/README.md docs/technical/SERVER_RS_DDD_PARALLEL_TASKLIST_2026-04-29.md +``` + +结果:通过。`cargo test -p module-runtime` 共 13 个测试通过。 + +后端启动: + +```powershell +npm.cmd run api-server:maincloud +``` + +结果:未重新执行;上一轮已确认当前工作区并行的 `WP-CW Custom World` 改动在 `module-custom-world/src/domain.rs` 与 `module-custom-world/src/lib.rs` 中存在重复 `as_str/from_client_str` 定义,导致 `api-server:maincloud` 编译阶段失败。该阻塞不来自本次 WP-RT 修改。 + +### 2026-04-29 WP-RT 错误层拆分切片 + +已完成: + +1. 继续认领 `WP-RT Runtime/Profile/Save` 中可并行的错误层拆分切片。 +2. 新增 `SERVER_RS_DDD_WP_RT_ERROR_LAYER_REFACTOR_2026-04-29.md`,冻结本次错误层迁移边界。 +3. 将 `RuntimeSettingsFieldError`、`RuntimeBrowseHistoryFieldError`、`RuntimeProfileFieldError` 迁入 `module-runtime/src/errors.rs`。 +4. 将三组错误的 `Display` 实现和中文错误文案同步迁入 `errors.rs`,保持原错误语义不变。 +5. `module-runtime/src/lib.rs` 通过 `pub use errors::*` 保持原公开 API。 +6. 更新 `module-runtime/README.md` 与 `docs/technical/README.md`,补齐本次 WP-RT 错误层文档入口。 + +边界说明: + +1. 本次不调整错误枚举变体,不改任何业务校验语义。 +2. 本次不移动 `build_runtime_*` 构造函数,命令构造拆分留给后续切片。 +3. 本次不改 `spacetime-module`、`spacetime-client`、`api-server` 或前端接线。 +4. 本次不改表结构、reducer、procedure 或 `migration.rs`。 + +验证: + +```powershell +cargo fmt -p module-runtime --manifest-path server-rs/Cargo.toml --check +cargo test -p module-runtime --manifest-path server-rs/Cargo.toml +npm.cmd run check:server-rs-ddd +npm.cmd run check:encoding -- docs/technical/SERVER_RS_DDD_WP_RT_ERROR_LAYER_REFACTOR_2026-04-29.md server-rs/crates/module-runtime/src/errors.rs server-rs/crates/module-runtime/src/lib.rs server-rs/crates/module-runtime/README.md docs/technical/README.md docs/technical/SERVER_RS_DDD_PARALLEL_TASKLIST_2026-04-29.md +``` + +结果:通过。`cargo test -p module-runtime` 共 13 个测试通过。 + +后端启动: + +```powershell +npm.cmd run api-server:maincloud +``` + +结果:未通过,当前工作区并行的 `WP-CW Custom World` 改动在 `module-custom-world/src/domain.rs` 与 `module-custom-world/src/lib.rs` 中存在重复 `as_str/from_client_str` 定义,导致 `api-server:maincloud` 编译阶段失败;该阻塞不来自本次 WP-RT 修改。随后探测 `http://127.0.0.1:3100/healthz` 也无法连接,本地未发现 `api-server` 进程。 + +### 2026-04-29 WP-RT Snapshot/Profile/Save Archive 领域快照与记录类型拆分切片 + +已完成: + +1. 继续认领 `WP-RT Runtime/Profile/Save` 中可并行的 snapshot/profile/save archive 领域快照与记录类型拆分切片。 +2. 新增 `SERVER_RS_DDD_WP_RT_DOMAIN_SNAPSHOT_RECORD_REFACTOR_2026-04-29.md`,冻结本次类型搬迁边界。 +3. 将 runtime snapshot、runtime setting、browse history、profile dashboard、wallet ledger、recharge、reward code、redeem code、referral、played world、play stats、save archive 的 snapshot/input/procedure result 迁入 `module-runtime/src/domain.rs`。 +4. 将 `RuntimeBrowseHistoryThemeMode`、profile/wallet/recharge/referral 相关枚举及其 `as_str` / `from_client_str` 方法迁入 `domain.rs`。 +5. 将 `RuntimeSettingsRecord`、`RuntimeProfileDashboardRecord`、`RuntimeProfileWalletLedgerEntryRecord`、`RuntimeProfilePlayedWorldRecord`、`RuntimeProfilePlayStatsRecord`、`RuntimeSnapshotRecord`、`RuntimeProfileSaveArchiveRecord` 等记录投影类型迁入 `domain.rs`。 +6. 更新 `module-runtime/README.md` 与 `docs/technical/README.md`,补齐本次 WP-RT 专项文档入口。 + +边界说明: + +1. 本次只移动纯类型与类型自带方法,不改 `build_runtime_*` 构造函数、归一化函数、错误枚举或测试。 +2. 本次不改 `spacetime-module`、`spacetime-client`、`api-server` 或前端接线。 +3. 本次不改表结构、reducer、procedure 或 `migration.rs`。 +4. `WP-RT` 大包仍未整体完成,后续应继续拆 `commands.rs`、`errors.rs`、`application.rs` 以及 Adapter/BFF 接线。 + +验证: + +```powershell +cargo fmt -p module-runtime --manifest-path server-rs/Cargo.toml --check +cargo test -p module-runtime --manifest-path server-rs/Cargo.toml +npm.cmd run check:server-rs-ddd +npm.cmd run check:encoding -- docs/technical/SERVER_RS_DDD_WP_RT_DOMAIN_SNAPSHOT_RECORD_REFACTOR_2026-04-29.md server-rs/crates/module-runtime/src/domain.rs server-rs/crates/module-runtime/src/lib.rs server-rs/crates/module-runtime/README.md docs/technical/README.md docs/technical/SERVER_RS_DDD_PARALLEL_TASKLIST_2026-04-29.md +git diff --check -- docs/technical/SERVER_RS_DDD_WP_RT_DOMAIN_SNAPSHOT_RECORD_REFACTOR_2026-04-29.md server-rs/crates/module-runtime/src/domain.rs server-rs/crates/module-runtime/src/lib.rs server-rs/crates/module-runtime/README.md docs/technical/README.md docs/technical/SERVER_RS_DDD_PARALLEL_TASKLIST_2026-04-29.md +``` + +结果:通过。`cargo test -p module-runtime` 共 13 个测试通过。 + +### 2026-04-29 WP-AS Assets 资产对象类型归位切片 + +已完成: + +1. 认领 `WP-AS Assets` 中可并行的 `module-assets` 纯类型归位切片,完成后认领状态已更新为 `已关闭`。 +2. 新增 `SERVER_RS_DDD_WP_AS_ASSET_OBJECT_TYPE_REHOME_2026-04-29.md`,冻结本次切片边界。 +3. `module-assets/src/domain.rs` 承接资产对象、资产历史、实体绑定的纯快照、记录、访问策略、ID 前缀和版本常量。 +4. `module-assets/src/commands.rs` 承接确认资产对象、资产历史查询、对象 upsert、实体绑定等输入 DTO。 +5. `module-assets/src/application.rs` 承接 procedure result 和确认资产对象结果等应用返回 DTO。 +6. `module-assets/src/errors.rs` 承接 `AssetObjectFieldError` 与中文错误文案。 +7. `module-assets/src/asset_object_core.rs` 收敛为字段校验、输入构建、记录构建、ID 生成和归一化函数,不再承载类型定义。 +8. `module-assets/README.md` 与 `docs/technical/README.md` 已补充本次切片索引。 + +边界说明: + +1. 本次不改 `spacetime-module`、`spacetime-client`、`api-server`、`platform-oss` 或前端接线。 +2. 本次不改表结构、reducer、procedure 或 `migration.rs`。 +3. `WP-AS Assets` 大包仍未整体完成,资产 API、OSS adapter 全链 facade、资产任务和 manifest 仍待后续切片推进。 + +验证: + +```powershell +cargo fmt -p module-assets --manifest-path server-rs/Cargo.toml --check +cargo test -p module-assets --manifest-path server-rs/Cargo.toml +npm.cmd run check:server-rs-ddd +npm.cmd run check:encoding -- docs/technical/SERVER_RS_DDD_WP_AS_ASSET_OBJECT_TYPE_REHOME_2026-04-29.md server-rs/crates/module-assets/src/domain.rs server-rs/crates/module-assets/src/commands.rs server-rs/crates/module-assets/src/application.rs server-rs/crates/module-assets/src/errors.rs server-rs/crates/module-assets/src/asset_object_core.rs server-rs/crates/module-assets/src/lib.rs server-rs/crates/module-assets/README.md docs/technical/SERVER_RS_DDD_PARALLEL_TASKLIST_2026-04-29.md docs/technical/README.md +``` + +结果:通过。`cargo test -p module-assets` 共 8 个测试通过。 + +补充: + +```powershell +cargo check -p module-assets --manifest-path server-rs/Cargo.toml --all-features +``` + +结果:通过,`spacetime-types` feature 下类型派生可编译。 + +后端启动: + +```powershell +npm.cmd run api-server:maincloud +``` + +结果:失败。错误来自当前工作区中 `module-custom-world` 的重复 `as_str/from_client_str` 实现(`domain.rs` 与 `lib.rs` 同时定义),不在本次 `WP-AS Assets` 文件边界内;随后探测 `http://127.0.0.1:3100/healthz` 无法连接,当前无 3100 监听。 + +### 2026-04-29 WP-RT Runtime Settings 领域值对象拆分切片 + +已完成: + +1. 避开已被认领的 `WP-A Auth`,认领 `WP-RT Runtime/Profile/Save` 中可并行的 runtime settings 领域值对象拆分切片。 +2. 新增 `SERVER_RS_DDD_WP_RT_RUNTIME_SETTINGS_DOMAIN_REFACTOR_2026-04-29.md`,冻结本次切片边界。 +3. 将 `DEFAULT_MUSIC_VOLUME`、`DEFAULT_PLATFORM_THEME`、`RuntimePlatformTheme`、`RuntimeSettings` 及其方法迁入 `module-runtime/src/domain.rs`。 +4. `module-runtime/src/lib.rs` 改为通过 `pub use domain::*` 暴露 settings 领域对象,保留既有外部 crate API。 +5. 更新 `module-runtime/README.md`,记录 runtime settings 领域拆分现状和专项文档入口。 + +边界说明: + +1. 本次不改 `spacetime-module`、`spacetime-client`、`api-server` 或前端接线。 +2. 本次不改表结构、reducer、procedure 或 `migration.rs`。 +3. `WP-RT` 大包仍未整体完成,snapshot、wallet、played world、save archive 等切片待后续认领。 + +验证: + +```powershell +cargo fmt -p module-runtime --manifest-path server-rs/Cargo.toml --check +cargo test -p module-runtime --manifest-path server-rs/Cargo.toml +npm.cmd run check:server-rs-ddd +npm.cmd run check:encoding -- docs/technical/SERVER_RS_DDD_WP_RT_RUNTIME_SETTINGS_DOMAIN_REFACTOR_2026-04-29.md server-rs/crates/module-runtime/src/domain.rs server-rs/crates/module-runtime/src/lib.rs server-rs/crates/module-runtime/README.md docs/technical/SERVER_RS_DDD_PARALLEL_TASKLIST_2026-04-29.md +``` + +结果:通过。`cargo test -p module-runtime` 共 13 个测试通过。 + +后端启动: + +```powershell +npm.cmd run api-server:maincloud +``` + +结果:命令在 90 秒观察窗口内超时,因为 `cargo run` 前台常驻;随后探测 `http://127.0.0.1:3100/healthz` 返回 `200`,本地存在 `server-rs/target/debug/api-server.exe` 运行进程。 + +### 2026-04-29 WP-A Auth 领域值对象拆分启动 + +已完成: + +1. 新增 `SERVER_RS_DDD_WP_A_AUTH_DOMAIN_VALUE_OBJECT_REFACTOR_2026-04-29.md`,冻结本次 Auth 启动切片边界。 +2. `module-auth/src/domain.rs` 承接账号、登录方式、绑定状态、手机号、微信授权 state、微信身份资料、refresh session 和 auth store snapshot 等纯领域快照。 +3. `module-auth/src/lib.rs` 通过 `pub use domain::*` 保持既有公开 API,不改 `api-server`、`spacetime-module`、`spacetime-client`、`platform-auth` 或前端。 +4. `module-auth/README.md` 与 `docs/technical/README.md` 已补充本次切片索引。 + +边界说明: + +1. 本次不移动 `InMemoryAuthStore`、`PhoneAuthService`、`PasswordEntryService`、错误枚举或任何平台副作用 adapter。 +2. 本次不改变 SpacetimeDB 表、reducer、procedure,不需要修改 `migration.rs`。 +3. 本次保留后续 Auth 领域拆分空间:短信验证码规则、微信绑定编排、refresh session 应用服务和 SpacetimeDB adapter 仍需后续切片完成。 + +验证: + +```powershell +cargo fmt -p module-auth --manifest-path server-rs/Cargo.toml --check +cargo test -p module-auth --manifest-path server-rs/Cargo.toml +npm.cmd run check:server-rs-ddd +npm.cmd run check:encoding -- docs/technical/SERVER_RS_DDD_WP_A_AUTH_DOMAIN_VALUE_OBJECT_REFACTOR_2026-04-29.md server-rs/crates/module-auth/src/domain.rs server-rs/crates/module-auth/src/lib.rs server-rs/crates/module-auth/README.md docs/technical/SERVER_RS_DDD_PARALLEL_TASKLIST_2026-04-29.md docs/technical/README.md +``` + +结果:通过。`cargo test -p module-auth` 共 17 个测试通过。 + +后端启动: + +```powershell +npm.cmd run api-server:maincloud +``` + +结果:命令在 90 秒观察窗口内超时,因为 `cargo run` 前台常驻;随后探测 `http://127.0.0.1:3100/healthz` 返回 `200`,本地 `api-server` 运行中。 + ### 2026-04-29 前置等待解除与 WP-BF 领域小步落地 已确认: @@ -495,6 +1110,44 @@ npm.cmd run api-server:maincloud 1. 未执行 SpacetimeDB 发布、绑定生成或 migration 更新,原因是本次未改 SpacetimeDB table/reducer/procedure。 +### 2026-04-29 WP-AI BFF 收口与关闭 + +已完成: + +1. 新增 `SERVER_RS_DDD_WP_AI_TASK_BFF_CLOSURE_2026-04-29.md`,记录本次 AI task BFF 收口边界。 +2. 在 `api-server/src/ai_tasks.rs` 补齐 AI task mutation route 定向测试,覆盖阶段启动、文本片段追加、阶段完成、结果引用挂接、任务完成、任务失败和任务取消。 +3. 上述写接口均验证未登录返回 `401 UNAUTHORIZED`。 +4. 上述写接口均验证 token 有效但 SpacetimeDB 未发布时返回 `502 BAD_GATEWAY`,错误详情保持 `provider = spacetimedb`。 +5. 复核 `module-ai`、`spacetime-client` 和 `spacetime-module`,确认本次未改变 SpacetimeDB table、reducer、procedure 或绑定形状,未触碰 `migration.rs`。 +6. `WP-AI AI Task` 工作包认领状态已更新为 `已关闭`。 + +边界说明: + +1. 本次不新增真实 LLM provider 调用、prompt 组装、SSE 流式输出、订阅 projection 或前端消费 UI。 +2. 真实模型调用继续归 `WP-PF platform side effects`,API 编排和 SSE 继续归 `WP-API api-server BFF`,前端消费继续归 `WP-FE`。 +3. `module-ai` 继续只承载 AI task 编排领域模型与状态机,不承接供应商 HTTP 适配。 + +验证: + +```powershell +cargo fmt -p api-server --manifest-path server-rs/Cargo.toml --check +cargo test -p api-server ai_task --manifest-path server-rs/Cargo.toml +cargo test -p module-ai --manifest-path server-rs/Cargo.toml +cargo check -p spacetime-client --manifest-path server-rs/Cargo.toml +cargo check -p spacetime-module --manifest-path server-rs/Cargo.toml +cargo check -p api-server --manifest-path server-rs/Cargo.toml +npm.cmd run check:server-rs-ddd +npm.cmd run check:encoding -- docs/technical/SERVER_RS_DDD_PARALLEL_TASKLIST_2026-04-29.md docs/technical/SERVER_RS_DDD_WP_AI_TASK_BFF_CLOSURE_2026-04-29.md docs/technical/README.md server-rs/crates/api-server/src/ai_tasks.rs server-rs/crates/api-server/src/wechat_auth.rs +npm.cmd run api-server:maincloud +``` + +结果:通过。`api-server ai_task` 定向测试 7 个通过;`module-ai` 9 个测试通过;`spacetime-client`、`spacetime-module` 和 `api-server` 编译通过;DDD 边界检查通过;编码检查 5 个文件通过;`npm.cmd run api-server:maincloud` 已启动本仓库 `api-server.exe`,`GET /healthz` 返回 `200`。`api-server` 编译仍保留既有未使用代码 warning,非本次新增。 + +补充说明: + +1. 首轮 `cargo fmt -p api-server --check` 被并行工作包中的 `assets.rs` 与 `platform_errors.rs` 格式差异挡住,未对这些非 WP-AI 文件做机械格式化。 +2. 后续执行中并行 `WP-A` / `WP-RT` 短暂写入导致 `module-auth` 骨架文件缺失、`module-runtime` 旧导入缺失等瞬时编译错误;按最终工作区复跑后 `api-server` 编译、DDD 边界检查和 `api-server:maincloud` 均已通过。 + ### 2026-04-29 WP-RS 前置满足后启动执行 已完成: @@ -631,6 +1284,61 @@ npm.cmd run check:encoding -- docs/technical/SERVER_RS_DDD_PARALLEL_TASKLIST_202 4. 明确命名收口规则:运行代码中不再出现 `compat`;`legacy` 只允许出现在历史文档或迁移说明中。 5. 明确 `WP-V` 必须紧跟 `WP-DEL` 执行,中途不插入新功能;后端代码变更后必须执行 `npm.cmd run api-server:maincloud`。 +### 2026-04-29 WP-PF 平台副作用错误分类切片 + +已完成: + +1. 认领 `WP-PF platform side effects` 的平台错误分类基础设施切片。 +2. 新增 `SERVER_RS_DDD_WP_PF_PLATFORM_ERROR_CLASSIFICATION_2026-04-29.md`,记录本次范围与边界。 +3. 在 `platform-llm` 中新增 `LlmErrorKind` 和 `LlmError::kind()`。 +4. 在 `platform-oss` 中新增 `OssErrorKind` 和 `OssError::kind()`。 +5. 在 `platform-auth` 中新增 `AuthPlatformErrorKind`,并为 `JwtError`、`RefreshCookieError`、`PasswordHashError`、`SmsProviderError` 增加 `kind()`。 +6. 本次不改变外部请求行为,不接 API route,不改 SpacetimeDB table/reducer/procedure,不触碰前端。 + +验证: + +```powershell +cargo test -p platform-llm -p platform-oss -p platform-auth --manifest-path server-rs/Cargo.toml +cargo fmt -p platform-llm -p platform-oss -p platform-auth --manifest-path server-rs/Cargo.toml --check +npm.cmd run check:server-rs-ddd +npm.cmd run check:encoding -- docs/technical/SERVER_RS_DDD_WP_PF_PLATFORM_ERROR_CLASSIFICATION_2026-04-29.md docs/technical/SERVER_RS_DDD_PARALLEL_TASKLIST_2026-04-29.md server-rs/crates/platform-llm/src/lib.rs server-rs/crates/platform-oss/src/lib.rs server-rs/crates/platform-auth/src/lib.rs +``` + +结果:通过。`platform-auth` 18 个、`platform-llm` 10 个、`platform-oss` 11 个测试通过;格式检查、DDD 边界检查、编码检查均通过。 + +### 2026-04-29 WP-PF 平台副作用剩余收口 + +已完成: + +1. 继续认领 `WP-PF platform side effects` 剩余内容,并将认领状态在执行期间改为 `已认领`。 +2. 扩展 `SERVER_RS_DDD_WP_PF_PLATFORM_ERROR_CLASSIFICATION_2026-04-29.md`,明确本轮完整收口范围、禁止触碰边界和验收命令。 +3. 在 `platform-auth` 中新增微信 OAuth provider、微信配置、微信身份资料、微信 provider 错误与 `WechatProviderError::kind()`。 +4. 将 `api-server/src/wechat_provider.rs` 收敛为配置薄适配,真实微信授权 URL、access_token 请求、用户信息请求与 mock provider 都下沉到 `platform-auth`。 +5. 新增 `api-server/src/platform_errors.rs`,统一把 LLM、OSS、SMS/手机号、微信平台错误分类映射为 HTTP status、中文 message 和 provider details。 +6. `api-server` 中 LLM 代理、OSS 相关资产 BFF、手机号/微信重试头处理均改为复用统一 platform error mapper。 +7. 未修改 SpacetimeDB table/reducer/procedure/migration,未触碰前端;图像/视频模型直连接口仍按视觉资产生成专项后续迁移,不混入本轮 WP-PF。 + +验证: + +```powershell +cargo test -p platform-llm -p platform-oss -p platform-auth --manifest-path server-rs/Cargo.toml +cargo test -p api-server platform_errors --manifest-path server-rs/Cargo.toml +cargo test -p api-server llm --manifest-path server-rs/Cargo.toml +cargo test -p api-server wechat --manifest-path server-rs/Cargo.toml +cargo check -p api-server --manifest-path server-rs/Cargo.toml +cargo fmt -p platform-llm -p platform-oss -p platform-auth -p api-server --manifest-path server-rs/Cargo.toml --check +npm.cmd run check:server-rs-ddd +npm.cmd run check:encoding -- docs/technical/SERVER_RS_DDD_WP_PF_PLATFORM_ERROR_CLASSIFICATION_2026-04-29.md docs/technical/SERVER_RS_DDD_PARALLEL_TASKLIST_2026-04-29.md server-rs/crates/platform-llm/src/lib.rs server-rs/crates/platform-oss/src/lib.rs server-rs/crates/platform-auth/src/lib.rs server-rs/crates/platform-auth/Cargo.toml server-rs/crates/api-server/src/platform_errors.rs server-rs/crates/api-server/src/llm.rs server-rs/crates/api-server/src/state.rs server-rs/crates/api-server/src/wechat_provider.rs server-rs/crates/api-server/src/wechat_auth.rs server-rs/crates/api-server/src/assets.rs server-rs/crates/api-server/src/big_fish.rs server-rs/crates/api-server/src/character_visual_assets.rs server-rs/crates/api-server/src/character_animation_assets.rs server-rs/crates/api-server/src/custom_world_ai.rs server-rs/crates/api-server/src/legacy_generated_assets.rs server-rs/crates/api-server/src/puzzle.rs server-rs/crates/api-server/src/phone_auth.rs server-rs/crates/api-server/src/http_error.rs server-rs/crates/api-server/src/main.rs server-rs/crates/module-auth/src/lib.rs +npm.cmd run api-server:maincloud +``` + +结果:通过。`platform-auth` 20 个、`platform-llm` 10 个、`platform-oss` 11 个测试通过;`api-server platform_errors` 3 个、`api-server llm` 9 个、`api-server wechat` 5 个定向测试通过;`api-server` 编译通过;格式检查、DDD 边界检查、编码检查均通过。`npm.cmd run api-server:maincloud` 在 90 秒观察窗口超时,随后 `GET http://127.0.0.1:3100/healthz` 返回 `200`,本仓库 `api-server` 已在 3100 端口监听。`api-server` 编译仍有既有 prompt / runtime 拆分未使用代码 warning,非本轮 WP-PF 新增。 + +补充说明: + +1. 定向测试期间曾被并行 `module-auth` / `module-runtime` 切片的瞬时编译缺口挡住;按最终工作区顺序复跑后 `api-server` 已编译通过。 +2. 为避免并行切片持续阻塞 WP-PF 验收,只对 `module-auth/src/lib.rs` 做了标准库导入恢复级别的最小修复,不改变 Auth 领域逻辑。 + ### 2026-04-29 第 2 批 Adapter / BFF 接线顺序校准 已完成: @@ -889,6 +1597,167 @@ npm.cmd run check:encoding -- docs/technical/SERVER_RS_DDD_PARALLEL_TASKLIST_202 结果:通过。`cargo check -p api-server` 仍有既有 `api-server/src/prompt/rpg/runtime_chat.rs` 未使用 prompt helper warning,非本次新增。 +### 2026-04-29 WP-AI 内部子模块拆分 + +已完成: + +1. 新增 `SERVER_RS_DDD_WP_AI_INTERNAL_MODULE_SPLIT_2026-04-29.md`,冻结本次 `module-ai` 内部拆分边界。 +2. `domain.rs` 改为轻量 re-export,真实内容拆入: + - `domain/types.rs` + - `domain/stages.rs` + - `domain/ids.rs` +3. `commands.rs` 改为轻量 re-export,真实内容拆入: + - `commands/inputs.rs` + - `commands/validation.rs` +4. `application.rs` 改为轻量 re-export,真实内容拆入: + - `application/result.rs` + - `application/service.rs` + - `application/store.rs` +5. 原 `lib.rs` 内行为测试迁入 `src/tests.rs`,`lib.rs` 只保留模块声明与公开导出。 +6. 保持 `module_ai::*` 对外公开导出不变,未修改 SpacetimeDB table、reducer、procedure、event table、migration、HTTP route、共享契约或前端。 + +验证: + +```powershell +cargo fmt -p module-ai --manifest-path server-rs/Cargo.toml --check +cargo test -p module-ai --manifest-path server-rs/Cargo.toml +cargo check -p spacetime-module --manifest-path server-rs/Cargo.toml +npm.cmd run check:server-rs-ddd +npm.cmd run check:encoding -- docs/technical/SERVER_RS_DDD_WP_AI_INTERNAL_MODULE_SPLIT_2026-04-29.md docs/technical/README.md docs/technical/SERVER_RS_DDD_PARALLEL_TASKLIST_2026-04-29.md server-rs/crates/module-ai/README.md server-rs/crates/module-ai/src/lib.rs server-rs/crates/module-ai/src/domain.rs server-rs/crates/module-ai/src/domain/ids.rs server-rs/crates/module-ai/src/domain/stages.rs server-rs/crates/module-ai/src/domain/types.rs server-rs/crates/module-ai/src/commands.rs server-rs/crates/module-ai/src/commands/inputs.rs server-rs/crates/module-ai/src/commands/validation.rs server-rs/crates/module-ai/src/application.rs server-rs/crates/module-ai/src/application/result.rs server-rs/crates/module-ai/src/application/service.rs server-rs/crates/module-ai/src/application/store.rs server-rs/crates/module-ai/src/tests.rs +npm.cmd run api-server:maincloud +``` + +结果:通过。`module-ai` 格式检查通过;`module-ai` 9 个测试全部通过;`spacetime-module` 编译通过;DDD 边界检查通过 15 个 module crate;编码检查通过 17 个文件;`git diff --check` 通过;`npm.cmd run api-server:maincloud` 已启动本仓库 `api-server.exe`,`GET http://127.0.0.1:3100/healthz` 返回 `200`。 + +### 2026-04-29 WP-FE-S Story Session API Client 收口切片 + +已完成: + +1. 认领 `WP-FE-S Frontend API client 迁移` 中可并行的 story session API client 收口切片,完成后认领状态已更新为 `已关闭`。 +2. `src/services/rpg-runtime/rpgRuntimeStoryClient.ts` 新增 `beginStorySession`、`continueStorySession`、`getStorySessionState`、`getStoryRuntimeProjection`,统一封装 `/api/story/sessions*` 新主链路由。 +3. 新 client 统一 trim `storySessionId` 并在空值时抛中文错误,避免后续 hooks/components 再散落路径常量和空 ID 兜底分支。 +4. `getRuntimeStoryState` 复用 `getStoryRuntimeProjection`,读取侧继续保持不回退 `runtimeSessionId` 的约束。 +5. `src/services/rpg-runtime/index.ts` 导出新 story session client 函数和结果类型,供后续 `WP-FE-H` 迁移 hooks 使用。 +6. 补回 `src/services/customWorldAgentGenerationProgress.ts` 的“建立场景连接”进度阶段,使前端草稿生成进度重新对齐既有 13 步文档与 service 全量验收。 +7. 更新 `SERVER_RS_DDD_WP_FE_S_RPG_RUNTIME_STORY_CLIENT_MIGRATION_2026-04-29.md` 与 `docs/technical/README.md`,明确本轮只收口 service client;旧 `beginRuntimeStorySession` 与 `resolveRuntimeStoryAction` 仍等待后端完整开局快照和 session scoped 动作结算接口。 + +验证: + +```powershell +npm.cmd run test -- src/services/rpg-runtime/rpgRuntimeStoryClient.test.ts +npm.cmd run test -- src/hooks/rpg-runtime-story/runtimeStoryCoordinator.test.ts +npm.cmd run test -- src/services/customWorldAgentGenerationProgress.test.ts +npm.cmd run test -- src/services +npm.cmd run check:server-rs-ddd +npm.cmd run check:encoding -- packages/shared/src/contracts/story.ts src/services/rpg-runtime/rpgRuntimeStoryClient.ts src/services/rpg-runtime/index.ts src/services/customWorldAgentGenerationProgress.ts src/services/customWorldAgentGenerationProgress.test.ts docs/technical/SERVER_RS_DDD_WP_FE_S_RPG_RUNTIME_STORY_CLIENT_MIGRATION_2026-04-29.md docs/technical/README.md docs/technical/SERVER_RS_DDD_PARALLEL_TASKLIST_2026-04-29.md +``` + +结果:通过。`rpgRuntimeStoryClient.test.ts` 共 14 个测试通过;`runtimeStoryCoordinator.test.ts` 共 8 个测试通过;`customWorldAgentGenerationProgress.test.ts` 共 8 个测试通过;`src/services` 全量 68 个测试文件、196 个测试通过;DDD 边界检查和编码检查通过。 + +### 2026-04-29 WP-ST Auth Adapter 目录化切片 + +已完成: + +1. 认领 `WP-ST SpacetimeDB Adapter` 中可并行的 Auth adapter 目录化切片,完成后认领状态已更新为 `已关闭`。 +2. 新增 `SERVER_RS_DDD_WP_ST_AUTH_ADAPTER_SPLIT_2026-04-29.md`,记录本次 Auth adapter 目录化边界。 +3. 将原 `spacetime-module/src/auth.rs` 拆为: + - `spacetime-module/src/auth/mod.rs` + - `spacetime-module/src/auth/tables.rs` + - `spacetime-module/src/auth/procedures.rs` + - `spacetime-module/src/auth/mapper.rs` +4. `tables.rs` 保留 `AuthStoreSnapshot`、`UserAccount`、`AuthIdentity`、`RefreshSession` 表定义和索引,不改字段、accessor 或索引名。 +5. `procedures.rs` 保留四个既有 Auth procedure:`get_auth_store_snapshot`、`upsert_auth_store_snapshot`、`import_auth_store_snapshot`、`export_auth_store_snapshot_from_tables`。 +6. `mapper.rs` 收口 module-auth 持久化快照 JSON 结构和 identity id 组件清理函数,仅供 Auth adapter 内部使用。 +7. 本次不改变 SpacetimeDB 表结构、不改 procedure 签名、不改 `migration.rs`、不生成绑定。 +8. 本次未修改 `api-server`、`spacetime-client`、前端 services/hooks/components。 + +验证: + +```powershell +cargo fmt -p spacetime-module --manifest-path server-rs/Cargo.toml --check +npm.cmd run check:server-rs-ddd +npm.cmd run check:encoding -- docs/technical/SERVER_RS_DDD_WP_ST_AUTH_ADAPTER_SPLIT_2026-04-29.md docs/technical/README.md docs/technical/SERVER_RS_DDD_PARALLEL_TASKLIST_2026-04-29.md server-rs/crates/spacetime-module/src/auth/mod.rs server-rs/crates/spacetime-module/src/auth/tables.rs server-rs/crates/spacetime-module/src/auth/procedures.rs server-rs/crates/spacetime-module/src/auth/mapper.rs +cargo check -p spacetime-module --manifest-path server-rs/Cargo.toml +``` + +结果: + +1. `cargo fmt -p spacetime-module --manifest-path server-rs/Cargo.toml --check` 通过。 +2. `npm.cmd run check:server-rs-ddd` 通过。 +3. `npm.cmd run check:encoding -- ...` 通过,7 个文件编码检查通过。 +4. 当时 `cargo check -p spacetime-module --manifest-path server-rs/Cargo.toml` 被并行 `WP-BF Big Fish` 改动阻断;后续 `WP-BF Big Fish` 关闭切片已修复该问题,并在关闭验收中重新验证 `spacetime-module` 编译。 + +### 2026-04-29 WP-RS Compat 残留审计切片 + +已完成: + +1. 认领 `WP-RS Runtime Story 去兼容层` 中可并行的 compat 残留审计切片,完成后认领状态已更新为 `已关闭`。 +2. 新增 `SERVER_RS_DDD_WP_RS_COMPAT_RESIDUE_AUDIT_2026-04-29.md`,冻结本次残留审计边界。 +3. 清理 `module-runtime-story` 运行代码注释里的 `compat / 兼容` 阶段性定位,将 crate 口径收束为 runtime story 主链纯规则。 +4. 更新 `module-runtime-story/README.md`,明确后续要迁的是旧 `/api/runtime/story/*` 写侧能力,而不是继续扩展兼容桥。 +5. 冻结本次不删除的残留: + - `src/services/rpg-runtime/rpgRuntimeStoryClient.ts` 仍指向 `/api/runtime/story` + - `src/services/rpg-runtime/rpgRuntimeStoryClient.test.ts` 仍断言旧写接口路径 + - `packages/shared/src/contracts/rpgRuntimeStoryState.ts` 与 `server-rs/crates/shared-contracts/src/runtime_story.rs` 仍保留 `RuntimeStoryActionResponse` + - `src/hooks/rpg-runtime-story/**` 仍保留部分旧调用面兼容注释 +6. 上述残留需等待 session scoped 新写接口稳定后,由 `WP-FE-S -> WP-FE-H -> WP-DEL` 顺序处理。 + +验证: + +```powershell +cargo fmt -p module-runtime-story --manifest-path server-rs/Cargo.toml --check +cargo test -p module-runtime-story --manifest-path server-rs/Cargo.toml +npm.cmd run check:server-rs-ddd +npm.cmd run check:encoding -- docs/technical/SERVER_RS_DDD_WP_RS_COMPAT_RESIDUE_AUDIT_2026-04-29.md docs/technical/README.md docs/technical/SERVER_RS_DDD_PARALLEL_TASKLIST_2026-04-29.md server-rs/crates/module-runtime-story/README.md server-rs/crates/module-runtime-story/src/domain.rs server-rs/crates/module-runtime-story/src/commands.rs server-rs/crates/module-runtime-story/src/errors.rs server-rs/crates/module-runtime-story/src/events.rs server-rs/crates/module-runtime-story/src/core.rs server-rs/crates/module-runtime-story/src/game_state.rs server-rs/crates/module-runtime-story/src/battle.rs server-rs/crates/module-runtime-story/src/forge.rs server-rs/crates/module-runtime-story/src/forge_actions.rs server-rs/crates/module-runtime-story/src/npc_support.rs +``` + +结果:通过。`cargo test -p module-runtime-story` 共 8 个测试通过。 + +### 2026-04-29 WP-ST Custom World 根入口瘦身切片 + +已完成: + +1. 新增 `SERVER_RS_DDD_WP_ST_CUSTOM_WORLD_ROOT_SPLIT_2026-04-29.md`,记录本次 Custom World 根入口瘦身边界。 +2. `spacetime-module/src/lib.rs` 接入 `mod custom_world; pub use custom_world::*;`,根入口继续公开绑定生成所需类型。 +3. 将当前根入口已公开的 Custom World table、reducer、procedure、事务 helper 与单测迁入 `spacetime-module/src/custom_world/mod.rs`。 +4. `custom_world/mod.rs` 以 `use crate::*;` 复用根模块公开的 SpacetimeDB 类型、领域类型和 JSON helper。 +5. 移除 `lib.rs` 中 Custom World 的重复实现,使根入口只负责组合 `ai`、`asset_metadata`、`auth`、`big_fish`、`custom_world`、`gameplay`、`runtime` 等 adapter。 + +边界说明: + +1. 本次不改变 SpacetimeDB 表名、字段、索引、reducer/procedure 对外名称。 +2. 本次不修改 `migration.rs` 或表目录。 +3. 本次不启用 `custom_world` 子目录中此前未由根入口正式导出的浏览历史过程。 +4. 本次未修改 `api-server`、`spacetime-client`、前端 services/hooks/components。 + +验证: + +```powershell +cargo check -p spacetime-module --manifest-path server-rs/Cargo.toml +cargo test -p module-custom-world --manifest-path server-rs/Cargo.toml +npm.cmd run check:server-rs-ddd +npm.cmd run check:encoding -- docs/technical/SERVER_RS_DDD_PARALLEL_TASKLIST_2026-04-29.md docs/technical/SERVER_RS_DDD_WP_ST_CUSTOM_WORLD_ROOT_SPLIT_2026-04-29.md server-rs/crates/spacetime-module/src/lib.rs server-rs/crates/spacetime-module/src/custom_world/mod.rs +``` + +结果:通过。`module-custom-world` 共 13 个测试通过。 + +格式化补充: + +```powershell +cargo fmt -p spacetime-module --manifest-path server-rs/Cargo.toml +git diff --check -- server-rs/crates/spacetime-module/src/lib.rs server-rs/crates/spacetime-module/src/custom_world/mod.rs docs/technical/SERVER_RS_DDD_WP_ST_CUSTOM_WORLD_ROOT_SPLIT_2026-04-29.md +``` + +结果:通过。`cargo fmt --manifest-path server-rs/Cargo.toml` 曾因 workspace 根清单无 target 报 `Failed to find targets`,随后改用 `-p spacetime-module` 完成格式化。 + +后端启动: + +```powershell +npm.cmd run api-server:maincloud +``` + +结果:命令在 90 秒观察窗口内超时,因为 `cargo run` 前台常驻;随后探测 `http://127.0.0.1:3100/healthz` 返回 `200`,本地 `api-server` 运行中。 + 后端启动: ```powershell @@ -974,6 +1843,94 @@ npm.cmd run api-server:maincloud 结果:编译通过,仅有既有 prompt helper warning;运行阶段因 `127.0.0.1:3100` 端口已被既有 `api-server` 进程占用而退出,错误为 `AddrInUse / 10048`。随后探测 `http://127.0.0.1:3100/healthz` 返回 `200`,确认本地已有服务在线。 +### 2026-04-29 WP-API story owner guard 边界闭环 + +已完成: + +1. `continue_story` 在写入 story event 前,先通过 `spacetime-client.get_story_session_state` 读取 session,并校验 token 用户与 `story_session.actor_user_id` 一致。 +2. `get_story_session_state` 返回前校验 token 用户与 `story_session.actor_user_id` 一致。 +3. `create_story_battle` 与 `create_story_npc_battle` 创建战斗前校验目标 `story_session` 属主,避免用当前 token 给其他用户 session 挂 battle。 +4. `get_story_battle_state` 返回前校验 token 用户与 `battle_state.actor_user_id` 一致。 +5. `resolve_story_battle` 执行战斗结算前先读取 battle state 并校验属主;结算本身仍只走 `spacetime-client.resolve_combat_action`。 +6. 新增 story session / story battle owner guard 的轻量单元测试。 + +边界说明: + +1. API 层只做登录用户与资源属主的 HTTP 鉴权边界检查,不复制 story 推进、战斗结算、奖励发放等领域规则。 +2. 所有 SpacetimeDB 访问仍通过 `spacetime-client` facade,不绕过生成绑定。 +3. 本次未修改 SpacetimeDB 表结构,未触碰 `migration.rs`。 + +验证: + +```powershell +cargo test -p api-server story_session --manifest-path server-rs/Cargo.toml +cargo test -p api-server story_battle --manifest-path server-rs/Cargo.toml +cargo fmt -p api-server -p shared-contracts --manifest-path server-rs/Cargo.toml --check +cargo check -p api-server --manifest-path server-rs/Cargo.toml +npm.cmd run check:server-rs-ddd +npm.cmd run check:encoding -- server-rs/crates/api-server/src/story_sessions.rs server-rs/crates/api-server/src/story_battles.rs docs/technical/SERVER_RS_DDD_PARALLEL_TASKLIST_2026-04-29.md +``` + +结果:通过。`story_session` 与 `story_battle` 定向测试各 9 个通过;`cargo check -p api-server` 仍有既有 `api-server/src/prompt/rpg/runtime_chat.rs` 未使用 prompt helper warning,非本次新增。 + +后端启动: + +```powershell +npm.cmd run api-server:maincloud +``` + +结果:命令在 90 秒观察窗口内超时,因为 `cargo run` 前台常驻;随后确认 `127.0.0.1:3100` 已由本仓库 `server-rs/target/debug/api-server.exe` 监听。`GET /healthz` 返回 `200`,`GET /api/story/battles/battle_001`、`GET /api/story/sessions/storysess_001/state`、`GET /api/story/sessions/storysess_001/runtime-projection` 未登录均返回 `401`。 + +### 2026-04-29 WP-API story/game facade 当前阶段收口 + +已完成: + +1. 对照 G1 冻结矩阵复核 `WP-API` 当前 story/game facade: + - `POST /api/story/sessions` + - `POST /api/story/sessions/continue` + - `GET /api/story/sessions/{story_session_id}/state` + - `GET /api/story/sessions/{story_session_id}/runtime-projection` + - `POST /api/story/battles` + - `GET /api/story/battles/{battle_state_id}` + - `POST /api/story/npc/battle` + - `POST /api/story/battles/resolve` +2. 补齐 `continue_story` 与 `resolve_story_battle` 的未登录回归测试。 +3. `create_story_battle` 与 `create_story_npc_battle` 在创建 battle 前,除校验 story session 属主外,额外校验请求体 `runtimeSessionId` 与 story session 的 `runtimeSessionId` 一致。 +4. 当前 WP-API 不恢复旧 `/api/runtime/story/*` 总入口,也不在 API 层复刻旧 `RuntimeStoryActionResponse`。 + +当前完成口径: + +1. G1 已冻结的 `/api/story/*` story session、battle、NPC battle BFF 当前阶段已具备鉴权、request context、DTO 映射、`spacetime-client` facade 调用、错误 envelope 和 owner/runtime 边界检查。 +2. `runtime-projection` 读取侧已可作为 `WP-FE-S` 迁移输入。 +3. 旧 runtime story 写总入口拆分后,forge、inventory action、NPC trade/gift、AI story continuation 等尚无完整 session scoped `WP-ST/WP-SC` facade;这些能力不得由 `WP-API` 绕过 `spacetime-client` 或复制旧前端/Node 规则硬补。 + +后续依赖: + +1. `WP-ST/WP-SC` 提供更细的 story action / inventory action / NPC interaction facade 后,`WP-API` 再新增对应 session scoped route。 +2. `WP-FE-S` 可继续基于已稳定的读取投影迁移;写侧需要等待上述新 facade,不应回退到旧 `/api/runtime/story/*`。 +3. `WP-DEL` 仍需等前端不再引用旧 DTO 后,统一删除 `shared-contracts/src/runtime_story.rs`、`packages/shared/src/contracts/rpgRuntimeStory*` 和旧前端 helper。 + +验证: + +```powershell +cargo test -p api-server story_session --manifest-path server-rs/Cargo.toml +cargo test -p api-server story_battle --manifest-path server-rs/Cargo.toml +cargo test -p api-server runtime_story_legacy_routes_are_not_mounted --manifest-path server-rs/Cargo.toml +cargo check -p api-server --manifest-path server-rs/Cargo.toml +npm.cmd run check:server-rs-ddd +npm.cmd run check:encoding -- server-rs/crates/api-server/src/story_sessions.rs server-rs/crates/api-server/src/story_battles.rs docs/technical/SERVER_RS_DDD_PARALLEL_TASKLIST_2026-04-29.md +``` + +结果:通过。`story_session` 定向测试 10 个通过,`story_battle` 定向测试 12 个通过,旧 runtime story 路由未挂载回归测试通过;`cargo check -p api-server` 仍有既有 `api-server/src/prompt/rpg/runtime_chat.rs` 未使用 prompt helper warning,非本次新增。 + +后端启动: + +```powershell +npm.cmd run api-server:maincloud +``` + +结果:命令在 124 秒观察窗口内超时,因为 `cargo run` 前台常驻;随后确认 `127.0.0.1:3100` 已由本仓库 `server-rs/target/debug/api-server.exe` 监听。`GET /healthz` 返回 `200`;`GET /api/story/sessions/storysess_001/state`、`GET /api/story/sessions/storysess_001/runtime-projection`、`GET /api/story/battles/battle_001`、`POST /api/story/battles/resolve` 未登录均返回 `401`,旧 `POST /api/runtime/story/actions/resolve` 返回 `404`。 + ### 2026-04-29 WP-API story runtime projection route 接线 已完成: @@ -1016,6 +1973,37 @@ npm.cmd run api-server:maincloud 结果:命令在 60 秒观察窗口内超时,但随后探测 `http://127.0.0.1:3100/healthz` 返回 `200`,本地存在新的 `api-server` 运行进程。本切片未触发新的 Rust 编译错误。 +### 2026-04-29 WP-FE-S RPG runtime story client 读取侧迁移 + +已完成: + +1. 新增前端 story session 投影契约 `packages/shared/src/contracts/story.ts`,并从 `packages/shared/src/index.ts` 导出。 +2. `GameState` 与 runtime snapshot 水合类型新增 `storySessionId`,水合时保留为独立 story 会话主键。 +3. `src/services/rpg-runtime/rpgRuntimeStoryClient.ts` 的读取侧迁到: + - `GET /api/story/sessions/{storySessionId}/runtime-projection` + - `StoryRuntimeProjectionResponse` +4. `loadRuntimeInventoryView` 改为从新投影映射背包视图。 +5. `rpgRuntimeStoryGateway` 的 option catalog / resume 读取侧改为使用 `storySessionId`,缺失时显式失败,不再用 `runtimeSessionId` 兜底。 + +当前边界: + +1. `beginRuntimeStorySession` 暂未迁移,因为新 `/api/story/sessions` 当前只创建 story session,不返回开局 `GameState` 快照。 +2. `resolveRuntimeStoryAction` 暂未迁移,因为新主链尚未提供等价的 story session scoped 动作结算与状态更新回包。 +3. `WP-FE-H` hooks 大迁移继续等待后端写接口稳定;本轮只做读取侧必要网关接线。 + +验证: + +```powershell +npm.cmd run test -- src/services/rpg-runtime/rpgRuntimeStoryClient.test.ts +npm.cmd run test -- src/hooks/rpg-runtime-story/runtimeStoryCoordinator.test.ts +npm.cmd run test -- src/persistence/runtimeSnapshot.test.ts +npm.cmd run check:encoding -- packages/shared/src/contracts/story.ts src/services/rpg-runtime/rpgRuntimeStoryClient.ts src/hooks/rpg-runtime-story/rpgRuntimeStoryGateway.ts src/hooks/rpg-runtime-story/inventoryActions.ts src/persistence/runtimeSnapshot.ts src/persistence/runtimeSnapshotTypes.ts src/types/game.ts docs/technical/SERVER_RS_DDD_WP_FE_S_RPG_RUNTIME_STORY_CLIENT_MIGRATION_2026-04-29.md docs/technical/SERVER_RS_DDD_PARALLEL_TASKLIST_2026-04-29.md docs/technical/README.md +npm.cmd run check:server-rs-ddd +npm.cmd run typecheck -- --pretty false +``` + +结果:前三组定向测试通过;编码检查通过;`check:server-rs-ddd` 通过。`typecheck` 失败在既有文件 `src/components/rpg-runtime-shell/RpgRuntimeShell.test.tsx`、`src/data/sceneEncounterPreviews.ts`、`src/services/ai.ts`,错误包括测试桩字段与当前类型不匹配、`slot` 可能为 `undefined`、`hasMixedNarrativeLanguage` 未定义,未指向本轮 WP-FE-S 修改文件。 + ### 2026-04-29 WP-SC story runtime projection source 接线 已完成: @@ -1056,6 +2044,51 @@ npm.cmd run api-server:maincloud 结果:命令在 60 秒观察窗口内超时,但随后探测 `http://127.0.0.1:3100/healthz` 返回 `200`,本地存在 `api-server` 运行进程。本切片未触发新的 Rust 编译错误。 +### 2026-04-29 WP-SC story runtime inventory source 接线 + +已完成: + +1. 认领 `WP-SC Spacetime Client` 中可并行的 story runtime inventory source 接线切片,完成后认领状态已更新为 `已关闭`。 +2. 更新 `SERVER_RS_DDD_WP_SC_SPACETIME_CLIENT_REFACTOR_2026-04-29.md`,补齐本次切片目标、文件边界、实现约束与验收命令。 +3. `SpacetimeClient::get_story_runtime_projection_source` 在读取 story session state 和 runtime snapshot 后,继续读取稳定的 `get_runtime_inventory_state` typed facade。 +4. 新增投影 source 内部 mapper,将 typed inventory 背包/装备记录折回 `game_state.playerInventory` 与 `game_state.playerEquipment`,供 `module-runtime-story::build_story_runtime_projection` 继续统一生成前端投影。 +5. 新增 runtime inventory 与 runtime snapshot 的 `runtimeSessionId` 校验,以及 inventory `actorUserId` 与 story session actor 的一致性校验。 +6. inventory 覆盖只刷新 canonical 背包字段,并保留旧 `game_state` 物品里的 `useProfile`、`statProfile`、`buildProfile` 等扩展字段,避免读取投影洗掉战斗/装备展示信息。 +7. 为完成后端启动验收,最小修复并行 `runtime_save.rs` 中两处纯编译阻塞:`RuntimeSessionMismatch` 错误映射的局部 move,以及重复 `#[tokio::test]` 属性;未改动 runtime save 业务语义。 + +边界说明: + +1. 本次未修改 `spacetime-module`、`migration.rs` 或 `spacetime-client/src/module_bindings/**`。 +2. 本次未修改 HTTP DTO、前端 services/hooks/components,也未触碰 `api-server/src/app.rs`。 +3. 本次不新增 story action、inventory action、NPC interaction 写侧接口;这些仍等待 `WP-ST/WP-SC` 后续更细 facade。 + +验证: + +```powershell +cargo test -p spacetime-client story_runtime --manifest-path server-rs/Cargo.toml +cargo check -p spacetime-client --manifest-path server-rs/Cargo.toml +cargo fmt -p spacetime-client --manifest-path server-rs/Cargo.toml --check +cargo check -p api-server --manifest-path server-rs/Cargo.toml +cargo test -p api-server story_session --manifest-path server-rs/Cargo.toml +rustfmt --edition 2024 --check server-rs/crates/api-server/src/runtime_save.rs +npm.cmd run check:server-rs-ddd +npm.cmd run check:encoding -- docs/technical/SERVER_RS_DDD_WP_SC_SPACETIME_CLIENT_REFACTOR_2026-04-29.md docs/technical/SERVER_RS_DDD_PARALLEL_TASKLIST_2026-04-29.md server-rs/crates/spacetime-client/src/story_runtime.rs server-rs/crates/api-server/src/runtime_save.rs +git diff --check -- docs/technical/SERVER_RS_DDD_WP_SC_SPACETIME_CLIENT_REFACTOR_2026-04-29.md docs/technical/SERVER_RS_DDD_PARALLEL_TASKLIST_2026-04-29.md server-rs/crates/spacetime-client/src/story_runtime.rs server-rs/crates/api-server/src/runtime_save.rs +npm.cmd run api-server:maincloud +``` + +结果:通过。`spacetime-client story_runtime` 定向测试 7 个通过;`spacetime-client` 与 `api-server` 编译通过;`api-server story_session` 定向测试 10 个通过;DDD 边界检查通过 15 个 module crate;编码检查和 diff 空白检查通过。`api-server` 编译仍有既有 `api-server/src/prompt/rpg/runtime_chat.rs` 未使用代码 warning,非本次新增。 + +格式说明:`cargo fmt -p api-server --check` 会被当前工作区既有的 `api-server/src/app.rs` 格式差异挡住;该文件是 `WP-API` 单 owner 文件,本次未触碰。已用 `rustfmt --edition 2024 --check server-rs/crates/api-server/src/runtime_save.rs` 验证本次最小编译修复文件格式。 + +后端启动: + +```powershell +npm.cmd run api-server:maincloud +``` + +结果:命令按常驻服务启动,本地 `server-rs/target/debug/api-server.exe` 已运行并监听 `127.0.0.1:3100`;`GET http://127.0.0.1:3100/healthz` 返回 `200`。 + ### 2026-04-29 WP-ST Big Fish 发布门禁 Adapter 切片 已完成: @@ -1148,6 +2181,56 @@ npm.cmd run check:encoding -- server-rs/crates/spacetime-module/src/puzzle.rs se 结果:通过。`cargo test -p module-puzzle` 共 13 个测试通过。 +### 2026-04-29 WP-ST Gameplay 根入口瘦身切片 + +已完成: + +1. 新增 `SERVER_RS_DDD_WP_ST_GAMEPLAY_ROOT_SPLIT_2026-04-29.md`,记录本次根入口瘦身边界。 +2. `spacetime-module/src/lib.rs` 接入 `mod gameplay; pub use gameplay::*;`。 +3. 从 `lib.rs` 删除已迁入 `gameplay/mod.rs` 的 RPG gameplay table、reducer、procedure、row mapper 和事务 helper。 +4. 在 `gameplay/mod.rs` 补齐模块内依赖导入,并补齐 `continue_story_tx`、`get_story_session_state_tx` 两个 story 内部 helper。 +5. 根入口当前继续保留 Custom World adapter,后续可单独拆分,不与本次 RPG gameplay 瘦身混写。 + +边界说明: + +1. 本次不改变 SpacetimeDB 表名、字段、reducer/procedure 对外名称。 +2. 本次不修改 `migration.rs` 或 `SPACETIMEDB_TABLE_CATALOG.md`。 +3. 本次未修改 `api-server`、`spacetime-client`、前端 services/hooks/components。 + +验证: + +```powershell +cargo check -p spacetime-module --manifest-path server-rs/Cargo.toml +npm.cmd run check:server-rs-ddd +npm.cmd run check:encoding -- server-rs/crates/spacetime-module/src/lib.rs server-rs/crates/spacetime-module/src/gameplay/mod.rs docs/technical/SERVER_RS_DDD_WP_ST_GAMEPLAY_ROOT_SPLIT_2026-04-29.md docs/technical/SERVER_RS_DDD_PARALLEL_TASKLIST_2026-04-29.md +``` + +结果:通过。额外执行 `module-story`、`module-combat`、`module-inventory`、`module-quest`、`module-npc`、`module-progression`、`module-runtime-item` 定向测试,均通过。 + +补充: + +```powershell +cargo test -p spacetime-module --manifest-path server-rs/Cargo.toml +``` + +结果:Windows host 链接失败,`link.exe` 报 SpacetimeDB runtime 外部符号缺失(如 `datastore_insert_bsatn`、`console_log`、`table_id_from_name`),属于 SpacetimeDB module crate 不能直接按 native test executable 链接的工具链限制;本次以 `cargo check -p spacetime-module` 和各领域 crate 定向测试作为有效验证。 + +SpacetimeDB build: + +```powershell +spacetime build -p server-rs/crates/spacetime-module --debug +``` + +结果:命令在 180 秒观察窗口内超时,已清理残留构建进程;本切片仍以 `cargo check -p spacetime-module` 作为本地编译门禁。 + +后端启动: + +```powershell +npm.cmd run api-server:maincloud +``` + +结果:命令在 90 秒观察窗口内超时,随后探测 `http://127.0.0.1:3100/healthz` 返回 `200`,本地 `api-server` 运行中。 + ### 2026-04-29 WP-ST Asset Row Mapper Adapter 切片 已完成: @@ -1209,3 +2292,31 @@ npm.cmd run check:server-rs-ddd ``` 结果:通过。`cargo check -p api-server` 仍有既有 `api-server/src/prompt/rpg/runtime_chat.rs` 未使用 prompt helper warning,非本次新增。 + +### 2026-04-29 WP-RPG module-story 领域拆分收口切片 + +已完成: + +1. 新增 `SERVER_RS_DDD_WP_RPG_STORY_DOMAIN_SPLIT_2026-04-29.md`,记录本次 `module-story` 收口边界。 +2. `module-story/src/domain.rs` 收口 story session 领域快照、状态、ID 前缀和 `generate_story_session_id`。 +3. `module-story/src/commands.rs` 收口 `StorySessionInput`、`StoryContinueInput`、`StorySessionStateInput`、输入构造与字段校验。 +4. `module-story/src/events.rs` 收口 `StoryEventKind`、`StoryEventSnapshot`、开局事件与事件 ID 生成。 +5. `module-story/src/application.rs` 收口会话快照构造、续写应用服务、procedure result 类型和只读 record mapper。 +6. `module-story/src/errors.rs` 收口 `StorySessionFieldError` 与中文错误文案。 +7. `module-story/src/lib.rs` 收口为模块声明和公开导出,继续保持 `module_story::*` 公开 API。 +8. `module-story/README.md` 已从“目录占位”改为当前真实边界,并明确不恢复旧 `/api/runtime/story/*` 兼容路由。 + +边界说明: + +1. 本次不改 SpacetimeDB 表、reducer、procedure、绑定 shape 或 `migration.rs`。 +2. 本次不新增 story action 写接口,不接 LLM/SSE/HTTP route,也不改前端 hooks。 +3. 完整动作结算、inventory action、NPC interaction、forge/battle/quest 写侧闭环继续等待 `WP-RS/WP-ST/WP-SC/WP-API/WP-FE`。 + +验证: + +```powershell +cargo test -p module-story --manifest-path server-rs\Cargo.toml +cargo check -p module-story --manifest-path server-rs\Cargo.toml +``` + +结果:通过,`module-story` 8 个单元测试通过。 diff --git a/docs/technical/SERVER_RS_DDD_WP_AI_INTERNAL_MODULE_SPLIT_2026-04-29.md b/docs/technical/SERVER_RS_DDD_WP_AI_INTERNAL_MODULE_SPLIT_2026-04-29.md new file mode 100644 index 00000000..a915a25a --- /dev/null +++ b/docs/technical/SERVER_RS_DDD_WP_AI_INTERNAL_MODULE_SPLIT_2026-04-29.md @@ -0,0 +1,94 @@ +# server-rs DDD WP-AI 内部子模块拆分记录(2026-04-29) + +## 1. 背景 + +`WP-AI AI Task` 已完成领域层、SpacetimeDB adapter、spacetime-client facade 与 BFF route 闭环。当前继续推进 DDD 收口时,`module-ai` 虽然已经从首轮 `lib.rs` 大文件拆成 `domain / commands / application / events / errors`,但 `domain.rs`、`commands.rs`、`application.rs` 仍承载多类职责,后续继续演进阶段规则、任务结果聚合或 store 实现时容易重新堆成大文件。 + +本次只做 `module-ai` crate 内部子模块拆分,保持 `module_ai::*` 对外公开导出不变,不改变 SpacetimeDB table / reducer / procedure / event table,不改变 HTTP DTO、route 或前端调用。 + +## 2. 本次拆分范围 + +允许修改: + +1. `server-rs/crates/module-ai/src/domain.rs` +2. `server-rs/crates/module-ai/src/domain/*` +3. `server-rs/crates/module-ai/src/commands.rs` +4. `server-rs/crates/module-ai/src/commands/*` +5. `server-rs/crates/module-ai/src/application.rs` +6. `server-rs/crates/module-ai/src/application/*` +7. `server-rs/crates/module-ai/src/lib.rs` +8. `server-rs/crates/module-ai/src/tests.rs` +9. `server-rs/crates/module-ai/README.md` +10. 本文档、`docs/technical/README.md` 与全局任务清单进度记录 + +禁止修改: + +1. `server-rs/crates/spacetime-module/src/**` +2. `server-rs/crates/spacetime-client/src/**` +3. `server-rs/crates/api-server/src/**` +4. `server-rs/crates/shared-contracts/src/**` +5. `packages/shared/src/contracts/**` +6. `server-rs/crates/spacetime-module/src/migration.rs` + +## 3. 拆分落点 + +```text +server-rs/crates/module-ai/src/ +├─ application.rs +├─ application/ +│ ├─ result.rs +│ ├─ service.rs +│ └─ store.rs +├─ commands.rs +├─ commands/ +│ ├─ inputs.rs +│ └─ validation.rs +├─ domain.rs +├─ domain/ +│ ├─ ids.rs +│ ├─ stages.rs +│ └─ types.rs +├─ errors.rs +├─ events.rs +├─ lib.rs +└─ tests.rs +``` + +职责说明: + +1. `domain/types.rs` 只放 AI task、stage、chunk、result reference 的领域类型与快照。 +2. `domain/stages.rs` 只放默认阶段蓝图、阶段 slug、阶段中文标签与终态判断。 +3. `domain/ids.rs` 只放 ID 前缀、ID helper 和共享字符串归一 helper re-export。 +4. `commands/inputs.rs` 只放写入输入结构。 +5. `commands/validation.rs` 只放创建任务输入校验。 +6. `application/result.rs` 只放面向 SpacetimeDB procedure 的轻量结果结构。 +7. `application/service.rs` 只放 AI task 状态机应用服务。 +8. `application/store.rs` 只放当前内存 store 与流式文本片段聚合。 +9. `tests.rs` 承接原 `lib.rs` 行为测试,`lib.rs` 只保留模块声明与公开导出。 + +## 4. 行为不变口径 + +本次必须保持: + +1. `module_ai::*` 公开导出兼容。 +2. 默认阶段蓝图顺序不变。 +3. 任务状态迁移不变。 +4. 终态任务不允许继续写入阶段、文本片段、结果引用或完成状态。 +5. 流式文本片段继续按 `sequence` 聚合到阶段输出和 `latest_text_output`。 +6. 中文错误文案不改写为英文。 +7. 不新增真实 LLM provider、prompt 组装、SSE 协议或前端消费逻辑。 + +## 5. 验收 + +必须执行: + +```powershell +cargo fmt -p module-ai --manifest-path server-rs/Cargo.toml --check +cargo test -p module-ai --manifest-path server-rs/Cargo.toml +cargo check -p spacetime-module --manifest-path server-rs/Cargo.toml +npm.cmd run check:server-rs-ddd +npm.cmd run check:encoding -- docs/technical/SERVER_RS_DDD_WP_AI_INTERNAL_MODULE_SPLIT_2026-04-29.md docs/technical/README.md docs/technical/SERVER_RS_DDD_PARALLEL_TASKLIST_2026-04-29.md server-rs/crates/module-ai/README.md server-rs/crates/module-ai/src/lib.rs server-rs/crates/module-ai/src/domain.rs server-rs/crates/module-ai/src/domain/ids.rs server-rs/crates/module-ai/src/domain/stages.rs server-rs/crates/module-ai/src/domain/types.rs server-rs/crates/module-ai/src/commands.rs server-rs/crates/module-ai/src/commands/inputs.rs server-rs/crates/module-ai/src/commands/validation.rs server-rs/crates/module-ai/src/application.rs server-rs/crates/module-ai/src/application/result.rs server-rs/crates/module-ai/src/application/service.rs server-rs/crates/module-ai/src/application/store.rs server-rs/crates/module-ai/src/tests.rs +npm.cmd run api-server:maincloud +``` + +`api-server:maincloud` 是常驻后端启动命令,验收时以命令启动和 `GET http://127.0.0.1:3100/healthz` 探测结果记录为准。 diff --git a/docs/technical/SERVER_RS_DDD_WP_AI_TASK_BFF_CLOSURE_2026-04-29.md b/docs/technical/SERVER_RS_DDD_WP_AI_TASK_BFF_CLOSURE_2026-04-29.md new file mode 100644 index 00000000..85cffcb0 --- /dev/null +++ b/docs/technical/SERVER_RS_DDD_WP_AI_TASK_BFF_CLOSURE_2026-04-29.md @@ -0,0 +1,73 @@ +# server-rs DDD WP-AI AI Task BFF 收口记录(2026-04-29) + +## 1. 背景 + +`WP-AI AI Task` 的领域层已完成 DDD 拆分,`spacetime-module/src/ai/*` 已具备 AI task 真相表、阶段表、文本片段、结果引用、事件表和最小 reducer / procedure,`spacetime-client` 已提供 typed facade,`api-server` 已挂载 AI task mutation route。 + +本次认领的目标不是新增模型供应商能力,也不是改表,而是把现有 AI task BFF 链路做闭环验证,确认前端或后续业务模块调用 AI task 写接口时不会绕过鉴权、不会在 SpacetimeDB 未发布时返回错误形态不一致的响应。 + +## 2. 本次完成范围 + +1. 补齐 `api-server/src/ai_tasks.rs` 的 AI task mutation route 定向测试。 +2. 覆盖以下写接口的未登录拦截: + - `POST /api/ai/tasks/{task_id}/stages/{stage_kind}/start` + - `POST /api/ai/tasks/{task_id}/chunks` + - `POST /api/ai/tasks/{task_id}/stages/{stage_kind}/complete` + - `POST /api/ai/tasks/{task_id}/references` + - `POST /api/ai/tasks/{task_id}/complete` + - `POST /api/ai/tasks/{task_id}/fail` + - `POST /api/ai/tasks/{task_id}/cancel` +3. 覆盖上述写接口在 token 有效但 SpacetimeDB 未发布时统一返回 `502 BAD_GATEWAY`,并保持错误详情 `provider = spacetimedb`。 +4. 复核 `module-ai`、`spacetime-client`、`spacetime-module` 与 BFF 定向测试,确认本次不需要修改 `migration.rs`。 + +## 3. 边界 + +本次未进入: + +1. 真实 LLM provider 调用、prompt 组装和供应商降级策略。 +2. SSE 流式输出协议。 +3. AI task 订阅 projection、清理调度或前端消费 UI。 +4. SpacetimeDB table、reducer、procedure、event table 的结构变更。 +5. 共享契约字段调整或前端 API client 改造。 + +这些能力继续归入 `WP-PF platform side effects`、`WP-API api-server BFF`、`WP-FE` 和后续对应业务域工作包,不在 `module-ai` 领域核心中混入。 + +## 4. 验收 + +已执行: + +```powershell +cargo fmt -p api-server --manifest-path server-rs\Cargo.toml --check +cargo test -p api-server ai_task --manifest-path server-rs\Cargo.toml +cargo test -p module-ai --manifest-path server-rs\Cargo.toml +cargo check -p spacetime-client --manifest-path server-rs\Cargo.toml +cargo check -p spacetime-module --manifest-path server-rs\Cargo.toml +cargo check -p api-server --manifest-path server-rs\Cargo.toml +npm.cmd run check:server-rs-ddd +npm.cmd run check:encoding -- docs/technical/SERVER_RS_DDD_PARALLEL_TASKLIST_2026-04-29.md docs/technical/SERVER_RS_DDD_WP_AI_TASK_BFF_CLOSURE_2026-04-29.md docs/technical/README.md server-rs/crates/api-server/src/ai_tasks.rs server-rs/crates/api-server/src/wechat_auth.rs +npm.cmd run api-server:maincloud +``` + +结果: + +1. `api-server ai_task` 定向测试通过,7 个测试全部通过。 +2. `module-ai` 测试通过,9 个测试全部通过。 +3. `spacetime-client` 编译通过。 +4. `spacetime-module` 编译通过。 +5. `api-server` 编译通过,仅保留既有未使用代码 warning。 +6. `npm.cmd run check:server-rs-ddd` 通过。 +7. 编码检查通过,5 个文件均为 UTF-8。 +8. `npm.cmd run api-server:maincloud` 已启动本仓库 `server-rs/target/debug/api-server.exe`,`GET http://127.0.0.1:3100/healthz` 返回 `200`。 +9. 未执行 SpacetimeDB 发布、绑定生成或 migration 更新,原因是本次未改变 SpacetimeDB schema、reducer/procedure 签名或绑定形状。 + +## 5. 关闭口径 + +`WP-AI AI Task` 当前已完成: + +1. 领域层 DDD 拆分。 +2. SpacetimeDB AI task 真相表、阶段表、文本片段、结果引用和事件表 adapter。 +3. `spacetime-client` typed facade。 +4. `api-server` AI task mutation route 与错误 envelope。 +5. BFF 鉴权和 SpacetimeDB 未发布错误形态的定向回归测试。 + +因此本次将工作包认领状态从 `已认领` 更新为 `已关闭`。后续真实模型调用、SSE 和前端消费不再阻塞 `WP-AI` 关闭,分别由平台副作用、API 编排和前端迁移工作包继续承接。 diff --git a/docs/technical/SERVER_RS_DDD_WP_AS_ASSET_OBJECT_TYPE_REHOME_2026-04-29.md b/docs/technical/SERVER_RS_DDD_WP_AS_ASSET_OBJECT_TYPE_REHOME_2026-04-29.md new file mode 100644 index 00000000..35a7b14d --- /dev/null +++ b/docs/technical/SERVER_RS_DDD_WP_AS_ASSET_OBJECT_TYPE_REHOME_2026-04-29.md @@ -0,0 +1,58 @@ +# WP-AS Assets 资产对象类型归位落地说明 + +## 背景 + +`module-assets` 已具备 `domain / commands / application / errors / events` DDD 骨架,但 `asset_object_core.rs` 仍同时承载领域模型、命令 DTO、应用返回 DTO、字段错误和纯构建函数。继续推进资产对象、实体绑定、OSS adapter 与 SpacetimeDB row mapper 时,容易把纯领域事实和外层编排混在同一个文件里。 + +本次作为 `WP-AS Assets` 的小切片,只在 `module-assets` crate 内做类型归位,不改变现有公开 API、SpacetimeDB 表结构、reducer/procedure、OSS 行为、api-server 路由或前端行为。 + +## 本次范围 + +允许修改: + +1. `server-rs/crates/module-assets/src/domain.rs` +2. `server-rs/crates/module-assets/src/commands.rs` +3. `server-rs/crates/module-assets/src/application.rs` +4. `server-rs/crates/module-assets/src/errors.rs` +5. `server-rs/crates/module-assets/src/asset_object_core.rs` +6. `server-rs/crates/module-assets/src/lib.rs` +7. `server-rs/crates/module-assets/README.md` +8. 本文档与全局 DDD 任务清单 + +禁止修改: + +1. `server-rs/crates/spacetime-module/src/**` +2. `server-rs/crates/spacetime-client/src/**` +3. `server-rs/crates/api-server/src/**` +4. `server-rs/crates/platform-oss/src/**` +5. 前端 services/hooks/components + +## 设计 + +本次按 DDD 骨架进行类型归位: + +1. `domain.rs` 承接资产对象、资产历史、实体绑定的纯快照、记录、访问策略、ID 前缀和版本常量。 +2. `commands.rs` 承接确认资产对象、资产历史查询、对象 upsert、实体绑定等输入 DTO。 +3. `application.rs` 承接 procedure result 和确认资产对象结果等应用返回 DTO。 +4. `errors.rs` 承接 `AssetObjectFieldError` 及其中文错误文案。 +5. `asset_object_core.rs` 保留字段校验、输入构建、记录构建、ID 生成和可复用归一化函数。 + +`lib.rs` 继续按原名称导出,避免影响 `spacetime-module`、`api-server` 或既有测试。 + +## 边界说明 + +1. 本次不移动 `AssetObjectService`,因为它仍依赖 `platform-oss` 的对象探测和进程内仓储。 +2. 本次不新增资产任务、manifest 或专业资产表。 +3. 本次不修改 `migration.rs`,因为没有表结构变更。 +4. 本次不改变 `bucket + object_key` 双列真相字段。 + +## 验收 + +```powershell +cargo fmt -p module-assets --manifest-path server-rs/Cargo.toml --check +cargo test -p module-assets --manifest-path server-rs/Cargo.toml +npm.cmd run check:server-rs-ddd +npm.cmd run check:encoding -- docs/technical/SERVER_RS_DDD_WP_AS_ASSET_OBJECT_TYPE_REHOME_2026-04-29.md server-rs/crates/module-assets/src/domain.rs server-rs/crates/module-assets/src/commands.rs server-rs/crates/module-assets/src/application.rs server-rs/crates/module-assets/src/errors.rs server-rs/crates/module-assets/src/asset_object_core.rs server-rs/crates/module-assets/src/lib.rs server-rs/crates/module-assets/README.md docs/technical/SERVER_RS_DDD_PARALLEL_TASKLIST_2026-04-29.md docs/technical/README.md +``` + +后端启动按项目约束执行 `npm.cmd run api-server:maincloud`,若命令以前台服务常驻,则以 `/healthz` 结果记录。 diff --git a/docs/technical/SERVER_RS_DDD_WP_A_AUTH_DOMAIN_VALUE_OBJECT_REFACTOR_2026-04-29.md b/docs/technical/SERVER_RS_DDD_WP_A_AUTH_DOMAIN_VALUE_OBJECT_REFACTOR_2026-04-29.md new file mode 100644 index 00000000..953373a0 --- /dev/null +++ b/docs/technical/SERVER_RS_DDD_WP_A_AUTH_DOMAIN_VALUE_OBJECT_REFACTOR_2026-04-29.md @@ -0,0 +1,121 @@ +# WP-A Auth DDD 分层收口说明 + +## 背景 + +`module-auth` 当前已经具备 `domain / commands / application / events / errors` 文件骨架,但真实认证类型、服务、内存仓、文件持久化、短信 provider、密码哈希和微信状态逻辑仍主要集中在 `src/lib.rs`。这会让后续继续迁移 Auth 领域规则时难以区分纯领域模型和外层 adapter 能力。 + +本次从原先的领域值对象启动切片继续推进到 `WP-A Auth` 收口:把认证上下文的纯领域事实、命令输入、应用返回、领域错误和领域事件落回 DDD 骨架文件,同时核查 `api-server / platform-auth / spacetime-module` 的职责边界。 + +## 本次范围 + +允许修改: + +1. `server-rs/crates/module-auth/src/domain.rs` +2. `server-rs/crates/module-auth/src/commands.rs` +3. `server-rs/crates/module-auth/src/application.rs` +4. `server-rs/crates/module-auth/src/events.rs` +5. `server-rs/crates/module-auth/src/errors.rs` +6. `server-rs/crates/module-auth/src/lib.rs` +7. `server-rs/crates/spacetime-module/src/auth.rs` +8. `server-rs/crates/module-auth/README.md` +9. 本文档 +10. 全局 DDD 任务清单进度记录 + +本次只核查但不改动: + +1. `server-rs/crates/api-server/src/auth*.rs` +2. `server-rs/crates/api-server/src/password_entry.rs` +3. `server-rs/crates/api-server/src/phone_auth.rs` +4. `server-rs/crates/api-server/src/refresh_session.rs` +5. `server-rs/crates/api-server/src/wechat_auth.rs` +6. `server-rs/crates/platform-auth/src/**` + +禁止修改: + +1. 其他玩法域。 +2. 前端 services / hooks / components。 +3. `server-node` 或旧 PostgreSQL 实现。 +4. SpacetimeDB 表结构、reducer/procedure 签名和 `migration.rs`。 + +## 设计 + +本次将以下纯领域事实和规则落入 `domain.rs`: + +1. `AuthLoginMethod` +2. `AuthBindingStatus` +3. `AuthUser` +4. `PhoneNumberSnapshot` +5. `PhoneAuthScene` +6. `WechatIdentityProfile` +7. `WechatAuthScene` +8. `WechatAuthStateRecord` +9. `RefreshSessionClientInfo` +10. `RefreshSessionRecord` +11. `AuthStoreSnapshotRecord` +12. 密码长度、短信验证码长度、验证码 TTL、冷却、失败次数等领域常量。 +13. 手机号规范化、手机号脱敏、公开叙世号规范化、验证码 key 构造等纯函数。 + +本次将以下写入输入落入 `commands.rs`: + +1. `PasswordEntryInput` +2. `ChangePasswordInput` +3. `ResetPasswordInput` +4. `SendPhoneCodeInput` +5. `PhoneLoginInput` +6. `ResolveWechatLoginInput` +7. `CreateWechatAuthStateInput` +8. `BindWechatPhoneInput` +9. `CreateRefreshSessionInput` +10. `RotateRefreshSessionInput` +11. `LogoutCurrentSessionInput` +12. `LogoutAllSessionsInput` +13. `AuthStoreSnapshotUpsertInput` + +本次将以下应用返回落入 `application.rs`: + +1. 登录、换密、重置密码、验证码发送、手机号登录、微信登录、微信绑定、会话签发/轮换/查询/登出等 result。 +2. `AuthStoreSnapshotProcedureResult`。 + +本次将以下错误落入 `errors.rs`: + +1. `PasswordEntryError` +2. `PhoneAuthError` +3. `WechatAuthError` +4. `RefreshSessionError` +5. `LogoutError` +6. 错误展示文案和模块内错误映射辅助函数。 + +本次将领域事件落入 `events.rs`: + +1. `UserCreated` +2. `RefreshSessionIssued` +3. `RefreshSessionRevoked` +4. `PhoneVerified` +5. `WechatIdentityBound` + +`lib.rs` 保留当前服务、进程内仓储和文件持久化实现,但不再继续拥有上述命令、结果、错误、事件和领域值对象定义;公开 API 继续通过 `pub use application::* / commands::* / domain::* / errors::* / events::*` 导出,避免影响现有 BFF 调用。 + +## 边界说明 + +1. `module-auth` 承接账号、refresh session、短信验证码状态、微信 state 和微信绑定规则,不依赖 Axum、SpacetimeDB table API、HTTP status、真实短信 SDK、微信 HTTP 或 JWT/cookie 细节。 +2. `platform-auth` 继续承接短信 provider、微信 OAuth provider、密码哈希、refresh token、JWT 和 cookie 等平台副作用。 +3. `api-server` 只做请求解析、鉴权、DTO 映射、session cookie/JWT 编排和平台/领域服务装配;本次核查未发现 Auth route 需要复制领域状态机。 +4. `spacetime-module/src/auth.rs` 当前仍是认证快照与正式表之间的 SpacetimeDB adapter,不调用短信、微信、JWT、HTTP 或文件系统;本次只修正历史乱码中文注释和错误文案,不改变表结构或 procedure 签名。 +5. `InMemoryAuthStore` 和文件持久化仍保留在 `lib.rs`,作为当前 Auth 运行支撑;总纲中“仓储剥离到 adapter 或测试支撑”的更彻底物理拆分可作为后续非 WP-A 阻塞项继续推进,但本次 WP-A 已完成 DDD 骨架归位和边界核查。 + +## 验收 + +```powershell +cargo fmt -p module-auth --manifest-path server-rs/Cargo.toml --check +cargo test -p module-auth --manifest-path server-rs/Cargo.toml +cargo check -p module-auth --manifest-path server-rs/Cargo.toml --all-features +cargo check -p spacetime-module --manifest-path server-rs/Cargo.toml +cargo test -p api-server auth --manifest-path server-rs/Cargo.toml +cargo test -p api-server wechat --manifest-path server-rs/Cargo.toml +cargo check -p api-server --manifest-path server-rs/Cargo.toml +npm.cmd run check:server-rs-ddd +npm.cmd run check:encoding -- docs/technical/SERVER_RS_DDD_WP_A_AUTH_DOMAIN_VALUE_OBJECT_REFACTOR_2026-04-29.md server-rs/crates/module-auth/src/domain.rs server-rs/crates/module-auth/src/commands.rs server-rs/crates/module-auth/src/application.rs server-rs/crates/module-auth/src/errors.rs server-rs/crates/module-auth/src/events.rs server-rs/crates/module-auth/src/lib.rs server-rs/crates/module-auth/README.md server-rs/crates/spacetime-module/src/auth.rs docs/technical/SERVER_RS_DDD_PARALLEL_TASKLIST_2026-04-29.md docs/technical/README.md +npm.cmd run api-server:maincloud +``` + +本次不发布 SpacetimeDB、不生成绑定、不修改 `migration.rs`,原因是没有表结构、reducer、procedure 或绑定形状变更。 diff --git a/docs/technical/SERVER_RS_DDD_WP_BF_RUNTIME_BACKEND_TRUTH_2026-04-29.md b/docs/technical/SERVER_RS_DDD_WP_BF_RUNTIME_BACKEND_TRUTH_2026-04-29.md new file mode 100644 index 00000000..867e00d2 --- /dev/null +++ b/docs/technical/SERVER_RS_DDD_WP_BF_RUNTIME_BACKEND_TRUTH_2026-04-29.md @@ -0,0 +1,84 @@ +# server-rs DDD WP-BF Big Fish 运行态后端真相源关闭记录(2026-04-29) + +## 1. 背景 + +`WP-BF Big Fish` 已完成发布门禁领域化,但运行态仍由前端本地规则推进,导致前端同时承担输入、实体移动、碰撞、合成、胜负结算和快照更新。按照本轮 DDD 重构边界,前端只负责表现和提交输入,正式运行态规则必须落到 `server-rs`。 + +本次关闭目标是把 Big Fish 从“前端本地运行态”切到“Rust 领域规则 + SpacetimeDB 运行表 + API facade + 前端 client 接入”的单主链,同时移除 Big Fish works mapper 中的旧形状兼容解析。 + +## 2. 本次完成范围 + +1. `module-big-fish` 新增运行态领域快照: + - `BigFishRunStatus` + - `BigFishVector2` + - `BigFishRuntimeEntitySnapshot` + - `BigFishRuntimeSnapshot` +2. `module-big-fish` 新增运行态应用服务: + - `start_big_fish_run` + - `submit_big_fish_input` + - `serialize_runtime_snapshot` + - `deserialize_runtime_snapshot` +3. 运行态规则已由领域层统一负责: + - 初始己方和野生实体生成。 + - 输入向量归一化。 + - 领队、跟随者和野生实体移动。 + - 同级收编、强弱碰撞、三合一升级。 + - 离屏野生实体裁剪和补刷。 + - 胜利、失败和结算事件。 +4. `spacetime-module/src/big_fish/runtime.rs` 新增 Big Fish 运行态 procedure: + - `start_big_fish_run` + - `get_big_fish_run` + - `submit_big_fish_input` +5. 新增 `big_fish_runtime_run` 表,保存 run 快照、最后输入方向、tick、归属用户和会话来源,并已同步 `migration.rs` 与表目录。 +6. `spacetime-client` 新增 Big Fish runtime typed facade 和 record mapper,并重新生成 Rust 绑定。 +7. `api-server` 新增鉴权路由: + - `POST /api/runtime/big-fish/sessions/{session_id}/runs` + - `GET /api/runtime/big-fish/runs/{run_id}` + - `POST /api/runtime/big-fish/runs/{run_id}/input` +8. `shared-contracts` 与前端 shared contract 新增 `BigFishRunResponse`、运行态实体/坐标 DTO 和 `SubmitBigFishInputRequest`。 +9. 前端 Big Fish 运行态 client 改为调用后端 run/input 接口,`PlatformEntryFlowShellImpl` 不再推进本地规则。 +10. 删除 `src/services/big-fish-runtime/bigFishLocalRuntime.ts`,`/big-fish` 调试直达页降级回平台入口,避免继续暴露本地运行态主链。 + +## 3. 边界说明 + +1. 本次不接入 `server-node`、Express 或 PostgreSQL,也不为旧 Node 兼容层保留双主链。 +2. `spacetime-module` 只负责表读写、授权、事务编排和调用领域服务,不在 adapter 内复制 Big Fish 玩法规则。 +3. `api-server` 只负责鉴权、请求校验、DTO 映射和 `spacetime-client` facade 调用,不直接访问 SpacetimeDB table。 +4. 前端只提交方向输入并渲染后端返回快照,正式实体状态、胜负和事件日志都以后端响应为准。 +5. Big Fish works mapper 已移除旧 JSON 兼容解析,后续要求 SpacetimeDB procedure 返回新的 `owner_user_id`、`play_count` 等稳定字段。 +6. `big_fish_runtime_run` 是运行态快照表,不替代创作会话、资产槽或发布门禁真相;删除作品时同步清理来源会话下的 run。 + +## 4. 后续边界 + +1. Big Fish 运行态资源展示仍复用现有 session / work 的资产槽数据;若要让公开作品 run 响应直接携带资产包,应由后续契约任务单独扩展 DTO。 +2. 真实 Maincloud 发布和订阅侧 smoke 仍归 `WP-V 全链验证与发布 smoke`。 +3. 若后续把运行态拆为更细表,而不是整份 `snapshot_json`,必须重新同步 `migration.rs`、绑定和 `SPACETIMEDB_TABLE_CATALOG.md`。 + +## 5. 验收 + +关闭前必须执行: + +```powershell +cargo test -p module-big-fish --manifest-path server-rs/Cargo.toml +cargo check -p spacetime-module --manifest-path server-rs/Cargo.toml +cargo check -p spacetime-client --manifest-path server-rs/Cargo.toml +cargo check -p api-server --manifest-path server-rs/Cargo.toml +npm.cmd run test -- src/components/big-fish-runtime/BigFishRuntimeShell.test.tsx src/components/rpg-entry/RpgEntryFlowShell.agent.interaction.test.tsx +npm.cmd run check:server-rs-ddd +npm.cmd run check:encoding -- docs/technical/SERVER_RS_DDD_WP_BF_RUNTIME_BACKEND_TRUTH_2026-04-29.md docs/technical/SERVER_RS_DDD_PARALLEL_TASKLIST_2026-04-29.md docs/technical/README.md docs/technical/SPACETIMEDB_TABLE_CATALOG.md server-rs/crates/module-big-fish/src/application.rs server-rs/crates/module-big-fish/src/domain.rs server-rs/crates/module-big-fish/src/commands.rs server-rs/crates/module-big-fish/src/errors.rs server-rs/crates/module-big-fish/src/events.rs server-rs/crates/module-big-fish/src/lib.rs server-rs/crates/spacetime-module/src/big_fish/runtime.rs server-rs/crates/spacetime-module/src/big_fish/tables.rs server-rs/crates/spacetime-module/src/big_fish/session.rs server-rs/crates/spacetime-module/src/migration.rs server-rs/crates/spacetime-client/src/big_fish.rs server-rs/crates/spacetime-client/src/mapper.rs server-rs/crates/spacetime-client/src/lib.rs server-rs/crates/shared-contracts/src/big_fish.rs server-rs/crates/api-server/src/big_fish.rs server-rs/crates/api-server/src/app.rs packages/shared/src/contracts/bigFish.ts src/services/big-fish-runtime/bigFishRuntimeClient.ts src/services/big-fish-runtime/index.ts src/components/platform-entry/PlatformEntryFlowShellImpl.tsx src/components/rpg-entry/RpgEntryFlowShell.agent.interaction.test.tsx src/BigFishPlaygroundApp.tsx +npm.cmd run api-server:maincloud +``` + +最终执行结果: + +1. `cargo test -p module-big-fish --manifest-path server-rs/Cargo.toml` 通过,6 个测试通过。 +2. `cargo check -p spacetime-module --manifest-path server-rs/Cargo.toml` 通过。 +3. `cargo check -p spacetime-client --manifest-path server-rs/Cargo.toml` 通过。 +4. `cargo check -p api-server --manifest-path server-rs/Cargo.toml` 通过,仅保留既有 `api-server/src/prompt/rpg/runtime_chat.rs` 未使用 helper warning。 +5. `npm.cmd run check:server-rs-ddd` 通过。 +6. `npm.cmd run test -- src/components/big-fish-runtime/BigFishRuntimeShell.test.tsx` 通过,4 个测试通过;测试已改为真实定时等待,不再使用全局 fake timers。 +7. `npm.cmd run test -- src/components/rpg-entry/RpgEntryFlowShell.agent.interaction.test.tsx -t "public code search opens a published big fish work by BF code"` 通过,确认 BF 编号搜索进入后端 run 主链。 +8. `npm.cmd run check:encoding -- ...` 对本次触达文件通过,收尾补充检查对 3 个文件通过。 +9. `npm.cmd run api-server:maincloud` 已按常驻服务启动;`GET http://127.0.0.1:3100/healthz` 返回 `200`,验收后已停止本次启动进程。 + +补充说明:完整 `src/components/rpg-entry/RpgEntryFlowShell.agent.interaction.test.tsx` 当前单独运行仍有既有长流程用例失败,失败集中在 RPG/拼图/创作结果页路径,例如 `create tab opens compiled agent draft in result refinement page`、`published puzzle detail returns to the source platform tab`、`agent draft result test button enters current draft without publish gate`。这些失败在不并跑 Big Fish runtime 测试时也存在,未指向本次 WP-BF 运行态迁移文件,后续应由对应 RPG/Puzzle/Custom World 前端接线包继续收口。 diff --git a/docs/technical/SERVER_RS_DDD_WP_CW_DOMAIN_ENUM_REHOME_2026-04-29.md b/docs/technical/SERVER_RS_DDD_WP_CW_DOMAIN_ENUM_REHOME_2026-04-29.md new file mode 100644 index 00000000..693380b1 --- /dev/null +++ b/docs/technical/SERVER_RS_DDD_WP_CW_DOMAIN_ENUM_REHOME_2026-04-29.md @@ -0,0 +1,41 @@ +# WP-CW Custom World 基础领域枚举归位切片 + +## 背景 + +`module-custom-world/src/lib.rs` 仍直接承载 Custom World 和 RPG Agent 的基础枚举、字符串口径与进度常量。随着 `custom_world` SpacetimeDB adapter 已完成根入口瘦身,领域 crate 也需要继续把纯领域对象迁入 `domain.rs`,避免后续 profile、agent session、draft card、gallery 与 publish gate 规则继续堆回根文件。 + +## 本次范围 + +1. 认领 `WP-CW Custom World` 的基础领域枚举归位切片。 +2. 将 `MAX_PROGRESS_PERCENT` 迁入 `module-custom-world/src/domain.rs`。 +3. 将 Custom World / RPG Agent 基础枚举迁入 `domain.rs`: + - `CustomWorldPublicationStatus` + - `CustomWorldThemeMode` + - `CustomWorldGenerationMode` + - `CustomWorldSessionStatus` + - `RpgAgentStage` + - `RpgAgentMessageRole` + - `RpgAgentMessageKind` + - `RpgAgentOperationType` + - `RpgAgentOperationStatus` + - `RpgAgentDraftCardKind` + - `RpgAgentDraftCardStatus` + - `CustomWorldRoleAssetStatus` +4. 将这些枚举的 `as_str` 与 `CustomWorldThemeMode::from_client_str` 一并迁入 `domain.rs`。 +5. `lib.rs` 通过 `pub use domain::*` 保持既有 `module_custom_world::*` 公开 API。 + +## 边界 + +1. 本次不改 SpacetimeDB table、reducer、procedure、row mapper 或 `migration.rs`。 +2. 本次不改 `api-server`、`spacetime-client`、platform adapter 或前端。 +3. 本次不移动 profile、agent session、draft card、publish gate 的结构体和校验函数,避免把大包拆分与本切片混在一起。 +4. 本次不改变任何序列化字段、枚举字符串值或中文错误文案。 + +## 验收 + +```powershell +cargo fmt -p module-custom-world --manifest-path server-rs/Cargo.toml --check +cargo test -p module-custom-world --manifest-path server-rs/Cargo.toml +npm.cmd run check:server-rs-ddd +npm.cmd run check:encoding -- docs/technical/SERVER_RS_DDD_WP_CW_DOMAIN_ENUM_REHOME_2026-04-29.md server-rs/crates/module-custom-world/src/domain.rs server-rs/crates/module-custom-world/src/lib.rs docs/technical/SERVER_RS_DDD_PARALLEL_TASKLIST_2026-04-29.md +``` diff --git a/docs/technical/SERVER_RS_DDD_WP_FE_C_RPG_RUNTIME_SHELL_TEST_FIXTURE_2026-04-29.md b/docs/technical/SERVER_RS_DDD_WP_FE_C_RPG_RUNTIME_SHELL_TEST_FIXTURE_2026-04-29.md new file mode 100644 index 00000000..242700c1 --- /dev/null +++ b/docs/technical/SERVER_RS_DDD_WP_FE_C_RPG_RUNTIME_SHELL_TEST_FIXTURE_2026-04-29.md @@ -0,0 +1,32 @@ +# WP-FE-C RPG runtime shell 组件测试夹具接线切片 + +## 背景 + +`WP-FE-S` 和 `WP-FE-H` 已经把 RPG runtime story 读取侧收口到 `storySessionId` scoped projection client 与 hooks 网关。组件层仍不能迁移完整开局、动作结算等写接口,但 `RpgRuntimeShell` 组件测试夹具还保留旧 hook UI 形状,导致 `typecheck` 在组件测试文件中失败。 + +## 本次范围 + +1. 认领 `WP-FE-C Frontend components 接线` 中可并行的 RPG runtime shell 组件测试夹具接线切片。 +2. 修正 `src/components/rpg-runtime-shell/RpgRuntimeShell.test.tsx`: + - `StoryMoment` mock 使用当前 `text/options` 字段。 + - `Character` mock 对齐当前角色类型,不再写入旧 `motivation/combatStyle/role/imageSrc/initialItems` 等字段。 + - `npcUi/characterChatUi/inventoryUi/battleRewardUi/questUi/npcChatQuestOfferUi/goalUi` mock 对齐当前 hooks 暴露的稳定 UI 对象形状。 +3. 保持 `RpgRuntimeShell` 正式组件行为不变,只消除测试夹具对旧组件接线形状的依赖。 + +## 边界 + +1. 本次不改 `src/services/**`、`src/hooks/**` 或任何后端接口。 +2. 本次不迁移 `beginRuntimeStorySession`、`resolveRuntimeStoryAction` 等尚未稳定的新写接口。 +3. 本次不新增 UI 文案,不改组件视觉布局。 +4. 本次不删除旧 runtime story contract 或旧 client alias;这些仍等待 `WP-DEL` 串行收口。 + +## 验收 + +```powershell +npm.cmd run typecheck -- --pretty false +npm.cmd run test -- src/components/rpg-runtime-shell/RpgRuntimeShell.test.tsx +npm.cmd run check:encoding -- docs/technical/SERVER_RS_DDD_WP_FE_C_RPG_RUNTIME_SHELL_TEST_FIXTURE_2026-04-29.md docs/technical/SERVER_RS_DDD_PARALLEL_TASKLIST_2026-04-29.md src/components/rpg-runtime-shell/RpgRuntimeShell.test.tsx +git diff --check -- docs/technical/SERVER_RS_DDD_WP_FE_C_RPG_RUNTIME_SHELL_TEST_FIXTURE_2026-04-29.md docs/technical/SERVER_RS_DDD_PARALLEL_TASKLIST_2026-04-29.md src/components/rpg-runtime-shell/RpgRuntimeShell.test.tsx +``` + +结果:`RpgRuntimeShell.test.tsx` 定向测试通过;编码检查和空白检查通过。`typecheck` 未全量通过,但不再报本切片文件,剩余阻塞来自既有非本切片文件 `src/data/sceneEncounterPreviews.ts` 与 `src/services/ai.ts`。 diff --git a/docs/technical/SERVER_RS_DDD_WP_FE_H_RPG_RUNTIME_STORY_HOOKS_PROJECTION_2026-04-29.md b/docs/technical/SERVER_RS_DDD_WP_FE_H_RPG_RUNTIME_STORY_HOOKS_PROJECTION_2026-04-29.md new file mode 100644 index 00000000..634b5e72 --- /dev/null +++ b/docs/technical/SERVER_RS_DDD_WP_FE_H_RPG_RUNTIME_STORY_HOOKS_PROJECTION_2026-04-29.md @@ -0,0 +1,32 @@ +# WP-FE-H RPG runtime story 读取侧 hooks 接线切片 + +## 背景 + +`WP-FE-S` 已经补齐 story session 新主链 client,并提供 `getRpgStoryRuntimeProjection` 读取 `/api/story/sessions/{storySessionId}/runtime-projection`。`WP-FE-H` 的完整 hooks 迁移仍依赖后端 session scoped 开局和动作结算写接口;但读取侧 option catalog 与继续游戏刷新已经具备稳定 projection contract,可以先把 hooks 网关的读取语义收口到 projection client。 + +## 本次范围 + +1. 认领 `WP-FE-H Frontend hooks 迁移` 中可并行的 RPG runtime story 读取侧 hooks 接线切片。 +2. 将 `src/hooks/rpg-runtime-story/rpgRuntimeStoryGateway.ts` 的读取侧从历史别名 `getRpgRuntimeStoryState` 改为显式调用 `getRpgStoryRuntimeProjection`。 +3. `loadServerRuntimeOptionCatalog` 继续只读取服务端 option catalog,不上传本地 `GameState` 参与动作合法性解析。 +4. `resumeServerRuntimeStory` 继续保持本地已水合快照主体,只同步服务端 `runtimeSessionId / storySessionId / runtimeActionVersion` 和投影故事。 +5. 更新 `runtimeStoryCoordinator.test.ts` mock 命名,确保 hooks 测试明确断言读取侧走 projection client。 +6. 补齐 `src/hooks/useGameFlow.customWorld.test.tsx` 的 `beginRpgRuntimeStorySession` 测试桩,避免 hooks 全量测试在 Node 环境直接 fetch 相对路径,同时保持自定义世界开局 hooks 仍消费服务端快照。 + +## 边界 + +1. 本次不迁移 `beginRuntimeStorySession`,因为新开局接口尚未返回可直接进入游戏的完整 `GameState` 快照。 +2. 本次不迁移 `resolveRuntimeStoryAction`,因为完整动作结算的新 session scoped 写接口尚未稳定。 +3. 本次不改 components,不新增 UI 文案,不在组件层拼 API 请求。 +4. 本次不改 `api-server`、`spacetime-client`、`spacetime-module` 或共享契约。 +5. 本次不删除旧 runtime story contract、旧 client alias 或兼容测试;这些仍等待 `WP-DEL` 串行收口。 + +## 验收 + +```powershell +npm.cmd run test -- src/hooks/rpg-runtime-story/runtimeStoryCoordinator.test.ts +npm.cmd run test -- src/hooks/useGameFlow.customWorld.test.tsx +npm.cmd run test -- src/hooks +npm.cmd run check:encoding -- docs/technical/SERVER_RS_DDD_WP_FE_H_RPG_RUNTIME_STORY_HOOKS_PROJECTION_2026-04-29.md src/hooks/rpg-runtime-story/rpgRuntimeStoryGateway.ts src/hooks/rpg-runtime-story/inventoryActions.ts src/hooks/rpg-runtime-story/runtimeStoryCoordinator.test.ts src/hooks/useGameFlow.customWorld.test.tsx docs/technical/README.md docs/technical/SERVER_RS_DDD_PARALLEL_TASKLIST_2026-04-29.md +git diff --check -- docs/technical/SERVER_RS_DDD_WP_FE_H_RPG_RUNTIME_STORY_HOOKS_PROJECTION_2026-04-29.md src/hooks/rpg-runtime-story/rpgRuntimeStoryGateway.ts src/hooks/rpg-runtime-story/inventoryActions.ts src/hooks/rpg-runtime-story/runtimeStoryCoordinator.test.ts src/hooks/useGameFlow.customWorld.test.tsx docs/technical/README.md docs/technical/SERVER_RS_DDD_PARALLEL_TASKLIST_2026-04-29.md +``` diff --git a/docs/technical/SERVER_RS_DDD_WP_FE_S_RPG_RUNTIME_STORY_CLIENT_MIGRATION_2026-04-29.md b/docs/technical/SERVER_RS_DDD_WP_FE_S_RPG_RUNTIME_STORY_CLIENT_MIGRATION_2026-04-29.md new file mode 100644 index 00000000..2b64fc44 --- /dev/null +++ b/docs/technical/SERVER_RS_DDD_WP_FE_S_RPG_RUNTIME_STORY_CLIENT_MIGRATION_2026-04-29.md @@ -0,0 +1,57 @@ +# WP-FE-S RPG runtime story client 迁移记录 + +## 背景 + +`WP-API` 已提供新主链读取接口: + +```text +GET /api/story/sessions/{storySessionId}/runtime-projection +``` + +该接口返回 `StoryRuntimeProjectionResponse`,不再返回旧 runtime story 的 `snapshot / viewModel / presentation / patches` 组合。前端读取侧必须改用 `storySessionId`,不能继续把 `runtimeSessionId` 当成 story 会话主键。 + +## 本轮落地边界 + +已落地: + +1. 在 `packages/shared/src/contracts/story.ts` 补齐前端 story session / runtime projection 契约,字段对齐 Rust `shared-contracts/src/story.rs` 的 camelCase 回包。 +2. 在 `GameState`、快照水合类型与水合逻辑中新增 `storySessionId?: string | null`。 +3. `src/services/rpg-runtime/rpgRuntimeStoryClient.ts` 的读取侧改为: + - `getRuntimeStoryState({ storySessionId })` 请求 `/api/story/sessions/{storySessionId}/runtime-projection`。 + - `loadRuntimeInventoryView` 从 `StoryRuntimeProjectionResponse` 映射背包视图。 + - 缺失 `storySessionId` 时直接抛出中文错误,不回退到 `runtimeSessionId`。 +4. `src/hooks/rpg-runtime-story/rpgRuntimeStoryGateway.ts` 的读取侧改为消费新投影: + - 选项目录来自 `projection.options`。 + - 继续游戏时保留本地已水合快照主体,只同步 `runtimeSessionId / storySessionId / runtimeActionVersion` 和展示故事。 +5. `src/services/rpg-runtime/rpgRuntimeStoryClient.ts` 新增 story session 新主链 API client: + - `beginStorySession` 请求 `POST /api/story/sessions`。 + - `continueStorySession` 请求 `POST /api/story/sessions/continue`。 + - `getStorySessionState` 请求 `GET /api/story/sessions/{storySessionId}/state`。 + - `getStoryRuntimeProjection` 请求 `GET /api/story/sessions/{storySessionId}/runtime-projection`。 + - 对 `storySessionId` 做统一 trim 与空值中文错误,避免后续 hooks 继续散落路径常量和空 ID 分支。 +6. `src/services/rpg-runtime/index.ts` 已导出新 client 函数与结果类型,供后续 `WP-FE-H` 迁 hook 时直接接入。 +7. 为满足 `WP-FE-S` 的 `src/services` 全量验收,补回 `src/services/customWorldAgentGenerationProgress.ts` 缺失的“建立场景连接”阶段,使草稿生成进度重新对齐既有 13 步文档与测试口径。 + +## 未完成边界 + +暂未迁移: + +1. `beginRuntimeStorySession` 仍调用旧 `/api/runtime/story/sessions`,因为当前新 `/api/story/sessions` 只创建 story session,不返回前端开局所需的完整 `GameState` 快照。新 `beginStorySession` 已作为稳定 client 先行提供,hook 是否切换等待后端开局投影组合稳定。 +2. `resolveRuntimeStoryAction` 仍调用旧 `/api/runtime/story/actions/resolve`,因为当前新主链尚未提供等价的 session scoped 动作结算接口与快照回包。新 `continueStorySession` 只覆盖 story event 续写,不等价于完整 runtime action settle。 +3. hooks/components 不在本轮大改;`WP-FE-H` 等后端写接口稳定后再迁。 + +## 后续依赖 + +1. `WP-API/WP-RS` 需要提供新的 story session scoped 开局接口,返回可直接进入游戏的 `GameState` 或明确的前端投影组合。 +2. `WP-API/WP-RS` 需要提供新的 story session scoped 动作结算接口,返回新投影及必要的状态更新结果。 +3. 上述接口稳定后,`WP-FE-S` 再删除旧 `/api/runtime/story/*` 写接口调用,`WP-FE-H` 迁 hooks,最后由 `WP-DEL` 物理删除旧 runtime story contract 与 compat 测试。 + +## 验收命令 + +```powershell +npm.cmd run test -- src/services/rpg-runtime/rpgRuntimeStoryClient.test.ts +npm.cmd run test -- src/hooks/rpg-runtime-story/runtimeStoryCoordinator.test.ts +npm.cmd run test -- src/services/customWorldAgentGenerationProgress.test.ts +npm.cmd run test -- src/services +npm.cmd run check:encoding -- packages/shared/src/contracts/story.ts src/services/rpg-runtime/rpgRuntimeStoryClient.ts src/services/rpg-runtime/index.ts src/services/customWorldAgentGenerationProgress.ts src/services/customWorldAgentGenerationProgress.test.ts src/hooks/rpg-runtime-story/rpgRuntimeStoryGateway.ts src/hooks/rpg-runtime-story/inventoryActions.ts src/persistence/runtimeSnapshot.ts src/persistence/runtimeSnapshotTypes.ts src/types/game.ts docs/technical/SERVER_RS_DDD_WP_FE_S_RPG_RUNTIME_STORY_CLIENT_MIGRATION_2026-04-29.md +``` diff --git a/docs/technical/SERVER_RS_DDD_WP_PF_PLATFORM_ERROR_CLASSIFICATION_2026-04-29.md b/docs/technical/SERVER_RS_DDD_WP_PF_PLATFORM_ERROR_CLASSIFICATION_2026-04-29.md new file mode 100644 index 00000000..ba60cba6 --- /dev/null +++ b/docs/technical/SERVER_RS_DDD_WP_PF_PLATFORM_ERROR_CLASSIFICATION_2026-04-29.md @@ -0,0 +1,69 @@ +# WP-PF 平台副作用错误分类收口记录(2026-04-29) + +## 1. 背景 + +`WP-PF platform side effects` 负责承载 LLM、OSS、SMS、微信等外部副作用实现。当前 `platform-llm`、`platform-oss`、`platform-auth` 已经各自有错误枚举,但 `api-server` 后续接入时仍容易重复用字符串或具体枚举分支判断 HTTP 错误类别。 + +第一段切片已补平台错误分类基础设施。继续收口时,需要把 `api-server` 中已接入的平台副作用统一改为消费这些稳定分类,并把微信 OAuth HTTP provider 下沉到 `platform-auth`,让 `api-server` 只保留 BFF 编排、会话签发、redirect 与错误 envelope 映射。 + +## 2. 范围 + +允许修改: + +1. `server-rs/crates/platform-llm/src/lib.rs` +2. `server-rs/crates/platform-oss/src/lib.rs` +3. `server-rs/crates/platform-auth/src/lib.rs` +4. `server-rs/crates/api-server/src/platform_errors.rs` +5. `server-rs/crates/api-server/src/llm.rs` +6. `server-rs/crates/api-server/src/assets.rs` +7. 已经直接映射 OSS 错误的资产相关 BFF 模块 +8. `server-rs/crates/api-server/src/state.rs` +9. `server-rs/crates/api-server/src/wechat_auth.rs` +10. `server-rs/crates/api-server/src/wechat_provider.rs` +11. 本文档 +12. 全局任务清单进度记录 + +禁止修改: + +1. `spacetime-module/src/**` +2. `spacetime-client/src/**` +3. `module-*` +4. 前端 services/hooks/components +5. SpacetimeDB table / reducer / procedure / migration +6. 玩法状态机和领域规则 + +## 3. 设计 + +每个 platform crate 暴露自己的错误分类枚举: + +1. `LlmErrorKind` +2. `OssErrorKind` +3. `AuthPlatformErrorKind` + +并在既有错误枚举上增加 `kind()` 方法。分类只表达 adapter 可消费的稳定错误类别,不承载业务状态机,也不直接绑定 HTTP status。 + +`api-server` 增加内部 `platform_errors` 模块,负责把平台错误分类映射到 HTTP status、统一 provider details 与中文 envelope。映射原则: + +1. `InvalidRequest` / `InvalidConfig` 等本地配置或请求错误不再散落在业务 route 中重复 match。 +2. `ObjectNotFound` 稳定映射为 `404`。 +3. LLM 上游 `429` 保留 `429`,其他上游、网络、序列化、签名类错误映射为 `502`。 +4. SMS/微信 provider 错误统一走 `platform-auth` 错误分类,领域态错误仍由 `module-auth` 自己的应用错误映射。 +5. `platform-*` 不绑定 HTTP status,不生成 Axum response,不持有玩法领域状态。 + +微信 OAuth provider 的 HTTP 请求、授权 URL 拼接、mock provider 和回调资料解析归入 `platform-auth`。由于 `platform-auth` 不能依赖 `module-auth`,微信 provider 输出独立的 `WechatIdentityProfile`;`api-server` 在 BFF 边界把它转换为 `module_auth::WechatIdentityProfile` 后再调用领域服务。 + +图像/视频模型直连接口仍分布在角色形象、角色动画、拼图和自定义世界资产模块中,当前属于视觉资产生成专项遗留,不在本次 WP-PF LLM/OSS/SMS/微信统一 adapter 收口内;后续若新建 image/video platform crate,需要另立工作包迁移。 + +## 4. 验收 + +```powershell +cargo test -p platform-llm -p platform-oss -p platform-auth --manifest-path server-rs/Cargo.toml +cargo fmt -p platform-llm -p platform-oss -p platform-auth --manifest-path server-rs/Cargo.toml --check +cargo test -p api-server platform_errors --manifest-path server-rs/Cargo.toml +cargo test -p api-server llm --manifest-path server-rs/Cargo.toml +cargo test -p api-server wechat --manifest-path server-rs/Cargo.toml +cargo check -p api-server --manifest-path server-rs/Cargo.toml +npm.cmd run check:server-rs-ddd +npm.cmd run check:encoding -- docs/technical/SERVER_RS_DDD_WP_PF_PLATFORM_ERROR_CLASSIFICATION_2026-04-29.md docs/technical/SERVER_RS_DDD_PARALLEL_TASKLIST_2026-04-29.md server-rs/crates/platform-llm/src/lib.rs server-rs/crates/platform-oss/src/lib.rs server-rs/crates/platform-auth/src/lib.rs server-rs/crates/api-server/src/platform_errors.rs server-rs/crates/api-server/src/llm.rs server-rs/crates/api-server/src/state.rs server-rs/crates/api-server/src/wechat_provider.rs server-rs/crates/api-server/src/wechat_auth.rs +npm.cmd run api-server:maincloud +``` diff --git a/docs/technical/SERVER_RS_DDD_WP_PZ_DOMAIN_ENUM_REHOME_2026-04-29.md b/docs/technical/SERVER_RS_DDD_WP_PZ_DOMAIN_ENUM_REHOME_2026-04-29.md new file mode 100644 index 00000000..0668706d --- /dev/null +++ b/docs/technical/SERVER_RS_DDD_WP_PZ_DOMAIN_ENUM_REHOME_2026-04-29.md @@ -0,0 +1,43 @@ +# WP-PZ Puzzle 基础领域常量与枚举归位切片 + +## 背景 + +`module-puzzle/src/lib.rs` 当前仍承载 Puzzle Agent、作品发布和运行态拼图的基础枚举、ID 前缀、标签数量规则与洗牌尝试次数。随着 DDD 骨架已经具备 `domain.rs`、`commands.rs`、`application.rs`、`events.rs`、`errors.rs`,本切片先把纯领域常量和基础枚举迁入 `domain.rs`,避免后续 Agent session、work profile、runtime run 与排行榜规则继续堆回根文件。 + +## 本次范围 + +1. 认领 `WP-PZ Puzzle` 的基础领域常量与枚举归位切片。 +2. 将以下常量迁入 `module-puzzle/src/domain.rs`: + - `PUZZLE_AGENT_SESSION_ID_PREFIX` + - `PUZZLE_AGENT_MESSAGE_ID_PREFIX` + - `PUZZLE_PROFILE_ID_PREFIX` + - `PUZZLE_RUN_ID_PREFIX` + - `PUZZLE_MIN_TAG_COUNT` + - `PUZZLE_MAX_TAG_COUNT` + - `PUZZLE_INITIAL_SHUFFLE_ATTEMPTS` +3. 将以下基础枚举迁入 `domain.rs`: + - `PuzzleAgentStage` + - `PuzzleAnchorStatus` + - `PuzzleAgentMessageRole` + - `PuzzleAgentMessageKind` + - `PuzzlePublicationStatus` + - `PuzzleRuntimeLevelStatus` +4. 将这些枚举的 `as_str` 方法一并迁入 `domain.rs`。 +5. `lib.rs` 通过 `pub use domain::*` 保持既有 `module_puzzle::*` 公开 API。 + +## 边界 + +1. 本次不改 SpacetimeDB table、reducer、procedure、row mapper 或 `migration.rs`。 +2. 本次不改 `api-server`、`spacetime-client`、platform adapter 或前端。 +3. 本次不移动 Agent session、work profile、runtime run、leaderboard 的结构体和校验函数,避免把大包拆分与本切片混在一起。 +4. 本次不改变任何序列化字段、枚举字符串值、标签数量规则、洗牌规则或中文错误文案。 + +## 验收 + +```powershell +cargo fmt -p module-puzzle --manifest-path server-rs/Cargo.toml --check +cargo test -p module-puzzle --manifest-path server-rs/Cargo.toml +cargo check -p module-puzzle --features spacetime-types --target wasm32-unknown-unknown --manifest-path server-rs/Cargo.toml +npm.cmd run check:server-rs-ddd +npm.cmd run check:encoding -- docs/technical/SERVER_RS_DDD_WP_PZ_DOMAIN_ENUM_REHOME_2026-04-29.md server-rs/crates/module-puzzle/src/domain.rs server-rs/crates/module-puzzle/src/lib.rs server-rs/crates/module-puzzle/README.md docs/technical/README.md docs/technical/SERVER_RS_DDD_PARALLEL_TASKLIST_2026-04-29.md +``` diff --git a/docs/technical/SERVER_RS_DDD_WP_PZ_DOMAIN_SPLIT_2026-04-29.md b/docs/technical/SERVER_RS_DDD_WP_PZ_DOMAIN_SPLIT_2026-04-29.md new file mode 100644 index 00000000..37b94a11 --- /dev/null +++ b/docs/technical/SERVER_RS_DDD_WP_PZ_DOMAIN_SPLIT_2026-04-29.md @@ -0,0 +1,33 @@ +# WP-PZ Puzzle 领域类型与规则拆分切片 + +## 背景 + +上一轮 `WP-PZ Puzzle` 已经把基础常量与枚举迁入 `module-puzzle/src/domain.rs`,但 `module-puzzle/src/lib.rs` 仍然承载 Agent 会话快照、作品 profile、运行态棋盘、procedure 输入、procedure 返回、字段错误与全部纯规则函数。 + +按照 `SERVER_RS_DDD_FULL_REFACTOR_2026-04-28.md`,`module-puzzle` 应继续把纯领域内容落入 `domain.rs`、`commands.rs`、`application.rs`、`events.rs`、`errors.rs`,让根入口只保留模块声明和公开导出。 + +## 本次范围 + +1. 将 Puzzle Agent、锚点包、草稿、作品 profile、运行态棋盘、排行榜等纯领域类型迁入 `module-puzzle/src/domain.rs`。 +2. 将 SpacetimeDB procedure/reducer 写入输入迁入 `module-puzzle/src/commands.rs`。 +3. 将 procedure 返回包装和拼图纯规则函数迁入 `module-puzzle/src/application.rs`。 +4. 将 `PuzzleFieldError` 和中文错误文案迁入 `module-puzzle/src/errors.rs`。 +5. 在 `module-puzzle/src/events.rs` 增加最小 `PuzzleDomainEvent`,先表达草稿变化、作品发布和运行态推进事实。 +6. `module-puzzle/src/lib.rs` 只保留 DDD 子模块声明和 `pub use`,保持既有 `module_puzzle::*` 外部 API。 + +## 边界 + +1. 本次不改 SpacetimeDB table、reducer、procedure、row mapper 或 `migration.rs`。 +2. 本次不改 `api-server`、`spacetime-client`、platform adapter 或前端。 +3. 本次不改变序列化字段、procedure 输入结构、procedure 返回 JSON 包装、标签数量规则、洗牌规则、移动/合并/拆分语义或中文错误文案。 +4. 本次新增的 `PuzzleDomainEvent` 只作为领域事件落位,不接入 `spacetime-module` event table。 + +## 验收 + +```powershell +cargo fmt -p module-puzzle --manifest-path server-rs/Cargo.toml --check +cargo test -p module-puzzle --manifest-path server-rs/Cargo.toml +cargo check -p module-puzzle --features spacetime-types --target wasm32-unknown-unknown --manifest-path server-rs/Cargo.toml +npm.cmd run check:server-rs-ddd +npm.cmd run check:encoding -- docs/technical/SERVER_RS_DDD_WP_PZ_DOMAIN_SPLIT_2026-04-29.md server-rs/crates/module-puzzle/src/lib.rs server-rs/crates/module-puzzle/src/domain.rs server-rs/crates/module-puzzle/src/commands.rs server-rs/crates/module-puzzle/src/application.rs server-rs/crates/module-puzzle/src/events.rs server-rs/crates/module-puzzle/src/errors.rs server-rs/crates/module-puzzle/README.md docs/technical/README.md docs/technical/SERVER_RS_DDD_PARALLEL_TASKLIST_2026-04-29.md +``` diff --git a/docs/technical/SERVER_RS_DDD_WP_RPG_COMBAT_DOMAIN_ENUM_REHOME_2026-04-29.md b/docs/technical/SERVER_RS_DDD_WP_RPG_COMBAT_DOMAIN_ENUM_REHOME_2026-04-29.md new file mode 100644 index 00000000..2527f5dd --- /dev/null +++ b/docs/technical/SERVER_RS_DDD_WP_RPG_COMBAT_DOMAIN_ENUM_REHOME_2026-04-29.md @@ -0,0 +1,41 @@ +# WP-RPG Combat 基础领域常量与枚举归位切片 + +## 背景 + +`WP-RPG Gameplay 域` 覆盖 combat、inventory、npc、progression、quest、runtime-item、story 等多个玩法 crate。本次选择其中最小且可并行的 `module-combat` 切片:`module-combat/src/lib.rs` 仍直接承载战斗 ID 前缀、版本/伤害常量、旧攻击 function 列表以及基础枚举。随着 DDD 骨架已经具备 `domain.rs`,这些纯领域对象应先归位到 `domain.rs`,为后续拆分命令、错误、应用结果和跨域事件留出边界。 + +## 本次范围 + +1. 认领 `WP-RPG Gameplay 域` 的 combat 基础领域常量与枚举归位切片。 +2. 将以下常量迁入 `module-combat/src/domain.rs`: + - `BATTLE_STATE_ID_PREFIX` + - `INITIAL_BATTLE_VERSION` + - `BASIC_FIGHT_COUNTER_RATIO` + - `MIN_FIGHT_COUNTER_DAMAGE` + - `SPAR_MIN_HP` + - `LEGACY_ATTACK_FUNCTION_IDS` +3. 将以下基础枚举迁入 `domain.rs`: + - `BattleMode` + - `BattleStatus` + - `CombatOutcome` +4. 将这些枚举的 `as_str` 方法一并迁入 `domain.rs`。 +5. `lib.rs` 通过 `pub use domain::*` 保持既有 `module_combat::*` 公开 API。 +6. `module-combat` 的 `spacetime-types` feature 同步启用 `module-runtime-item/spacetime-types`,确保战斗快照里引用的 `RuntimeItemRewardItemSnapshot` 在 wasm 目标下具备 SpacetimeDB 类型派生。 + +## 边界 + +1. 本次不改 SpacetimeDB table、reducer、procedure、row mapper 或 `migration.rs`。 +2. 本次不改 `api-server`、`spacetime-client`、platform adapter 或前端。 +3. 本次不移动 `BattleStateInput`、`BattleStateSnapshot`、`ResolveCombatActionInput`、`CombatFieldError` 和战斗结算函数,避免把常量枚举归位与大包拆分混在一起。 +4. 本次不改变任何战斗数值、支持的 function id、枚举字符串值或中文错误文案。 +5. Cargo feature 传播仅用于修复 `spacetime-types` 组合编译,不引入新依赖路径或运行时行为。 + +## 验收 + +```powershell +cargo fmt -p module-combat --manifest-path server-rs/Cargo.toml --check +cargo test -p module-combat --manifest-path server-rs/Cargo.toml +cargo check -p module-combat --features spacetime-types --target wasm32-unknown-unknown --manifest-path server-rs/Cargo.toml +npm.cmd run check:server-rs-ddd +npm.cmd run check:encoding -- docs/technical/SERVER_RS_DDD_WP_RPG_COMBAT_DOMAIN_ENUM_REHOME_2026-04-29.md server-rs/crates/module-combat/Cargo.toml server-rs/crates/module-combat/src/domain.rs server-rs/crates/module-combat/src/lib.rs server-rs/crates/module-combat/README.md docs/technical/README.md docs/technical/SERVER_RS_DDD_PARALLEL_TASKLIST_2026-04-29.md +``` diff --git a/docs/technical/SERVER_RS_DDD_WP_RPG_STORY_DOMAIN_SPLIT_2026-04-29.md b/docs/technical/SERVER_RS_DDD_WP_RPG_STORY_DOMAIN_SPLIT_2026-04-29.md new file mode 100644 index 00000000..d844e400 --- /dev/null +++ b/docs/technical/SERVER_RS_DDD_WP_RPG_STORY_DOMAIN_SPLIT_2026-04-29.md @@ -0,0 +1,38 @@ +# WP-RPG module-story 领域拆分收口(2026-04-29) + +## 1. 收口目标 + +本切片关闭 `SERVER_RS_DDD_PARALLEL_TASKLIST_2026-04-29.md` 中 `module-story` 的两类漂移: + +1. README 仍写“目录占位”和旧 `/api/runtime/story/*` 兼容链路。 +2. `domain / commands / application / events / errors` 文件仍停在“过渡落位”注释,真实规则集中在 `lib.rs`。 + +本次只做物理分层和文档对齐,不新增 story action 写接口,不修改 SpacetimeDB 表结构,不触碰 `migration.rs`。 + +## 2. 已完成内容 + +1. `src/domain.rs` 收口剧情会话领域快照、状态、ID 前缀和 `generate_story_session_id`。 +2. `src/commands.rs` 收口 `StorySessionInput`、`StoryContinueInput`、`StorySessionStateInput`、输入构造和字段校验。 +3. `src/events.rs` 收口 `StoryEventKind`、`StoryEventSnapshot`、开局事件和事件 ID 生成。 +4. `src/application.rs` 收口会话快照构造、续写应用服务、procedure result 类型和只读 record mapper。 +5. `src/errors.rs` 收口 `StorySessionFieldError` 与中文错误文案。 +6. `src/lib.rs` 只保留模块声明和 `pub use`,继续保持 `module_story::*` 公开 API。 +7. `module-story/README.md` 改为当前真实边界,明确不恢复旧 `/api/runtime/story/*` 兼容路由。 + +## 3. 边界 + +1. 本切片不改变 `story_session`、`story_event` 的 SpacetimeDB row shape、reducer/procedure 签名或前端 DTO。 +2. 本切片不接入 LLM、SSE、HTTP route 或前端 hooks。 +3. 后续完整动作结算仍等待 `WP-RS/WP-ST/WP-SC/WP-API/WP-FE` 继续推进。 +4. `module-story` 只承载纯 story session 规则;运行态投影、背包/NPC/战斗/任务联动继续由相邻领域模块和 adapter 编排。 + +## 4. 验收 + +已执行: + +```powershell +cargo test -p module-story --manifest-path server-rs\Cargo.toml +cargo check -p module-story --manifest-path server-rs\Cargo.toml +``` + +结果:通过,8 个单元测试通过。 diff --git a/docs/technical/SERVER_RS_DDD_WP_RS_COMPAT_RESIDUE_AUDIT_2026-04-29.md b/docs/technical/SERVER_RS_DDD_WP_RS_COMPAT_RESIDUE_AUDIT_2026-04-29.md new file mode 100644 index 00000000..8f3c2c49 --- /dev/null +++ b/docs/technical/SERVER_RS_DDD_WP_RS_COMPAT_RESIDUE_AUDIT_2026-04-29.md @@ -0,0 +1,56 @@ +# WP-RS Runtime Story Compat 残留审计切片 + +## 背景 + +`WP-RS Runtime Story 去兼容层` 已完成 crate 迁名、旧 HTTP compat 路由下线、runtime projection 契约/API/读取侧迁移等多轮推进。当前仍存在一些 `compat` 或旧 `/api/runtime/story/*` 命名残留,需要区分“可以立即清理的工程语义残留”和“必须等待后端写接口稳定后再删除的运行依赖”。 + +本次切片只处理前者,并冻结后者的交接清单。 + +## 本次范围 + +允许修改: + +1. `server-rs/crates/module-runtime-story/src/**` 中只描述历史兼容阶段的注释。 +2. `server-rs/crates/module-runtime-story/README.md` +3. 本文档 +4. `docs/technical/README.md` +5. `docs/technical/SERVER_RS_DDD_PARALLEL_TASKLIST_2026-04-29.md` + +禁止修改: + +1. `src/services/rpg-runtime/rpgRuntimeStoryClient.ts` +2. `src/hooks/rpg-runtime-story/**` +3. `packages/shared/src/contracts/rpgRuntimeStoryState.ts` +4. `server-rs/crates/shared-contracts/src/runtime_story.rs` +5. `api-server` route 挂载和 BFF 行为 +6. SpacetimeDB 表、procedure、绑定和 `migration.rs` + +## 本次处理 + +1. 清理 `module-runtime-story` 运行代码注释中的 `compat` 定位,将 crate 口径改为 runtime story 主链纯规则收口。 +2. 保留“历史 payload”相关说明,因为这些字段仍是实际输入兼容范围,不属于旧层命名。 +3. 更新 `module-runtime-story/README.md`,明确后续要迁的是旧 `/api/runtime/story/*` 写侧能力,而不是继续扩展兼容桥。 + +## 残留清单 + +以下残留本次不删除: + +1. `src/services/rpg-runtime/rpgRuntimeStoryClient.ts` 仍指向 `/api/runtime/story`,原因是开局和动作写侧 session scoped 新接口尚未完成。 +2. `src/services/rpg-runtime/rpgRuntimeStoryClient.test.ts` 仍断言旧写接口路径,需等待新写接口和 service contract 稳定后由 `WP-FE-S` 修改。 +3. `packages/shared/src/contracts/rpgRuntimeStoryState.ts` 和 `server-rs/crates/shared-contracts/src/runtime_story.rs` 仍保留 `RuntimeStoryActionResponse`,原因是前端旧写侧仍消费该响应形状。 +4. `src/hooks/rpg-runtime-story/**` 仍保留部分旧调用面兼容注释,需等 `WP-FE-H` 在 service 迁移后统一收口。 + +## 后续边界 + +1. 后端新写接口稳定前,不删除旧前端写 client。 +2. `WP-FE-S` 先迁 service,再由 `WP-FE-H` 迁 hooks,最后 `WP-FE-C` 接组件。 +3. `WP-DEL` 只能在旧接口无运行引用后删除 `RuntimeStoryActionResponse` 等旧 contract。 + +## 验收 + +```powershell +cargo fmt -p module-runtime-story --manifest-path server-rs/Cargo.toml --check +cargo test -p module-runtime-story --manifest-path server-rs/Cargo.toml +npm.cmd run check:server-rs-ddd +npm.cmd run check:encoding -- docs/technical/SERVER_RS_DDD_WP_RS_COMPAT_RESIDUE_AUDIT_2026-04-29.md docs/technical/README.md docs/technical/SERVER_RS_DDD_PARALLEL_TASKLIST_2026-04-29.md server-rs/crates/module-runtime-story/README.md server-rs/crates/module-runtime-story/src/domain.rs server-rs/crates/module-runtime-story/src/commands.rs server-rs/crates/module-runtime-story/src/errors.rs server-rs/crates/module-runtime-story/src/events.rs server-rs/crates/module-runtime-story/src/core.rs server-rs/crates/module-runtime-story/src/game_state.rs server-rs/crates/module-runtime-story/src/battle.rs server-rs/crates/module-runtime-story/src/forge.rs server-rs/crates/module-runtime-story/src/forge_actions.rs server-rs/crates/module-runtime-story/src/npc_support.rs +``` diff --git a/docs/technical/SERVER_RS_DDD_WP_RT_ADAPTER_API_CLOSURE_2026-04-29.md b/docs/technical/SERVER_RS_DDD_WP_RT_ADAPTER_API_CLOSURE_2026-04-29.md new file mode 100644 index 00000000..d98bec74 --- /dev/null +++ b/docs/technical/SERVER_RS_DDD_WP_RT_ADAPTER_API_CLOSURE_2026-04-29.md @@ -0,0 +1,130 @@ +# WP-RT Adapter/API 收口落地说明 + +## 背景 + +`WP-RT Runtime/Profile/Save` 已完成 runtime settings、snapshot/profile/save archive 类型、错误层、命令构造和应用记录投影拆分。剩余风险集中在 Adapter/API 层仍保留部分纯规则,以及 profile 旧兼容路径继续挂载,容易让后续前端或 BFF 再走 `/api/runtime/profile/*` 旧入口。 + +本次收口目标是继续遵循 DDD 边界:`module-runtime` 承载 runtime/profile/save 的纯规则和字段错误,`spacetime-module` 只保留 SpacetimeDB table、事务读写和 row mapper,`api-server` 只负责 HTTP/BFF 映射,前端请求层改用新的 profile API。 + +## 本次范围 + +允许修改: + +1. `server-rs/crates/module-runtime/src/domain.rs` +2. `server-rs/crates/module-runtime/src/errors.rs` +3. `server-rs/crates/module-runtime/src/commands.rs` +4. `server-rs/crates/module-runtime/src/application.rs` +5. `server-rs/crates/module-runtime/src/lib.rs` +6. `server-rs/crates/spacetime-module/src/runtime/profile.rs` +7. `server-rs/crates/api-server/src/app.rs` +8. `server-rs/crates/api-server/src/runtime_save.rs` +9. `server-rs/crates/api-server/src/runtime_profile.rs` +10. `server-rs/crates/api-server/src/runtime_browse_history.rs` +11. `src/services/rpg-runtime/rpgRuntimeRequest.ts` +12. `src/services/rpg-entry/rpgProfileClient.test.ts` +13. `src/services/rpg-entry/rpgEntryClients.routing.test.ts` +14. 相关 README、技术文档和全局任务清单 + +禁止修改: + +1. SpacetimeDB 表结构和 `migration.rs` +2. RPG story / runtime story 玩法规则 +3. 旧 `server-node` / PostgreSQL 兼容逻辑 +4. 非 WP-RT 并行包文件 + +## 设计 + +### 1. checkpoint 规则下沉 + +`api-server/src/runtime_save.rs` 不再本地维护 checkpoint 的 sessionId 校验、预览/测试态拒绝和 runtimeStats 时间水位刷新规则。 + +新增或迁入 `module-runtime`: + +1. `RuntimeSaveCheckpointInput` +2. `RuntimeSaveCheckpointSnapshotUpdate` +3. `build_runtime_save_checkpoint_input` +4. `build_runtime_save_checkpoint_update` +5. `refresh_runtime_snapshot_play_time` +6. `is_non_persistent_runtime_snapshot` +7. checkpoint 相关 `RuntimeProfileFieldError` + +`api-server` 只把 HTTP payload 转为领域输入,并把领域错误映射为 runtime-save 的 API envelope。 + +### 2. profile/save archive 投影 meta 规则下沉 + +`spacetime-module/src/runtime/profile.rs` 不再本地维护 save archive/world meta 的 JSON 解析规则。 + +新增或迁入 `module-runtime`: + +1. `RuntimeProfileWorldSnapshotMeta` +2. `RuntimeProfileSaveArchiveMeta` +3. `resolve_runtime_profile_world_snapshot_meta` +4. `resolve_runtime_profile_save_archive_meta` +5. `read_runtime_json_non_negative_u64` +6. `read_runtime_json_string_field` +7. `build_runtime_builtin_world_title` + +`spacetime-module` 继续负责读取 snapshot、写入 dashboard / played world / save archive 表,但 meta 判断和默认标题、摘要兜底由领域模块统一维护。 + +### 3. profile 剩余纯规则下沉 + +本次继续把留在 SpacetimeDB adapter 中的纯规则收回 `module-runtime`: + +1. played world、snapshot wallet ledger、save archive、recharge order、recharge wallet ledger、redeem usage、redeem ledger 等 ID 生成规则。 +2. 首充叙世币奖励计算。 +3. 会员购买续期时间计算。 +4. 邀请码 deterministic 生成、邀请链接、每日奖励窗口和邀请人奖励上限判断。 +5. 兑换码 public / unique / private 模式使用资格校验。 +6. 钱包正负 delta 转换、余额溢出和余额不足校验。 + +`spacetime-module` 中仍保留必须依赖表状态的逻辑:查找已有邀请码、统计当天奖励次数、读取/更新 wallet dashboard、写入 wallet ledger、membership、recharge order、redeem usage 和 referral relation。 + +### 4. profile 旧兼容路径移除 + +`api-server/src/app.rs` 移除 `/api/runtime/profile/*` 旧兼容挂载,只保留 `/api/profile/*` 新主路径。 + +保留的新路径: + +1. `/api/profile/browse-history` +2. `/api/profile/dashboard` +3. `/api/profile/wallet-ledger` +4. `/api/profile/recharge-center` +5. `/api/profile/recharge/orders` +6. `/api/profile/referrals/invite-center` +7. `/api/profile/referrals/redeem-code` +8. `/api/profile/redeem-codes/redeem` +9. `/api/profile/save-archives` +10. `/api/profile/save-archives/{world_key}` + +新增 `runtime_profile_legacy_routes_are_not_mounted` 测试,确认 `/api/runtime/profile/*` 旧路径返回 `404`。 + +### 5. 前端 profile 请求路径对齐 + +`src/services/rpg-runtime/rpgRuntimeRequest.ts` 对以 `/profile/` 开头的 runtime request 直接发送到 `/api/profile/*`,其他 runtime 路径继续发送到 `/api/runtime/*`。 + +`rpgProfileClient` 与 entry routing 测试同步改为断言 `/api/profile/*`。 + +## 边界说明 + +1. 本次没有新增、删除或调整 SpacetimeDB 表字段,因此不修改 `migration.rs`。 +2. 本次没有改 reducer/procedure 对外签名,也没有改 generated binding。 +3. 本次删除的是 HTTP 兼容挂载,不保留 `/api/runtime/profile/*` fallback。 +4. 本次不把 SpacetimeDB 表查询搬进 `module-runtime`;领域模块只接收纯输入并返回纯结果。 +5. 本次不处理 runtime story / RPG story 规则;相关内容仍归 `WP-RS` 和 `WP-RPG`。 + +## 验收 + +```powershell +cargo fmt -p module-runtime -p spacetime-module -p api-server --manifest-path server-rs/Cargo.toml --check +cargo test -p module-runtime --manifest-path server-rs/Cargo.toml +cargo check -p spacetime-module --manifest-path server-rs/Cargo.toml +cargo test -p api-server runtime_profile --manifest-path server-rs/Cargo.toml +cargo test -p api-server runtime_browse_history --manifest-path server-rs/Cargo.toml +cargo test -p api-server runtime_snapshot --manifest-path server-rs/Cargo.toml +cargo test -p api-server profile_save_archives --manifest-path server-rs/Cargo.toml +npm.cmd run test -- src/services/rpg-entry/rpgProfileClient.test.ts src/services/rpg-entry/rpgEntryClients.routing.test.ts +npm.cmd run check:server-rs-ddd +npm.cmd run check:encoding -- docs/technical/SERVER_RS_DDD_WP_RT_ADAPTER_API_CLOSURE_2026-04-29.md server-rs/crates/module-runtime/src/domain.rs server-rs/crates/module-runtime/src/errors.rs server-rs/crates/module-runtime/src/commands.rs server-rs/crates/module-runtime/src/application.rs server-rs/crates/module-runtime/src/lib.rs server-rs/crates/module-runtime/README.md server-rs/crates/spacetime-module/src/runtime/profile.rs server-rs/crates/api-server/src/app.rs server-rs/crates/api-server/src/runtime_save.rs server-rs/crates/api-server/src/runtime_profile.rs server-rs/crates/api-server/src/runtime_browse_history.rs src/services/rpg-runtime/rpgRuntimeRequest.ts src/services/rpg-entry/rpgProfileClient.test.ts src/services/rpg-entry/rpgEntryClients.routing.test.ts docs/technical/README.md docs/technical/SERVER_RS_DDD_PARALLEL_TASKLIST_2026-04-29.md +git diff --check -- docs/technical/SERVER_RS_DDD_WP_RT_ADAPTER_API_CLOSURE_2026-04-29.md server-rs/crates/module-runtime/src/domain.rs server-rs/crates/module-runtime/src/errors.rs server-rs/crates/module-runtime/src/commands.rs server-rs/crates/module-runtime/src/application.rs server-rs/crates/module-runtime/src/lib.rs server-rs/crates/module-runtime/README.md server-rs/crates/spacetime-module/src/runtime/profile.rs server-rs/crates/api-server/src/app.rs server-rs/crates/api-server/src/runtime_save.rs server-rs/crates/api-server/src/runtime_profile.rs server-rs/crates/api-server/src/runtime_browse_history.rs src/services/rpg-runtime/rpgRuntimeRequest.ts src/services/rpg-entry/rpgProfileClient.test.ts src/services/rpg-entry/rpgEntryClients.routing.test.ts docs/technical/README.md docs/technical/SERVER_RS_DDD_PARALLEL_TASKLIST_2026-04-29.md +npm.cmd run api-server:maincloud +``` diff --git a/docs/technical/SERVER_RS_DDD_WP_RT_APPLICATION_RECORD_REFACTOR_2026-04-29.md b/docs/technical/SERVER_RS_DDD_WP_RT_APPLICATION_RECORD_REFACTOR_2026-04-29.md new file mode 100644 index 00000000..d2060b07 --- /dev/null +++ b/docs/technical/SERVER_RS_DDD_WP_RT_APPLICATION_RECORD_REFACTOR_2026-04-29.md @@ -0,0 +1,47 @@ +# WP-RT 应用记录投影拆分落地说明 + +## 背景 + +`module-runtime` 已完成领域类型、错误类型和命令构造拆分。根入口 `lib.rs` 仍承载大量 `build_runtime_*_record`,负责把 SpacetimeDB/procedure 快照转换为 BFF 或上层 facade 使用的记录投影。该职责更接近应用层读模型映射,应迁入 `application.rs`。 + +## 本次范围 + +允许修改: + +1. `server-rs/crates/module-runtime/src/application.rs` +2. `server-rs/crates/module-runtime/src/lib.rs` +3. `server-rs/crates/module-runtime/README.md` +4. `docs/technical/README.md` +5. 全局 DDD 任务清单进度记录 + +禁止修改: + +1. `server-rs/crates/spacetime-module/src/**` +2. `server-rs/crates/spacetime-client/src/**` +3. `server-rs/crates/api-server/src/**` +4. 前端 services/hooks/components +5. `server-rs/crates/spacetime-module/src/migration.rs` + +## 设计 + +本次将以下记录投影函数迁入 `application.rs`: + +1. settings、browse history、profile dashboard、wallet ledger、recharge center、membership、referral、reward code、redeem code、played world、play stats、runtime snapshot、save archive 的 `build_runtime_*_record`。 +2. 记录投影专用 JSON helper:`parse_optional_json_value`。 + +`format_utc_micros` 暂留 `lib.rs`,作为跨 commands/application 复用的时间格式化工具。充值商品目录函数也暂留 `lib.rs`,因为命令构造仍需要用它校验充值商品 ID。 + +## 边界说明 + +1. 本次不改变任何记录投影字段、时间格式、JSON 解析或错误语义。 +2. 本次不迁移充值商品目录和商品查找函数。 +3. 本次不改 SpacetimeDB 表结构、reducer、procedure 或 API route。 + +## 验收 + +```powershell +cargo fmt -p module-runtime --manifest-path server-rs/Cargo.toml --check +cargo test -p module-runtime --manifest-path server-rs/Cargo.toml +npm.cmd run check:server-rs-ddd +npm.cmd run check:encoding -- docs/technical/SERVER_RS_DDD_WP_RT_APPLICATION_RECORD_REFACTOR_2026-04-29.md server-rs/crates/module-runtime/src/application.rs server-rs/crates/module-runtime/src/lib.rs server-rs/crates/module-runtime/README.md docs/technical/README.md docs/technical/SERVER_RS_DDD_PARALLEL_TASKLIST_2026-04-29.md +``` diff --git a/docs/technical/SERVER_RS_DDD_WP_RT_COMMANDS_REFACTOR_2026-04-29.md b/docs/technical/SERVER_RS_DDD_WP_RT_COMMANDS_REFACTOR_2026-04-29.md new file mode 100644 index 00000000..d86c1266 --- /dev/null +++ b/docs/technical/SERVER_RS_DDD_WP_RT_COMMANDS_REFACTOR_2026-04-29.md @@ -0,0 +1,50 @@ +# WP-RT 命令构造拆分落地说明 + +## 背景 + +`module-runtime` 已将领域类型迁入 `domain.rs`,错误类型迁入 `errors.rs`。根入口 `lib.rs` 仍承载大量 `build_runtime_*_input`、浏览历史写入准备、snapshot upsert JSON 归一化、邀请码/兑换码归一化等命令构造函数。为了继续推进 DDD 分层,本次将写入命令构造与字段归一化迁入 `commands.rs`。 + +## 本次范围 + +允许修改: + +1. `server-rs/crates/module-runtime/src/commands.rs` +2. `server-rs/crates/module-runtime/src/lib.rs` +3. `server-rs/crates/module-runtime/README.md` +4. `docs/technical/README.md` +5. 全局 DDD 任务清单进度记录 + +禁止修改: + +1. `server-rs/crates/spacetime-module/src/**` +2. `server-rs/crates/spacetime-client/src/**` +3. `server-rs/crates/api-server/src/**` +4. 前端 services/hooks/components +5. `server-rs/crates/spacetime-module/src/migration.rs` + +## 设计 + +本次将以下函数迁入 `commands.rs`: + +1. settings、browse history、profile dashboard、wallet、recharge、referral、reward code、redeem code、play stats、runtime snapshot、save archive 的 `build_runtime_*_input`。 +2. `build_runtime_browse_history_sync_input`、`prepare_runtime_browse_history_entries` 和 `build_runtime_browse_history_id`。 +3. `normalize_invite_code`、`normalize_redeem_code`。 +4. 仅服务命令构造的私有 helper:`normalize_runtime_*_user_id`、`parse_utc_rfc3339_to_micros`、`normalize_bottom_tab`、`normalize_current_story_json`。 + +`lib.rs` 继续通过 `pub use commands::*` 暴露原公开函数名。记录投影 builder、充值商品目录、通用时间格式化和 JSON 读取 helper 暂留 `lib.rs`,后续再拆 `application.rs`。 + +## 边界说明 + +1. 本次不改变任何输入构造的校验语义。 +2. 本次不移动记录投影 builder,避免命令层和应用读模型混写。 +3. 本次不改 SpacetimeDB 表结构、reducer、procedure 或 API route。 +4. 本次不触发 `migration.rs` 更新。 + +## 验收 + +```powershell +cargo fmt -p module-runtime --manifest-path server-rs/Cargo.toml --check +cargo test -p module-runtime --manifest-path server-rs/Cargo.toml +npm.cmd run check:server-rs-ddd +npm.cmd run check:encoding -- docs/technical/SERVER_RS_DDD_WP_RT_COMMANDS_REFACTOR_2026-04-29.md server-rs/crates/module-runtime/src/commands.rs server-rs/crates/module-runtime/src/lib.rs server-rs/crates/module-runtime/README.md docs/technical/README.md docs/technical/SERVER_RS_DDD_PARALLEL_TASKLIST_2026-04-29.md +``` diff --git a/docs/technical/SERVER_RS_DDD_WP_RT_DOMAIN_SNAPSHOT_RECORD_REFACTOR_2026-04-29.md b/docs/technical/SERVER_RS_DDD_WP_RT_DOMAIN_SNAPSHOT_RECORD_REFACTOR_2026-04-29.md new file mode 100644 index 00000000..e7bccb4e --- /dev/null +++ b/docs/technical/SERVER_RS_DDD_WP_RT_DOMAIN_SNAPSHOT_RECORD_REFACTOR_2026-04-29.md @@ -0,0 +1,52 @@ +# WP-RT Snapshot/Profile/Save Archive 领域快照与记录类型拆分落地说明 + +## 背景 + +`module-runtime/src/lib.rs` 仍集中承载 runtime snapshot、browse history、profile dashboard、wallet ledger、recharge、referral、played world、play stats 和 save archive 的大量快照、输入、过程结果与 BFF 记录类型。上一切片已将 runtime settings 值对象迁入 `domain.rs`,本次继续把这些纯数据事实迁入领域模型文件,降低根入口体积,并为后续 `commands.rs`、`application.rs` 和 SpacetimeDB adapter 接线拆分留出边界。 + +本次只移动纯类型和类型自带的字符串格式化方法,不修改构造函数、归一化函数、测试、SpacetimeDB 表结构、API route 或前端。 + +## 本次范围 + +允许修改: + +1. `server-rs/crates/module-runtime/src/domain.rs` +2. `server-rs/crates/module-runtime/src/lib.rs` +3. `server-rs/crates/module-runtime/README.md` +4. `docs/technical/README.md` +5. 全局 DDD 任务清单进度记录 + +禁止修改: + +1. `server-rs/crates/spacetime-module/src/**` +2. `server-rs/crates/spacetime-client/src/**` +3. `server-rs/crates/api-server/src/**` +4. 前端 services/hooks/components +5. `server-rs/crates/spacetime-module/src/migration.rs` + +## 设计 + +本次将以下类型迁入 `domain.rs`: + +1. runtime snapshot、runtime setting、browse history、profile dashboard、wallet ledger、recharge、reward code、redeem code、referral、played world、play stats、save archive 的 snapshot/input/procedure result。 +2. `RuntimeProfileWalletLedgerSourceType`、`RuntimeProfileRedeemCodeMode`、`RuntimeProfileRechargeProductKind`、`RuntimeProfileMembershipStatus`、`RuntimeProfileMembershipTier`、`RuntimeProfileRechargeOrderStatus`、`RuntimeBrowseHistoryThemeMode` 等领域枚举及其 `as_str` / `from_client_str` 方法。 +3. `RuntimeSettingsRecord`、`RuntimeProfileDashboardRecord`、`RuntimeProfileWalletLedgerEntryRecord`、`RuntimeProfilePlayedWorldRecord`、`RuntimeProfilePlayStatsRecord`、`RuntimeSnapshotRecord`、`RuntimeProfileSaveArchiveRecord` 等回包投影记录类型。 +4. 与这些类型强绑定、但不携带构造逻辑的默认常量:`DEFAULT_BROWSE_HISTORY_AUTHOR_DISPLAY_NAME`、`MAX_BROWSE_HISTORY_BATCH_SIZE`、`PROFILE_WALLET_LEDGER_LIST_LIMIT`、`PROFILE_REFERRAL_REWARD_POINTS`、`PROFILE_REFERRAL_DAILY_INVITER_REWARD_LIMIT`、`SAVE_SNAPSHOT_VERSION`、`DEFAULT_SAVE_ARCHIVE_SUMMARY_TEXT`、`PROFILE_RECHARGE_PAYMENT_CHANNEL_MOCK`。 + +`lib.rs` 继续通过 `pub use domain::*` 保持原公开 API。构造函数、归一化函数、充值商品目录函数、错误枚举和测试暂留 `lib.rs`,避免在同一切片里混入命令层与错误层重排。 + +## 边界说明 + +1. 这些类型包含条件 `SpacetimeType` 派生,但不是 SpacetimeDB table;本次只移动自定义类型位置,不修改 table/reducer/procedure。 +2. 本次不移动 `RuntimeSettingsFieldError`、`RuntimeBrowseHistoryFieldError`、`RuntimeProfileFieldError`,后续可单独迁入 `errors.rs`。 +3. 本次不移动 `build_runtime_*` 构造函数,后续可按 settings、browse history、profile/save 三组拆入 `commands.rs` / `application.rs`。 +4. 本次不触发 `migration.rs` 更新。 + +## 验收 + +```powershell +cargo fmt -p module-runtime --manifest-path server-rs/Cargo.toml --check +cargo test -p module-runtime --manifest-path server-rs/Cargo.toml +npm.cmd run check:server-rs-ddd +npm.cmd run check:encoding -- docs/technical/SERVER_RS_DDD_WP_RT_DOMAIN_SNAPSHOT_RECORD_REFACTOR_2026-04-29.md server-rs/crates/module-runtime/src/domain.rs server-rs/crates/module-runtime/src/lib.rs server-rs/crates/module-runtime/README.md docs/technical/README.md docs/technical/SERVER_RS_DDD_PARALLEL_TASKLIST_2026-04-29.md +``` diff --git a/docs/technical/SERVER_RS_DDD_WP_RT_ERROR_LAYER_REFACTOR_2026-04-29.md b/docs/technical/SERVER_RS_DDD_WP_RT_ERROR_LAYER_REFACTOR_2026-04-29.md new file mode 100644 index 00000000..23bb52d9 --- /dev/null +++ b/docs/technical/SERVER_RS_DDD_WP_RT_ERROR_LAYER_REFACTOR_2026-04-29.md @@ -0,0 +1,48 @@ +# WP-RT 错误层拆分落地说明 + +## 背景 + +`module-runtime` 已将 runtime settings 值对象,以及 snapshot/profile/save archive 的快照、输入、过程结果和记录投影类型迁入 `domain.rs`。根入口 `lib.rs` 仍承载 `RuntimeSettingsFieldError`、`RuntimeBrowseHistoryFieldError`、`RuntimeProfileFieldError` 及其中文错误文案,后续继续拆 `commands.rs` 和 `application.rs` 前,需要先把错误语义归位到 `errors.rs`。 + +## 本次范围 + +允许修改: + +1. `server-rs/crates/module-runtime/src/errors.rs` +2. `server-rs/crates/module-runtime/src/lib.rs` +3. `server-rs/crates/module-runtime/README.md` +4. `docs/technical/README.md` +5. 全局 DDD 任务清单进度记录 + +禁止修改: + +1. `server-rs/crates/spacetime-module/src/**` +2. `server-rs/crates/spacetime-client/src/**` +3. `server-rs/crates/api-server/src/**` +4. 前端 services/hooks/components +5. `server-rs/crates/spacetime-module/src/migration.rs` + +## 设计 + +本次将以下错误类型迁入 `errors.rs`: + +1. `RuntimeSettingsFieldError` +2. `RuntimeBrowseHistoryFieldError` +3. `RuntimeProfileFieldError` + +同步迁移三组 `Display` 实现,保持中文错误文案和 `MAX_BROWSE_HISTORY_BATCH_SIZE` 上限提示不变。`lib.rs` 继续通过 `pub use errors::*` 暴露原公开 API,调用点无需修改。 + +## 边界说明 + +1. 本次不调整错误枚举变体,不改任何业务校验语义。 +2. 本次不移动 `build_runtime_*` 构造函数;这些函数仍在 `lib.rs` 使用错误类型。 +3. 本次不改 SpacetimeDB 表结构、reducer、procedure 或 API route。 + +## 验收 + +```powershell +cargo fmt -p module-runtime --manifest-path server-rs/Cargo.toml --check +cargo test -p module-runtime --manifest-path server-rs/Cargo.toml +npm.cmd run check:server-rs-ddd +npm.cmd run check:encoding -- docs/technical/SERVER_RS_DDD_WP_RT_ERROR_LAYER_REFACTOR_2026-04-29.md server-rs/crates/module-runtime/src/errors.rs server-rs/crates/module-runtime/src/lib.rs server-rs/crates/module-runtime/README.md docs/technical/README.md docs/technical/SERVER_RS_DDD_PARALLEL_TASKLIST_2026-04-29.md +``` diff --git a/docs/technical/SERVER_RS_DDD_WP_RT_RUNTIME_SETTINGS_DOMAIN_REFACTOR_2026-04-29.md b/docs/technical/SERVER_RS_DDD_WP_RT_RUNTIME_SETTINGS_DOMAIN_REFACTOR_2026-04-29.md new file mode 100644 index 00000000..84a72b89 --- /dev/null +++ b/docs/technical/SERVER_RS_DDD_WP_RT_RUNTIME_SETTINGS_DOMAIN_REFACTOR_2026-04-29.md @@ -0,0 +1,52 @@ +# WP-RT Runtime Settings 领域值对象拆分落地说明 + +## 背景 + +`module-runtime` 已承接运行时设置、快照、浏览历史、资料页、钱包、充值、邀请、兑换码、游玩记录和存档等纯规则,但当前大量类型仍集中在 `src/lib.rs`。`WP-RT Runtime/Profile/Save` 需要逐步把纯领域事实和写入命令拆入 DDD 分层文件,避免后续 `spacetime-module` 与 `api-server` 接线时继续依赖巨型根文件。 + +本次只启动 `runtime settings` 这一条最小切片,不改 SpacetimeDB 表结构、不改 HTTP route、不改前端。 + +## 本次范围 + +允许修改: + +1. `server-rs/crates/module-runtime/src/domain.rs` +2. `server-rs/crates/module-runtime/src/lib.rs` +3. `server-rs/crates/module-runtime/README.md` +4. 本文档 +5. 全局 DDD 任务清单进度记录 + +禁止修改: + +1. `server-rs/crates/spacetime-module/src/**` +2. `server-rs/crates/spacetime-client/src/**` +3. `server-rs/crates/api-server/src/**` +4. 前端 services/hooks/components + +## 设计 + +本次将以下运行时设置领域对象迁入 `domain.rs`: + +1. `DEFAULT_MUSIC_VOLUME` +2. `DEFAULT_PLATFORM_THEME` +3. `RuntimePlatformTheme` +4. `RuntimeSettings` + +`RuntimePlatformTheme::as_str`、`RuntimePlatformTheme::from_client_str`、`RuntimeSettings::defaults` 和 `RuntimeSettings::normalized` 同步迁入 `domain.rs`。`lib.rs` 继续通过 `pub use domain::*` 暴露原有 API,保证 `spacetime-module`、`spacetime-client` 和既有测试无需改调用点。 + +## 边界说明 + +1. 本次不移动 `RuntimeSettingSnapshot`、`RuntimeSettingGetInput`、`RuntimeSettingUpsertInput` 和 procedure result,因为它们仍与 SpacetimeDB procedure DTO 强绑定,后续可作为 `commands.rs` / Adapter mapper 切片单独拆分。 +2. 本次不移动 `RuntimeSettingsFieldError`,避免把错误 Display 与多个 profile 错误枚举混在同一切片里改动。 +3. 本次不移动浏览历史、钱包、充值、邀请、兑换码、游玩记录和存档类型。 + +## 验收 + +```powershell +cargo test -p module-runtime --manifest-path server-rs/Cargo.toml +cargo fmt -p module-runtime --manifest-path server-rs/Cargo.toml --check +npm.cmd run check:server-rs-ddd +npm.cmd run check:encoding -- docs/technical/SERVER_RS_DDD_WP_RT_RUNTIME_SETTINGS_DOMAIN_REFACTOR_2026-04-29.md server-rs/crates/module-runtime/src/domain.rs server-rs/crates/module-runtime/src/lib.rs server-rs/crates/module-runtime/README.md docs/technical/SERVER_RS_DDD_PARALLEL_TASKLIST_2026-04-29.md +``` + +本次不改后端运行接线、不改 SpacetimeDB table/reducer/procedure,因此不触发 `migration.rs` 更新。 diff --git a/docs/technical/SERVER_RS_DDD_WP_SC_SPACETIME_CLIENT_REFACTOR_2026-04-29.md b/docs/technical/SERVER_RS_DDD_WP_SC_SPACETIME_CLIENT_REFACTOR_2026-04-29.md index 9a96aaf9..ea300f96 100644 --- a/docs/technical/SERVER_RS_DDD_WP_SC_SPACETIME_CLIENT_REFACTOR_2026-04-29.md +++ b/docs/technical/SERVER_RS_DDD_WP_SC_SPACETIME_CLIENT_REFACTOR_2026-04-29.md @@ -77,3 +77,45 @@ npm.cmd run api-server:maincloud ``` 说明:本次不改 SpacetimeDB 表、reducer、procedure,不刷新生成绑定,不同步 `migration.rs`。 + +## 8. story runtime inventory source 接线切片 + +### 8.1 目标 + +本轮继续在 `WP-SC` 内认领一个可并行切片:在 `SpacetimeClient::get_story_runtime_projection_source` 中复用已稳定的 `get_runtime_inventory_state` typed facade,将 SpacetimeDB 背包/装备快照折回投影所需的 `game_state.playerInventory` 与 `game_state.playerEquipment`。 + +目标是让 `/api/story/sessions/{story_session_id}/runtime-projection` 的读取投影优先消费新的 inventory adapter 结果,而不是只依赖 runtime snapshot 中的历史 JSON 背包副本。 + +### 8.2 边界 + +本轮允许修改: + +1. `server-rs/crates/spacetime-client/src/story_runtime.rs` +2. 本文档 +3. `docs/technical/SERVER_RS_DDD_PARALLEL_TASKLIST_2026-04-29.md` + +本轮禁止修改: + +1. `server-rs/crates/spacetime-module/src/**` +2. `server-rs/crates/spacetime-client/src/module_bindings/**` +3. `server-rs/crates/api-server/src/app.rs` +4. HTTP DTO、前端 services/hooks/components +5. `migration.rs` + +### 8.3 实现约束 + +1. 只在 `spacetime-client` 中组合已存在 facade,不新增 table、reducer、procedure。 +2. `StoryRuntimeProjectionSource` 的输出结构保持不变,投影规则继续由 `module-runtime-story::build_story_runtime_projection` 承接。 +3. inventory slot 到 runtime story JSON 的转换只做字段映射,不新增玩法规则。 +4. `get_runtime_inventory_state` 若返回作用域不匹配,必须中止投影,避免把其他会话的背包装进当前 story session。 + +### 8.4 验收 + +```powershell +cargo test -p spacetime-client story_runtime --manifest-path server-rs/Cargo.toml +cargo check -p spacetime-client --manifest-path server-rs/Cargo.toml +cargo fmt -p spacetime-client --manifest-path server-rs/Cargo.toml --check +npm.cmd run check:server-rs-ddd +npm.cmd run check:encoding -- docs/technical/SERVER_RS_DDD_WP_SC_SPACETIME_CLIENT_REFACTOR_2026-04-29.md docs/technical/SERVER_RS_DDD_PARALLEL_TASKLIST_2026-04-29.md server-rs/crates/spacetime-client/src/story_runtime.rs +npm.cmd run api-server:maincloud +``` diff --git a/docs/technical/SERVER_RS_DDD_WP_ST_AUTH_ADAPTER_SPLIT_2026-04-29.md b/docs/technical/SERVER_RS_DDD_WP_ST_AUTH_ADAPTER_SPLIT_2026-04-29.md new file mode 100644 index 00000000..fa73a930 --- /dev/null +++ b/docs/technical/SERVER_RS_DDD_WP_ST_AUTH_ADAPTER_SPLIT_2026-04-29.md @@ -0,0 +1,56 @@ +# WP-ST Auth Adapter 目录化切片说明 + +## 背景 + +`spacetime-module/src/auth.rs` 同时承接认证表定义、procedure 入口、事务内导入导出逻辑和 module-auth 快照 JSON mapper。随着 `WP-A Auth` 已完成 DDD 骨架归位,SpacetimeDB 侧也需要把 Auth adapter 从单文件拆到上下文目录,避免后续继续堆回根文件。 + +本次属于 `WP-ST SpacetimeDB Adapter` 的可并行切片,只做 Auth adapter 目录化,不改变 SpacetimeDB schema、procedure 名称、procedure 入参/出参或绑定形状。 + +## 本次范围 + +允许修改: + +1. `server-rs/crates/spacetime-module/src/auth.rs` +2. `server-rs/crates/spacetime-module/src/auth/mod.rs` +3. `server-rs/crates/spacetime-module/src/auth/tables.rs` +4. `server-rs/crates/spacetime-module/src/auth/procedures.rs` +5. `server-rs/crates/spacetime-module/src/auth/mapper.rs` +6. 本文档 +7. `docs/technical/README.md` +8. `docs/technical/SERVER_RS_DDD_PARALLEL_TASKLIST_2026-04-29.md` + +禁止修改: + +1. `spacetime-module/src/lib.rs` +2. `spacetime-module/src/migration.rs` +3. `docs/technical/SPACETIMEDB_TABLE_CATALOG.md` +4. `spacetime-client/src/module_bindings/**` +5. `api-server/src/**` +6. 前端 services / hooks / components + +## 设计 + +本次将原 `auth.rs` 拆成: + +1. `auth/mod.rs`:Auth adapter 子模块入口,继续 `pub use procedures::*` 与 `pub use tables::*`,保持根模块 `pub use auth::*` 的对外导出不变。 +2. `auth/tables.rs`:保留 `AuthStoreSnapshot`、`UserAccount`、`AuthIdentity`、`RefreshSession` 四张表定义和索引,不改字段、可见性、accessor 或索引名。 +3. `auth/procedures.rs`:保留 `get_auth_store_snapshot`、`upsert_auth_store_snapshot`、`import_auth_store_snapshot`、`export_auth_store_snapshot_from_tables` 四个 procedure 入口,以及对应事务内读写逻辑。 +4. `auth/mapper.rs`:收口 module-auth 持久化快照 JSON 结构、refresh session client info JSON 结构和 identity id 组件清理函数,仅供 Auth procedure 内部使用。 + +## 边界说明 + +1. 本次未新增 reducer,现有 Auth 同步仍通过 procedure 完成。 +2. 本次未改变表结构,不需要修改 `migration.rs`。 +3. 本次未生成 SpacetimeDB 绑定,原因是导出的表、类型和 procedure 名称未改变。 +4. Auth adapter 仍只负责认证快照与正式表的导入导出,不承接短信、微信 OAuth、JWT、cookie、HTTP 或文件持久化。 + +## 验收 + +```powershell +cargo fmt -p spacetime-module --manifest-path server-rs/Cargo.toml --check +cargo check -p spacetime-module --manifest-path server-rs/Cargo.toml +npm.cmd run check:server-rs-ddd +npm.cmd run check:encoding -- docs/technical/SERVER_RS_DDD_WP_ST_AUTH_ADAPTER_SPLIT_2026-04-29.md docs/technical/README.md docs/technical/SERVER_RS_DDD_PARALLEL_TASKLIST_2026-04-29.md server-rs/crates/spacetime-module/src/auth/mod.rs server-rs/crates/spacetime-module/src/auth/tables.rs server-rs/crates/spacetime-module/src/auth/procedures.rs server-rs/crates/spacetime-module/src/auth/mapper.rs +``` + +若 `cargo check -p spacetime-module` 被非 WP-ST 依赖 crate 当前并行改动阻断,应记录具体阻断 crate 和错误位置;本切片不越界修改其他已认领工作包。 diff --git a/docs/technical/SERVER_RS_DDD_WP_ST_CUSTOM_WORLD_ROOT_SPLIT_2026-04-29.md b/docs/technical/SERVER_RS_DDD_WP_ST_CUSTOM_WORLD_ROOT_SPLIT_2026-04-29.md new file mode 100644 index 00000000..2b7af159 --- /dev/null +++ b/docs/technical/SERVER_RS_DDD_WP_ST_CUSTOM_WORLD_ROOT_SPLIT_2026-04-29.md @@ -0,0 +1,30 @@ +# WP-ST Custom World 根入口瘦身落地说明 + +## 背景 + +`spacetime-module/src/lib.rs` 已完成 gameplay 根入口瘦身,但仍直接承载 Custom World 的 SpacetimeDB 表、reducer、procedure 与私有事务 helper。根入口继续膨胀会让后续 `module-custom-world` 领域化、绑定生成检查和并行任务边界都难以维护。 + +本次迁移按 WP-ST Adapter 边界执行:根入口只负责声明模块与 re-export,Custom World 的 SpacetimeDB adapter 真正落到 `spacetime-module/src/custom_world/mod.rs`。 + +## 落地范围 + +1. `lib.rs` 新增 `mod custom_world;` 与 `pub use custom_world::*;`,保留根模块对绑定生成需要的公开导出。 +2. 将当前 `lib.rs` 已公开的 Custom World 表、procedure、reducer、事务 helper 与单测整体迁移到 `custom_world/mod.rs`。 +3. `custom_world/mod.rs` 以 `use crate::*;` 复用根模块已经公开的领域类型、SpacetimeDB 类型和 JSON helper,避免复制跨模块前置导入。 +4. 移除 `lib.rs` 中 Custom World 的重复实现,让根入口只承担组合职责。 + +## 边界 + +1. 不新增、删除或重命名 Custom World 表。 +2. 不新增、删除或重命名当前已公开的 reducer / procedure。 +3. 不修改 `CustomWorldProfile`、`CustomWorldGalleryEntry` 等表字段,本次不触发 `migration.rs` 更新。 +4. 不启用 `custom_world` 子目录中尚未成为根入口正式导出的新过程,避免把后续行为变更混入本次根入口迁移。 +5. 不修改前端、BFF、`server-node` 或 PostgreSQL 兼容逻辑。 + +## 验收 + +1. `cargo check -p spacetime-module --manifest-path server-rs/Cargo.toml` +2. `cargo test -p module-custom-world --manifest-path server-rs/Cargo.toml` +3. `npm.cmd run check:server-rs-ddd` +4. `npm.cmd run check:encoding -- docs/technical/SERVER_RS_DDD_PARALLEL_TASKLIST_2026-04-29.md docs/technical/SERVER_RS_DDD_WP_ST_CUSTOM_WORLD_ROOT_SPLIT_2026-04-29.md server-rs/crates/spacetime-module/src/lib.rs server-rs/crates/spacetime-module/src/custom_world/mod.rs` +5. 修改后端 Rust 代码后,按项目约束运行 `npm.cmd run api-server:maincloud` 并探测 `/healthz`。 diff --git a/docs/technical/SERVER_RS_DDD_WP_ST_GAMEPLAY_ROOT_SPLIT_2026-04-29.md b/docs/technical/SERVER_RS_DDD_WP_ST_GAMEPLAY_ROOT_SPLIT_2026-04-29.md new file mode 100644 index 00000000..a70cc364 --- /dev/null +++ b/docs/technical/SERVER_RS_DDD_WP_ST_GAMEPLAY_ROOT_SPLIT_2026-04-29.md @@ -0,0 +1,31 @@ +# WP-ST Gameplay 根入口瘦身落地说明 + +## 背景 + +`spacetime-module/src/gameplay/mod.rs` 已经承接 RPG gameplay 的 SpacetimeDB table、reducer、procedure、row mapper 和事务 helper,但根入口 `spacetime-module/src/lib.rs` 仍保留同一批 gameplay 代码,导致根文件继续承担 Custom World 与 RPG gameplay 两条大链路。 + +本次切片属于 `WP-ST SpacetimeDB Adapter`,目标是让根入口只做模块声明、re-export 和仍未拆出的 Custom World adapter,不改变 SpacetimeDB schema、reducer/procedure 对外名称或业务规则。 + +## 落地范围 + +1. 在 `spacetime-module/src/lib.rs` 接入 `mod gameplay; pub use gameplay::*;`。 +2. 将根入口中已迁入 `gameplay/mod.rs` 的 RPG gameplay table、reducer、procedure、row mapper 和事务 helper 删除。 +3. 在 `gameplay/mod.rs` 补齐模块内依赖导入。 +4. 补齐 story session 继续推进与读取所需的内部 helper: + - `continue_story_tx` + - `get_story_session_state_tx` +5. 保留根入口中的 Custom World table、procedure、reducer 和测试,本次不拆 Custom World。 + +## 边界 + +1. 本次不修改 `migration.rs`,因为表名、字段、reducer/procedure 名称没有变化。 +2. 本次不修改 `SPACETIMEDB_TABLE_CATALOG.md`,因为表目录没有变化。 +3. 本次不修改 `api-server`、`spacetime-client`、前端 services/hooks/components。 +4. 本次只移动 adapter 归属,不把业务规则从 `module-*` 拉回 SpacetimeDB adapter。 + +## 验收 + +1. `cargo check -p spacetime-module --manifest-path server-rs/Cargo.toml` +2. `npm.cmd run check:server-rs-ddd` +3. `npm.cmd run check:encoding -- server-rs/crates/spacetime-module/src/lib.rs server-rs/crates/spacetime-module/src/gameplay/mod.rs docs/technical/SERVER_RS_DDD_WP_ST_GAMEPLAY_ROOT_SPLIT_2026-04-29.md docs/technical/SERVER_RS_DDD_PARALLEL_TASKLIST_2026-04-29.md` +4. 如需联调发布,再执行当前 CLI 支持的 `spacetime build -p server-rs/crates/spacetime-module`。 diff --git a/docs/technical/SPACETIMEDB_TABLE_CATALOG.md b/docs/technical/SPACETIMEDB_TABLE_CATALOG.md index 28459539..d5a2bc1c 100644 --- a/docs/technical/SPACETIMEDB_TABLE_CATALOG.md +++ b/docs/technical/SPACETIMEDB_TABLE_CATALOG.md @@ -458,7 +458,7 @@ SELECT * FROM puzzle_leaderboard_entry WHERE user_id = '' AND profile_i ### `big_fish_creation_session` - 作用:大鱼吃小鱼创作会话表,保存种子、阶段、锚点包、草稿、资产覆盖和发布就绪状态。 -- 结构:`session_id PK: String`, `owner_user_id: String`, `seed_text: String`, `current_turn: u32`, `progress_percent: u32`, `stage: BigFishCreationStage`, `anchor_pack_json: String`, `draft_json: Option`, `asset_coverage_json: String`, `last_assistant_reply: Option`, `publish_ready: bool`, `created_at: Timestamp`, `updated_at: Timestamp`。 +- 结构:`session_id PK: String`, `owner_user_id: String`, `seed_text: String`, `current_turn: u32`, `progress_percent: u32`, `stage: BigFishCreationStage`, `anchor_pack_json: String`, `draft_json: Option`, `asset_coverage_json: String`, `last_assistant_reply: Option`, `publish_ready: bool`, `play_count: u32`, `created_at: Timestamp`, `updated_at: Timestamp`。 - 索引:`owner_user_id`。 ```sql diff --git a/packages/shared/src/contracts/bigFish.ts b/packages/shared/src/contracts/bigFish.ts index f7fc523b..1cb02dfc 100644 --- a/packages/shared/src/contracts/bigFish.ts +++ b/packages/shared/src/contracts/bigFish.ts @@ -193,3 +193,7 @@ export type BigFishRuntimeSnapshotResponse = { eventLog: string[]; updatedAt: string; }; + +export type BigFishRunResponse = { + run: BigFishRuntimeSnapshotResponse; +}; diff --git a/packages/shared/src/contracts/story.ts b/packages/shared/src/contracts/story.ts new file mode 100644 index 00000000..9820018c --- /dev/null +++ b/packages/shared/src/contracts/story.ts @@ -0,0 +1,104 @@ +import type { JsonObject } from './common'; + +/** + * story session 主链共享契约。 + * 字段命名与 server-rs/shared-contracts/src/story.rs 的 camelCase 回包保持一致。 + */ + +export type BeginStorySessionRequest = { + runtimeSessionId: string; + worldProfileId: string; + initialPrompt: string; + openingSummary?: string | null; +}; + +export type ContinueStoryRequest = { + storySessionId: string; + narrativeText: string; + choiceFunctionId?: string | null; +}; + +export type StorySessionPayload = { + storySessionId: string; + runtimeSessionId: string; + actorUserId: string; + worldProfileId: string; + initialPrompt: string; + openingSummary?: string | null; + latestNarrativeText: string; + latestChoiceFunctionId?: string | null; + status: string; + version: number; + createdAt: string; + updatedAt: string; +}; + +export type StoryEventPayload = { + eventId: string; + storySessionId: string; + eventKind: string; + narrativeText: string; + choiceFunctionId?: string | null; + createdAt: string; +}; + +export type StorySessionMutationResponse = { + storySession: StorySessionPayload; + storyEvent: StoryEventPayload; +}; + +export type StorySessionStateResponse = { + storySession: StorySessionPayload; + storyEvents: StoryEventPayload[]; +}; + +export type StoryRuntimeProjectionRequest = { + storySessionId: string; + clientVersion?: number; +}; + +export type StoryRuntimeActorProjection = { + hp: number; + maxHp: number; + mana: number; + maxMana: number; + currency: number; + currencyText: string; +}; + +export type StoryRuntimeInventoryProjection = { + backpackItems: JsonObject[]; + equipmentSlots: JsonObject[]; + forgeRecipes: JsonObject[]; +}; + +export type StoryRuntimeOptionProjection = { + functionId: string; + actionText: string; + detailText?: string | null; + scope: string; + payload?: JsonObject | null; + enabled: boolean; + reason?: string | null; +}; + +export type StoryRuntimeStatusProjection = { + inBattle: boolean; + npcInteractionActive: boolean; + currentEncounterId?: string | null; + currentNpcBattleMode?: string | null; + currentNpcBattleOutcome?: string | null; +}; + +export type StoryRuntimeProjectionResponse = { + storySession: StorySessionPayload; + storyEvents: StoryEventPayload[]; + serverVersion: number; + actor: StoryRuntimeActorProjection; + inventory: StoryRuntimeInventoryProjection; + options: StoryRuntimeOptionProjection[]; + status: StoryRuntimeStatusProjection; + currentNarrativeText?: string | null; + actionResultText?: string | null; + toast?: string | null; +}; diff --git a/packages/shared/src/index.ts b/packages/shared/src/index.ts index 0744e6f2..6eb9609f 100644 --- a/packages/shared/src/index.ts +++ b/packages/shared/src/index.ts @@ -2,6 +2,7 @@ export * from './assets/qwenSprite'; export * from './contracts/auth'; export type * from './contracts/bigFish'; export * from './contracts/common'; +export type * from './contracts/creationAgentDocumentInput'; export type * from './contracts/customWorldAgent'; export * from './contracts/rpgAgentActions'; export * from './contracts/rpgAgentAnchors'; @@ -22,6 +23,7 @@ export * from './contracts/rpgRuntimeQuestAssist'; export * from './contracts/rpgRuntimeStoryAction'; export * from './contracts/rpgRuntimeStoryState'; export * from './contracts/runtime'; +export type * from './contracts/story'; export * from './http'; export * from './llm/narrativeLanguage'; export * from './llm/parsers'; diff --git a/server-rs/Cargo.lock b/server-rs/Cargo.lock index b802434e..de53fc36 100644 --- a/server-rs/Cargo.lock +++ b/server-rs/Cargo.lock @@ -1864,6 +1864,7 @@ dependencies = [ "time", "tokio", "tracing", + "url", "urlencoding", "uuid", ] diff --git a/server-rs/crates/api-server/src/ai_tasks.rs b/server-rs/crates/api-server/src/ai_tasks.rs index 27b3c756..d90c0be7 100644 --- a/server-rs/crates/api-server/src/ai_tasks.rs +++ b/server-rs/crates/api-server/src/ai_tasks.rs @@ -500,6 +500,7 @@ fn current_utc_micros() -> i64 { #[cfg(test)] mod tests { use axum::{ + Router, body::Body, http::{Request, StatusCode}, }; @@ -639,6 +640,129 @@ mod tests { ); } + #[tokio::test] + async fn ai_task_mutation_routes_require_authentication() { + let app = build_router(AppState::new(AppConfig::default()).expect("state should build")); + + for route in ai_task_mutation_route_cases() { + let (status, _) = post_ai_task_route(app.clone(), route.uri, None, route.body).await; + assert_eq!(status, StatusCode::UNAUTHORIZED, "{}", route.uri); + } + } + + #[tokio::test] + async fn ai_task_mutation_routes_return_bad_gateway_when_spacetime_not_published() { + let state = seed_authenticated_state().await; + let token = issue_access_token(&state); + let app = build_router(state); + + for route in ai_task_mutation_route_cases() { + let (status, payload) = + post_ai_task_route(app.clone(), route.uri, Some(&token), route.body).await; + assert_eq!(status, StatusCode::BAD_GATEWAY, "{}", route.uri); + assert_eq!( + payload["error"]["details"]["provider"], + Value::String("spacetimedb".to_string()), + "{}", + route.uri + ); + } + } + + struct AiTaskRouteCase { + uri: &'static str, + body: Option, + } + + fn ai_task_mutation_route_cases() -> Vec { + vec![ + AiTaskRouteCase { + uri: "/api/ai/tasks/aitask_001/stages/request_model/start", + body: None, + }, + AiTaskRouteCase { + uri: "/api/ai/tasks/aitask_001/chunks", + body: Some(json!({ + "stageKind": "request_model", + "sequence": 1, + "deltaText": "你听见远处的铃声。" + })), + }, + AiTaskRouteCase { + uri: "/api/ai/tasks/aitask_001/stages/request_model/complete", + body: Some(json!({ + "textOutput": "你听见远处的铃声。", + "structuredPayloadJson": "{\"scene\":\"camp\"}", + "warningMessages": [] + })), + }, + AiTaskRouteCase { + uri: "/api/ai/tasks/aitask_001/references", + body: Some(json!({ + "referenceKind": "story_event", + "referenceId": "storyevt_001", + "label": "营地开场" + })), + }, + AiTaskRouteCase { + uri: "/api/ai/tasks/aitask_001/complete", + body: None, + }, + AiTaskRouteCase { + uri: "/api/ai/tasks/aitask_001/fail", + body: Some(json!({ + "failureMessage": "模型返回内容为空" + })), + }, + AiTaskRouteCase { + uri: "/api/ai/tasks/aitask_001/cancel", + body: None, + }, + ] + } + + async fn post_ai_task_route( + app: Router, + uri: &str, + bearer_token: Option<&str>, + body: Option, + ) -> (StatusCode, Value) { + let mut request = Request::builder() + .method("POST") + .uri(uri) + .header("x-genarrative-response-envelope", "v1"); + + if let Some(token) = bearer_token { + request = request.header("authorization", format!("Bearer {token}")); + } + + let body = if let Some(payload) = body { + request = request.header("content-type", "application/json"); + Body::from(payload.to_string()) + } else { + Body::empty() + }; + + let response = app + .oneshot(request.body(body).expect("request should build")) + .await + .expect("request should succeed"); + let status = response.status(); + let body = response + .into_body() + .collect() + .await + .expect("body should collect") + .to_bytes(); + let payload = if body.is_empty() { + Value::Null + } else { + serde_json::from_slice(&body).expect("response body should be valid json") + }; + + (status, payload) + } + async fn seed_authenticated_state() -> AppState { let state = AppState::new(AppConfig::default()).expect("state should build"); state diff --git a/server-rs/crates/api-server/src/app.rs b/server-rs/crates/api-server/src/app.rs index 33727ea3..91c59692 100644 --- a/server-rs/crates/api-server/src/app.rs +++ b/server-rs/crates/api-server/src/app.rs @@ -33,9 +33,10 @@ use crate::{ auth_public_user::{get_public_user_by_code, get_public_user_by_id}, auth_sessions::auth_sessions, big_fish::{ - create_big_fish_session, delete_big_fish_work, execute_big_fish_action, + create_big_fish_session, delete_big_fish_work, execute_big_fish_action, get_big_fish_run, get_big_fish_session, get_big_fish_works, list_big_fish_gallery, record_big_fish_play, - stream_big_fish_message, submit_big_fish_message, + start_big_fish_run, stream_big_fish_message, submit_big_fish_input, + submit_big_fish_message, }, character_animation_assets::{ generate_character_animation, get_character_animation_job, get_character_workflow_cache, @@ -652,6 +653,27 @@ pub fn build_router(state: AppState) -> Router { require_bearer_auth, )), ) + .route( + "/api/runtime/big-fish/sessions/{session_id}/runs", + post(start_big_fish_run).route_layer(middleware::from_fn_with_state( + state.clone(), + require_bearer_auth, + )), + ) + .route( + "/api/runtime/big-fish/runs/{run_id}", + get(get_big_fish_run).route_layer(middleware::from_fn_with_state( + state.clone(), + require_bearer_auth, + )), + ) + .route( + "/api/runtime/big-fish/runs/{run_id}/input", + post(submit_big_fish_input).route_layer(middleware::from_fn_with_state( + state.clone(), + require_bearer_auth, + )), + ) .route( "/api/runtime/puzzle/agent/sessions", post(create_puzzle_agent_session).route_layer(middleware::from_fn_with_state( @@ -828,16 +850,6 @@ pub fn build_router(state: AppState) -> Router { require_bearer_auth, )), ) - .route( - "/api/runtime/profile/browse-history", - get(get_runtime_browse_history) - .post(post_runtime_browse_history) - .delete(delete_runtime_browse_history) - .route_layer(middleware::from_fn_with_state( - state.clone(), - require_bearer_auth, - )), - ) .route( "/api/profile/browse-history", get(get_runtime_browse_history) @@ -848,13 +860,6 @@ pub fn build_router(state: AppState) -> Router { require_bearer_auth, )), ) - .route( - "/api/runtime/profile/dashboard", - get(get_profile_dashboard).route_layer(middleware::from_fn_with_state( - state.clone(), - require_bearer_auth, - )), - ) .route( "/api/profile/dashboard", get(get_profile_dashboard).route_layer(middleware::from_fn_with_state( @@ -862,13 +867,6 @@ pub fn build_router(state: AppState) -> Router { require_bearer_auth, )), ) - .route( - "/api/runtime/profile/wallet-ledger", - get(get_profile_wallet_ledger).route_layer(middleware::from_fn_with_state( - state.clone(), - require_bearer_auth, - )), - ) .route( "/api/profile/wallet-ledger", get(get_profile_wallet_ledger).route_layer(middleware::from_fn_with_state( @@ -876,13 +874,6 @@ pub fn build_router(state: AppState) -> Router { require_bearer_auth, )), ) - .route( - "/api/runtime/profile/recharge-center", - get(get_profile_recharge_center).route_layer(middleware::from_fn_with_state( - state.clone(), - require_bearer_auth, - )), - ) .route( "/api/profile/recharge-center", get(get_profile_recharge_center).route_layer(middleware::from_fn_with_state( @@ -890,13 +881,6 @@ pub fn build_router(state: AppState) -> Router { require_bearer_auth, )), ) - .route( - "/api/runtime/profile/recharge/orders", - post(create_profile_recharge_order).route_layer(middleware::from_fn_with_state( - state.clone(), - require_bearer_auth, - )), - ) .route( "/api/profile/recharge/orders", post(create_profile_recharge_order).route_layer(middleware::from_fn_with_state( @@ -904,13 +888,6 @@ pub fn build_router(state: AppState) -> Router { require_bearer_auth, )), ) - .route( - "/api/runtime/profile/referrals/invite-center", - get(get_profile_referral_invite_center).route_layer(middleware::from_fn_with_state( - state.clone(), - require_bearer_auth, - )), - ) .route( "/api/profile/referrals/invite-center", get(get_profile_referral_invite_center).route_layer(middleware::from_fn_with_state( @@ -918,13 +895,6 @@ pub fn build_router(state: AppState) -> Router { require_bearer_auth, )), ) - .route( - "/api/runtime/profile/referrals/redeem-code", - post(redeem_profile_referral_invite_code).route_layer(middleware::from_fn_with_state( - state.clone(), - require_bearer_auth, - )), - ) .route( "/api/profile/referrals/redeem-code", post(redeem_profile_referral_invite_code).route_layer(middleware::from_fn_with_state( @@ -932,13 +902,6 @@ pub fn build_router(state: AppState) -> Router { require_bearer_auth, )), ) - .route( - "/api/runtime/profile/redeem-codes/redeem", - post(redeem_profile_reward_code).route_layer(middleware::from_fn_with_state( - state.clone(), - require_bearer_auth, - )), - ) .route( "/api/profile/redeem-codes/redeem", post(redeem_profile_reward_code).route_layer(middleware::from_fn_with_state( @@ -946,20 +909,6 @@ pub fn build_router(state: AppState) -> Router { require_bearer_auth, )), ) - .route( - "/api/runtime/profile/play-stats", - get(get_profile_play_stats).route_layer(middleware::from_fn_with_state( - state.clone(), - require_bearer_auth, - )), - ) - .route( - "/api/runtime/profile/save-archives", - get(list_profile_save_archives).route_layer(middleware::from_fn_with_state( - state.clone(), - require_bearer_auth, - )), - ) .route( "/api/profile/save-archives", get(list_profile_save_archives).route_layer(middleware::from_fn_with_state( @@ -967,13 +916,6 @@ pub fn build_router(state: AppState) -> Router { require_bearer_auth, )), ) - .route( - "/api/runtime/profile/save-archives/{world_key}", - post(resume_profile_save_archive).route_layer(middleware::from_fn_with_state( - state.clone(), - require_bearer_auth, - )), - ) .route( "/api/profile/save-archives/{world_key}", post(resume_profile_save_archive).route_layer(middleware::from_fn_with_state( diff --git a/server-rs/crates/api-server/src/assets.rs b/server-rs/crates/api-server/src/assets.rs index b38e3ba6..b93e642c 100644 --- a/server-rs/crates/api-server/src/assets.rs +++ b/server-rs/crates/api-server/src/assets.rs @@ -23,8 +23,8 @@ use shared_contracts::assets::{ use spacetime_client::SpacetimeClientError; use crate::{ - api_response::json_success_body, http_error::AppError, request_context::RequestContext, - state::AppState, + api_response::json_success_body, http_error::AppError, platform_errors::map_oss_error, + request_context::RequestContext, state::AppState, }; // 历史素材类型需要与 SpacetimeDB 侧白名单保持同一口径,避免新增素材类型时 HTTP 门面漏同步。 @@ -377,17 +377,7 @@ fn map_confirm_asset_object_prepare_error(error: ConfirmAssetObjectPrepareError) "message": error.to_string(), })) } - ConfirmAssetObjectPrepareError::Oss(platform_oss::OssError::ObjectNotFound(_)) => { - AppError::from_status(StatusCode::NOT_FOUND).with_details(json!({ - "provider": "aliyun-oss", - "message": error.to_string(), - })) - } - ConfirmAssetObjectPrepareError::Oss(_) => AppError::from_status(StatusCode::BAD_GATEWAY) - .with_details(json!({ - "provider": "aliyun-oss", - "message": error.to_string(), - })), + ConfirmAssetObjectPrepareError::Oss(error) => map_oss_error(error, "aliyun-oss"), } } diff --git a/server-rs/crates/api-server/src/big_fish.rs b/server-rs/crates/api-server/src/big_fish.rs index 66aea5b6..dc14147d 100644 --- a/server-rs/crates/api-server/src/big_fish.rs +++ b/server-rs/crates/api-server/src/big_fish.rs @@ -23,9 +23,10 @@ use shared_contracts::big_fish::{ BigFishActionResponse, BigFishAgentMessageResponse, BigFishAnchorItemResponse, BigFishAnchorPackResponse, BigFishAssetCoverageResponse, BigFishAssetSlotResponse, BigFishBackgroundBlueprintResponse, BigFishGameDraftResponse, BigFishLevelBlueprintResponse, - BigFishRuntimeParamsResponse, BigFishSessionResponse, BigFishSessionSnapshotResponse, - CreateBigFishSessionRequest, ExecuteBigFishActionRequest, RecordBigFishPlayRequest, - SendBigFishMessageRequest, + BigFishRunResponse, BigFishRuntimeEntityResponse, BigFishRuntimeParamsResponse, + BigFishRuntimeSnapshotResponse, BigFishSessionResponse, BigFishSessionSnapshotResponse, + BigFishVector2Response, CreateBigFishSessionRequest, ExecuteBigFishActionRequest, + RecordBigFishPlayRequest, SendBigFishMessageRequest, SubmitBigFishInputRequest, }; use shared_contracts::big_fish_works::{BigFishWorkSummaryResponse, BigFishWorksResponse}; use shared_kernel::{build_prefixed_uuid_id, format_timestamp_micros}; @@ -33,9 +34,10 @@ use spacetime_client::{ BigFishAgentMessageRecord, BigFishAnchorItemRecord, BigFishAnchorPackRecord, BigFishAssetCoverageRecord, BigFishAssetGenerateRecordInput, BigFishAssetSlotRecord, BigFishBackgroundBlueprintRecord, BigFishDraftCompileRecordInput, BigFishGameDraftRecord, - BigFishLevelBlueprintRecord, BigFishMessageSubmitRecordInput, BigFishPlayReportRecordInput, - BigFishRuntimeParamsRecord, BigFishSessionCreateRecordInput, BigFishSessionRecord, - BigFishWorkSummaryRecord, SpacetimeClientError, + BigFishInputSubmitRecordInput, BigFishLevelBlueprintRecord, BigFishMessageSubmitRecordInput, + BigFishPlayReportRecordInput, BigFishRunStartRecordInput, BigFishRuntimeEntityRecord, + BigFishRuntimeParamsRecord, BigFishRuntimeRunRecord, BigFishSessionCreateRecordInput, + BigFishSessionRecord, BigFishVector2Record, BigFishWorkSummaryRecord, SpacetimeClientError, }; use tokio::time::sleep; @@ -58,6 +60,7 @@ use crate::{ auth::AuthenticatedAccessToken, character_visual_assets::try_apply_background_alpha_to_png, http_error::AppError, + platform_errors::map_oss_error, request_context::RequestContext, state::AppState, }; @@ -251,6 +254,102 @@ pub async fn record_big_fish_play( )) } +pub async fn start_big_fish_run( + State(state): State, + Path(session_id): Path, + Extension(request_context): Extension, + Extension(authenticated): Extension, +) -> Result, Response> { + ensure_non_empty(&request_context, &session_id, "sessionId")?; + + let run = state + .spacetime_client() + .start_big_fish_run(BigFishRunStartRecordInput { + run_id: build_prefixed_uuid_id("big-fish-run-"), + session_id, + owner_user_id: authenticated.claims().user_id().to_string(), + started_at_micros: current_utc_micros(), + }) + .await + .map_err(|error| { + big_fish_error_response(&request_context, map_big_fish_client_error(error)) + })?; + + Ok(json_success_body( + Some(&request_context), + BigFishRunResponse { + run: map_big_fish_run_response(run), + }, + )) +} + +pub async fn get_big_fish_run( + State(state): State, + Path(run_id): Path, + Extension(request_context): Extension, + Extension(authenticated): Extension, +) -> Result, Response> { + ensure_non_empty(&request_context, &run_id, "runId")?; + + let run = state + .spacetime_client() + .get_big_fish_run(run_id, authenticated.claims().user_id().to_string()) + .await + .map_err(|error| { + big_fish_error_response(&request_context, map_big_fish_client_error(error)) + })?; + + Ok(json_success_body( + Some(&request_context), + BigFishRunResponse { + run: map_big_fish_run_response(run), + }, + )) +} + +pub async fn submit_big_fish_input( + State(state): State, + Path(run_id): Path, + Extension(request_context): Extension, + Extension(authenticated): Extension, + payload: Result, JsonRejection>, +) -> Result, Response> { + let Json(payload) = payload.map_err(|error| { + big_fish_error_response( + &request_context, + AppError::from_status(StatusCode::BAD_REQUEST).with_details(json!({ + "provider": "big-fish", + "message": error.body_text(), + })), + ) + })?; + ensure_non_empty(&request_context, &run_id, "runId")?; + if !payload.x.is_finite() || !payload.y.is_finite() { + return Err(big_fish_bad_request(&request_context, "input is invalid")); + } + + let run = state + .spacetime_client() + .submit_big_fish_input(BigFishInputSubmitRecordInput { + run_id, + owner_user_id: authenticated.claims().user_id().to_string(), + x: payload.x, + y: payload.y, + submitted_at_micros: current_utc_micros(), + }) + .await + .map_err(|error| { + big_fish_error_response(&request_context, map_big_fish_client_error(error)) + })?; + + Ok(json_success_body( + Some(&request_context), + BigFishRunResponse { + run: map_big_fish_run_response(run), + }, + )) +} + pub async fn submit_big_fish_message( State(state): State, Path(session_id): Path, @@ -799,6 +898,51 @@ fn map_big_fish_asset_coverage_response( } } +fn map_big_fish_run_response(run: BigFishRuntimeRunRecord) -> BigFishRuntimeSnapshotResponse { + BigFishRuntimeSnapshotResponse { + run_id: run.run_id, + session_id: run.session_id, + status: run.status, + tick: run.tick, + player_level: run.player_level, + win_level: run.win_level, + leader_entity_id: run.leader_entity_id, + owned_entities: run + .owned_entities + .into_iter() + .map(map_big_fish_runtime_entity_response) + .collect(), + wild_entities: run + .wild_entities + .into_iter() + .map(map_big_fish_runtime_entity_response) + .collect(), + camera_center: map_big_fish_vector2_response(run.camera_center), + last_input: map_big_fish_vector2_response(run.last_input), + event_log: run.event_log, + updated_at: run.updated_at, + } +} + +fn map_big_fish_runtime_entity_response( + entity: BigFishRuntimeEntityRecord, +) -> BigFishRuntimeEntityResponse { + BigFishRuntimeEntityResponse { + entity_id: entity.entity_id, + level: entity.level, + position: map_big_fish_vector2_response(entity.position), + radius: entity.radius, + offscreen_seconds: entity.offscreen_seconds, + } +} + +fn map_big_fish_vector2_response(vector: BigFishVector2Record) -> BigFishVector2Response { + BigFishVector2Response { + x: vector.x, + y: vector.y, + } +} + async fn compile_big_fish_draft_only( state: &AppState, session_id: String, @@ -1570,19 +1714,7 @@ fn map_big_fish_asset_spacetime_error(error: SpacetimeClientError) -> AppError { } fn map_big_fish_asset_oss_error(error: platform_oss::OssError) -> AppError { - let status = match error { - platform_oss::OssError::InvalidConfig(_) | platform_oss::OssError::InvalidRequest(_) => { - StatusCode::BAD_REQUEST - } - platform_oss::OssError::ObjectNotFound(_) => StatusCode::NOT_FOUND, - platform_oss::OssError::Request(_) - | platform_oss::OssError::SerializePolicy(_) - | platform_oss::OssError::Sign(_) => StatusCode::BAD_GATEWAY, - }; - AppError::from_status(status).with_details(json!({ - "provider": "aliyun-oss", - "message": error.to_string(), - })) + map_oss_error(error, "aliyun-oss") } fn build_big_fish_level_part(level: Option) -> String { @@ -1659,6 +1791,14 @@ fn map_big_fish_client_error(error: SpacetimeClientError) -> AppError { { StatusCode::NOT_FOUND } + SpacetimeClientError::Procedure(message) + if message.contains("big_fish_runtime_run 不存在") => + { + StatusCode::NOT_FOUND + } + SpacetimeClientError::Procedure(message) if message.contains("无权访问") => { + StatusCode::FORBIDDEN + } SpacetimeClientError::Procedure(message) if message.contains("不能为空") || message.contains("尚未编译") diff --git a/server-rs/crates/api-server/src/character_animation_assets.rs b/server-rs/crates/api-server/src/character_animation_assets.rs index 471bbd18..a2a5d0c6 100644 --- a/server-rs/crates/api-server/src/character_animation_assets.rs +++ b/server-rs/crates/api-server/src/character_animation_assets.rs @@ -50,6 +50,7 @@ use crate::{ build_character_animation_prompt, build_fallback_moderation_safe_animation_prompt, }, http_error::AppError, + platform_errors::map_oss_error, prompt::role_asset_studio::{ build_role_asset_workflow, normalize_animation_prompt_text_by_key, }, @@ -1639,7 +1640,9 @@ async fn load_workflow_cache( expire_seconds: Some(60), }) { Ok(signed) => signed, - Err(platform_oss::OssError::ObjectNotFound(_)) => return Ok(None), + Err(error) if error.kind() == platform_oss::OssErrorKind::ObjectNotFound => { + return Ok(None); + } Err(error) => return Err(map_character_animation_oss_error(error)), }; let response = reqwest::Client::new() @@ -3303,19 +3306,7 @@ fn map_character_animation_spacetime_error(error: SpacetimeClientError) -> AppEr } fn map_character_animation_oss_error(error: platform_oss::OssError) -> AppError { - let status = match error { - platform_oss::OssError::InvalidConfig(_) | platform_oss::OssError::InvalidRequest(_) => { - StatusCode::BAD_REQUEST - } - platform_oss::OssError::ObjectNotFound(_) => StatusCode::NOT_FOUND, - platform_oss::OssError::Request(_) - | platform_oss::OssError::SerializePolicy(_) - | platform_oss::OssError::Sign(_) => StatusCode::BAD_GATEWAY, - }; - AppError::from_status(status).with_details(json!({ - "provider": "aliyun-oss", - "message": error.to_string(), - })) + map_oss_error(error, "aliyun-oss") } fn character_animation_error_response( diff --git a/server-rs/crates/api-server/src/character_visual_assets.rs b/server-rs/crates/api-server/src/character_visual_assets.rs index f5033f67..bbe8f84d 100644 --- a/server-rs/crates/api-server/src/character_visual_assets.rs +++ b/server-rs/crates/api-server/src/character_visual_assets.rs @@ -38,6 +38,7 @@ use crate::{ build_fallback_moderation_safe_character_visual_prompt, }, http_error::AppError, + platform_errors::map_oss_error, request_context::RequestContext, state::AppState, }; @@ -1335,19 +1336,7 @@ fn map_character_visual_spacetime_error(error: SpacetimeClientError) -> AppError } fn map_character_visual_oss_error(error: platform_oss::OssError) -> AppError { - let status = match error { - platform_oss::OssError::InvalidConfig(_) | platform_oss::OssError::InvalidRequest(_) => { - StatusCode::BAD_REQUEST - } - platform_oss::OssError::ObjectNotFound(_) => StatusCode::NOT_FOUND, - platform_oss::OssError::Request(_) - | platform_oss::OssError::SerializePolicy(_) - | platform_oss::OssError::Sign(_) => StatusCode::BAD_GATEWAY, - }; - AppError::from_status(status).with_details(json!({ - "provider": "aliyun-oss", - "message": error.to_string(), - })) + map_oss_error(error, "aliyun-oss") } fn parse_json_payload( diff --git a/server-rs/crates/api-server/src/custom_world_ai.rs b/server-rs/crates/api-server/src/custom_world_ai.rs index 225a8fee..3789b764 100644 --- a/server-rs/crates/api-server/src/custom_world_ai.rs +++ b/server-rs/crates/api-server/src/custom_world_ai.rs @@ -35,6 +35,7 @@ use crate::{ build_result_scene_npc_system_prompt, build_result_scene_npc_user_prompt, }, http_error::AppError, + platform_errors::map_oss_error, prompt::scene_background::{ DEFAULT_CUSTOM_WORLD_SCENE_IMAGE_NEGATIVE_PROMPT, SceneImagePromptLandmark, SceneImagePromptParams, SceneImagePromptProfile, build_custom_world_scene_image_prompt, @@ -1016,19 +1017,7 @@ fn map_custom_world_asset_spacetime_error(error: SpacetimeClientError) -> AppErr } fn map_custom_world_asset_oss_error(error: platform_oss::OssError) -> AppError { - let status = match error { - platform_oss::OssError::InvalidConfig(_) | platform_oss::OssError::InvalidRequest(_) => { - StatusCode::BAD_REQUEST - } - platform_oss::OssError::ObjectNotFound(_) => StatusCode::NOT_FOUND, - platform_oss::OssError::Request(_) - | platform_oss::OssError::SerializePolicy(_) - | platform_oss::OssError::Sign(_) => StatusCode::BAD_GATEWAY, - }; - AppError::from_status(status).with_details(json!({ - "provider": "aliyun-oss", - "message": error.to_string(), - })) + map_oss_error(error, "aliyun-oss") } async fn generate_entity_with_fallback(state: &AppState, profile: &Value, kind: &str) -> Value { diff --git a/server-rs/crates/api-server/src/http_error.rs b/server-rs/crates/api-server/src/http_error.rs index eb3f3bb4..3f398f6e 100644 --- a/server-rs/crates/api-server/src/http_error.rs +++ b/server-rs/crates/api-server/src/http_error.rs @@ -34,6 +34,11 @@ impl AppError { self.code } + #[cfg(test)] + pub fn status_code(&self) -> StatusCode { + self.status_code + } + pub fn message(&self) -> &str { &self.message } diff --git a/server-rs/crates/api-server/src/legacy_generated_assets.rs b/server-rs/crates/api-server/src/legacy_generated_assets.rs index 19b5df3f..37ae7c47 100644 --- a/server-rs/crates/api-server/src/legacy_generated_assets.rs +++ b/server-rs/crates/api-server/src/legacy_generated_assets.rs @@ -6,7 +6,7 @@ use axum::{ use platform_oss::{LegacyAssetPrefix, OssSignedGetObjectUrlRequest}; use serde_json::json; -use crate::{http_error::AppError, state::AppState}; +use crate::{http_error::AppError, platform_errors::map_oss_error, state::AppState}; const CACHE_CONTROL_VALUE: &str = "private, max-age=60"; const ASSET_OBJECT_KEY_HEADER: &str = "x-genarrative-asset-object-key"; @@ -183,19 +183,7 @@ fn is_invalid_path_segment(segment: &str) -> bool { } fn map_legacy_generated_oss_error(error: platform_oss::OssError) -> AppError { - let status = match error { - platform_oss::OssError::InvalidConfig(_) | platform_oss::OssError::InvalidRequest(_) => { - StatusCode::BAD_REQUEST - } - platform_oss::OssError::ObjectNotFound(_) => StatusCode::NOT_FOUND, - platform_oss::OssError::Request(_) - | platform_oss::OssError::SerializePolicy(_) - | platform_oss::OssError::Sign(_) => StatusCode::BAD_GATEWAY, - }; - AppError::from_status(status).with_details(json!({ - "provider": "aliyun-oss", - "message": error.to_string(), - })) + map_oss_error(error, "aliyun-oss") } fn map_legacy_generated_upstream_status( diff --git a/server-rs/crates/api-server/src/llm.rs b/server-rs/crates/api-server/src/llm.rs index 52425df7..d2da6485 100644 --- a/server-rs/crates/api-server/src/llm.rs +++ b/server-rs/crates/api-server/src/llm.rs @@ -4,7 +4,7 @@ use axum::{ http::StatusCode, response::Response, }; -use platform_llm::{LlmError, LlmMessage, LlmMessageRole, LlmTextRequest}; +use platform_llm::{LlmMessage, LlmMessageRole, LlmTextRequest}; use serde_json::Value; use shared_contracts::llm::{ LlmChatCompletionRequest, LlmChatCompletionResponse, LlmChatMessagePayload, LlmChatMessageRole, @@ -12,7 +12,7 @@ use shared_contracts::llm::{ use crate::{ api_response::json_success_body, auth::AuthenticatedAccessToken, http_error::AppError, - request_context::RequestContext, state::AppState, + platform_errors::map_llm_error, request_context::RequestContext, state::AppState, }; pub async fn proxy_llm_chat_completions( @@ -74,39 +74,6 @@ fn map_chat_message(message: LlmChatMessagePayload) -> LlmMessage { LlmMessage::new(role, message.content) } -fn map_llm_error(error: LlmError) -> AppError { - match error { - LlmError::InvalidRequest(message) => { - AppError::from_status(StatusCode::BAD_REQUEST).with_message(message) - } - LlmError::InvalidConfig(message) => { - AppError::from_status(StatusCode::SERVICE_UNAVAILABLE).with_message(message) - } - LlmError::Upstream { - status_code: 429, - message, - } => AppError::from_status(StatusCode::TOO_MANY_REQUESTS).with_message(message), - LlmError::Upstream { message, .. } => { - AppError::from_status(StatusCode::BAD_GATEWAY).with_message(message) - } - LlmError::Timeout { attempts } => AppError::from_status(StatusCode::BAD_GATEWAY) - .with_message(format!("LLM 请求超时,累计尝试 {attempts} 次")), - LlmError::Connectivity { attempts, message } => { - AppError::from_status(StatusCode::BAD_GATEWAY) - .with_message(format!("LLM 连接失败,累计尝试 {attempts} 次:{message}")) - } - LlmError::StreamUnavailable => { - AppError::from_status(StatusCode::BAD_GATEWAY).with_message("LLM 流式响应体不可用") - } - LlmError::EmptyResponse => { - AppError::from_status(StatusCode::BAD_GATEWAY).with_message("LLM 返回内容为空") - } - LlmError::Transport(message) | LlmError::Deserialize(message) => { - AppError::from_status(StatusCode::BAD_GATEWAY).with_message(message) - } - } -} - fn llm_error_response(request_context: &RequestContext, error: AppError) -> Response { error.into_response_with_context(Some(request_context)) } diff --git a/server-rs/crates/api-server/src/main.rs b/server-rs/crates/api-server/src/main.rs index 64e3097f..b19c5d5c 100644 --- a/server-rs/crates/api-server/src/main.rs +++ b/server-rs/crates/api-server/src/main.rs @@ -42,6 +42,7 @@ mod logout_all; mod password_entry; mod password_management; mod phone_auth; +mod platform_errors; mod prompt; mod puzzle; mod puzzle_agent_turn; diff --git a/server-rs/crates/api-server/src/phone_auth.rs b/server-rs/crates/api-server/src/phone_auth.rs index 39cb8f2b..cf26e631 100644 --- a/server-rs/crates/api-server/src/phone_auth.rs +++ b/server-rs/crates/api-server/src/phone_auth.rs @@ -1,7 +1,7 @@ use axum::{ Json, extract::{Extension, State}, - http::{HeaderMap, HeaderValue, StatusCode}, + http::{HeaderMap, StatusCode}, response::IntoResponse, }; use module_auth::{ @@ -21,6 +21,7 @@ use crate::{ attach_set_cookie_header, build_refresh_session_cookie_header, create_auth_session, }, http_error::AppError, + platform_errors::{attach_retry_after, map_phone_auth_platform_store_error}, request_context::RequestContext, session_client::resolve_session_client_context, state::AppState, @@ -237,10 +238,7 @@ pub fn map_phone_auth_error(error: PhoneAuthError) -> AppError { let app_error = AppError::from_status(StatusCode::TOO_MANY_REQUESTS) .with_message(error.to_string()) .with_details(json!({ "retryAfterSeconds": retry_after_seconds })); - match HeaderValue::from_str(&retry_after_seconds.to_string()) { - Ok(value) => app_error.with_header("retry-after", value), - Err(_) => app_error, - } + attach_retry_after(app_error, retry_after_seconds) } PhoneAuthError::VerifyAttemptsExceeded => { AppError::from_status(StatusCode::TOO_MANY_REQUESTS).with_message(error.to_string()) @@ -249,7 +247,7 @@ pub fn map_phone_auth_error(error: PhoneAuthError) -> AppError { AppError::from_status(StatusCode::UNAUTHORIZED).with_message(error.to_string()) } PhoneAuthError::Store(_) | PhoneAuthError::PasswordHash(_) => { - AppError::from_status(StatusCode::INTERNAL_SERVER_ERROR).with_message(error.to_string()) + map_phone_auth_platform_store_error(error.to_string()) } } } diff --git a/server-rs/crates/api-server/src/platform_errors.rs b/server-rs/crates/api-server/src/platform_errors.rs new file mode 100644 index 00000000..2acdd925 --- /dev/null +++ b/server-rs/crates/api-server/src/platform_errors.rs @@ -0,0 +1,133 @@ +use axum::http::{HeaderValue, StatusCode}; +use platform_auth::{AuthPlatformErrorKind, WechatProviderError}; +use platform_llm::{LlmError, LlmErrorKind}; +use platform_oss::{OssError, OssErrorKind}; +use serde_json::json; + +use crate::http_error::AppError; + +// API 层统一消费 platform 的稳定错误分类,避免各 route 重复 match 具体 provider 分支。 +pub fn map_llm_error(error: LlmError) -> AppError { + let message = llm_error_message(&error); + let status = match error.kind() { + LlmErrorKind::InvalidRequest => StatusCode::BAD_REQUEST, + LlmErrorKind::InvalidConfig => StatusCode::SERVICE_UNAVAILABLE, + LlmErrorKind::Upstream + if matches!( + error, + LlmError::Upstream { + status_code: 429, + .. + } + ) => + { + StatusCode::TOO_MANY_REQUESTS + } + LlmErrorKind::Timeout + | LlmErrorKind::Connectivity + | LlmErrorKind::Upstream + | LlmErrorKind::StreamUnavailable + | LlmErrorKind::EmptyResponse + | LlmErrorKind::Transport + | LlmErrorKind::Deserialize => StatusCode::BAD_GATEWAY, + }; + + AppError::from_status(status).with_message(message) +} + +pub fn map_oss_error(error: OssError, provider: &'static str) -> AppError { + let status = oss_error_status(error.kind()); + AppError::from_status(status).with_details(json!({ + "provider": provider, + "message": error.to_string(), + })) +} + +pub fn map_phone_auth_platform_store_error(message: String) -> AppError { + AppError::from_status(StatusCode::INTERNAL_SERVER_ERROR).with_message(message) +} + +pub fn map_wechat_provider_error(error: WechatProviderError) -> AppError { + let status = match error.kind() { + AuthPlatformErrorKind::Disabled + | AuthPlatformErrorKind::MissingCode + | AuthPlatformErrorKind::InvalidCallback => StatusCode::BAD_REQUEST, + AuthPlatformErrorKind::InvalidConfig => StatusCode::SERVICE_UNAVAILABLE, + AuthPlatformErrorKind::RequestFailed + | AuthPlatformErrorKind::DeserializeFailed + | AuthPlatformErrorKind::MissingProfile + | AuthPlatformErrorKind::Upstream => StatusCode::BAD_GATEWAY, + AuthPlatformErrorKind::InvalidClaims + | AuthPlatformErrorKind::SignFailed + | AuthPlatformErrorKind::VerifyFailed + | AuthPlatformErrorKind::CookieConfig + | AuthPlatformErrorKind::HashFailed + | AuthPlatformErrorKind::InvalidVerifyCode => StatusCode::INTERNAL_SERVER_ERROR, + }; + + AppError::from_status(status).with_message(error.to_string()) +} + +pub fn attach_retry_after(error: AppError, retry_after_seconds: u64) -> AppError { + match HeaderValue::from_str(&retry_after_seconds.to_string()) { + Ok(value) => error.with_header("retry-after", value), + Err(_) => error, + } +} + +fn oss_error_status(kind: OssErrorKind) -> StatusCode { + match kind { + OssErrorKind::InvalidConfig | OssErrorKind::InvalidRequest => StatusCode::BAD_REQUEST, + OssErrorKind::ObjectNotFound => StatusCode::NOT_FOUND, + OssErrorKind::Request | OssErrorKind::SerializePolicy | OssErrorKind::Sign => { + StatusCode::BAD_GATEWAY + } + } +} + +fn llm_error_message(error: &LlmError) -> String { + match error { + LlmError::InvalidConfig(message) + | LlmError::InvalidRequest(message) + | LlmError::Transport(message) + | LlmError::Deserialize(message) => message.clone(), + LlmError::Timeout { .. } + | LlmError::Connectivity { .. } + | LlmError::Upstream { .. } + | LlmError::StreamUnavailable + | LlmError::EmptyResponse => error.to_string(), + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn map_oss_error_uses_stable_kind_for_not_found() { + let error = map_oss_error( + OssError::ObjectNotFound("missing object".to_string()), + "oss", + ); + + assert_eq!(error.status_code(), StatusCode::NOT_FOUND); + } + + #[test] + fn map_llm_error_preserves_upstream_rate_limit() { + let error = map_llm_error(LlmError::Upstream { + status_code: 429, + message: "too many requests".to_string(), + }); + + assert_eq!(error.status_code(), StatusCode::TOO_MANY_REQUESTS); + } + + #[test] + fn map_wechat_provider_error_keeps_provider_boundary() { + let error = map_wechat_provider_error(WechatProviderError::MissingCode); + + assert_eq!(error.status_code(), StatusCode::BAD_REQUEST); + assert_eq!(error.message(), "缺少微信授权 code"); + } +} diff --git a/server-rs/crates/api-server/src/puzzle.rs b/server-rs/crates/api-server/src/puzzle.rs index 63b69a89..e52059c6 100644 --- a/server-rs/crates/api-server/src/puzzle.rs +++ b/server-rs/crates/api-server/src/puzzle.rs @@ -70,6 +70,7 @@ use crate::{ asset_billing::execute_billable_asset_operation, auth::AuthenticatedAccessToken, http_error::AppError, + platform_errors::map_oss_error, prompt::puzzle_image::{PUZZLE_DEFAULT_NEGATIVE_PROMPT, build_puzzle_image_prompt}, puzzle_agent_turn::{ PuzzleAgentTurnRequest, build_failed_finalize_record_input, build_finalize_record_input, @@ -2942,10 +2943,7 @@ fn parse_puzzle_api_error_message(raw_text: &str, fallback_message: &str) -> Str } fn map_puzzle_asset_oss_error(error: platform_oss::OssError) -> AppError { - AppError::from_status(StatusCode::BAD_GATEWAY).with_details(json!({ - "provider": "aliyun-oss", - "message": error.to_string(), - })) + map_oss_error(error, "aliyun-oss") } fn map_puzzle_asset_spacetime_error(error: SpacetimeClientError) -> AppError { diff --git a/server-rs/crates/api-server/src/runtime_browse_history.rs b/server-rs/crates/api-server/src/runtime_browse_history.rs index dd4a9027..1bf5dc0b 100644 --- a/server-rs/crates/api-server/src/runtime_browse_history.rs +++ b/server-rs/crates/api-server/src/runtime_browse_history.rs @@ -258,7 +258,7 @@ mod tests { .oneshot( Request::builder() .method("GET") - .uri("/api/runtime/profile/browse-history") + .uri("/api/profile/browse-history") .body(Body::empty()) .expect("request should build"), ) @@ -278,7 +278,7 @@ mod tests { .oneshot( Request::builder() .method("POST") - .uri("/api/runtime/profile/browse-history") + .uri("/api/profile/browse-history") .header("authorization", format!("Bearer {token}")) .header("content-type", "application/json") .header("x-genarrative-response-envelope", "v1") @@ -324,7 +324,7 @@ mod tests { .oneshot( Request::builder() .method("POST") - .uri("/api/runtime/profile/browse-history") + .uri("/api/profile/browse-history") .header("authorization", format!("Bearer {token}")) .header("content-type", "application/json") .header("x-genarrative-response-envelope", "v1") @@ -361,64 +361,6 @@ mod tests { ); } - #[tokio::test] - async fn runtime_browse_history_compat_route_matches_main_route_error_shape() { - let state = seed_authenticated_state().await; - let token = issue_access_token(&state); - let app = build_router(state); - - let main_response = app - .clone() - .oneshot( - Request::builder() - .method("GET") - .uri("/api/runtime/profile/browse-history") - .header("authorization", format!("Bearer {token}")) - .header("x-genarrative-response-envelope", "v1") - .body(Body::empty()) - .expect("request should build"), - ) - .await - .expect("request should succeed"); - - let compat_response = app - .oneshot( - Request::builder() - .method("GET") - .uri("/api/profile/browse-history") - .header("authorization", format!("Bearer {token}")) - .header("x-genarrative-response-envelope", "v1") - .body(Body::empty()) - .expect("request should build"), - ) - .await - .expect("request should succeed"); - - assert_eq!(main_response.status(), compat_response.status()); - - let main_body = main_response - .into_body() - .collect() - .await - .expect("body should collect") - .to_bytes(); - let compat_body = compat_response - .into_body() - .collect() - .await - .expect("body should collect") - .to_bytes(); - let main_payload: Value = - serde_json::from_slice(&main_body).expect("response body should be valid json"); - let compat_payload: Value = - serde_json::from_slice(&compat_body).expect("response body should be valid json"); - - assert_eq!( - main_payload["error"]["details"]["provider"], - compat_payload["error"]["details"]["provider"] - ); - } - async fn seed_authenticated_state() -> AppState { let state = AppState::new(AppConfig::default()).expect("state should build"); state diff --git a/server-rs/crates/api-server/src/runtime_profile.rs b/server-rs/crates/api-server/src/runtime_profile.rs index c9cc5c7c..67fe93d7 100644 --- a/server-rs/crates/api-server/src/runtime_profile.rs +++ b/server-rs/crates/api-server/src/runtime_profile.rs @@ -551,12 +551,6 @@ mod tests { body::Body, http::{Request, StatusCode}, }; - use http_body_util::BodyExt; - use platform_auth::{ - AccessTokenClaims, AccessTokenClaimsInput, AuthProvider, BindingStatus, sign_access_token, - }; - use serde_json::Value; - use time::OffsetDateTime; use tower::ServiceExt; use crate::{app::build_router, config::AppConfig, state::AppState}; @@ -585,7 +579,7 @@ mod tests { .oneshot( Request::builder() .method("GET") - .uri("/api/runtime/profile/dashboard") + .uri("/api/profile/dashboard") .body(Body::empty()) .expect("request should build"), ) @@ -603,7 +597,7 @@ mod tests { .oneshot( Request::builder() .method("GET") - .uri("/api/runtime/profile/wallet-ledger") + .uri("/api/profile/wallet-ledger") .body(Body::empty()) .expect("request should build"), ) @@ -621,7 +615,7 @@ mod tests { .oneshot( Request::builder() .method("GET") - .uri("/api/runtime/profile/play-stats") + .uri("/api/profile/play-stats") .body(Body::empty()) .expect("request should build"), ) @@ -706,118 +700,35 @@ mod tests { } #[tokio::test] - async fn profile_dashboard_compat_route_matches_main_route_error_shape() { - assert_compat_route_matches_main_route_error_shape( + async fn runtime_profile_legacy_routes_are_not_mounted() { + let app = build_router(AppState::new(AppConfig::default()).expect("state should build")); + + for uri in [ "/api/runtime/profile/dashboard", - "/api/profile/dashboard", - ) - .await; - } - - #[tokio::test] - async fn profile_wallet_ledger_compat_route_matches_main_route_error_shape() { - assert_compat_route_matches_main_route_error_shape( "/api/runtime/profile/wallet-ledger", - "/api/profile/wallet-ledger", - ) - .await; - } - - #[tokio::test] - async fn profile_play_stats_compat_route_matches_main_route_error_shape() { - assert_compat_route_matches_main_route_error_shape( + "/api/runtime/profile/recharge-center", + "/api/runtime/profile/recharge/orders", + "/api/runtime/profile/referrals/invite-center", + "/api/runtime/profile/referrals/redeem-code", + "/api/runtime/profile/redeem-codes/redeem", "/api/runtime/profile/play-stats", - "/api/profile/play-stats", - ) - .await; - } + "/api/runtime/profile/save-archives", + "/api/runtime/profile/save-archives/world-1", + "/api/runtime/profile/browse-history", + ] { + let response = app + .clone() + .oneshot( + Request::builder() + .method("GET") + .uri(uri) + .body(Body::empty()) + .expect("request should build"), + ) + .await + .expect("request should succeed"); - async fn assert_compat_route_matches_main_route_error_shape( - main_route: &str, - compat_route: &str, - ) { - let state = seed_authenticated_state().await; - let token = issue_access_token(&state); - let app = build_router(state); - - let main_response = app - .clone() - .oneshot( - Request::builder() - .method("GET") - .uri(main_route) - .header("authorization", format!("Bearer {token}")) - .header("x-genarrative-response-envelope", "v1") - .body(Body::empty()) - .expect("request should build"), - ) - .await - .expect("request should succeed"); - - let compat_response = app - .oneshot( - Request::builder() - .method("GET") - .uri(compat_route) - .header("authorization", format!("Bearer {token}")) - .header("x-genarrative-response-envelope", "v1") - .body(Body::empty()) - .expect("request should build"), - ) - .await - .expect("request should succeed"); - - assert_eq!(main_response.status(), compat_response.status()); - - let main_body = main_response - .into_body() - .collect() - .await - .expect("body should collect") - .to_bytes(); - let compat_body = compat_response - .into_body() - .collect() - .await - .expect("body should collect") - .to_bytes(); - let main_payload: Value = - serde_json::from_slice(&main_body).expect("response body should be valid json"); - let compat_payload: Value = - serde_json::from_slice(&compat_body).expect("response body should be valid json"); - - assert_eq!( - main_payload["error"]["details"]["provider"], - compat_payload["error"]["details"]["provider"] - ); - } - - async fn seed_authenticated_state() -> AppState { - let state = AppState::new(AppConfig::default()).expect("state should build"); - state - .seed_test_phone_user_with_password("13800138104", "secret123") - .await - .id; - state - } - - fn issue_access_token(state: &AppState) -> String { - let claims = AccessTokenClaims::from_input( - AccessTokenClaimsInput { - user_id: "user_00000001".to_string(), - session_id: "sess_runtime_profile".to_string(), - provider: AuthProvider::Password, - roles: vec!["user".to_string()], - token_version: 2, - phone_verified: true, - binding_status: BindingStatus::Active, - display_name: Some("资料页用户".to_string()), - }, - state.auth_jwt_config(), - OffsetDateTime::now_utc(), - ) - .expect("claims should build"); - - sign_access_token(&claims, state.auth_jwt_config()).expect("token should sign") + assert_eq!(response.status(), StatusCode::NOT_FOUND, "{uri}"); + } } } diff --git a/server-rs/crates/api-server/src/runtime_save.rs b/server-rs/crates/api-server/src/runtime_save.rs index 5f1fca17..85a50621 100644 --- a/server-rs/crates/api-server/src/runtime_save.rs +++ b/server-rs/crates/api-server/src/runtime_save.rs @@ -4,7 +4,10 @@ use axum::{ http::StatusCode, response::Response, }; -use module_runtime::format_utc_micros; +use module_runtime::{ + RuntimeProfileFieldError, build_runtime_save_checkpoint_input, + build_runtime_save_checkpoint_update, +}; use serde::Deserialize; use serde_json::{Value, json}; use shared_contracts::runtime::{ @@ -52,26 +55,6 @@ pub async fn put_runtime_snapshot( Json(payload): Json, ) -> Result, Response> { let user_id = authenticated.claims().user_id().to_string(); - let session_id = normalize_required_string(payload.session_id.as_str()).ok_or_else(|| { - runtime_save_error_response( - &request_context, - AppError::from_status(StatusCode::BAD_REQUEST).with_details(json!({ - "provider": "runtime-save", - "field": "sessionId", - "message": "sessionId 不能为空", - })), - ) - })?; - let bottom_tab = normalize_required_string(payload.bottom_tab.as_str()).ok_or_else(|| { - runtime_save_error_response( - &request_context, - AppError::from_status(StatusCode::BAD_REQUEST).with_details(json!({ - "provider": "runtime-save", - "field": "bottomTab", - "message": "bottomTab 不能为空", - })), - ) - })?; let now = OffsetDateTime::now_utc(); let saved_at = payload .saved_at @@ -90,6 +73,15 @@ pub async fn put_runtime_snapshot( .unwrap_or(now); let updated_at_micros = offset_datetime_to_unix_micros(now); let saved_at_micros = offset_datetime_to_unix_micros(saved_at); + let checkpoint_input = build_runtime_save_checkpoint_input( + payload.session_id, + payload.bottom_tab, + saved_at_micros, + updated_at_micros, + ) + .map_err(|error| { + runtime_save_error_response(&request_context, map_runtime_save_domain_error(error)) + })?; let existing = state .get_runtime_snapshot_record(user_id.clone()) @@ -107,16 +99,18 @@ pub async fn put_runtime_snapshot( ) })?; - validate_checkpoint_snapshot(&request_context, &session_id, &existing.game_state)?; - let game_state = sync_runtime_snapshot_play_time(existing.game_state, updated_at_micros); + let update = + build_runtime_save_checkpoint_update(checkpoint_input, existing).map_err(|error| { + runtime_save_error_response(&request_context, map_runtime_save_domain_error(error)) + })?; let record = state .put_runtime_snapshot_record( user_id, - saved_at_micros, - bottom_tab, - game_state, - existing.current_story, - updated_at_micros, + update.saved_at_micros, + update.bottom_tab, + update.game_state, + update.current_story, + update.updated_at_micros, ) .await .map_err(|error| { @@ -223,132 +217,6 @@ fn build_saved_game_snapshot_response( } } -fn is_non_persistent_runtime_snapshot(game_state: &Value) -> bool { - let Some(game_state) = game_state.as_object() else { - return false; - }; - - if game_state - .get("runtimePersistenceDisabled") - .and_then(Value::as_bool) - .unwrap_or(false) - { - return true; - } - - matches!( - game_state - .get("runtimeMode") - .and_then(Value::as_str) - .map(str::trim), - Some("preview") | Some("test") - ) -} - -fn validate_checkpoint_snapshot( - request_context: &RequestContext, - session_id: &str, - game_state: &Value, -) -> Result<(), Response> { - if is_non_persistent_runtime_snapshot(game_state) { - return Err(runtime_save_error_response( - request_context, - AppError::from_status(StatusCode::CONFLICT).with_details(json!({ - "provider": "runtime-save", - "message": "预览或测试运行态不能创建正式 checkpoint", - })), - )); - } - - let persisted_session_id = - read_string_field(game_state, "runtimeSessionId").ok_or_else(|| { - runtime_save_error_response( - request_context, - AppError::from_status(StatusCode::CONFLICT).with_details(json!({ - "provider": "runtime-save", - "message": "服务端运行时快照缺少 runtimeSessionId,无法创建 checkpoint", - })), - ) - })?; - - if persisted_session_id != session_id { - return Err(runtime_save_error_response( - request_context, - AppError::from_status(StatusCode::CONFLICT).with_details(json!({ - "provider": "runtime-save", - "message": "checkpoint sessionId 与服务端运行时快照不一致", - "expectedSessionId": persisted_session_id, - "actualSessionId": session_id, - })), - )); - } - - Ok(()) -} - -fn sync_runtime_snapshot_play_time(mut game_state: Value, now_micros: i64) -> Value { - let Some(game_state_object) = game_state.as_object_mut() else { - return game_state; - }; - let now_text = format_utc_micros(now_micros); - let Some(runtime_stats) = game_state_object - .get_mut("runtimeStats") - .and_then(Value::as_object_mut) - else { - game_state_object.insert( - "runtimeStats".to_string(), - json!({ - "playTimeMs": 0, - "lastPlayTickAt": now_text, - "hostileNpcsDefeated": 0, - "questsAccepted": 0, - "itemsUsed": 0, - "scenesTraveled": 0, - }), - ); - return game_state; - }; - - let current_play_time = runtime_stats - .get("playTimeMs") - .and_then(Value::as_f64) - .filter(|value| value.is_finite() && *value >= 0.0) - .unwrap_or(0.0); - let elapsed_ms = runtime_stats - .get("lastPlayTickAt") - .and_then(Value::as_str) - .and_then(|last_tick| parse_rfc3339(last_tick).ok()) - .map(offset_datetime_to_unix_micros) - .map(|last_tick_micros| now_micros.saturating_sub(last_tick_micros).max(0) as f64 / 1000.0) - .unwrap_or(0.0); - let next_play_time = (current_play_time + elapsed_ms).floor().max(0.0); - - // 中文注释:checkpoint 只刷新服务端已有 runtimeStats 的时间水位, - // 不从浏览器接收任何任务、背包、战斗或剧情状态。 - runtime_stats.insert("playTimeMs".to_string(), Value::from(next_play_time as i64)); - runtime_stats.insert("lastPlayTickAt".to_string(), Value::String(now_text)); - game_state -} - -fn read_string_field(value: &Value, field: &str) -> Option { - value - .as_object()? - .get(field)? - .as_str() - .map(str::trim) - .filter(|value| !value.is_empty()) - .map(ToOwned::to_owned) -} - -fn normalize_required_string(value: &str) -> Option { - let normalized = value.trim(); - if normalized.is_empty() { - None - } else { - Some(normalized.to_string()) - } -} - fn build_profile_save_archive_summary_response( record: &module_runtime::RuntimeProfileSaveArchiveRecord, ) -> ProfileSaveArchiveSummaryResponse { @@ -395,6 +263,51 @@ fn map_runtime_save_resume_client_error(error: SpacetimeClientError) -> AppError })) } +fn map_runtime_save_domain_error(error: RuntimeProfileFieldError) -> AppError { + let message = error.to_string(); + match error { + RuntimeProfileFieldError::MissingCheckpointSessionId => { + AppError::from_status(StatusCode::BAD_REQUEST).with_details(json!({ + "provider": "runtime-save", + "field": "sessionId", + "message": "sessionId 不能为空", + })) + } + RuntimeProfileFieldError::MissingRuntimeSessionId => { + AppError::from_status(StatusCode::CONFLICT).with_details(json!({ + "provider": "runtime-save", + "message": message, + })) + } + RuntimeProfileFieldError::MissingBottomTab => { + AppError::from_status(StatusCode::BAD_REQUEST).with_details(json!({ + "provider": "runtime-save", + "field": "bottomTab", + "message": "bottomTab 不能为空", + })) + } + RuntimeProfileFieldError::NonPersistentRuntimeSnapshot => { + AppError::from_status(StatusCode::CONFLICT).with_details(json!({ + "provider": "runtime-save", + "message": message, + })) + } + RuntimeProfileFieldError::RuntimeSessionMismatch { + expected_session_id, + actual_session_id, + } => AppError::from_status(StatusCode::CONFLICT).with_details(json!({ + "provider": "runtime-save", + "message": message, + "expectedSessionId": expected_session_id, + "actualSessionId": actual_session_id, + })), + _ => AppError::from_status(StatusCode::BAD_REQUEST).with_details(json!({ + "provider": "runtime-save", + "message": message, + })), + } +} + fn runtime_save_error_response(request_context: &RequestContext, error: AppError) -> Response { error.into_response_with_context(Some(request_context)) } @@ -584,7 +497,7 @@ mod tests { .oneshot( Request::builder() .method("GET") - .uri("/api/runtime/profile/save-archives") + .uri("/api/profile/save-archives") .body(Body::empty()) .expect("request should build"), ) @@ -594,15 +507,6 @@ mod tests { assert_eq!(response.status(), StatusCode::UNAUTHORIZED); } - #[tokio::test] - async fn profile_save_archives_compat_route_matches_main_route_error_shape() { - assert_compat_route_matches_main_route_error_shape( - "/api/runtime/profile/save-archives", - "/api/profile/save-archives", - ) - .await; - } - #[tokio::test] async fn resume_profile_save_archive_rejects_blank_world_key() { let state = seed_authenticated_state().await; @@ -613,7 +517,7 @@ mod tests { .oneshot( Request::builder() .method("POST") - .uri("/api/runtime/profile/save-archives/%20%20") + .uri("/api/profile/save-archives/%20%20") .header("authorization", format!("Bearer {token}")) .header("x-genarrative-response-envelope", "v1") .body(Body::empty()) @@ -625,68 +529,6 @@ mod tests { assert_eq!(response.status(), StatusCode::BAD_REQUEST); } - async fn assert_compat_route_matches_main_route_error_shape( - main_route: &str, - compat_route: &str, - ) { - let state = seed_authenticated_state().await; - let token = issue_access_token(&state); - let app = build_router(state); - - let main_response = app - .clone() - .oneshot( - Request::builder() - .method("GET") - .uri(main_route) - .header("authorization", format!("Bearer {token}")) - .header("x-genarrative-response-envelope", "v1") - .body(Body::empty()) - .expect("request should build"), - ) - .await - .expect("request should succeed"); - - let compat_response = app - .oneshot( - Request::builder() - .method("GET") - .uri(compat_route) - .header("authorization", format!("Bearer {token}")) - .header("x-genarrative-response-envelope", "v1") - .body(Body::empty()) - .expect("request should build"), - ) - .await - .expect("request should succeed"); - - assert_eq!(main_response.status(), compat_response.status()); - - let main_payload: Value = serde_json::from_slice( - &main_response - .into_body() - .collect() - .await - .expect("body should collect") - .to_bytes(), - ) - .expect("response body should be valid json"); - let compat_payload: Value = serde_json::from_slice( - &compat_response - .into_body() - .collect() - .await - .expect("body should collect") - .to_bytes(), - ) - .expect("response body should be valid json"); - - assert_eq!( - main_payload["error"]["details"]["provider"], - compat_payload["error"]["details"]["provider"] - ); - } - async fn seed_authenticated_state() -> AppState { let state = AppState::new(AppConfig::default()).expect("state should build"); state diff --git a/server-rs/crates/api-server/src/state.rs b/server-rs/crates/api-server/src/state.rs index 7e2b1b35..b5746284 100644 --- a/server-rs/crates/api-server/src/state.rs +++ b/server-rs/crates/api-server/src/state.rs @@ -14,7 +14,7 @@ use module_runtime::{SAVE_SNAPSHOT_VERSION, format_utc_micros}; use platform_auth::{ AccessTokenClaims, AccessTokenClaimsInput, AuthProvider, BindingStatus, JwtConfig, JwtError, RefreshCookieConfig, RefreshCookieError, RefreshCookieSameSite, SmsAuthConfig, SmsAuthProvider, - SmsAuthProviderKind, SmsProviderError, sign_access_token, verify_access_token, + SmsAuthProviderKind, SmsProviderError, WechatProvider, sign_access_token, verify_access_token, }; use platform_llm::{LlmClient, LlmConfig, LlmError}; use platform_oss::{OssClient, OssConfig, OssError}; @@ -24,7 +24,7 @@ use time::OffsetDateTime; use tracing::{info, warn}; use crate::config::AppConfig; -use crate::wechat_provider::{WechatProvider, build_wechat_provider}; +use crate::wechat_provider::build_wechat_provider; const ADMIN_ROLE: &str = "admin"; diff --git a/server-rs/crates/api-server/src/story_battles.rs b/server-rs/crates/api-server/src/story_battles.rs index 30fa87f8..35ea4c8e 100644 --- a/server-rs/crates/api-server/src/story_battles.rs +++ b/server-rs/crates/api-server/src/story_battles.rs @@ -51,6 +51,23 @@ pub async fn create_story_battle( })), ) })?; + let story_state = state + .spacetime_client() + .get_story_session_state(payload.story_session_id.clone()) + .await + .map_err(|error| { + story_battles_error_response(&request_context, map_story_battle_client_error(error)) + })?; + require_story_session_owner_for_battle( + &request_context, + &story_state.session.actor_user_id, + &actor_user_id, + )?; + require_story_session_runtime_for_battle( + &request_context, + &story_state.session.runtime_session_id, + &payload.runtime_session_id, + )?; let result = state .spacetime_client() @@ -89,14 +106,29 @@ pub async fn create_story_battle( pub async fn resolve_story_battle( State(state): State, Extension(request_context): Extension, - Extension(_authenticated): Extension, + Extension(authenticated): Extension, Json(payload): Json, ) -> Result, Response> { let now_micros = current_utc_micros(); + let actor_user_id = authenticated.claims().user_id().to_string(); + let battle_state_id = payload.battle_state_id; + let current_battle = state + .spacetime_client() + .get_battle_state(battle_state_id.clone()) + .await + .map_err(|error| { + story_battles_error_response(&request_context, map_story_battle_client_error(error)) + })?; + require_story_battle_owner( + &request_context, + ¤t_battle.actor_user_id, + &actor_user_id, + )?; + let result = state .spacetime_client() .resolve_combat_action(ResolveCombatActionInput { - battle_state_id: payload.battle_state_id, + battle_state_id, function_id: payload.function_id, action_text: payload.action_text, base_damage: payload.base_damage, @@ -128,8 +160,9 @@ pub async fn get_story_battle_state( State(state): State, Path(battle_state_id): Path, Extension(request_context): Extension, - Extension(_authenticated): Extension, + Extension(authenticated): Extension, ) -> Result, Response> { + let actor_user_id = authenticated.claims().user_id().to_string(); let result = state .spacetime_client() .get_battle_state(battle_state_id) @@ -137,6 +170,7 @@ pub async fn get_story_battle_state( .map_err(|error| { story_battles_error_response(&request_context, map_story_battle_client_error(error)) })?; + require_story_battle_owner(&request_context, &result.actor_user_id, &actor_user_id)?; Ok(json_success_body( Some(&request_context), @@ -175,6 +209,23 @@ pub async fn create_story_npc_battle( })), ) })?; + let story_state = state + .spacetime_client() + .get_story_session_state(payload.story_session_id.clone()) + .await + .map_err(|error| { + story_battles_error_response(&request_context, map_story_battle_client_error(error)) + })?; + require_story_session_owner_for_battle( + &request_context, + &story_state.session.actor_user_id, + &actor_user_id, + )?; + require_story_session_runtime_for_battle( + &request_context, + &story_state.session.runtime_session_id, + &payload.runtime_session_id, + )?; let result = state .spacetime_client() @@ -431,6 +482,73 @@ fn story_battles_error_response(request_context: &RequestContext, error: AppErro error.into_response_with_context(Some(request_context)) } +fn require_story_session_owner_for_battle( + request_context: &RequestContext, + resource_actor_user_id: &str, + authenticated_actor_user_id: &str, +) -> Result<(), Response> { + require_resource_owner( + request_context, + resource_actor_user_id, + authenticated_actor_user_id, + "story-session", + "story session 不属于当前用户,不能创建战斗", + ) +} + +fn require_story_battle_owner( + request_context: &RequestContext, + resource_actor_user_id: &str, + authenticated_actor_user_id: &str, +) -> Result<(), Response> { + require_resource_owner( + request_context, + resource_actor_user_id, + authenticated_actor_user_id, + "story-battle", + "battle state 不属于当前用户", + ) +} + +fn require_story_session_runtime_for_battle( + request_context: &RequestContext, + session_runtime_id: &str, + requested_runtime_id: &str, +) -> Result<(), Response> { + if session_runtime_id == requested_runtime_id { + return Ok(()); + } + + Err(story_battles_error_response( + request_context, + AppError::from_status(StatusCode::BAD_REQUEST).with_details(json!({ + "provider": "story-session", + "message": "runtimeSessionId 与 story session 不匹配,不能创建战斗", + })), + )) +} + +fn require_resource_owner( + request_context: &RequestContext, + resource_actor_user_id: &str, + authenticated_actor_user_id: &str, + provider: &'static str, + message: &'static str, +) -> Result<(), Response> { + if resource_actor_user_id == authenticated_actor_user_id { + return Ok(()); + } + + // API 层只做登录用户与资源属主的边界检查;战斗结算仍由 module-combat 与 SpacetimeDB 承担。 + Err(story_battles_error_response( + request_context, + AppError::from_status(StatusCode::FORBIDDEN).with_details(json!({ + "provider": provider, + "message": message, + })), + )) +} + fn current_utc_micros() -> i64 { use std::time::{SystemTime, UNIX_EPOCH}; @@ -442,6 +560,8 @@ fn current_utc_micros() -> i64 { #[cfg(test)] mod tests { + use std::time::Duration; + use axum::{ body::Body, http::{Request, StatusCode}, @@ -454,7 +574,10 @@ mod tests { use time::OffsetDateTime; use tower::ServiceExt; - use crate::{app::build_router, config::AppConfig, state::AppState}; + use super::{require_story_battle_owner, require_story_session_runtime_for_battle}; + use crate::{ + app::build_router, config::AppConfig, request_context::RequestContext, state::AppState, + }; #[tokio::test] async fn create_story_battle_requires_authentication() { @@ -648,6 +771,37 @@ mod tests { assert_eq!(response.status(), StatusCode::UNAUTHORIZED); } + #[tokio::test] + async fn resolve_story_battle_requires_authentication() { + let app = build_router(AppState::new(AppConfig::default()).expect("state should build")); + + let response = app + .oneshot( + Request::builder() + .method("POST") + .uri("/api/story/battles/resolve") + .header("content-type", "application/json") + .body(Body::from( + json!({ + "battleStateId": "battle_001", + "functionId": "battle_attack_basic", + "actionText": "普通攻击", + "baseDamage": 10, + "manaCost": 0, + "heal": 0, + "manaRestore": 0, + "counterMultiplierBasisPoints": 10000 + }) + .to_string(), + )) + .expect("request should build"), + ) + .await + .expect("request should succeed"); + + assert_eq!(response.status(), StatusCode::UNAUTHORIZED); + } + #[tokio::test] async fn get_story_battle_state_returns_bad_gateway_when_spacetime_not_published() { let state = seed_authenticated_state().await; @@ -735,6 +889,63 @@ mod tests { ); } + #[test] + fn story_battle_owner_guard_rejects_mismatched_actor() { + let context = RequestContext::new( + "req_story_battle_owner_guard".to_string(), + "GET /api/story/battles/battle_001".to_string(), + Duration::ZERO, + true, + ); + + let response = require_story_battle_owner(&context, "user_owner", "user_other") + .expect_err("mismatched actor should be forbidden"); + + assert_eq!(response.status(), StatusCode::FORBIDDEN); + } + + #[test] + fn story_battle_owner_guard_accepts_matching_actor() { + let context = RequestContext::new( + "req_story_battle_owner_guard".to_string(), + "GET /api/story/battles/battle_001".to_string(), + Duration::ZERO, + true, + ); + + require_story_battle_owner(&context, "user_owner", "user_owner") + .expect("matching actor should pass"); + } + + #[test] + fn story_battle_runtime_guard_rejects_mismatched_runtime_session() { + let context = RequestContext::new( + "req_story_battle_runtime_guard".to_string(), + "POST /api/story/battles".to_string(), + Duration::ZERO, + true, + ); + + let response = + require_story_session_runtime_for_battle(&context, "runtime_owner", "runtime_other") + .expect_err("mismatched runtime session should be bad request"); + + assert_eq!(response.status(), StatusCode::BAD_REQUEST); + } + + #[test] + fn story_battle_runtime_guard_accepts_matching_runtime_session() { + let context = RequestContext::new( + "req_story_battle_runtime_guard".to_string(), + "POST /api/story/battles".to_string(), + Duration::ZERO, + true, + ); + + require_story_session_runtime_for_battle(&context, "runtime_owner", "runtime_owner") + .expect("matching runtime session should pass"); + } + async fn seed_authenticated_state() -> AppState { let state = AppState::new(AppConfig::default()).expect("state should build"); state diff --git a/server-rs/crates/api-server/src/story_sessions.rs b/server-rs/crates/api-server/src/story_sessions.rs index e2c9ca56..75f57699 100644 --- a/server-rs/crates/api-server/src/story_sessions.rs +++ b/server-rs/crates/api-server/src/story_sessions.rs @@ -72,14 +72,29 @@ pub async fn begin_story_session( pub async fn continue_story( State(state): State, Extension(request_context): Extension, - Extension(_authenticated): Extension, + Extension(authenticated): Extension, Json(payload): Json, ) -> Result, Response> { let now_micros = current_utc_micros(); + let actor_user_id = authenticated.claims().user_id().to_string(); + let story_session_id = payload.story_session_id; + let current_state = state + .spacetime_client() + .get_story_session_state(story_session_id.clone()) + .await + .map_err(|error| { + story_sessions_error_response(&request_context, map_story_session_client_error(error)) + })?; + require_story_session_owner( + &request_context, + ¤t_state.session.actor_user_id, + &actor_user_id, + )?; + let result = state .spacetime_client() .continue_story( - payload.story_session_id, + story_session_id, module_story::generate_story_event_id(now_micros), payload.narrative_text, payload.choice_function_id, @@ -123,8 +138,9 @@ pub async fn get_story_session_state( State(state): State, Path(story_session_id): Path, Extension(request_context): Extension, - Extension(_authenticated): Extension, + Extension(authenticated): Extension, ) -> Result, Response> { + let actor_user_id = authenticated.claims().user_id().to_string(); let result = state .spacetime_client() .get_story_session_state(story_session_id) @@ -132,6 +148,11 @@ pub async fn get_story_session_state( .map_err(|error| { story_sessions_error_response(&request_context, map_story_session_client_error(error)) })?; + require_story_session_owner( + &request_context, + &result.session.actor_user_id, + &actor_user_id, + )?; Ok(json_success_body( Some(&request_context), @@ -204,6 +225,25 @@ fn story_sessions_error_response(request_context: &RequestContext, error: AppErr error.into_response_with_context(Some(request_context)) } +fn require_story_session_owner( + request_context: &RequestContext, + resource_actor_user_id: &str, + authenticated_actor_user_id: &str, +) -> Result<(), Response> { + if resource_actor_user_id == authenticated_actor_user_id { + return Ok(()); + } + + // 这里只做 HTTP 鉴权边界判断,story session 的推进规则仍由 SpacetimeDB 领域层处理。 + Err(story_sessions_error_response( + request_context, + AppError::from_status(StatusCode::FORBIDDEN).with_details(json!({ + "provider": "story-session", + "message": "story session 不属于当前用户", + })), + )) +} + fn current_utc_micros() -> i64 { use std::time::{SystemTime, UNIX_EPOCH}; @@ -215,6 +255,8 @@ fn current_utc_micros() -> i64 { #[cfg(test)] mod tests { + use std::time::Duration; + use axum::{ body::Body, http::{Request, StatusCode}, @@ -227,7 +269,10 @@ mod tests { use time::OffsetDateTime; use tower::ServiceExt; - use crate::{app::build_router, config::AppConfig, state::AppState}; + use super::require_story_session_owner; + use crate::{ + app::build_router, config::AppConfig, request_context::RequestContext, state::AppState, + }; #[tokio::test] async fn begin_story_session_requires_authentication() { @@ -302,6 +347,32 @@ mod tests { ); } + #[tokio::test] + async fn continue_story_requires_authentication() { + let app = build_router(AppState::new(AppConfig::default()).expect("state should build")); + + let response = app + .oneshot( + Request::builder() + .method("POST") + .uri("/api/story/sessions/continue") + .header("content-type", "application/json") + .body(Body::from( + json!({ + "storySessionId": "storysess_001", + "narrativeText": "你看见篝火边有人招手。", + "choiceFunctionId": "talk_to_npc" + }) + .to_string(), + )) + .expect("request should build"), + ) + .await + .expect("request should succeed"); + + assert_eq!(response.status(), StatusCode::UNAUTHORIZED); + } + #[tokio::test] async fn continue_story_returns_bad_gateway_when_spacetime_not_published() { let state = seed_authenticated_state().await; @@ -457,6 +528,34 @@ mod tests { ); } + #[test] + fn story_session_owner_guard_rejects_mismatched_actor() { + let context = RequestContext::new( + "req_story_owner_guard".to_string(), + "GET /api/story/sessions/storysess_001/state".to_string(), + Duration::ZERO, + true, + ); + + let response = require_story_session_owner(&context, "user_owner", "user_other") + .expect_err("mismatched actor should be forbidden"); + + assert_eq!(response.status(), StatusCode::FORBIDDEN); + } + + #[test] + fn story_session_owner_guard_accepts_matching_actor() { + let context = RequestContext::new( + "req_story_owner_guard".to_string(), + "GET /api/story/sessions/storysess_001/state".to_string(), + Duration::ZERO, + true, + ); + + require_story_session_owner(&context, "user_owner", "user_owner") + .expect("matching actor should pass"); + } + async fn seed_authenticated_state() -> AppState { let state = AppState::new(AppConfig::default()).expect("state should build"); state diff --git a/server-rs/crates/api-server/src/wechat_auth.rs b/server-rs/crates/api-server/src/wechat_auth.rs index fc4c3511..05f43da5 100644 --- a/server-rs/crates/api-server/src/wechat_auth.rs +++ b/server-rs/crates/api-server/src/wechat_auth.rs @@ -1,13 +1,13 @@ use axum::{ Json, extract::{Extension, Query, State}, - http::{HeaderMap, HeaderValue, StatusCode}, + http::{HeaderMap, StatusCode}, response::{IntoResponse, Redirect, Response}, }; use module_auth::{ AuthLoginMethod, BindWechatPhoneInput, CreateWechatAuthStateInput, WechatAuthError, - WechatAuthScene, }; +use platform_auth::WechatAuthScene; use shared_contracts::auth::{ WechatBindPhoneRequest, WechatBindPhoneResponse, WechatCallbackQuery, WechatStartQuery, WechatStartResponse, @@ -23,6 +23,7 @@ use crate::{ attach_set_cookie_header, build_refresh_session_cookie_header, create_auth_session, }, http_error::AppError, + platform_errors::{attach_retry_after, map_wechat_provider_error}, request_context::RequestContext, session_client::resolve_session_client_context, state::AppState, @@ -50,17 +51,20 @@ pub async fn start_wechat_login( query.redirect_path.as_deref(), &state.config.wechat_redirect_path, ), - scene: scene.clone(), + scene: map_wechat_scene_to_domain(&scene), request_user_agent: user_agent.clone(), }, OffsetDateTime::now_utc(), ) .map_err(map_wechat_auth_error)?; - let authorization_url = state.wechat_provider().build_authorization_url( - &resolve_wechat_callback_url(&state, &headers)?, - &state_record.state.state_token, - &scene, - )?; + let authorization_url = state + .wechat_provider() + .build_authorization_url( + &resolve_wechat_callback_url(&state, &headers)?, + &state_record.state.state_token, + &scene, + ) + .map_err(map_wechat_provider_error)?; Ok(json_success_body( Some(&request_context), @@ -121,10 +125,12 @@ pub async fn handle_wechat_callback( { Ok(profile) => state .wechat_auth_service() - .resolve_login(module_auth::ResolveWechatLoginInput { profile }) + .resolve_login(module_auth::ResolveWechatLoginInput { + profile: map_wechat_profile_to_domain(profile), + }) .await .map_err(map_wechat_auth_error), - Err(error) => Err(error), + Err(error) => Err(map_wechat_provider_error(error)), }; match result { @@ -239,6 +245,24 @@ fn resolve_wechat_scene(user_agent: Option<&str>) -> Result module_auth::WechatAuthScene { + match scene { + WechatAuthScene::Desktop => module_auth::WechatAuthScene::Desktop, + WechatAuthScene::WechatInApp => module_auth::WechatAuthScene::WechatInApp, + } +} + +fn map_wechat_profile_to_domain( + profile: platform_auth::WechatIdentityProfile, +) -> module_auth::WechatIdentityProfile { + module_auth::WechatIdentityProfile { + provider_uid: profile.provider_uid, + provider_union_id: profile.provider_union_id, + display_name: profile.display_name, + avatar_url: profile.avatar_url, + } +} + fn normalize_redirect_path(raw_value: Option<&str>, fallback: &str) -> String { let Some(raw_value) = raw_value.map(str::trim).filter(|value| !value.is_empty()) else { return fallback.to_string(); @@ -340,10 +364,7 @@ fn map_wechat_bind_phone_error(error: module_auth::PhoneAuthError) -> AppError { let app_error = AppError::from_status(StatusCode::TOO_MANY_REQUESTS) .with_message(error.to_string()) .with_details(serde_json::json!({ "retryAfterSeconds": retry_after_seconds })); - match HeaderValue::from_str(&retry_after_seconds.to_string()) { - Ok(value) => app_error.with_header("retry-after", value), - Err(_) => app_error, - } + attach_retry_after(app_error, retry_after_seconds) } module_auth::PhoneAuthError::VerifyAttemptsExceeded => { AppError::from_status(StatusCode::TOO_MANY_REQUESTS).with_message(error.to_string()) diff --git a/server-rs/crates/api-server/src/wechat_provider.rs b/server-rs/crates/api-server/src/wechat_provider.rs index f6ff9da7..da4448ef 100644 --- a/server-rs/crates/api-server/src/wechat_provider.rs +++ b/server-rs/crates/api-server/src/wechat_provider.rs @@ -1,280 +1,40 @@ -use module_auth::{WechatAuthScene, WechatIdentityProfile}; -use reqwest::Client; -use serde::Deserialize; -use tracing::warn; -use url::Url; +use platform_auth::{ + DEFAULT_WECHAT_ACCESS_TOKEN_ENDPOINT, DEFAULT_WECHAT_AUTHORIZE_ENDPOINT, + DEFAULT_WECHAT_USER_INFO_ENDPOINT, WechatAuthConfig, WechatProvider, +}; -use crate::{config::AppConfig, http_error::AppError}; -use axum::http::StatusCode; - -#[derive(Clone, Debug)] -pub enum WechatProvider { - Disabled, - Mock(MockWechatProvider), - Real(RealWechatProvider), -} - -#[derive(Clone, Debug)] -pub struct MockWechatProvider { - mock_user_id: String, - mock_union_id: Option, - mock_display_name: String, - mock_avatar_url: Option, -} - -#[derive(Clone, Debug)] -pub struct RealWechatProvider { - client: Client, - app_id: String, - app_secret: String, - authorize_endpoint: String, - access_token_endpoint: String, - user_info_endpoint: String, -} - -#[derive(Debug, Deserialize)] -struct WechatAccessTokenResponse { - access_token: Option, - openid: Option, - unionid: Option, - errmsg: Option, -} - -#[derive(Debug, Deserialize)] -struct WechatUserInfoResponse { - openid: Option, - unionid: Option, - nickname: Option, - headimgurl: Option, - errmsg: Option, -} +use crate::config::AppConfig; pub fn build_wechat_provider(config: &AppConfig) -> WechatProvider { - if !config.wechat_auth_enabled { - return WechatProvider::Disabled; - } - - if config - .wechat_auth_provider - .trim() - .eq_ignore_ascii_case("mock") - { - return WechatProvider::Mock(MockWechatProvider { - mock_user_id: config.wechat_mock_user_id.clone(), - mock_union_id: config.wechat_mock_union_id.clone(), - mock_display_name: config.wechat_mock_display_name.clone(), - mock_avatar_url: config.wechat_mock_avatar_url.clone(), - }); - } - - let Some(app_id) = config.wechat_app_id.clone() else { - return WechatProvider::Disabled; - }; - let Some(app_secret) = config.wechat_app_secret.clone() else { - return WechatProvider::Disabled; - }; - - WechatProvider::Real(RealWechatProvider { - client: Client::new(), - app_id, - app_secret, - authorize_endpoint: config.wechat_authorize_endpoint.clone(), - access_token_endpoint: config.wechat_access_token_endpoint.clone(), - user_info_endpoint: config.wechat_user_info_endpoint.clone(), - }) + WechatProvider::new(WechatAuthConfig::new( + config.wechat_auth_enabled, + config.wechat_auth_provider.clone(), + config.wechat_app_id.clone(), + config.wechat_app_secret.clone(), + normalize_wechat_endpoint( + &config.wechat_authorize_endpoint, + DEFAULT_WECHAT_AUTHORIZE_ENDPOINT, + ), + normalize_wechat_endpoint( + &config.wechat_access_token_endpoint, + DEFAULT_WECHAT_ACCESS_TOKEN_ENDPOINT, + ), + normalize_wechat_endpoint( + &config.wechat_user_info_endpoint, + DEFAULT_WECHAT_USER_INFO_ENDPOINT, + ), + config.wechat_mock_user_id.clone(), + config.wechat_mock_union_id.clone(), + config.wechat_mock_display_name.clone(), + config.wechat_mock_avatar_url.clone(), + )) } -impl WechatProvider { - pub fn build_authorization_url( - &self, - callback_url: &str, - state: &str, - scene: &WechatAuthScene, - ) -> Result { - match self { - Self::Disabled => { - Err(AppError::from_status(StatusCode::BAD_REQUEST).with_message("微信登录暂未启用")) - } - Self::Mock(_) => { - let mut callback = Url::parse(callback_url).map_err(|error| { - AppError::from_status(StatusCode::INTERNAL_SERVER_ERROR) - .with_message(format!("微信回调地址非法:{error}")) - })?; - callback - .query_pairs_mut() - .append_pair("mock_code", "wx-mock-code") - .append_pair("state", state); - Ok(callback.to_string()) - } - Self::Real(provider) => provider.build_authorization_url(callback_url, state, scene), - } - } - - pub async fn resolve_callback_profile( - &self, - code: Option<&str>, - mock_code: Option<&str>, - ) -> Result { - match self { - Self::Disabled => { - Err(AppError::from_status(StatusCode::BAD_REQUEST).with_message("微信登录暂未启用")) - } - Self::Mock(provider) => Ok(provider.resolve_callback_profile(mock_code)), - Self::Real(provider) => provider.resolve_callback_profile(code).await, - } - } -} - -impl MockWechatProvider { - fn resolve_callback_profile(&self, mock_code: Option<&str>) -> WechatIdentityProfile { - let provider_uid = mock_code - .map(str::trim) - .filter(|value| !value.is_empty()) - .unwrap_or(self.mock_user_id.as_str()) - .to_string(); - WechatIdentityProfile { - provider_uid, - provider_union_id: self.mock_union_id.clone(), - display_name: Some(self.mock_display_name.clone()), - avatar_url: self.mock_avatar_url.clone(), - } - } -} - -impl RealWechatProvider { - fn build_authorization_url( - &self, - callback_url: &str, - state: &str, - scene: &WechatAuthScene, - ) -> Result { - let mut url = Url::parse(match scene { - WechatAuthScene::Desktop => &self.authorize_endpoint, - WechatAuthScene::WechatInApp => "https://open.weixin.qq.com/connect/oauth2/authorize", - }) - .map_err(|error| { - AppError::from_status(StatusCode::INTERNAL_SERVER_ERROR) - .with_message(format!("微信授权地址非法:{error}")) - })?; - url.query_pairs_mut() - .append_pair("appid", &self.app_id) - .append_pair("redirect_uri", callback_url) - .append_pair("response_type", "code") - .append_pair( - "scope", - match scene { - WechatAuthScene::Desktop => "snsapi_login", - WechatAuthScene::WechatInApp => "snsapi_userinfo", - }, - ) - .append_pair("state", state); - Ok(format!("{url}#wechat_redirect")) - } - - async fn resolve_callback_profile( - &self, - code: Option<&str>, - ) -> Result { - let code = code - .map(str::trim) - .filter(|value| !value.is_empty()) - .ok_or_else(|| { - AppError::from_status(StatusCode::BAD_REQUEST).with_message("缺少微信授权 code") - })?; - - let mut access_token_url = Url::parse(&self.access_token_endpoint).map_err(|error| { - AppError::from_status(StatusCode::INTERNAL_SERVER_ERROR) - .with_message(format!("微信 access_token 地址非法:{error}")) - })?; - access_token_url - .query_pairs_mut() - .append_pair("appid", &self.app_id) - .append_pair("secret", &self.app_secret) - .append_pair("code", code) - .append_pair("grant_type", "authorization_code"); - - let access_token_payload = self - .client - .get(access_token_url.as_str()) - .send() - .await - .map_err(|error| { - warn!(error = %error, "微信 access_token 请求失败"); - AppError::from_status(StatusCode::BAD_GATEWAY) - .with_message("微信登录失败:access_token 请求失败") - })? - .json::() - .await - .map_err(|error| { - warn!(error = %error, "微信 access_token 响应解析失败"); - AppError::from_status(StatusCode::BAD_GATEWAY) - .with_message("微信登录失败:access_token 响应非法") - })?; - - let access_token = access_token_payload - .access_token - .filter(|value| !value.trim().is_empty()) - .ok_or_else(|| { - AppError::from_status(StatusCode::BAD_GATEWAY).with_message(format!( - "微信登录失败:{}", - access_token_payload - .errmsg - .unwrap_or_else(|| "缺少 access_token".to_string()) - )) - })?; - let openid = access_token_payload - .openid - .filter(|value| !value.trim().is_empty()) - .ok_or_else(|| { - AppError::from_status(StatusCode::BAD_GATEWAY) - .with_message("微信登录失败:缺少 openid") - })?; - - let mut user_info_url = Url::parse(&self.user_info_endpoint).map_err(|error| { - AppError::from_status(StatusCode::INTERNAL_SERVER_ERROR) - .with_message(format!("微信用户信息地址非法:{error}")) - })?; - user_info_url - .query_pairs_mut() - .append_pair("access_token", &access_token) - .append_pair("openid", &openid) - .append_pair("lang", "zh_CN"); - - let user_info_payload = self - .client - .get(user_info_url.as_str()) - .send() - .await - .map_err(|error| { - warn!(error = %error, "微信用户信息请求失败"); - AppError::from_status(StatusCode::BAD_GATEWAY) - .with_message("微信登录失败:用户信息请求失败") - })? - .json::() - .await - .map_err(|error| { - warn!(error = %error, "微信用户信息响应解析失败"); - AppError::from_status(StatusCode::BAD_GATEWAY) - .with_message("微信登录失败:用户信息响应非法") - })?; - - let provider_uid = user_info_payload - .openid - .filter(|value| !value.trim().is_empty()) - .ok_or_else(|| { - AppError::from_status(StatusCode::BAD_GATEWAY).with_message(format!( - "微信登录失败:{}", - user_info_payload - .errmsg - .unwrap_or_else(|| "缺少 openid".to_string()) - )) - })?; - - Ok(WechatIdentityProfile { - provider_uid, - provider_union_id: user_info_payload.unionid.or(access_token_payload.unionid), - display_name: user_info_payload.nickname, - avatar_url: user_info_payload.headimgurl, - }) +fn normalize_wechat_endpoint(value: &str, fallback: &str) -> String { + let trimmed = value.trim(); + if trimmed.is_empty() { + fallback.to_string() + } else { + trimmed.to_string() } } diff --git a/server-rs/crates/module-ai/README.md b/server-rs/crates/module-ai/README.md index 8f722c85..09845ffa 100644 --- a/server-rs/crates/module-ai/README.md +++ b/server-rs/crates/module-ai/README.md @@ -16,10 +16,18 @@ 当前提交已完成: 1. `module-ai` 的 `Cargo.toml` -2. DDD 分层文件: +2. DDD 分层文件与内部子模块: - `src/domain.rs` + - `src/domain/types.rs` + - `src/domain/stages.rs` + - `src/domain/ids.rs` - `src/commands.rs` + - `src/commands/inputs.rs` + - `src/commands/validation.rs` - `src/application.rs` + - `src/application/service.rs` + - `src/application/store.rs` + - `src/application/result.rs` - `src/events.rs` - `src/errors.rs` 3. 首版核心类型: @@ -40,7 +48,7 @@ - `AiTaskFinishInput` - `AiTaskCancelInput` - `AiTaskFailureInput` -8. 基础单元测试 +8. `src/tests.rs` 中的基础单元测试 首版详细设计见: @@ -48,6 +56,7 @@ 2. [../../../docs/technical/M4_MODULE_AI_SPACETIMEDB_BASELINE_2026-04-21.md](../../../docs/technical/M4_MODULE_AI_SPACETIMEDB_BASELINE_2026-04-21.md) 3. [../../../docs/technical/M4_MODULE_AI_AXUM_FACADE_DESIGN_2026-04-22.md](../../../docs/technical/M4_MODULE_AI_AXUM_FACADE_DESIGN_2026-04-22.md) 4. [../../../docs/technical/SERVER_RS_DDD_WP_AI_TASK_DOMAIN_REFACTOR_2026-04-29.md](../../../docs/technical/SERVER_RS_DDD_WP_AI_TASK_DOMAIN_REFACTOR_2026-04-29.md) +5. [../../../docs/technical/SERVER_RS_DDD_WP_AI_INTERNAL_MODULE_SPLIT_2026-04-29.md](../../../docs/technical/SERVER_RS_DDD_WP_AI_INTERNAL_MODULE_SPLIT_2026-04-29.md) ## 3. 当前仍未进入的范围 diff --git a/server-rs/crates/module-ai/src/application.rs b/server-rs/crates/module-ai/src/application.rs index 950a7be9..7cbf3ba8 100644 --- a/server-rs/crates/module-ai/src/application.rs +++ b/server-rs/crates/module-ai/src/application.rs @@ -1,393 +1,12 @@ -use std::{ - collections::HashMap, - sync::{Arc, Mutex}, -}; +mod result; +mod service; +mod store; -use shared_kernel::normalize_required_string; +pub use result::AiTaskProcedureResult; +pub use service::AiTaskService; +pub use store::InMemoryAiTaskStore; -use crate::commands::validate_task_create_input; -use crate::{ - AiResultReferenceKind, AiResultReferenceSnapshot, AiStageCompletionInput, AiTaskCreateInput, - AiTaskFieldError, AiTaskServiceError, AiTaskSnapshot, AiTaskStageSnapshot, AiTaskStageStatus, - AiTaskStatus, AiTextChunkSnapshot, INITIAL_AI_TASK_VERSION, generate_ai_result_ref_id, - generate_ai_text_chunk_id, normalize_optional_text, normalize_string_list, -}; - -use serde::{Deserialize, Serialize}; -#[cfg(feature = "spacetime-types")] -use spacetimedb::SpacetimeType; - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct AiTaskProcedureResult { - pub ok: bool, - pub task: Option, - pub text_chunk: Option, - pub error_message: Option, -} - -#[derive(Clone, Debug, Default)] -pub struct InMemoryAiTaskStore { - inner: Arc>, -} - -#[derive(Debug, Default)] -struct InMemoryAiTaskStoreState { - tasks: HashMap, - text_chunks: HashMap>, -} - -#[derive(Clone, Debug)] -pub struct AiTaskService { - store: InMemoryAiTaskStore, -} - -impl AiTaskService { - pub fn new(store: InMemoryAiTaskStore) -> Self { - Self { store } - } - - pub fn create_task( - &self, - input: AiTaskCreateInput, - ) -> Result { - validate_task_create_input(&input).map_err(AiTaskServiceError::Field)?; - - let snapshot = AiTaskSnapshot { - task_id: input.task_id.clone(), - task_kind: input.task_kind, - owner_user_id: normalize_required_string(input.owner_user_id).unwrap_or_default(), - request_label: normalize_required_string(input.request_label).unwrap_or_default(), - source_module: normalize_required_string(input.source_module).unwrap_or_default(), - source_entity_id: normalize_optional_text(input.source_entity_id), - request_payload_json: normalize_optional_text(input.request_payload_json), - status: AiTaskStatus::Pending, - failure_message: None, - stages: input - .stages - .into_iter() - .map(|stage| AiTaskStageSnapshot { - stage_kind: stage.stage_kind, - label: normalize_required_string(stage.label).unwrap_or_default(), - detail: normalize_required_string(stage.detail).unwrap_or_default(), - order: stage.order, - status: AiTaskStageStatus::Pending, - text_output: None, - structured_payload_json: None, - warning_messages: Vec::new(), - started_at_micros: None, - completed_at_micros: None, - }) - .collect(), - result_references: Vec::new(), - latest_text_output: None, - latest_structured_payload_json: None, - version: INITIAL_AI_TASK_VERSION, - created_at_micros: input.created_at_micros, - started_at_micros: None, - completed_at_micros: None, - updated_at_micros: input.created_at_micros, - }; - - self.store.insert_task(snapshot) - } - - pub fn start_task( - &self, - task_id: &str, - started_at_micros: i64, - ) -> Result { - self.store.update_task(task_id, |task| { - ensure_task_is_not_terminal(task.status)?; - task.status = AiTaskStatus::Running; - task.started_at_micros.get_or_insert(started_at_micros); - task.updated_at_micros = started_at_micros; - task.version += 1; - Ok(()) - }) - } - - pub fn start_stage( - &self, - task_id: &str, - stage_kind: crate::AiTaskStageKind, - started_at_micros: i64, - ) -> Result { - self.store.update_task(task_id, |task| { - ensure_task_is_not_terminal(task.status)?; - task.status = AiTaskStatus::Running; - task.started_at_micros.get_or_insert(started_at_micros); - let stage = task - .stages - .iter_mut() - .find(|stage| stage.stage_kind == stage_kind) - .ok_or(AiTaskServiceError::StageNotFound)?; - stage.status = AiTaskStageStatus::Running; - stage.started_at_micros.get_or_insert(started_at_micros); - task.updated_at_micros = started_at_micros; - task.version += 1; - Ok(()) - }) - } - - pub fn append_text_chunk( - &self, - task_id: &str, - stage_kind: crate::AiTaskStageKind, - sequence: u32, - delta_text: String, - created_at_micros: i64, - ) -> Result<(AiTaskSnapshot, AiTextChunkSnapshot), AiTaskServiceError> { - if delta_text.trim().is_empty() { - return Err(AiTaskServiceError::Field( - AiTaskFieldError::MissingChunkText, - )); - } - if sequence == 0 { - return Err(AiTaskServiceError::Field(AiTaskFieldError::InvalidSequence)); - } - - let chunk = AiTextChunkSnapshot { - chunk_id: generate_ai_text_chunk_id(created_at_micros, sequence), - task_id: normalize_required_string(task_id).unwrap_or_default(), - stage_kind, - sequence, - delta_text: normalize_required_string(delta_text).unwrap_or_default(), - created_at_micros, - }; - - let task = self.store.append_text_chunk(chunk.clone())?; - Ok((task, chunk)) - } - - pub fn complete_stage( - &self, - input: AiStageCompletionInput, - ) -> Result { - self.store.update_task(&input.task_id, |task| { - ensure_task_is_not_terminal(task.status)?; - - let stage = task - .stages - .iter_mut() - .find(|stage| stage.stage_kind == input.stage_kind) - .ok_or(AiTaskServiceError::StageNotFound)?; - stage.status = AiTaskStageStatus::Completed; - stage.completed_at_micros = Some(input.completed_at_micros); - stage.text_output = normalize_optional_text(input.text_output.clone()); - stage.structured_payload_json = - normalize_optional_text(input.structured_payload_json.clone()); - stage.warning_messages = normalize_string_list(input.warning_messages.clone()); - - task.latest_text_output = stage.text_output.clone(); - task.latest_structured_payload_json = stage.structured_payload_json.clone(); - task.updated_at_micros = input.completed_at_micros; - task.version += 1; - Ok(()) - }) - } - - pub fn attach_result_reference( - &self, - task_id: &str, - reference_kind: AiResultReferenceKind, - reference_id: String, - label: Option, - created_at_micros: i64, - ) -> Result { - let Some(reference_id) = normalize_required_string(reference_id) else { - return Err(AiTaskServiceError::Field( - AiTaskFieldError::MissingReferenceId, - )); - }; - - self.store.update_task(task_id, |task| { - ensure_task_is_not_terminal(task.status)?; - task.result_references.push(AiResultReferenceSnapshot { - result_ref_id: generate_ai_result_ref_id(created_at_micros), - task_id: task.task_id.clone(), - reference_kind, - reference_id: reference_id.clone(), - label: normalize_optional_text(label.clone()), - created_at_micros, - }); - task.updated_at_micros = created_at_micros; - task.version += 1; - Ok(()) - }) - } - - pub fn complete_task( - &self, - task_id: &str, - completed_at_micros: i64, - ) -> Result { - self.store.update_task(task_id, |task| { - ensure_task_is_not_terminal(task.status)?; - task.status = AiTaskStatus::Completed; - task.completed_at_micros = Some(completed_at_micros); - task.updated_at_micros = completed_at_micros; - task.version += 1; - Ok(()) - }) - } - - pub fn fail_task( - &self, - task_id: &str, - failure_message: String, - completed_at_micros: i64, - ) -> Result { - let Some(failure_message) = normalize_required_string(failure_message) else { - return Err(AiTaskServiceError::Field( - AiTaskFieldError::MissingFailureMessage, - )); - }; - - self.store.update_task(task_id, |task| { - ensure_task_is_not_terminal(task.status)?; - task.status = AiTaskStatus::Failed; - task.failure_message = Some(failure_message.clone()); - task.completed_at_micros = Some(completed_at_micros); - task.updated_at_micros = completed_at_micros; - task.version += 1; - Ok(()) - }) - } - - pub fn cancel_task( - &self, - task_id: &str, - completed_at_micros: i64, - ) -> Result { - self.store.update_task(task_id, |task| { - ensure_task_is_not_terminal(task.status)?; - task.status = AiTaskStatus::Cancelled; - task.completed_at_micros = Some(completed_at_micros); - task.updated_at_micros = completed_at_micros; - task.version += 1; - Ok(()) - }) - } - - pub fn get_task(&self, task_id: &str) -> Result { - self.store.get_task(task_id) - } -} - -impl InMemoryAiTaskStore { - fn insert_task(&self, task: AiTaskSnapshot) -> Result { - let mut state = self - .inner - .lock() - .map_err(|_| AiTaskServiceError::Store("AI 任务仓储锁已中毒".to_string()))?; - - if state.tasks.contains_key(&task.task_id) { - return Err(AiTaskServiceError::TaskAlreadyExists); - } - - state.text_chunks.insert(task.task_id.clone(), Vec::new()); - state.tasks.insert(task.task_id.clone(), task.clone()); - Ok(task) - } - - fn update_task( - &self, - task_id: &str, - mut apply: F, - ) -> Result - where - F: FnMut(&mut AiTaskSnapshot) -> Result<(), AiTaskServiceError>, - { - let mut state = self - .inner - .lock() - .map_err(|_| AiTaskServiceError::Store("AI 任务仓储锁已中毒".to_string()))?; - let task = state - .tasks - .get_mut(task_id.trim()) - .ok_or(AiTaskServiceError::TaskNotFound)?; - apply(task)?; - Ok(task.clone()) - } - - fn append_text_chunk( - &self, - chunk: AiTextChunkSnapshot, - ) -> Result { - let mut state = self - .inner - .lock() - .map_err(|_| AiTaskServiceError::Store("AI 任务仓储锁已中毒".to_string()))?; - { - let task = state - .tasks - .get_mut(&chunk.task_id) - .ok_or(AiTaskServiceError::TaskNotFound)?; - ensure_task_is_not_terminal(task.status)?; - - let stage = task - .stages - .iter_mut() - .find(|stage| stage.stage_kind == chunk.stage_kind) - .ok_or(AiTaskServiceError::StageNotFound)?; - if stage.status == AiTaskStageStatus::Pending { - stage.status = AiTaskStageStatus::Running; - stage.started_at_micros = Some(chunk.created_at_micros); - } - - task.status = AiTaskStatus::Running; - task.started_at_micros - .get_or_insert(chunk.created_at_micros); - } - - let chunks = state - .text_chunks - .get_mut(&chunk.task_id) - .ok_or(AiTaskServiceError::TaskNotFound)?; - chunks.push(chunk.clone()); - chunks.sort_by_key(|value| value.sequence); - - let aggregated_text = chunks - .iter() - .filter(|value| value.stage_kind == chunk.stage_kind) - .map(|value| value.delta_text.as_str()) - .collect::>() - .join(""); - let normalized_output = if aggregated_text.trim().is_empty() { - None - } else { - Some(aggregated_text) - }; - - let task = state - .tasks - .get_mut(&chunk.task_id) - .ok_or(AiTaskServiceError::TaskNotFound)?; - let stage = task - .stages - .iter_mut() - .find(|stage| stage.stage_kind == chunk.stage_kind) - .ok_or(AiTaskServiceError::StageNotFound)?; - stage.text_output = normalized_output.clone(); - task.latest_text_output = normalized_output; - task.updated_at_micros = chunk.created_at_micros; - task.version += 1; - Ok(task.clone()) - } - - fn get_task(&self, task_id: &str) -> Result { - let state = self - .inner - .lock() - .map_err(|_| AiTaskServiceError::Store("AI 任务仓储锁已中毒".to_string()))?; - state - .tasks - .get(task_id.trim()) - .cloned() - .ok_or(AiTaskServiceError::TaskNotFound) - } -} +use crate::{AiTaskFieldError, AiTaskServiceError, AiTaskStatus}; fn ensure_task_is_not_terminal(status: AiTaskStatus) -> Result<(), AiTaskServiceError> { if status.is_terminal() { diff --git a/server-rs/crates/module-ai/src/application/result.rs b/server-rs/crates/module-ai/src/application/result.rs new file mode 100644 index 00000000..54b382e9 --- /dev/null +++ b/server-rs/crates/module-ai/src/application/result.rs @@ -0,0 +1,14 @@ +use serde::{Deserialize, Serialize}; +#[cfg(feature = "spacetime-types")] +use spacetimedb::SpacetimeType; + +use crate::{AiTaskSnapshot, AiTextChunkSnapshot}; + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct AiTaskProcedureResult { + pub ok: bool, + pub task: Option, + pub text_chunk: Option, + pub error_message: Option, +} diff --git a/server-rs/crates/module-ai/src/application/service.rs b/server-rs/crates/module-ai/src/application/service.rs new file mode 100644 index 00000000..713f3cd3 --- /dev/null +++ b/server-rs/crates/module-ai/src/application/service.rs @@ -0,0 +1,250 @@ +use shared_kernel::normalize_required_string; + +use crate::commands::validate_task_create_input; +use crate::{ + AiResultReferenceKind, AiResultReferenceSnapshot, AiStageCompletionInput, AiTaskCreateInput, + AiTaskFieldError, AiTaskServiceError, AiTaskSnapshot, AiTaskStageKind, AiTaskStageSnapshot, + AiTaskStageStatus, AiTaskStatus, AiTextChunkSnapshot, INITIAL_AI_TASK_VERSION, + generate_ai_result_ref_id, generate_ai_text_chunk_id, normalize_optional_text, + normalize_string_list, +}; + +use super::{InMemoryAiTaskStore, ensure_task_is_not_terminal}; + +#[derive(Clone, Debug)] +pub struct AiTaskService { + store: InMemoryAiTaskStore, +} + +impl AiTaskService { + pub fn new(store: InMemoryAiTaskStore) -> Self { + Self { store } + } + + pub fn create_task( + &self, + input: AiTaskCreateInput, + ) -> Result { + validate_task_create_input(&input).map_err(AiTaskServiceError::Field)?; + + let snapshot = AiTaskSnapshot { + task_id: input.task_id.clone(), + task_kind: input.task_kind, + owner_user_id: normalize_required_string(input.owner_user_id).unwrap_or_default(), + request_label: normalize_required_string(input.request_label).unwrap_or_default(), + source_module: normalize_required_string(input.source_module).unwrap_or_default(), + source_entity_id: normalize_optional_text(input.source_entity_id), + request_payload_json: normalize_optional_text(input.request_payload_json), + status: AiTaskStatus::Pending, + failure_message: None, + stages: input + .stages + .into_iter() + .map(|stage| AiTaskStageSnapshot { + stage_kind: stage.stage_kind, + label: normalize_required_string(stage.label).unwrap_or_default(), + detail: normalize_required_string(stage.detail).unwrap_or_default(), + order: stage.order, + status: AiTaskStageStatus::Pending, + text_output: None, + structured_payload_json: None, + warning_messages: Vec::new(), + started_at_micros: None, + completed_at_micros: None, + }) + .collect(), + result_references: Vec::new(), + latest_text_output: None, + latest_structured_payload_json: None, + version: INITIAL_AI_TASK_VERSION, + created_at_micros: input.created_at_micros, + started_at_micros: None, + completed_at_micros: None, + updated_at_micros: input.created_at_micros, + }; + + self.store.insert_task(snapshot) + } + + pub fn start_task( + &self, + task_id: &str, + started_at_micros: i64, + ) -> Result { + self.store.update_task(task_id, |task| { + ensure_task_is_not_terminal(task.status)?; + task.status = AiTaskStatus::Running; + task.started_at_micros.get_or_insert(started_at_micros); + task.updated_at_micros = started_at_micros; + task.version += 1; + Ok(()) + }) + } + + pub fn start_stage( + &self, + task_id: &str, + stage_kind: AiTaskStageKind, + started_at_micros: i64, + ) -> Result { + self.store.update_task(task_id, |task| { + ensure_task_is_not_terminal(task.status)?; + task.status = AiTaskStatus::Running; + task.started_at_micros.get_or_insert(started_at_micros); + let stage = task + .stages + .iter_mut() + .find(|stage| stage.stage_kind == stage_kind) + .ok_or(AiTaskServiceError::StageNotFound)?; + stage.status = AiTaskStageStatus::Running; + stage.started_at_micros.get_or_insert(started_at_micros); + task.updated_at_micros = started_at_micros; + task.version += 1; + Ok(()) + }) + } + + pub fn append_text_chunk( + &self, + task_id: &str, + stage_kind: AiTaskStageKind, + sequence: u32, + delta_text: String, + created_at_micros: i64, + ) -> Result<(AiTaskSnapshot, AiTextChunkSnapshot), AiTaskServiceError> { + if delta_text.trim().is_empty() { + return Err(AiTaskServiceError::Field( + AiTaskFieldError::MissingChunkText, + )); + } + if sequence == 0 { + return Err(AiTaskServiceError::Field(AiTaskFieldError::InvalidSequence)); + } + + let chunk = AiTextChunkSnapshot { + chunk_id: generate_ai_text_chunk_id(created_at_micros, sequence), + task_id: normalize_required_string(task_id).unwrap_or_default(), + stage_kind, + sequence, + delta_text: normalize_required_string(delta_text).unwrap_or_default(), + created_at_micros, + }; + + let task = self.store.append_text_chunk(chunk.clone())?; + Ok((task, chunk)) + } + + pub fn complete_stage( + &self, + input: AiStageCompletionInput, + ) -> Result { + self.store.update_task(&input.task_id, |task| { + ensure_task_is_not_terminal(task.status)?; + + let stage = task + .stages + .iter_mut() + .find(|stage| stage.stage_kind == input.stage_kind) + .ok_or(AiTaskServiceError::StageNotFound)?; + stage.status = AiTaskStageStatus::Completed; + stage.completed_at_micros = Some(input.completed_at_micros); + stage.text_output = normalize_optional_text(input.text_output.clone()); + stage.structured_payload_json = + normalize_optional_text(input.structured_payload_json.clone()); + stage.warning_messages = normalize_string_list(input.warning_messages.clone()); + + task.latest_text_output = stage.text_output.clone(); + task.latest_structured_payload_json = stage.structured_payload_json.clone(); + task.updated_at_micros = input.completed_at_micros; + task.version += 1; + Ok(()) + }) + } + + pub fn attach_result_reference( + &self, + task_id: &str, + reference_kind: AiResultReferenceKind, + reference_id: String, + label: Option, + created_at_micros: i64, + ) -> Result { + let Some(reference_id) = normalize_required_string(reference_id) else { + return Err(AiTaskServiceError::Field( + AiTaskFieldError::MissingReferenceId, + )); + }; + + self.store.update_task(task_id, |task| { + ensure_task_is_not_terminal(task.status)?; + task.result_references.push(AiResultReferenceSnapshot { + result_ref_id: generate_ai_result_ref_id(created_at_micros), + task_id: task.task_id.clone(), + reference_kind, + reference_id: reference_id.clone(), + label: normalize_optional_text(label.clone()), + created_at_micros, + }); + task.updated_at_micros = created_at_micros; + task.version += 1; + Ok(()) + }) + } + + pub fn complete_task( + &self, + task_id: &str, + completed_at_micros: i64, + ) -> Result { + self.store.update_task(task_id, |task| { + ensure_task_is_not_terminal(task.status)?; + task.status = AiTaskStatus::Completed; + task.completed_at_micros = Some(completed_at_micros); + task.updated_at_micros = completed_at_micros; + task.version += 1; + Ok(()) + }) + } + + pub fn fail_task( + &self, + task_id: &str, + failure_message: String, + completed_at_micros: i64, + ) -> Result { + let Some(failure_message) = normalize_required_string(failure_message) else { + return Err(AiTaskServiceError::Field( + AiTaskFieldError::MissingFailureMessage, + )); + }; + + self.store.update_task(task_id, |task| { + ensure_task_is_not_terminal(task.status)?; + task.status = AiTaskStatus::Failed; + task.failure_message = Some(failure_message.clone()); + task.completed_at_micros = Some(completed_at_micros); + task.updated_at_micros = completed_at_micros; + task.version += 1; + Ok(()) + }) + } + + pub fn cancel_task( + &self, + task_id: &str, + completed_at_micros: i64, + ) -> Result { + self.store.update_task(task_id, |task| { + ensure_task_is_not_terminal(task.status)?; + task.status = AiTaskStatus::Cancelled; + task.completed_at_micros = Some(completed_at_micros); + task.updated_at_micros = completed_at_micros; + task.version += 1; + Ok(()) + }) + } + + pub fn get_task(&self, task_id: &str) -> Result { + self.store.get_task(task_id) + } +} diff --git a/server-rs/crates/module-ai/src/application/store.rs b/server-rs/crates/module-ai/src/application/store.rs new file mode 100644 index 00000000..f2d0c117 --- /dev/null +++ b/server-rs/crates/module-ai/src/application/store.rs @@ -0,0 +1,138 @@ +use std::{ + collections::HashMap, + sync::{Arc, Mutex}, +}; + +use crate::{ + AiTaskServiceError, AiTaskSnapshot, AiTaskStageStatus, AiTaskStatus, AiTextChunkSnapshot, +}; + +use super::ensure_task_is_not_terminal; + +#[derive(Clone, Debug, Default)] +pub struct InMemoryAiTaskStore { + inner: Arc>, +} + +#[derive(Debug, Default)] +struct InMemoryAiTaskStoreState { + tasks: HashMap, + text_chunks: HashMap>, +} + +impl InMemoryAiTaskStore { + pub(super) fn insert_task( + &self, + task: AiTaskSnapshot, + ) -> Result { + let mut state = self + .inner + .lock() + .map_err(|_| AiTaskServiceError::Store("AI 任务仓储锁已中毒".to_string()))?; + + if state.tasks.contains_key(&task.task_id) { + return Err(AiTaskServiceError::TaskAlreadyExists); + } + + state.text_chunks.insert(task.task_id.clone(), Vec::new()); + state.tasks.insert(task.task_id.clone(), task.clone()); + Ok(task) + } + + pub(super) fn update_task( + &self, + task_id: &str, + mut apply: F, + ) -> Result + where + F: FnMut(&mut AiTaskSnapshot) -> Result<(), AiTaskServiceError>, + { + let mut state = self + .inner + .lock() + .map_err(|_| AiTaskServiceError::Store("AI 任务仓储锁已中毒".to_string()))?; + let task = state + .tasks + .get_mut(task_id.trim()) + .ok_or(AiTaskServiceError::TaskNotFound)?; + apply(task)?; + Ok(task.clone()) + } + + pub(super) fn append_text_chunk( + &self, + chunk: AiTextChunkSnapshot, + ) -> Result { + let mut state = self + .inner + .lock() + .map_err(|_| AiTaskServiceError::Store("AI 任务仓储锁已中毒".to_string()))?; + { + let task = state + .tasks + .get_mut(&chunk.task_id) + .ok_or(AiTaskServiceError::TaskNotFound)?; + ensure_task_is_not_terminal(task.status)?; + + let stage = task + .stages + .iter_mut() + .find(|stage| stage.stage_kind == chunk.stage_kind) + .ok_or(AiTaskServiceError::StageNotFound)?; + if stage.status == AiTaskStageStatus::Pending { + stage.status = AiTaskStageStatus::Running; + stage.started_at_micros = Some(chunk.created_at_micros); + } + + task.status = AiTaskStatus::Running; + task.started_at_micros + .get_or_insert(chunk.created_at_micros); + } + + let chunks = state + .text_chunks + .get_mut(&chunk.task_id) + .ok_or(AiTaskServiceError::TaskNotFound)?; + chunks.push(chunk.clone()); + chunks.sort_by_key(|value| value.sequence); + + let aggregated_text = chunks + .iter() + .filter(|value| value.stage_kind == chunk.stage_kind) + .map(|value| value.delta_text.as_str()) + .collect::>() + .join(""); + let normalized_output = if aggregated_text.trim().is_empty() { + None + } else { + Some(aggregated_text) + }; + + let task = state + .tasks + .get_mut(&chunk.task_id) + .ok_or(AiTaskServiceError::TaskNotFound)?; + let stage = task + .stages + .iter_mut() + .find(|stage| stage.stage_kind == chunk.stage_kind) + .ok_or(AiTaskServiceError::StageNotFound)?; + stage.text_output = normalized_output.clone(); + task.latest_text_output = normalized_output; + task.updated_at_micros = chunk.created_at_micros; + task.version += 1; + Ok(task.clone()) + } + + pub(super) fn get_task(&self, task_id: &str) -> Result { + let state = self + .inner + .lock() + .map_err(|_| AiTaskServiceError::Store("AI 任务仓储锁已中毒".to_string()))?; + state + .tasks + .get(task_id.trim()) + .cloned() + .ok_or(AiTaskServiceError::TaskNotFound) + } +} diff --git a/server-rs/crates/module-ai/src/commands.rs b/server-rs/crates/module-ai/src/commands.rs index 88ab8101..ce9f97a3 100644 --- a/server-rs/crates/module-ai/src/commands.rs +++ b/server-rs/crates/module-ai/src/commands.rs @@ -1,125 +1,9 @@ -use std::collections::HashMap; +mod inputs; +mod validation; -use serde::{Deserialize, Serialize}; -use shared_kernel::normalize_required_string; -#[cfg(feature = "spacetime-types")] -use spacetimedb::SpacetimeType; - -use crate::{ - AiResultReferenceKind, AiTaskFieldError, AiTaskKind, AiTaskStageBlueprint, AiTaskStageKind, +pub use inputs::{ + AiResultReferenceInput, AiStageCompletionInput, AiTaskCancelInput, AiTaskCreateInput, + AiTaskFailureInput, AiTaskFinishInput, AiTaskStageStartInput, AiTaskStartInput, + AiTextChunkAppendInput, }; - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct AiTaskCreateInput { - pub task_id: String, - pub task_kind: AiTaskKind, - pub owner_user_id: String, - pub request_label: String, - pub source_module: String, - pub source_entity_id: Option, - pub request_payload_json: Option, - pub stages: Vec, - pub created_at_micros: i64, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct AiTaskStartInput { - pub task_id: String, - pub started_at_micros: i64, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct AiTaskStageStartInput { - pub task_id: String, - pub stage_kind: AiTaskStageKind, - pub started_at_micros: i64, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct AiTextChunkAppendInput { - pub task_id: String, - pub stage_kind: AiTaskStageKind, - pub sequence: u32, - pub delta_text: String, - pub created_at_micros: i64, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct AiStageCompletionInput { - pub task_id: String, - pub stage_kind: AiTaskStageKind, - pub text_output: Option, - pub structured_payload_json: Option, - pub warning_messages: Vec, - pub completed_at_micros: i64, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct AiResultReferenceInput { - pub task_id: String, - pub reference_kind: AiResultReferenceKind, - pub reference_id: String, - pub label: Option, - pub created_at_micros: i64, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct AiTaskFinishInput { - pub task_id: String, - pub completed_at_micros: i64, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct AiTaskCancelInput { - pub task_id: String, - pub completed_at_micros: i64, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct AiTaskFailureInput { - pub task_id: String, - pub failure_message: String, - pub completed_at_micros: i64, -} - -pub fn validate_task_create_input(input: &AiTaskCreateInput) -> Result<(), AiTaskFieldError> { - if normalize_required_string(&input.task_id).is_none() { - return Err(AiTaskFieldError::MissingTaskId); - } - if normalize_required_string(&input.owner_user_id).is_none() { - return Err(AiTaskFieldError::MissingOwnerUserId); - } - if normalize_required_string(&input.request_label).is_none() { - return Err(AiTaskFieldError::MissingRequestLabel); - } - if normalize_required_string(&input.source_module).is_none() { - return Err(AiTaskFieldError::MissingSourceModule); - } - if input.stages.is_empty() { - return Err(AiTaskFieldError::MissingStageBlueprints); - } - - let mut seen = HashMap::new(); - for stage in &input.stages { - if normalize_required_string(&stage.label).is_none() - || normalize_required_string(&stage.detail).is_none() - { - return Err(AiTaskFieldError::MissingStageBlueprints); - } - - if seen.insert(stage.stage_kind, true).is_some() { - return Err(AiTaskFieldError::DuplicateStageBlueprint); - } - } - - Ok(()) -} +pub use validation::validate_task_create_input; diff --git a/server-rs/crates/module-ai/src/commands/inputs.rs b/server-rs/crates/module-ai/src/commands/inputs.rs new file mode 100644 index 00000000..d8fde14f --- /dev/null +++ b/server-rs/crates/module-ai/src/commands/inputs.rs @@ -0,0 +1,87 @@ +use serde::{Deserialize, Serialize}; +#[cfg(feature = "spacetime-types")] +use spacetimedb::SpacetimeType; + +use crate::{AiResultReferenceKind, AiTaskKind, AiTaskStageBlueprint, AiTaskStageKind}; + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct AiTaskCreateInput { + pub task_id: String, + pub task_kind: AiTaskKind, + pub owner_user_id: String, + pub request_label: String, + pub source_module: String, + pub source_entity_id: Option, + pub request_payload_json: Option, + pub stages: Vec, + pub created_at_micros: i64, +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct AiTaskStartInput { + pub task_id: String, + pub started_at_micros: i64, +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct AiTaskStageStartInput { + pub task_id: String, + pub stage_kind: AiTaskStageKind, + pub started_at_micros: i64, +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct AiTextChunkAppendInput { + pub task_id: String, + pub stage_kind: AiTaskStageKind, + pub sequence: u32, + pub delta_text: String, + pub created_at_micros: i64, +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct AiStageCompletionInput { + pub task_id: String, + pub stage_kind: AiTaskStageKind, + pub text_output: Option, + pub structured_payload_json: Option, + pub warning_messages: Vec, + pub completed_at_micros: i64, +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct AiResultReferenceInput { + pub task_id: String, + pub reference_kind: AiResultReferenceKind, + pub reference_id: String, + pub label: Option, + pub created_at_micros: i64, +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct AiTaskFinishInput { + pub task_id: String, + pub completed_at_micros: i64, +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct AiTaskCancelInput { + pub task_id: String, + pub completed_at_micros: i64, +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct AiTaskFailureInput { + pub task_id: String, + pub failure_message: String, + pub completed_at_micros: i64, +} diff --git a/server-rs/crates/module-ai/src/commands/validation.rs b/server-rs/crates/module-ai/src/commands/validation.rs new file mode 100644 index 00000000..e1bde985 --- /dev/null +++ b/server-rs/crates/module-ai/src/commands/validation.rs @@ -0,0 +1,40 @@ +use std::collections::HashMap; + +use shared_kernel::normalize_required_string; + +use crate::{AiTaskFieldError, AiTaskStageKind}; + +use super::inputs::AiTaskCreateInput; + +pub fn validate_task_create_input(input: &AiTaskCreateInput) -> Result<(), AiTaskFieldError> { + if normalize_required_string(&input.task_id).is_none() { + return Err(AiTaskFieldError::MissingTaskId); + } + if normalize_required_string(&input.owner_user_id).is_none() { + return Err(AiTaskFieldError::MissingOwnerUserId); + } + if normalize_required_string(&input.request_label).is_none() { + return Err(AiTaskFieldError::MissingRequestLabel); + } + if normalize_required_string(&input.source_module).is_none() { + return Err(AiTaskFieldError::MissingSourceModule); + } + if input.stages.is_empty() { + return Err(AiTaskFieldError::MissingStageBlueprints); + } + + let mut seen: HashMap = HashMap::new(); + for stage in &input.stages { + if normalize_required_string(&stage.label).is_none() + || normalize_required_string(&stage.detail).is_none() + { + return Err(AiTaskFieldError::MissingStageBlueprints); + } + + if seen.insert(stage.stage_kind, true).is_some() { + return Err(AiTaskFieldError::DuplicateStageBlueprint); + } + } + + Ok(()) +} diff --git a/server-rs/crates/module-ai/src/domain.rs b/server-rs/crates/module-ai/src/domain.rs index eac99270..a9931048 100644 --- a/server-rs/crates/module-ai/src/domain.rs +++ b/server-rs/crates/module-ai/src/domain.rs @@ -1,239 +1,15 @@ -use serde::{Deserialize, Serialize}; -use shared_kernel::{ - build_prefixed_seed_id, normalize_optional_string as normalize_shared_optional_string, - normalize_string_list as normalize_shared_string_list, +mod ids; +mod stages; +mod types; + +pub use ids::{ + AI_RESULT_REF_ID_PREFIX, AI_TASK_ID_PREFIX, AI_TASK_STAGE_ID_PREFIX, AI_TEXT_CHUNK_ID_PREFIX, + INITIAL_AI_TASK_VERSION, generate_ai_result_ref_id, generate_ai_task_id, + generate_ai_task_stage_id, generate_ai_text_chunk_id, normalize_optional_text, + normalize_string_list, +}; +pub use types::{ + AiResultReferenceKind, AiResultReferenceSnapshot, AiTaskKind, AiTaskSnapshot, + AiTaskStageBlueprint, AiTaskStageKind, AiTaskStageSnapshot, AiTaskStageStatus, AiTaskStatus, + AiTextChunkSnapshot, }; -#[cfg(feature = "spacetime-types")] -use spacetimedb::SpacetimeType; - -pub const AI_TASK_ID_PREFIX: &str = "aitask_"; -pub const AI_TASK_STAGE_ID_PREFIX: &str = "aistage_"; -pub const AI_RESULT_REF_ID_PREFIX: &str = "aires_"; -pub const AI_TEXT_CHUNK_ID_PREFIX: &str = "aichunk_"; -pub const INITIAL_AI_TASK_VERSION: u32 = 1; - -// AI 编排类型与当前正式运行时主链保持一致,具体 prompt 策略留给上层模块。 -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub enum AiTaskKind { - StoryGeneration, - CharacterChat, - NpcChat, - CustomWorldGeneration, - QuestIntent, - RuntimeItemIntent, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub enum AiTaskStatus { - Pending, - Running, - Completed, - Failed, - Cancelled, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] -pub enum AiTaskStageKind { - PreparePrompt, - RequestModel, - RepairResponse, - NormalizeResult, - PersistResult, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub enum AiTaskStageStatus { - Pending, - Running, - Completed, - Skipped, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub enum AiResultReferenceKind { - StorySession, - StoryEvent, - CustomWorldProfile, - QuestRecord, - RuntimeItemRecord, - AssetObject, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct AiTaskStageBlueprint { - pub stage_kind: AiTaskStageKind, - pub label: String, - pub detail: String, - pub order: u32, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct AiTaskStageSnapshot { - pub stage_kind: AiTaskStageKind, - pub label: String, - pub detail: String, - pub order: u32, - pub status: AiTaskStageStatus, - pub text_output: Option, - pub structured_payload_json: Option, - pub warning_messages: Vec, - pub started_at_micros: Option, - pub completed_at_micros: Option, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct AiTaskSnapshot { - pub task_id: String, - pub task_kind: AiTaskKind, - pub owner_user_id: String, - pub request_label: String, - pub source_module: String, - pub source_entity_id: Option, - pub request_payload_json: Option, - pub status: AiTaskStatus, - pub failure_message: Option, - pub stages: Vec, - pub result_references: Vec, - pub latest_text_output: Option, - pub latest_structured_payload_json: Option, - pub version: u32, - pub created_at_micros: i64, - pub started_at_micros: Option, - pub completed_at_micros: Option, - pub updated_at_micros: i64, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct AiTextChunkSnapshot { - pub chunk_id: String, - pub task_id: String, - pub stage_kind: AiTaskStageKind, - pub sequence: u32, - pub delta_text: String, - pub created_at_micros: i64, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct AiResultReferenceSnapshot { - pub result_ref_id: String, - pub task_id: String, - pub reference_kind: AiResultReferenceKind, - pub reference_id: String, - pub label: Option, - pub created_at_micros: i64, -} - -impl AiTaskKind { - pub fn default_stage_blueprints(self) -> Vec { - let ordered_kinds = match self { - Self::StoryGeneration => vec![ - AiTaskStageKind::PreparePrompt, - AiTaskStageKind::RequestModel, - AiTaskStageKind::RepairResponse, - AiTaskStageKind::NormalizeResult, - ], - Self::CharacterChat | Self::NpcChat | Self::QuestIntent | Self::RuntimeItemIntent => { - vec![ - AiTaskStageKind::PreparePrompt, - AiTaskStageKind::RequestModel, - AiTaskStageKind::NormalizeResult, - ] - } - Self::CustomWorldGeneration => vec![ - AiTaskStageKind::PreparePrompt, - AiTaskStageKind::RequestModel, - AiTaskStageKind::RepairResponse, - AiTaskStageKind::NormalizeResult, - AiTaskStageKind::PersistResult, - ], - }; - - ordered_kinds - .into_iter() - .enumerate() - .map(|(index, stage_kind)| AiTaskStageBlueprint { - stage_kind, - label: stage_kind.default_label().to_string(), - detail: stage_kind.default_detail().to_string(), - order: index as u32, - }) - .collect() - } -} - -impl AiTaskStageKind { - pub fn as_str(self) -> &'static str { - match self { - Self::PreparePrompt => "prepare_prompt", - Self::RequestModel => "request_model", - Self::RepairResponse => "repair_response", - Self::NormalizeResult => "normalize_result", - Self::PersistResult => "persist_result", - } - } - - pub fn default_label(self) -> &'static str { - match self { - Self::PreparePrompt => "整理提示词", - Self::RequestModel => "请求模型", - Self::RepairResponse => "修复响应", - Self::NormalizeResult => "归一结果", - Self::PersistResult => "回写结果", - } - } - - pub fn default_detail(self) -> &'static str { - match self { - Self::PreparePrompt => "整理输入上下文并构建本轮提示词。", - Self::RequestModel => "向上游模型发起正式推理请求。", - Self::RepairResponse => "对非严格输出做补救修复或二次编排。", - Self::NormalizeResult => "把模型输出归一成模块可消费结构。", - Self::PersistResult => "把结果引用或聚合状态回写到下游模块。", - } - } -} - -impl AiTaskStatus { - pub fn is_terminal(self) -> bool { - matches!(self, Self::Completed | Self::Failed | Self::Cancelled) - } -} - -pub fn generate_ai_task_id(seed_micros: i64) -> String { - build_prefixed_seed_id(AI_TASK_ID_PREFIX, seed_micros) -} - -pub fn generate_ai_task_stage_id(task_id: &str, stage_kind: AiTaskStageKind) -> String { - format!( - "{}{}_{}", - AI_TASK_STAGE_ID_PREFIX, - task_id.trim(), - stage_kind.as_str() - ) -} - -pub fn generate_ai_result_ref_id(seed_micros: i64) -> String { - build_prefixed_seed_id(AI_RESULT_REF_ID_PREFIX, seed_micros) -} - -pub fn generate_ai_text_chunk_id(seed_micros: i64, sequence: u32) -> String { - format!("{}{seed_micros:x}_{sequence:x}", AI_TEXT_CHUNK_ID_PREFIX) -} - -pub fn normalize_optional_text(value: Option) -> Option { - normalize_shared_optional_string(value) -} - -pub fn normalize_string_list(values: Vec) -> Vec { - normalize_shared_string_list(values) -} diff --git a/server-rs/crates/module-ai/src/domain/ids.rs b/server-rs/crates/module-ai/src/domain/ids.rs new file mode 100644 index 00000000..b3ecc4ca --- /dev/null +++ b/server-rs/crates/module-ai/src/domain/ids.rs @@ -0,0 +1,41 @@ +use shared_kernel::{ + build_prefixed_seed_id, normalize_optional_string as normalize_shared_optional_string, + normalize_string_list as normalize_shared_string_list, +}; + +use super::types::AiTaskStageKind; + +pub const AI_TASK_ID_PREFIX: &str = "aitask_"; +pub const AI_TASK_STAGE_ID_PREFIX: &str = "aistage_"; +pub const AI_RESULT_REF_ID_PREFIX: &str = "aires_"; +pub const AI_TEXT_CHUNK_ID_PREFIX: &str = "aichunk_"; +pub const INITIAL_AI_TASK_VERSION: u32 = 1; + +pub fn generate_ai_task_id(seed_micros: i64) -> String { + build_prefixed_seed_id(AI_TASK_ID_PREFIX, seed_micros) +} + +pub fn generate_ai_task_stage_id(task_id: &str, stage_kind: AiTaskStageKind) -> String { + format!( + "{}{}_{}", + AI_TASK_STAGE_ID_PREFIX, + task_id.trim(), + stage_kind.as_str() + ) +} + +pub fn generate_ai_result_ref_id(seed_micros: i64) -> String { + build_prefixed_seed_id(AI_RESULT_REF_ID_PREFIX, seed_micros) +} + +pub fn generate_ai_text_chunk_id(seed_micros: i64, sequence: u32) -> String { + format!("{}{seed_micros:x}_{sequence:x}", AI_TEXT_CHUNK_ID_PREFIX) +} + +pub fn normalize_optional_text(value: Option) -> Option { + normalize_shared_optional_string(value) +} + +pub fn normalize_string_list(values: Vec) -> Vec { + normalize_shared_string_list(values) +} diff --git a/server-rs/crates/module-ai/src/domain/stages.rs b/server-rs/crates/module-ai/src/domain/stages.rs new file mode 100644 index 00000000..35812904 --- /dev/null +++ b/server-rs/crates/module-ai/src/domain/stages.rs @@ -0,0 +1,77 @@ +use super::types::{AiTaskKind, AiTaskStageBlueprint, AiTaskStageKind, AiTaskStatus}; + +impl AiTaskKind { + pub fn default_stage_blueprints(self) -> Vec { + let ordered_kinds = match self { + Self::StoryGeneration => vec![ + AiTaskStageKind::PreparePrompt, + AiTaskStageKind::RequestModel, + AiTaskStageKind::RepairResponse, + AiTaskStageKind::NormalizeResult, + ], + Self::CharacterChat | Self::NpcChat | Self::QuestIntent | Self::RuntimeItemIntent => { + vec![ + AiTaskStageKind::PreparePrompt, + AiTaskStageKind::RequestModel, + AiTaskStageKind::NormalizeResult, + ] + } + Self::CustomWorldGeneration => vec![ + AiTaskStageKind::PreparePrompt, + AiTaskStageKind::RequestModel, + AiTaskStageKind::RepairResponse, + AiTaskStageKind::NormalizeResult, + AiTaskStageKind::PersistResult, + ], + }; + + ordered_kinds + .into_iter() + .enumerate() + .map(|(index, stage_kind)| AiTaskStageBlueprint { + stage_kind, + label: stage_kind.default_label().to_string(), + detail: stage_kind.default_detail().to_string(), + order: index as u32, + }) + .collect() + } +} + +impl AiTaskStageKind { + pub fn as_str(self) -> &'static str { + match self { + Self::PreparePrompt => "prepare_prompt", + Self::RequestModel => "request_model", + Self::RepairResponse => "repair_response", + Self::NormalizeResult => "normalize_result", + Self::PersistResult => "persist_result", + } + } + + pub fn default_label(self) -> &'static str { + match self { + Self::PreparePrompt => "整理提示词", + Self::RequestModel => "请求模型", + Self::RepairResponse => "修复响应", + Self::NormalizeResult => "归一结果", + Self::PersistResult => "回写结果", + } + } + + pub fn default_detail(self) -> &'static str { + match self { + Self::PreparePrompt => "整理输入上下文并构建本轮提示词。", + Self::RequestModel => "向上游模型发起正式推理请求。", + Self::RepairResponse => "对非严格输出做补救修复或二次编排。", + Self::NormalizeResult => "把模型输出归一成模块可消费结构。", + Self::PersistResult => "把结果引用或聚合状态回写到下游模块。", + } + } +} + +impl AiTaskStatus { + pub fn is_terminal(self) -> bool { + matches!(self, Self::Completed | Self::Failed | Self::Cancelled) + } +} diff --git a/server-rs/crates/module-ai/src/domain/types.rs b/server-rs/crates/module-ai/src/domain/types.rs new file mode 100644 index 00000000..b2b8128a --- /dev/null +++ b/server-rs/crates/module-ai/src/domain/types.rs @@ -0,0 +1,124 @@ +use serde::{Deserialize, Serialize}; +#[cfg(feature = "spacetime-types")] +use spacetimedb::SpacetimeType; + +// AI 编排类型只表达领域意图,具体 prompt 策略留给业务模块和平台层。 +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub enum AiTaskKind { + StoryGeneration, + CharacterChat, + NpcChat, + CustomWorldGeneration, + QuestIntent, + RuntimeItemIntent, +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub enum AiTaskStatus { + Pending, + Running, + Completed, + Failed, + Cancelled, +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +pub enum AiTaskStageKind { + PreparePrompt, + RequestModel, + RepairResponse, + NormalizeResult, + PersistResult, +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub enum AiTaskStageStatus { + Pending, + Running, + Completed, + Skipped, +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub enum AiResultReferenceKind { + StorySession, + StoryEvent, + CustomWorldProfile, + QuestRecord, + RuntimeItemRecord, + AssetObject, +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct AiTaskStageBlueprint { + pub stage_kind: AiTaskStageKind, + pub label: String, + pub detail: String, + pub order: u32, +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct AiTaskStageSnapshot { + pub stage_kind: AiTaskStageKind, + pub label: String, + pub detail: String, + pub order: u32, + pub status: AiTaskStageStatus, + pub text_output: Option, + pub structured_payload_json: Option, + pub warning_messages: Vec, + pub started_at_micros: Option, + pub completed_at_micros: Option, +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct AiTaskSnapshot { + pub task_id: String, + pub task_kind: AiTaskKind, + pub owner_user_id: String, + pub request_label: String, + pub source_module: String, + pub source_entity_id: Option, + pub request_payload_json: Option, + pub status: AiTaskStatus, + pub failure_message: Option, + pub stages: Vec, + pub result_references: Vec, + pub latest_text_output: Option, + pub latest_structured_payload_json: Option, + pub version: u32, + pub created_at_micros: i64, + pub started_at_micros: Option, + pub completed_at_micros: Option, + pub updated_at_micros: i64, +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct AiTextChunkSnapshot { + pub chunk_id: String, + pub task_id: String, + pub stage_kind: AiTaskStageKind, + pub sequence: u32, + pub delta_text: String, + pub created_at_micros: i64, +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct AiResultReferenceSnapshot { + pub result_ref_id: String, + pub task_id: String, + pub reference_kind: AiResultReferenceKind, + pub reference_id: String, + pub label: Option, + pub created_at_micros: i64, +} diff --git a/server-rs/crates/module-ai/src/lib.rs b/server-rs/crates/module-ai/src/lib.rs index b69211ac..e69b5609 100644 --- a/server-rs/crates/module-ai/src/lib.rs +++ b/server-rs/crates/module-ai/src/lib.rs @@ -22,225 +22,4 @@ pub use errors::{AiTaskFieldError, AiTaskServiceError}; pub use events::AiTaskDomainEvent; #[cfg(test)] -mod tests { - use super::*; - - fn build_service() -> AiTaskService { - AiTaskService::new(InMemoryAiTaskStore::default()) - } - - fn build_create_input(task_kind: AiTaskKind) -> AiTaskCreateInput { - AiTaskCreateInput { - task_id: generate_ai_task_id(1_713_680_000_000_000), - task_kind, - owner_user_id: "user_001".to_string(), - request_label: "首轮故事生成".to_string(), - source_module: "story".to_string(), - source_entity_id: Some("storysess_001".to_string()), - request_payload_json: Some("{\"scene\":\"camp\"}".to_string()), - stages: task_kind.default_stage_blueprints(), - created_at_micros: 1_713_680_000_000_000, - } - } - - #[test] - fn default_stage_blueprints_match_story_baseline() { - let stages = AiTaskKind::StoryGeneration.default_stage_blueprints(); - - assert_eq!(stages.len(), 4); - assert_eq!(stages[0].stage_kind, AiTaskStageKind::PreparePrompt); - assert_eq!(stages[1].stage_kind, AiTaskStageKind::RequestModel); - assert_eq!(stages[2].stage_kind, AiTaskStageKind::RepairResponse); - assert_eq!(stages[3].stage_kind, AiTaskStageKind::NormalizeResult); - } - - #[test] - fn create_task_rejects_duplicate_stage_blueprints() { - let mut input = build_create_input(AiTaskKind::StoryGeneration); - input.stages.push(AiTaskStageBlueprint { - stage_kind: AiTaskStageKind::PreparePrompt, - label: "重复阶段".to_string(), - detail: "重复阶段".to_string(), - order: 99, - }); - - let error = validate_task_create_input(&input).expect_err("duplicate stages should fail"); - assert_eq!(error, AiTaskFieldError::DuplicateStageBlueprint); - } - - #[test] - fn generate_ai_task_stage_id_contains_task_and_stage_slug() { - let stage_id = generate_ai_task_stage_id("aitask_demo", AiTaskStageKind::NormalizeResult); - - assert_eq!(stage_id, "aistage_aitask_demo_normalize_result"); - } - - #[test] - fn create_and_start_task_updates_status() { - let service = build_service(); - let created = service - .create_task(build_create_input(AiTaskKind::QuestIntent)) - .expect("task should create"); - let started = service - .start_task(&created.task_id, created.created_at_micros + 1) - .expect("task should start"); - - assert_eq!(created.status, AiTaskStatus::Pending); - assert_eq!(started.status, AiTaskStatus::Running); - assert_eq!( - started.started_at_micros, - Some(created.created_at_micros + 1) - ); - assert_eq!(started.version, INITIAL_AI_TASK_VERSION + 1); - } - - #[test] - fn append_text_chunk_aggregates_stream_output_by_stage() { - let service = build_service(); - let task = service - .create_task(build_create_input(AiTaskKind::CharacterChat)) - .expect("task should create"); - service - .start_stage( - &task.task_id, - AiTaskStageKind::RequestModel, - task.created_at_micros + 10, - ) - .expect("stage should start"); - - let (after_first, _) = service - .append_text_chunk( - &task.task_id, - AiTaskStageKind::RequestModel, - 1, - "你".to_string(), - task.created_at_micros + 20, - ) - .expect("first chunk should append"); - let (after_second, second_chunk) = service - .append_text_chunk( - &task.task_id, - AiTaskStageKind::RequestModel, - 2, - "好。".to_string(), - task.created_at_micros + 30, - ) - .expect("second chunk should append"); - - assert_eq!(after_first.latest_text_output.as_deref(), Some("你")); - assert_eq!(after_second.latest_text_output.as_deref(), Some("你好。")); - assert_eq!(second_chunk.sequence, 2); - } - - #[test] - fn complete_stage_updates_latest_outputs() { - let service = build_service(); - let task = service - .create_task(build_create_input(AiTaskKind::StoryGeneration)) - .expect("task should create"); - - let completed = service - .complete_stage(AiStageCompletionInput { - task_id: task.task_id.clone(), - stage_kind: AiTaskStageKind::NormalizeResult, - text_output: Some("营地前的篝火重新亮了起来。".to_string()), - structured_payload_json: Some("{\"choices\":3}".to_string()), - warning_messages: vec!["使用了 fallback 选项池".to_string()], - completed_at_micros: task.created_at_micros + 50, - }) - .expect("stage should complete"); - - let stage = completed - .stages - .iter() - .find(|stage| stage.stage_kind == AiTaskStageKind::NormalizeResult) - .expect("normalize stage should exist"); - assert_eq!(stage.status, AiTaskStageStatus::Completed); - assert_eq!( - completed.latest_text_output.as_deref(), - Some("营地前的篝火重新亮了起来。") - ); - assert_eq!( - completed.latest_structured_payload_json.as_deref(), - Some("{\"choices\":3}") - ); - assert_eq!(stage.warning_messages, vec!["使用了 fallback 选项池"]); - } - - #[test] - fn attach_result_reference_appends_binding() { - let service = build_service(); - let task = service - .create_task(build_create_input(AiTaskKind::CustomWorldGeneration)) - .expect("task should create"); - - let updated = service - .attach_result_reference( - &task.task_id, - AiResultReferenceKind::CustomWorldProfile, - "profile_001".to_string(), - Some("主世界档案".to_string()), - task.created_at_micros + 10, - ) - .expect("reference should attach"); - - assert_eq!(updated.result_references.len(), 1); - assert_eq!( - updated.result_references[0].reference_kind, - AiResultReferenceKind::CustomWorldProfile - ); - assert_eq!(updated.result_references[0].reference_id, "profile_001"); - } - - #[test] - fn fail_and_cancel_task_move_into_terminal_states() { - let service = build_service(); - let first = service - .create_task(build_create_input(AiTaskKind::NpcChat)) - .expect("task should create"); - let failed = service - .fail_task( - &first.task_id, - "上游模型超时".to_string(), - first.created_at_micros + 10, - ) - .expect("task should fail"); - - assert_eq!(failed.status, AiTaskStatus::Failed); - assert_eq!(failed.failure_message.as_deref(), Some("上游模型超时")); - - let second = service - .create_task(AiTaskCreateInput { - task_id: generate_ai_task_id(1_713_680_000_000_999), - ..build_create_input(AiTaskKind::RuntimeItemIntent) - }) - .expect("second task should create"); - let cancelled = service - .cancel_task(&second.task_id, second.created_at_micros + 20) - .expect("task should cancel"); - - assert_eq!(cancelled.status, AiTaskStatus::Cancelled); - assert_eq!( - cancelled.completed_at_micros, - Some(second.created_at_micros + 20) - ); - } - - #[test] - fn complete_task_marks_terminal_success() { - let service = build_service(); - let task = service - .create_task(build_create_input(AiTaskKind::QuestIntent)) - .expect("task should create"); - - let completed = service - .complete_task(&task.task_id, task.created_at_micros + 100) - .expect("task should complete"); - - assert_eq!(completed.status, AiTaskStatus::Completed); - assert_eq!( - completed.completed_at_micros, - Some(task.created_at_micros + 100) - ); - } -} +mod tests; diff --git a/server-rs/crates/module-ai/src/tests.rs b/server-rs/crates/module-ai/src/tests.rs new file mode 100644 index 00000000..e6d2e5dc --- /dev/null +++ b/server-rs/crates/module-ai/src/tests.rs @@ -0,0 +1,220 @@ +use super::*; + +fn build_service() -> AiTaskService { + AiTaskService::new(InMemoryAiTaskStore::default()) +} + +fn build_create_input(task_kind: AiTaskKind) -> AiTaskCreateInput { + AiTaskCreateInput { + task_id: generate_ai_task_id(1_713_680_000_000_000), + task_kind, + owner_user_id: "user_001".to_string(), + request_label: "首轮故事生成".to_string(), + source_module: "story".to_string(), + source_entity_id: Some("storysess_001".to_string()), + request_payload_json: Some("{\"scene\":\"camp\"}".to_string()), + stages: task_kind.default_stage_blueprints(), + created_at_micros: 1_713_680_000_000_000, + } +} + +#[test] +fn default_stage_blueprints_match_story_baseline() { + let stages = AiTaskKind::StoryGeneration.default_stage_blueprints(); + + assert_eq!(stages.len(), 4); + assert_eq!(stages[0].stage_kind, AiTaskStageKind::PreparePrompt); + assert_eq!(stages[1].stage_kind, AiTaskStageKind::RequestModel); + assert_eq!(stages[2].stage_kind, AiTaskStageKind::RepairResponse); + assert_eq!(stages[3].stage_kind, AiTaskStageKind::NormalizeResult); +} + +#[test] +fn create_task_rejects_duplicate_stage_blueprints() { + let mut input = build_create_input(AiTaskKind::StoryGeneration); + input.stages.push(AiTaskStageBlueprint { + stage_kind: AiTaskStageKind::PreparePrompt, + label: "重复阶段".to_string(), + detail: "重复阶段".to_string(), + order: 99, + }); + + let error = validate_task_create_input(&input).expect_err("duplicate stages should fail"); + assert_eq!(error, AiTaskFieldError::DuplicateStageBlueprint); +} + +#[test] +fn generate_ai_task_stage_id_contains_task_and_stage_slug() { + let stage_id = generate_ai_task_stage_id("aitask_demo", AiTaskStageKind::NormalizeResult); + + assert_eq!(stage_id, "aistage_aitask_demo_normalize_result"); +} + +#[test] +fn create_and_start_task_updates_status() { + let service = build_service(); + let created = service + .create_task(build_create_input(AiTaskKind::QuestIntent)) + .expect("task should create"); + let started = service + .start_task(&created.task_id, created.created_at_micros + 1) + .expect("task should start"); + + assert_eq!(created.status, AiTaskStatus::Pending); + assert_eq!(started.status, AiTaskStatus::Running); + assert_eq!( + started.started_at_micros, + Some(created.created_at_micros + 1) + ); + assert_eq!(started.version, INITIAL_AI_TASK_VERSION + 1); +} + +#[test] +fn append_text_chunk_aggregates_stream_output_by_stage() { + let service = build_service(); + let task = service + .create_task(build_create_input(AiTaskKind::CharacterChat)) + .expect("task should create"); + service + .start_stage( + &task.task_id, + AiTaskStageKind::RequestModel, + task.created_at_micros + 10, + ) + .expect("stage should start"); + + let (after_first, _) = service + .append_text_chunk( + &task.task_id, + AiTaskStageKind::RequestModel, + 1, + "你".to_string(), + task.created_at_micros + 20, + ) + .expect("first chunk should append"); + let (after_second, second_chunk) = service + .append_text_chunk( + &task.task_id, + AiTaskStageKind::RequestModel, + 2, + "好。".to_string(), + task.created_at_micros + 30, + ) + .expect("second chunk should append"); + + assert_eq!(after_first.latest_text_output.as_deref(), Some("你")); + assert_eq!(after_second.latest_text_output.as_deref(), Some("你好。")); + assert_eq!(second_chunk.sequence, 2); +} + +#[test] +fn complete_stage_updates_latest_outputs() { + let service = build_service(); + let task = service + .create_task(build_create_input(AiTaskKind::StoryGeneration)) + .expect("task should create"); + + let completed = service + .complete_stage(AiStageCompletionInput { + task_id: task.task_id.clone(), + stage_kind: AiTaskStageKind::NormalizeResult, + text_output: Some("营地前的篝火重新亮了起来。".to_string()), + structured_payload_json: Some("{\"choices\":3}".to_string()), + warning_messages: vec!["使用了 fallback 选项池".to_string()], + completed_at_micros: task.created_at_micros + 50, + }) + .expect("stage should complete"); + + let stage = completed + .stages + .iter() + .find(|stage| stage.stage_kind == AiTaskStageKind::NormalizeResult) + .expect("normalize stage should exist"); + assert_eq!(stage.status, AiTaskStageStatus::Completed); + assert_eq!( + completed.latest_text_output.as_deref(), + Some("营地前的篝火重新亮了起来。") + ); + assert_eq!( + completed.latest_structured_payload_json.as_deref(), + Some("{\"choices\":3}") + ); + assert_eq!(stage.warning_messages, vec!["使用了 fallback 选项池"]); +} + +#[test] +fn attach_result_reference_appends_binding() { + let service = build_service(); + let task = service + .create_task(build_create_input(AiTaskKind::CustomWorldGeneration)) + .expect("task should create"); + + let updated = service + .attach_result_reference( + &task.task_id, + AiResultReferenceKind::CustomWorldProfile, + "profile_001".to_string(), + Some("主世界档案".to_string()), + task.created_at_micros + 10, + ) + .expect("reference should attach"); + + assert_eq!(updated.result_references.len(), 1); + assert_eq!( + updated.result_references[0].reference_kind, + AiResultReferenceKind::CustomWorldProfile + ); + assert_eq!(updated.result_references[0].reference_id, "profile_001"); +} + +#[test] +fn fail_and_cancel_task_move_into_terminal_states() { + let service = build_service(); + let first = service + .create_task(build_create_input(AiTaskKind::NpcChat)) + .expect("task should create"); + let failed = service + .fail_task( + &first.task_id, + "上游模型超时".to_string(), + first.created_at_micros + 10, + ) + .expect("task should fail"); + + assert_eq!(failed.status, AiTaskStatus::Failed); + assert_eq!(failed.failure_message.as_deref(), Some("上游模型超时")); + + let second = service + .create_task(AiTaskCreateInput { + task_id: generate_ai_task_id(1_713_680_000_000_999), + ..build_create_input(AiTaskKind::RuntimeItemIntent) + }) + .expect("second task should create"); + let cancelled = service + .cancel_task(&second.task_id, second.created_at_micros + 20) + .expect("task should cancel"); + + assert_eq!(cancelled.status, AiTaskStatus::Cancelled); + assert_eq!( + cancelled.completed_at_micros, + Some(second.created_at_micros + 20) + ); +} + +#[test] +fn complete_task_marks_terminal_success() { + let service = build_service(); + let task = service + .create_task(build_create_input(AiTaskKind::QuestIntent)) + .expect("task should create"); + + let completed = service + .complete_task(&task.task_id, task.created_at_micros + 100) + .expect("task should complete"); + + assert_eq!(completed.status, AiTaskStatus::Completed); + assert_eq!( + completed.completed_at_micros, + Some(task.created_at_micros + 100) + ); +} diff --git a/server-rs/crates/module-assets/README.md b/server-rs/crates/module-assets/README.md index b930404f..c5fd4330 100644 --- a/server-rs/crates/module-assets/README.md +++ b/server-rs/crates/module-assets/README.md @@ -25,12 +25,14 @@ - `assetobj_` ID 前缀与初始版本常量 - `asset_entity_binding` 输入、快照、返回记录与字段校验 helper - `assetbind_` ID 前缀 +5. `WP-AS Assets` 资产对象类型归位已完成,领域快照、命令 DTO、应用返回 DTO 和字段错误已分别落到 DDD 骨架文件中。 当前 `asset_object` 表的字段、索引与可编码约束见: 1. [../../../docs/technical/SPACETIMEDB_ASSET_OBJECT_TABLE_DESIGN_2026-04-21.md](../../../docs/technical/SPACETIMEDB_ASSET_OBJECT_TABLE_DESIGN_2026-04-21.md) 2. [../../../docs/technical/ASSET_OBJECT_CONFIRM_FLOW_DESIGN_2026-04-21.md](../../../docs/technical/ASSET_OBJECT_CONFIRM_FLOW_DESIGN_2026-04-21.md) 3. [../../../docs/technical/ASSET_ENTITY_BINDING_REDUCER_DESIGN_2026-04-21.md](../../../docs/technical/ASSET_ENTITY_BINDING_REDUCER_DESIGN_2026-04-21.md) +4. [../../../docs/technical/SERVER_RS_DDD_WP_AS_ASSET_OBJECT_TYPE_REHOME_2026-04-29.md](../../../docs/technical/SERVER_RS_DDD_WP_AS_ASSET_OBJECT_TYPE_REHOME_2026-04-29.md) 当前还已补齐: diff --git a/server-rs/crates/module-assets/src/application.rs b/server-rs/crates/module-assets/src/application.rs index 84782f3a..bad6ddbe 100644 --- a/server-rs/crates/module-assets/src/application.rs +++ b/server-rs/crates/module-assets/src/application.rs @@ -1,8 +1,45 @@ -//! 资产应用编排落位。 +//! 资产应用编排返回类型。 //! //! 这里只组合纯校验与应用结果;对象探测、签名和持久化由 adapter 层完成。 -pub use crate::asset_object_core::{ - AssetEntityBindingProcedureResult, AssetHistoryListResult, AssetObjectProcedureResult, - ConfirmAssetObjectResult, build_asset_entity_binding_input, build_asset_object_upsert_input, +use serde::{Deserialize, Serialize}; +#[cfg(feature = "spacetime-types")] +use spacetimedb::SpacetimeType; + +use crate::domain::{ + AssetEntityBindingSnapshot, AssetHistoryEntrySnapshot, AssetObjectRecord, + AssetObjectUpsertSnapshot, +}; + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct AssetObjectProcedureResult { + pub ok: bool, + pub record: Option, + pub error_message: Option, +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct AssetHistoryListResult { + pub ok: bool, + pub entries: Vec, + pub error_message: Option, +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct AssetEntityBindingProcedureResult { + pub ok: bool, + pub record: Option, + pub error_message: Option, +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct ConfirmAssetObjectResult { + pub record: AssetObjectRecord, +} + +pub use crate::asset_object_core::{ + build_asset_entity_binding_input, build_asset_object_upsert_input, }; diff --git a/server-rs/crates/module-assets/src/asset_object_core.rs b/server-rs/crates/module-assets/src/asset_object_core.rs index a825c901..56f66f70 100644 --- a/server-rs/crates/module-assets/src/asset_object_core.rs +++ b/server-rs/crates/module-assets/src/asset_object_core.rs @@ -1,223 +1,18 @@ -use std::{error::Error, fmt}; - -use serde::{Deserialize, Serialize}; use shared_kernel::{ build_prefixed_seed_id, format_timestamp_micros, normalize_optional_string, normalize_required_string, }; -#[cfg(feature = "spacetime-types")] -use spacetimedb::SpacetimeType; -pub const ASSET_OBJECT_ID_PREFIX: &str = "assetobj_"; -pub const ASSET_BINDING_ID_PREFIX: &str = "assetbind_"; -pub const INITIAL_ASSET_OBJECT_VERSION: u32 = 1; - -// 资产对象访问策略先冻结为枚举,避免后续在 reducer、HTTP DTO 和脚本里散落字符串字面量。 -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub enum AssetObjectAccessPolicy { - Private, - PublicRead, -} - -#[derive(Clone, Debug, PartialEq, Eq)] -pub enum AssetObjectFieldError { - MissingBucket, - MissingObjectKey, - MissingAssetKind, - MissingAssetObjectId, - MissingBindingId, - MissingEntityKind, - MissingEntityId, - MissingSlot, - InvalidVersion, -} - -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct ConfirmAssetObjectInput { - pub bucket: Option, - pub object_key: String, - pub content_type: Option, - pub content_length: Option, - pub content_hash: Option, - pub asset_kind: String, - pub access_policy: Option, - pub source_job_id: Option, - pub owner_user_id: Option, - pub profile_id: Option, - pub entity_id: Option, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct AssetObjectProcedureResult { - pub ok: bool, - pub record: Option, - pub error_message: Option, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct AssetHistoryListInput { - pub asset_kind: String, - pub limit: u32, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct AssetHistoryEntrySnapshot { - pub asset_object_id: String, - pub asset_kind: String, - pub image_src: String, - pub owner_user_id: Option, - pub profile_id: Option, - pub entity_id: Option, - pub created_at_micros: i64, - pub updated_at_micros: i64, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct AssetHistoryListResult { - pub ok: bool, - pub entries: Vec, - pub error_message: Option, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct AssetEntityBindingProcedureResult { - pub ok: bool, - pub record: Option, - pub error_message: Option, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct AssetObjectUpsertInput { - pub asset_object_id: String, - pub bucket: String, - pub object_key: String, - pub access_policy: AssetObjectAccessPolicy, - pub content_type: Option, - pub content_length: u64, - pub content_hash: Option, - pub version: u32, - pub source_job_id: Option, - pub owner_user_id: Option, - pub profile_id: Option, - pub entity_id: Option, - pub asset_kind: String, - pub updated_at_micros: i64, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct AssetObjectUpsertSnapshot { - pub asset_object_id: String, - pub bucket: String, - pub object_key: String, - pub access_policy: AssetObjectAccessPolicy, - pub content_type: Option, - pub content_length: u64, - pub content_hash: Option, - pub version: u32, - pub source_job_id: Option, - pub owner_user_id: Option, - pub profile_id: Option, - pub entity_id: Option, - pub asset_kind: String, - pub created_at_micros: i64, - pub updated_at_micros: i64, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct AssetEntityBindingInput { - pub binding_id: String, - pub asset_object_id: String, - pub entity_kind: String, - pub entity_id: String, - pub slot: String, - pub asset_kind: String, - pub owner_user_id: Option, - pub profile_id: Option, - pub updated_at_micros: i64, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct AssetEntityBindingSnapshot { - pub binding_id: String, - pub asset_object_id: String, - pub entity_kind: String, - pub entity_id: String, - pub slot: String, - pub asset_kind: String, - pub owner_user_id: Option, - pub profile_id: Option, - pub created_at_micros: i64, - pub updated_at_micros: i64, -} - -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct AssetObjectRecord { - pub asset_object_id: String, - pub bucket: String, - pub object_key: String, - pub access_policy: AssetObjectAccessPolicy, - pub content_type: Option, - pub content_length: u64, - pub content_hash: Option, - pub version: u32, - pub source_job_id: Option, - pub owner_user_id: Option, - pub profile_id: Option, - pub entity_id: Option, - pub asset_kind: String, - pub created_at: String, - pub updated_at: String, -} - -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct AssetHistoryEntryRecord { - pub asset_object_id: String, - pub asset_kind: String, - pub image_src: String, - pub owner_user_id: Option, - pub profile_id: Option, - pub entity_id: Option, - pub created_at: String, - pub updated_at: String, -} - -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct ConfirmAssetObjectResult { - pub record: AssetObjectRecord, -} - -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct AssetEntityBindingRecord { - pub binding_id: String, - pub asset_object_id: String, - pub entity_kind: String, - pub entity_id: String, - pub slot: String, - pub asset_kind: String, - pub owner_user_id: Option, - pub profile_id: Option, - pub created_at: String, - pub updated_at: String, -} - -impl AssetObjectAccessPolicy { - pub fn as_str(&self) -> &'static str { - match self { - Self::Private => "private", - Self::PublicRead => "public_read", - } - } -} +use crate::{ + commands::{AssetEntityBindingInput, AssetObjectUpsertInput}, + domain::{ + ASSET_BINDING_ID_PREFIX, ASSET_OBJECT_ID_PREFIX, AssetEntityBindingRecord, + AssetEntityBindingSnapshot, AssetHistoryEntryRecord, AssetHistoryEntrySnapshot, + AssetObjectAccessPolicy, AssetObjectRecord, AssetObjectUpsertSnapshot, + INITIAL_ASSET_OBJECT_VERSION, + }, + errors::AssetObjectFieldError, +}; // 资产核心对象字段需要继续保留模块自己的错误语义,但基础必填字符串归一化统一走 shared-kernel。 fn normalize_required_asset_field( @@ -420,24 +215,6 @@ pub fn normalize_optional_value(value: Option) -> Option { normalize_optional_string(value) } -impl fmt::Display for AssetObjectFieldError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::MissingBucket => f.write_str("asset_object.bucket 不能为空"), - Self::MissingObjectKey => f.write_str("asset_object.object_key 不能为空"), - Self::MissingAssetKind => f.write_str("asset_object.asset_kind 不能为空"), - Self::MissingAssetObjectId => f.write_str("asset_object.asset_object_id 不能为空"), - Self::MissingBindingId => f.write_str("asset_entity_binding.binding_id 不能为空"), - Self::MissingEntityKind => f.write_str("asset_entity_binding.entity_kind 不能为空"), - Self::MissingEntityId => f.write_str("asset_entity_binding.entity_id 不能为空"), - Self::MissingSlot => f.write_str("asset_entity_binding.slot 不能为空"), - Self::InvalidVersion => f.write_str("asset_object.version 必须大于 0"), - } - } -} - -impl Error for AssetObjectFieldError {} - #[cfg(test)] mod tests { use super::*; diff --git a/server-rs/crates/module-assets/src/commands.rs b/server-rs/crates/module-assets/src/commands.rs index e9ece91a..0c3443f3 100644 --- a/server-rs/crates/module-assets/src/commands.rs +++ b/server-rs/crates/module-assets/src/commands.rs @@ -1,7 +1,105 @@ -//! 资产写入命令落位。 +//! 资产写入命令。 //! //! 用于表达确认资产对象、绑定实体槽位和查询资产历史的输入,不直接访问 OSS。 -pub use crate::asset_object_core::{ - AssetEntityBindingInput, AssetHistoryListInput, AssetObjectUpsertInput, ConfirmAssetObjectInput, +use serde::{Deserialize, Serialize}; +#[cfg(feature = "spacetime-types")] +use spacetimedb::SpacetimeType; + +use crate::domain::{ + AssetEntityBindingSnapshot, AssetObjectAccessPolicy, AssetObjectUpsertSnapshot, }; + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct ConfirmAssetObjectInput { + pub bucket: Option, + pub object_key: String, + pub content_type: Option, + pub content_length: Option, + pub content_hash: Option, + pub asset_kind: String, + pub access_policy: Option, + pub source_job_id: Option, + pub owner_user_id: Option, + pub profile_id: Option, + pub entity_id: Option, +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct AssetHistoryListInput { + pub asset_kind: String, + pub limit: u32, +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct AssetObjectUpsertInput { + pub asset_object_id: String, + pub bucket: String, + pub object_key: String, + pub access_policy: AssetObjectAccessPolicy, + pub content_type: Option, + pub content_length: u64, + pub content_hash: Option, + pub version: u32, + pub source_job_id: Option, + pub owner_user_id: Option, + pub profile_id: Option, + pub entity_id: Option, + pub asset_kind: String, + pub updated_at_micros: i64, +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct AssetEntityBindingInput { + pub binding_id: String, + pub asset_object_id: String, + pub entity_kind: String, + pub entity_id: String, + pub slot: String, + pub asset_kind: String, + pub owner_user_id: Option, + pub profile_id: Option, + pub updated_at_micros: i64, +} + +impl From for AssetObjectUpsertSnapshot { + fn from(value: AssetObjectUpsertInput) -> Self { + Self { + asset_object_id: value.asset_object_id, + bucket: value.bucket, + object_key: value.object_key, + access_policy: value.access_policy, + content_type: value.content_type, + content_length: value.content_length, + content_hash: value.content_hash, + version: value.version, + source_job_id: value.source_job_id, + owner_user_id: value.owner_user_id, + profile_id: value.profile_id, + entity_id: value.entity_id, + asset_kind: value.asset_kind, + created_at_micros: value.updated_at_micros, + updated_at_micros: value.updated_at_micros, + } + } +} + +impl From for AssetEntityBindingSnapshot { + fn from(value: AssetEntityBindingInput) -> Self { + Self { + binding_id: value.binding_id, + asset_object_id: value.asset_object_id, + entity_kind: value.entity_kind, + entity_id: value.entity_id, + slot: value.slot, + asset_kind: value.asset_kind, + owner_user_id: value.owner_user_id, + profile_id: value.profile_id, + created_at_micros: value.updated_at_micros, + updated_at_micros: value.updated_at_micros, + } + } +} diff --git a/server-rs/crates/module-assets/src/domain.rs b/server-rs/crates/module-assets/src/domain.rs index 86ca5df6..fba91cc0 100644 --- a/server-rs/crates/module-assets/src/domain.rs +++ b/server-rs/crates/module-assets/src/domain.rs @@ -1,15 +1,128 @@ -//! 资产领域模型落位。 +//! 资产领域模型。 //! -//! 当前先通过本文件承接对外领域 API 分层导出,旧实现仍留在 -//! `asset_object_core.rs` 内部文件中,后续再逐段搬入本文件或 `domain/` 子目录。 -//! 本层只允许保留资产对象、实体绑定、访问策略、版本和业务归属等纯规则。 +//! 本层只保留资产对象、实体绑定、访问策略、版本和业务归属等纯领域事实。 +//! OSS 对象探测、HTTP DTO 映射和 SpacetimeDB row 写入都属于外层 adapter。 -pub use crate::asset_object_core::{ - ASSET_BINDING_ID_PREFIX, ASSET_OBJECT_ID_PREFIX, AssetEntityBindingRecord, - AssetEntityBindingSnapshot, AssetHistoryEntryRecord, AssetHistoryEntrySnapshot, - AssetObjectAccessPolicy, AssetObjectRecord, AssetObjectUpsertSnapshot, - INITIAL_ASSET_OBJECT_VERSION, build_asset_entity_binding_record, - build_asset_history_entry_record, build_asset_object_record, generate_asset_binding_id, - generate_asset_object_id, normalize_optional_value, validate_asset_entity_binding_fields, - validate_asset_object_fields, -}; +use serde::{Deserialize, Serialize}; +#[cfg(feature = "spacetime-types")] +use spacetimedb::SpacetimeType; + +pub const ASSET_OBJECT_ID_PREFIX: &str = "assetobj_"; +pub const ASSET_BINDING_ID_PREFIX: &str = "assetbind_"; +pub const INITIAL_ASSET_OBJECT_VERSION: u32 = 1; + +// 资产对象访问策略先冻结为枚举,避免 reducer、HTTP DTO 和脚本里散落字符串字面量。 +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub enum AssetObjectAccessPolicy { + Private, + PublicRead, +} + +impl AssetObjectAccessPolicy { + pub fn as_str(&self) -> &'static str { + match self { + Self::Private => "private", + Self::PublicRead => "public_read", + } + } +} + +/// SpacetimeDB 写入前的资产对象快照。 +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct AssetObjectUpsertSnapshot { + pub asset_object_id: String, + pub bucket: String, + pub object_key: String, + pub access_policy: AssetObjectAccessPolicy, + pub content_type: Option, + pub content_length: u64, + pub content_hash: Option, + pub version: u32, + pub source_job_id: Option, + pub owner_user_id: Option, + pub profile_id: Option, + pub entity_id: Option, + pub asset_kind: String, + pub created_at_micros: i64, + pub updated_at_micros: i64, +} + +/// 资产历史列表的领域快照。 +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct AssetHistoryEntrySnapshot { + pub asset_object_id: String, + pub asset_kind: String, + pub image_src: String, + pub owner_user_id: Option, + pub profile_id: Option, + pub entity_id: Option, + pub created_at_micros: i64, + pub updated_at_micros: i64, +} + +/// 业务实体与资产对象的绑定快照。 +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct AssetEntityBindingSnapshot { + pub binding_id: String, + pub asset_object_id: String, + pub entity_kind: String, + pub entity_id: String, + pub slot: String, + pub asset_kind: String, + pub owner_user_id: Option, + pub profile_id: Option, + pub created_at_micros: i64, + pub updated_at_micros: i64, +} + +/// 面向 API 与前端展示的资产对象记录。 +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct AssetObjectRecord { + pub asset_object_id: String, + pub bucket: String, + pub object_key: String, + pub access_policy: AssetObjectAccessPolicy, + pub content_type: Option, + pub content_length: u64, + pub content_hash: Option, + pub version: u32, + pub source_job_id: Option, + pub owner_user_id: Option, + pub profile_id: Option, + pub entity_id: Option, + pub asset_kind: String, + pub created_at: String, + pub updated_at: String, +} + +/// 面向 API 与前端展示的资产历史记录。 +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct AssetHistoryEntryRecord { + pub asset_object_id: String, + pub asset_kind: String, + pub image_src: String, + pub owner_user_id: Option, + pub profile_id: Option, + pub entity_id: Option, + pub created_at: String, + pub updated_at: String, +} + +/// 面向 API 与前端展示的实体绑定记录。 +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct AssetEntityBindingRecord { + pub binding_id: String, + pub asset_object_id: String, + pub entity_kind: String, + pub entity_id: String, + pub slot: String, + pub asset_kind: String, + pub owner_user_id: Option, + pub profile_id: Option, + pub created_at: String, + pub updated_at: String, +} diff --git a/server-rs/crates/module-assets/src/errors.rs b/server-rs/crates/module-assets/src/errors.rs index a5a61bf5..8a845561 100644 --- a/server-rs/crates/module-assets/src/errors.rs +++ b/server-rs/crates/module-assets/src/errors.rs @@ -1,5 +1,36 @@ -//! 资产领域错误落位。 +//! 资产领域错误。 //! //! 字段错误和业务错误在这里收口,HTTP 状态码与 SpacetimeDB 字符串错误由 adapter 映射。 -pub use crate::asset_object_core::AssetObjectFieldError; +use std::{error::Error, fmt}; + +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum AssetObjectFieldError { + MissingBucket, + MissingObjectKey, + MissingAssetKind, + MissingAssetObjectId, + MissingBindingId, + MissingEntityKind, + MissingEntityId, + MissingSlot, + InvalidVersion, +} + +impl fmt::Display for AssetObjectFieldError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::MissingBucket => f.write_str("asset_object.bucket 不能为空"), + Self::MissingObjectKey => f.write_str("asset_object.object_key 不能为空"), + Self::MissingAssetKind => f.write_str("asset_object.asset_kind 不能为空"), + Self::MissingAssetObjectId => f.write_str("asset_object.asset_object_id 不能为空"), + Self::MissingBindingId => f.write_str("asset_entity_binding.binding_id 不能为空"), + Self::MissingEntityKind => f.write_str("asset_entity_binding.entity_kind 不能为空"), + Self::MissingEntityId => f.write_str("asset_entity_binding.entity_id 不能为空"), + Self::MissingSlot => f.write_str("asset_entity_binding.slot 不能为空"), + Self::InvalidVersion => f.write_str("asset_object.version 必须大于 0"), + } + } +} + +impl Error for AssetObjectFieldError {} diff --git a/server-rs/crates/module-assets/src/lib.rs b/server-rs/crates/module-assets/src/lib.rs index 384fd60a..18e43dfe 100644 --- a/server-rs/crates/module-assets/src/lib.rs +++ b/server-rs/crates/module-assets/src/lib.rs @@ -23,9 +23,12 @@ pub use domain::{ ASSET_BINDING_ID_PREFIX, ASSET_OBJECT_ID_PREFIX, AssetEntityBindingRecord, AssetEntityBindingSnapshot, AssetHistoryEntryRecord, AssetHistoryEntrySnapshot, AssetObjectAccessPolicy, AssetObjectRecord, AssetObjectUpsertSnapshot, - INITIAL_ASSET_OBJECT_VERSION, build_asset_entity_binding_record, - build_asset_history_entry_record, build_asset_object_record, generate_asset_binding_id, - generate_asset_object_id, normalize_optional_value, validate_asset_entity_binding_fields, - validate_asset_object_fields, + INITIAL_ASSET_OBJECT_VERSION, }; pub use errors::AssetObjectFieldError; + +pub use asset_object_core::{ + build_asset_entity_binding_record, build_asset_history_entry_record, build_asset_object_record, + generate_asset_binding_id, generate_asset_object_id, normalize_optional_value, + validate_asset_entity_binding_fields, validate_asset_object_fields, +}; diff --git a/server-rs/crates/module-auth/README.md b/server-rs/crates/module-auth/README.md index ce88299b..3040a98a 100644 --- a/server-rs/crates/module-auth/README.md +++ b/server-rs/crates/module-auth/README.md @@ -18,15 +18,18 @@ 1. JWT claims 设计与 `platform-auth` 落地。 2. refresh cookie 读取适配。 3. `module-auth` 真实 crate 与首版密码登录用例落地。 -4. 微信登录链路暂缓执行,不进入当前连续实现顺序。 +4. `WP-A Auth` DDD 分层收口,账号、会话、验证码、微信 state/绑定规则、命令输入、应用返回、领域错误和领域事件已归位到 `domain / commands / application / errors / events`。 +5. `api-server / platform-auth / spacetime-module` 认证边界已核查:真实短信、微信 OAuth、JWT、cookie 和密码哈希仍由平台层或 BFF 装配承接,SpacetimeDB 侧只保留快照与表适配。 -当前连续实现优先顺序固定为: +当前已覆盖的鉴权用例: 1. 密码登录 2. refresh token 轮换 3. `me` 查询 4. 会话吊销 5. 手机验证码登录 +6. 微信登录 state 创建/消费 +7. 微信身份解析与手机号绑定 ## 3. 当前已冻结文档 @@ -44,6 +47,7 @@ 12. [../../../docs/technical/AUTH_REFRESH_ROTATION_DESIGN_2026-04-21.md](../../../docs/technical/AUTH_REFRESH_ROTATION_DESIGN_2026-04-21.md) 13. [../../../docs/technical/AUTH_LOGOUT_CURRENT_SESSION_DESIGN_2026-04-21.md](../../../docs/technical/AUTH_LOGOUT_CURRENT_SESSION_DESIGN_2026-04-21.md) 14. [../../../docs/technical/PHONE_AUTH_AXUM_REAL_SMS_PROVIDER_DESIGN_2026-04-22.md](../../../docs/technical/PHONE_AUTH_AXUM_REAL_SMS_PROVIDER_DESIGN_2026-04-22.md) +15. [../../../docs/technical/SERVER_RS_DDD_WP_A_AUTH_DOMAIN_VALUE_OBJECT_REFACTOR_2026-04-29.md](../../../docs/technical/SERVER_RS_DDD_WP_A_AUTH_DOMAIN_VALUE_OBJECT_REFACTOR_2026-04-29.md) ## 4. 边界约束 @@ -56,3 +60,4 @@ 7. 当前 `module-auth` 已承接进程内 refresh session 创建与轮换能力,供 `/api/auth/refresh` 复用。 8. 当前 `module-auth` 已承接当前 refresh session 吊销与用户 `token_version` 递增能力,供 `/api/auth/logout` 复用。 9. 当前手机号验证码真实 provider 由 `platform-auth` 注入,`module-auth` 只保留冷却、TTL、失败次数和账号编排,不保存验证码明文。 +10. 当前 `lib.rs` 仍保留进程内仓储和文件持久化支撑,但不再继续拥有命令、结果、错误、事件和纯领域值对象定义。 diff --git a/server-rs/crates/module-auth/src/application.rs b/server-rs/crates/module-auth/src/application.rs index 0ee4d360..31e618c6 100644 --- a/server-rs/crates/module-auth/src/application.rs +++ b/server-rs/crates/module-auth/src/application.rs @@ -1,3 +1,112 @@ -//! 认证应用编排过渡落位。 +//! 认证应用返回类型。 //! //! 这里只返回纯应用结果与领域事件;短信 provider、JWT 签发和持久化由外层 adapter 完成。 + +use serde::{Deserialize, Serialize}; + +use crate::domain::{ + AuthStoreSnapshotRecord, AuthUser, RefreshSessionRecord, WechatAuthStateRecord, +}; + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct AuthMeResult { + pub user: AuthUser, +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct PublicUserSearchResult { + pub user: AuthUser, +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct PasswordEntryResult { + pub user: AuthUser, + pub created: bool, +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct ChangePasswordResult { + pub user: AuthUser, +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct ResetPasswordResult { + pub user: AuthUser, + pub provider: String, + pub provider_out_id: Option, + pub phone_number_masked: String, +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct SendPhoneCodeResult { + pub cooldown_seconds: u64, + pub expires_in_seconds: u64, + pub provider_request_id: Option, + pub provider_out_id: Option, + pub provider: String, + pub scene: String, + pub phone_number_masked: String, +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct PhoneLoginResult { + pub user: AuthUser, + pub created: bool, + pub provider: String, + pub provider_out_id: Option, + pub phone_number_masked: String, +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct ResolveWechatLoginResult { + pub user: AuthUser, + pub created: bool, +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct CreateWechatAuthStateResult { + pub state: WechatAuthStateRecord, +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct ConsumeWechatAuthStateResult { + pub state: WechatAuthStateRecord, +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct BindWechatPhoneResult { + pub user: AuthUser, +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct CreateRefreshSessionResult { + pub session: RefreshSessionRecord, +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct RotateRefreshSessionResult { + pub session: RefreshSessionRecord, + pub user: AuthUser, +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct ListActiveRefreshSessionsResult { + pub sessions: Vec, +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct LogoutCurrentSessionResult { + pub user: AuthUser, +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct LogoutAllSessionsResult { + pub user: AuthUser, +} + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct AuthStoreSnapshotProcedureResult { + pub ok: bool, + pub record: Option, + pub error_message: Option, +} diff --git a/server-rs/crates/module-auth/src/commands.rs b/server-rs/crates/module-auth/src/commands.rs index 0bb82399..eaf5ee28 100644 --- a/server-rs/crates/module-auth/src/commands.rs +++ b/server-rs/crates/module-auth/src/commands.rs @@ -1,3 +1,92 @@ -//! 认证写入命令过渡落位。 +//! 认证写入命令。 //! //! 用于表达密码入口、手机号验证码、微信登录、刷新会话签发和吊销等用例输入。 + +use serde::{Deserialize, Serialize}; + +use crate::domain::{ + AuthLoginMethod, PhoneAuthScene, RefreshSessionClientInfo, WechatAuthScene, + WechatIdentityProfile, +}; + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct PasswordEntryInput { + pub phone_number: String, + pub password: String, +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct ChangePasswordInput { + pub user_id: String, + pub current_password: Option, + pub new_password: String, +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct ResetPasswordInput { + pub phone_number: String, + pub verify_code: String, + pub new_password: String, +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct SendPhoneCodeInput { + pub phone_number: String, + pub scene: PhoneAuthScene, +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct PhoneLoginInput { + pub phone_number: String, + pub verify_code: String, +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct ResolveWechatLoginInput { + pub profile: WechatIdentityProfile, +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct CreateWechatAuthStateInput { + pub redirect_path: String, + pub scene: WechatAuthScene, + pub request_user_agent: Option, +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct BindWechatPhoneInput { + pub user_id: String, + pub phone_number: String, + pub verify_code: String, +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct CreateRefreshSessionInput { + pub user_id: String, + pub refresh_token_hash: String, + pub issued_by_provider: AuthLoginMethod, + pub client_info: RefreshSessionClientInfo, +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct RotateRefreshSessionInput { + pub refresh_token_hash: String, + pub next_refresh_token_hash: String, +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct LogoutCurrentSessionInput { + pub user_id: String, + pub refresh_token_hash: Option, +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct LogoutAllSessionsInput { + pub user_id: String, +} + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct AuthStoreSnapshotUpsertInput { + pub snapshot_json: String, + pub updated_at_micros: i64, +} diff --git a/server-rs/crates/module-auth/src/domain.rs b/server-rs/crates/module-auth/src/domain.rs index 96d1450c..d139138f 100644 --- a/server-rs/crates/module-auth/src/domain.rs +++ b/server-rs/crates/module-auth/src/domain.rs @@ -1,4 +1,252 @@ -//! 认证领域模型过渡落位。 +//! 认证领域模型。 //! -//! 后续迁移账号、刷新会话、验证码和微信绑定聚合时,只保留认证规则; -//! 文件持久化、真实短信发送、cookie 写入和 HTTP 上下文都不属于领域核心。 +//! 这里只保留账号、登录方式、绑定状态等纯领域事实。文件持久化、真实短信发送、 +//! cookie 写入、JWT 签发和 HTTP 上下文都属于外层 adapter。 + +use serde::{Deserialize, Serialize}; + +use crate::errors::{PasswordEntryError, PhoneAuthError}; + +pub const PASSWORD_MIN_LENGTH: usize = 6; +pub const PASSWORD_MAX_LENGTH: usize = 128; +pub const SMS_CODE_LENGTH: usize = 6; +pub const SMS_CODE_TTL_MINUTES: i64 = 5; +pub const SMS_CODE_COOLDOWN_SECONDS: u64 = 60; +pub const SMS_CODE_MAX_FAILED_ATTEMPTS: u32 = 5; + +/// 用户最近一次完成认证的入口类型。 +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub enum AuthLoginMethod { + Password, + Phone, + Wechat, +} + +impl AuthLoginMethod { + pub fn as_str(&self) -> &'static str { + match self { + Self::Password => "password", + Self::Phone => "phone", + Self::Wechat => "wechat", + } + } +} + +/// 账号是否已经完成必要绑定。 +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub enum AuthBindingStatus { + Active, + PendingBindPhone, +} + +impl AuthBindingStatus { + pub fn as_str(&self) -> &'static str { + match self { + Self::Active => "active", + Self::PendingBindPhone => "pending_bind_phone", + } + } +} + +/// 认证用户快照。 +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct AuthUser { + pub id: String, + pub public_user_code: String, + pub username: String, + pub display_name: String, + pub phone_number_masked: Option, + pub login_method: AuthLoginMethod, + pub binding_status: AuthBindingStatus, + pub wechat_bound: bool, + pub token_version: u64, +} + +/// 规范化后的手机号快照。 +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct PhoneNumberSnapshot { + pub e164: String, + pub masked_national_number: String, +} + +/// 手机验证码使用场景。 +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum PhoneAuthScene { + Login, + BindPhone, + ChangePhone, + ResetPassword, +} + +impl PhoneAuthScene { + pub fn as_str(&self) -> &'static str { + match self { + Self::Login => "login", + Self::BindPhone => "bind_phone", + Self::ChangePhone => "change_phone", + Self::ResetPassword => "reset_password", + } + } +} + +/// 微信授权入口场景。 +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum WechatAuthScene { + Desktop, + WechatInApp, +} + +impl WechatAuthScene { + pub fn as_str(&self) -> &'static str { + match self { + Self::Desktop => "desktop", + Self::WechatInApp => "wechat_in_app", + } + } +} + +/// 微信身份资料快照。 +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct WechatIdentityProfile { + pub provider_uid: String, + pub provider_union_id: Option, + pub display_name: Option, + pub avatar_url: Option, +} + +/// 微信授权 state 快照。 +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct WechatAuthStateRecord { + pub wechat_state_id: String, + pub state_token: String, + pub redirect_path: String, + pub scene: WechatAuthScene, + pub request_user_agent: Option, + pub expires_at: String, + pub consumed_at: Option, + pub created_at: String, + pub updated_at: String, +} + +/// refresh session 的客户端环境快照。 +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct RefreshSessionClientInfo { + pub client_type: String, + pub client_runtime: String, + pub client_platform: String, + pub client_instance_id: Option, + pub device_fingerprint: Option, + pub device_display_name: String, + pub mini_program_app_id: Option, + pub mini_program_env: Option, + pub user_agent: Option, + pub ip: Option, +} + +/// refresh session 快照。 +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct RefreshSessionRecord { + pub session_id: String, + pub user_id: String, + pub refresh_token_hash: String, + pub issued_by_provider: AuthLoginMethod, + pub client_info: RefreshSessionClientInfo, + pub expires_at: String, + pub revoked_at: Option, + pub created_at: String, + pub updated_at: String, + pub last_seen_at: String, +} + +/// Auth store 持久化快照记录。 +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct AuthStoreSnapshotRecord { + pub snapshot_json: Option, + pub updated_at_micros: Option, +} + +pub fn validate_password(password: &str) -> Result<(), PasswordEntryError> { + let length = password.chars().count(); + if !(PASSWORD_MIN_LENGTH..=PASSWORD_MAX_LENGTH).contains(&length) { + return Err(PasswordEntryError::InvalidPasswordLength); + } + + Ok(()) +} + +pub fn verify_sms_code_format(verify_code: &str) -> Result<(), PhoneAuthError> { + let verify_code = verify_code.trim(); + if verify_code.len() != SMS_CODE_LENGTH + || !verify_code + .chars() + .all(|character| character.is_ascii_digit()) + { + return Err(PhoneAuthError::InvalidVerifyCode); + } + + Ok(()) +} + +pub fn normalize_mainland_china_phone_number( + raw_phone_number: &str, +) -> Result { + let digits = raw_phone_number + .trim() + .chars() + .filter(|character| character.is_ascii_digit()) + .collect::(); + if digits.len() != 11 || !digits.starts_with('1') { + return Err(PhoneAuthError::InvalidPhoneNumber); + } + + Ok(PhoneNumberSnapshot { + e164: format!("+86{digits}"), + masked_national_number: mask_phone_number(&digits), + }) +} + +pub fn mask_phone_number(phone_number: &str) -> String { + format!("{}****{}", &phone_number[..3], &phone_number[7..11]) +} + +pub fn build_national_phone_number(e164_phone_number: &str) -> Result { + let digits = e164_phone_number.trim().trim_start_matches('+'); + if let Some(national) = digits.strip_prefix("86") + && national.len() == 11 + { + return Ok(national.to_string()); + } + Err(PhoneAuthError::InvalidPhoneNumber) +} + +pub fn build_system_username(prefix: &str, sequence: u64) -> String { + format!("{prefix}_{sequence:08}") +} + +// 公开叙世号是稳定的公开检索键,不替代内部 user_id,仅用于展示、分享与搜索。 +pub fn build_public_user_code(sequence: u64) -> String { + format!("SY-{sequence:08}") +} + +pub fn normalize_public_user_code(input: &str) -> Result { + let normalized = input + .trim() + .chars() + .filter(|character| character.is_ascii_alphanumeric()) + .collect::() + .to_ascii_uppercase(); + let digits = normalized.strip_prefix("SY").unwrap_or(&normalized); + + if digits.is_empty() + || digits.len() > 8 + || !digits.chars().all(|character| character.is_ascii_digit()) + { + return Err(PasswordEntryError::InvalidPublicUserCode); + } + + Ok(format!("SY-{digits:0>8}")) +} + +pub fn build_phone_code_key(phone_number: &str, scene: &PhoneAuthScene) -> String { + format!("{}:{}", phone_number.trim(), scene.as_str()) +} diff --git a/server-rs/crates/module-auth/src/errors.rs b/server-rs/crates/module-auth/src/errors.rs index 6dbf5350..539b0689 100644 --- a/server-rs/crates/module-auth/src/errors.rs +++ b/server-rs/crates/module-auth/src/errors.rs @@ -1,3 +1,179 @@ -//! 认证领域错误过渡落位。 +//! 认证领域错误。 //! //! 领域错误保持可测试、可映射,不能直接依赖 Axum、cookie 或平台 provider 错误模型。 + +use std::{error::Error, fmt}; + +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum PasswordEntryError { + InvalidPhoneNumber, + InvalidPasswordLength, + InvalidPublicUserCode, + InvalidCredentials, + UserNotFound, + Store(String), + PasswordHash(String), +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum PhoneAuthError { + InvalidPhoneNumber, + InvalidVerifyCode, + VerifyCodeNotFound, + VerifyCodeExpired, + SendCoolingDown { retry_after_seconds: u64 }, + VerifyAttemptsExceeded, + UserNotFound, + UserStateMismatch, + Store(String), + PasswordHash(String), +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum WechatAuthError { + MissingProfile, + StateNotFound, + StateExpired, + StateConsumed, + UserNotFound, + MissingWechatIdentity, + Store(String), + PasswordHash(String), +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum RefreshSessionError { + MissingToken, + SessionNotFound, + SessionExpired, + UserNotFound, + Store(String), +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum LogoutError { + UserNotFound, + Store(String), +} + +impl fmt::Display for PasswordEntryError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::InvalidPhoneNumber => f.write_str("手机号格式不正确"), + Self::InvalidPasswordLength => f.write_str("密码长度需要在 6 到 128 位之间"), + Self::InvalidPublicUserCode => f.write_str("叙世号格式不正确"), + Self::InvalidCredentials => f.write_str("手机号或密码错误"), + Self::UserNotFound => f.write_str("用户不存在"), + Self::Store(message) | Self::PasswordHash(message) => f.write_str(message), + } + } +} + +impl Error for PasswordEntryError {} + +impl fmt::Display for PhoneAuthError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::InvalidPhoneNumber => f.write_str("手机号格式不正确"), + Self::InvalidVerifyCode => f.write_str("验证码错误"), + Self::VerifyCodeNotFound => f.write_str("验证码不存在或已失效"), + Self::VerifyCodeExpired => f.write_str("验证码已过期"), + Self::SendCoolingDown { .. } => f.write_str("验证码发送过于频繁,请稍后再试"), + Self::VerifyAttemptsExceeded => f.write_str("验证码错误次数过多,请重新获取验证码"), + Self::UserNotFound => f.write_str("用户不存在"), + Self::UserStateMismatch => f.write_str("当前账号状态不允许执行该操作"), + Self::Store(message) | Self::PasswordHash(message) => f.write_str(message), + } + } +} + +impl Error for PhoneAuthError {} + +impl fmt::Display for WechatAuthError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::MissingProfile => f.write_str("缺少微信身份信息"), + Self::StateNotFound => f.write_str("微信登录状态已失效,请重新发起登录"), + Self::StateExpired => f.write_str("微信登录状态已过期,请重新发起登录"), + Self::StateConsumed => f.write_str("微信登录状态已被消费,请重新发起登录"), + Self::UserNotFound => f.write_str("用户不存在"), + Self::MissingWechatIdentity => f.write_str("当前账号缺少微信身份"), + Self::Store(message) | Self::PasswordHash(message) => f.write_str(message), + } + } +} + +impl Error for WechatAuthError {} + +impl fmt::Display for RefreshSessionError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::MissingToken => f.write_str("缺少刷新会话"), + Self::SessionNotFound | Self::SessionExpired | Self::UserNotFound => { + f.write_str("当前登录态已失效,请重新登录") + } + Self::Store(message) => f.write_str(message), + } + } +} + +impl Error for RefreshSessionError {} + +impl fmt::Display for LogoutError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::UserNotFound => f.write_str("当前登录态已失效,请重新登录"), + Self::Store(message) => f.write_str(message), + } + } +} + +impl Error for LogoutError {} + +pub(crate) fn map_password_store_error(error: PasswordEntryError) -> RefreshSessionError { + match error { + PasswordEntryError::Store(message) => RefreshSessionError::Store(message), + PasswordEntryError::InvalidPhoneNumber + | PasswordEntryError::InvalidPasswordLength + | PasswordEntryError::InvalidPublicUserCode + | PasswordEntryError::InvalidCredentials + | PasswordEntryError::UserNotFound + | PasswordEntryError::PasswordHash(_) => { + RefreshSessionError::Store("用户仓储读取失败".to_string()) + } + } +} + +pub(crate) fn map_password_error_to_phone_error(error: PasswordEntryError) -> PhoneAuthError { + match error { + PasswordEntryError::Store(message) => PhoneAuthError::Store(message), + PasswordEntryError::PasswordHash(message) => PhoneAuthError::PasswordHash(message), + PasswordEntryError::InvalidPhoneNumber + | PasswordEntryError::InvalidPasswordLength + | PasswordEntryError::InvalidPublicUserCode + | PasswordEntryError::InvalidCredentials + | PasswordEntryError::UserNotFound => PhoneAuthError::Store("用户仓储读取失败".to_string()), + } +} + +pub(crate) fn map_password_error_to_logout_error(error: PasswordEntryError) -> LogoutError { + match error { + PasswordEntryError::Store(message) => LogoutError::Store(message), + PasswordEntryError::InvalidPhoneNumber + | PasswordEntryError::InvalidPasswordLength + | PasswordEntryError::InvalidPublicUserCode + | PasswordEntryError::InvalidCredentials + | PasswordEntryError::UserNotFound + | PasswordEntryError::PasswordHash(_) => LogoutError::Store("用户仓储读取失败".to_string()), + } +} + +pub(crate) fn map_refresh_error_to_logout_error(error: RefreshSessionError) -> LogoutError { + match error { + RefreshSessionError::Store(message) => LogoutError::Store(message), + RefreshSessionError::MissingToken + | RefreshSessionError::SessionNotFound + | RefreshSessionError::SessionExpired + | RefreshSessionError::UserNotFound => LogoutError::Store("会话吊销失败".to_string()), + } +} diff --git a/server-rs/crates/module-auth/src/events.rs b/server-rs/crates/module-auth/src/events.rs index cc43780e..c9b1bd48 100644 --- a/server-rs/crates/module-auth/src/events.rs +++ b/server-rs/crates/module-auth/src/events.rs @@ -1,3 +1,27 @@ -//! 认证领域事件过渡落位。 +//! 认证领域事件。 //! //! 用于表达用户创建、会话签发/吊销、手机号验证通过和微信身份绑定等事实。 + +use crate::domain::AuthLoginMethod; + +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum AuthDomainEvent { + UserCreated { + user_id: String, + login_method: AuthLoginMethod, + }, + RefreshSessionIssued { + session_id: String, + user_id: String, + }, + RefreshSessionRevoked { + session_id: String, + user_id: String, + }, + PhoneVerified { + user_id: String, + }, + WechatIdentityBound { + user_id: String, + }, +} diff --git a/server-rs/crates/module-auth/src/lib.rs b/server-rs/crates/module-auth/src/lib.rs index a23d30ef..b5fb09e9 100644 --- a/server-rs/crates/module-auth/src/lib.rs +++ b/server-rs/crates/module-auth/src/lib.rs @@ -4,10 +4,15 @@ mod domain; mod errors; mod events; +pub use application::*; +pub use commands::*; +pub use domain::*; +pub use errors::*; +pub use events::*; + use std::{ collections::HashMap, - error::Error, - fmt, fs, + fs, path::{Path, PathBuf}, sync::{Arc, Mutex}, }; @@ -24,351 +29,6 @@ use shared_kernel::{ use time::{Duration, OffsetDateTime}; use tracing::{info, warn}; -const PASSWORD_MIN_LENGTH: usize = 6; -const PASSWORD_MAX_LENGTH: usize = 128; -const SMS_CODE_LENGTH: usize = 6; -const SMS_CODE_TTL_MINUTES: i64 = 5; -const SMS_CODE_COOLDOWN_SECONDS: u64 = 60; -const SMS_CODE_MAX_FAILED_ATTEMPTS: u32 = 5; - -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub enum AuthLoginMethod { - Password, - Phone, - Wechat, -} - -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub enum AuthBindingStatus { - Active, - PendingBindPhone, -} - -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct AuthUser { - pub id: String, - pub public_user_code: String, - pub username: String, - pub display_name: String, - pub phone_number_masked: Option, - pub login_method: AuthLoginMethod, - pub binding_status: AuthBindingStatus, - pub wechat_bound: bool, - pub token_version: u64, -} - -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct AuthMeResult { - pub user: AuthUser, -} - -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct PublicUserSearchResult { - pub user: AuthUser, -} - -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct PasswordEntryInput { - pub phone_number: String, - pub password: String, -} - -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct PasswordEntryResult { - pub user: AuthUser, - pub created: bool, -} - -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct ChangePasswordInput { - pub user_id: String, - pub current_password: Option, - pub new_password: String, -} - -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct ChangePasswordResult { - pub user: AuthUser, -} - -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct ResetPasswordInput { - pub phone_number: String, - pub verify_code: String, - pub new_password: String, -} - -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct ResetPasswordResult { - pub user: AuthUser, - pub provider: String, - pub provider_out_id: Option, - pub phone_number_masked: String, -} - -#[derive(Clone, Debug, PartialEq, Eq)] -pub enum PhoneAuthScene { - Login, - BindPhone, - ChangePhone, - ResetPassword, -} - -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct PhoneNumberSnapshot { - pub e164: String, - pub masked_national_number: String, -} - -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct SendPhoneCodeInput { - pub phone_number: String, - pub scene: PhoneAuthScene, -} - -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct SendPhoneCodeResult { - pub cooldown_seconds: u64, - pub expires_in_seconds: u64, - pub provider_request_id: Option, - pub provider_out_id: Option, - pub provider: String, - pub scene: String, - pub phone_number_masked: String, -} - -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct PhoneLoginInput { - pub phone_number: String, - pub verify_code: String, -} - -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct PhoneLoginResult { - pub user: AuthUser, - pub created: bool, - pub provider: String, - pub provider_out_id: Option, - pub phone_number_masked: String, -} - -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct WechatIdentityProfile { - pub provider_uid: String, - pub provider_union_id: Option, - pub display_name: Option, - pub avatar_url: Option, -} - -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct ResolveWechatLoginInput { - pub profile: WechatIdentityProfile, -} - -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct ResolveWechatLoginResult { - pub user: AuthUser, - pub created: bool, -} - -#[derive(Clone, Debug, PartialEq, Eq)] -pub enum WechatAuthScene { - Desktop, - WechatInApp, -} - -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct CreateWechatAuthStateInput { - pub redirect_path: String, - pub scene: WechatAuthScene, - pub request_user_agent: Option, -} - -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct WechatAuthStateRecord { - pub wechat_state_id: String, - pub state_token: String, - pub redirect_path: String, - pub scene: WechatAuthScene, - pub request_user_agent: Option, - pub expires_at: String, - pub consumed_at: Option, - pub created_at: String, - pub updated_at: String, -} - -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct CreateWechatAuthStateResult { - pub state: WechatAuthStateRecord, -} - -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct ConsumeWechatAuthStateResult { - pub state: WechatAuthStateRecord, -} - -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct BindWechatPhoneInput { - pub user_id: String, - pub phone_number: String, - pub verify_code: String, -} - -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct BindWechatPhoneResult { - pub user: AuthUser, -} - -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct CreateRefreshSessionInput { - pub user_id: String, - pub refresh_token_hash: String, - pub issued_by_provider: AuthLoginMethod, - pub client_info: RefreshSessionClientInfo, -} - -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct RefreshSessionClientInfo { - pub client_type: String, - pub client_runtime: String, - pub client_platform: String, - pub client_instance_id: Option, - pub device_fingerprint: Option, - pub device_display_name: String, - pub mini_program_app_id: Option, - pub mini_program_env: Option, - pub user_agent: Option, - pub ip: Option, -} - -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct RefreshSessionRecord { - pub session_id: String, - pub user_id: String, - pub refresh_token_hash: String, - pub issued_by_provider: AuthLoginMethod, - pub client_info: RefreshSessionClientInfo, - pub expires_at: String, - pub revoked_at: Option, - pub created_at: String, - pub updated_at: String, - pub last_seen_at: String, -} - -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct CreateRefreshSessionResult { - pub session: RefreshSessionRecord, -} - -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct RotateRefreshSessionInput { - pub refresh_token_hash: String, - pub next_refresh_token_hash: String, -} - -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct RotateRefreshSessionResult { - pub session: RefreshSessionRecord, - pub user: AuthUser, -} - -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct ListActiveRefreshSessionsResult { - pub sessions: Vec, -} - -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct LogoutCurrentSessionInput { - pub user_id: String, - pub refresh_token_hash: Option, -} - -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct LogoutCurrentSessionResult { - pub user: AuthUser, -} - -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct LogoutAllSessionsInput { - pub user_id: String, -} - -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct LogoutAllSessionsResult { - pub user: AuthUser, -} - -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct AuthStoreSnapshotRecord { - pub snapshot_json: Option, - pub updated_at_micros: Option, -} - -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct AuthStoreSnapshotUpsertInput { - pub snapshot_json: String, - pub updated_at_micros: i64, -} - -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct AuthStoreSnapshotProcedureResult { - pub ok: bool, - pub record: Option, - pub error_message: Option, -} - -#[derive(Clone, Debug, PartialEq, Eq)] -pub enum PasswordEntryError { - InvalidPhoneNumber, - InvalidPasswordLength, - InvalidPublicUserCode, - InvalidCredentials, - UserNotFound, - Store(String), - PasswordHash(String), -} - -#[derive(Clone, Debug, PartialEq, Eq)] -pub enum PhoneAuthError { - InvalidPhoneNumber, - InvalidVerifyCode, - VerifyCodeNotFound, - VerifyCodeExpired, - SendCoolingDown { retry_after_seconds: u64 }, - VerifyAttemptsExceeded, - UserNotFound, - UserStateMismatch, - Store(String), - PasswordHash(String), -} - -#[derive(Clone, Debug, PartialEq, Eq)] -pub enum WechatAuthError { - MissingProfile, - StateNotFound, - StateExpired, - StateConsumed, - UserNotFound, - MissingWechatIdentity, - Store(String), - PasswordHash(String), -} - -#[derive(Clone, Debug, PartialEq, Eq)] -pub enum RefreshSessionError { - MissingToken, - SessionNotFound, - SessionExpired, - UserNotFound, - Store(String), -} - -#[derive(Clone, Debug, PartialEq, Eq)] -pub enum LogoutError { - UserNotFound, - Store(String), -} - #[derive(Clone, Debug)] pub struct InMemoryAuthStore { inner: Arc>, @@ -2126,137 +1786,6 @@ impl InMemoryAuthStore { } } -impl AuthLoginMethod { - pub fn as_str(&self) -> &'static str { - match self { - Self::Password => "password", - Self::Phone => "phone", - Self::Wechat => "wechat", - } - } -} - -impl AuthBindingStatus { - pub fn as_str(&self) -> &'static str { - match self { - Self::Active => "active", - Self::PendingBindPhone => "pending_bind_phone", - } - } -} - -impl fmt::Display for PasswordEntryError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::InvalidPhoneNumber => f.write_str("手机号格式不正确"), - Self::InvalidPasswordLength => f.write_str("密码长度需要在 6 到 128 位之间"), - Self::InvalidPublicUserCode => f.write_str("叙世号格式不正确"), - Self::InvalidCredentials => f.write_str("手机号或密码错误"), - Self::UserNotFound => f.write_str("用户不存在"), - Self::Store(message) | Self::PasswordHash(message) => f.write_str(message), - } - } -} - -impl Error for PasswordEntryError {} - -impl fmt::Display for PhoneAuthError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::InvalidPhoneNumber => f.write_str("手机号格式不正确"), - Self::InvalidVerifyCode => f.write_str("验证码错误"), - Self::VerifyCodeNotFound => f.write_str("验证码不存在或已失效"), - Self::VerifyCodeExpired => f.write_str("验证码已过期"), - Self::SendCoolingDown { .. } => f.write_str("验证码发送过于频繁,请稍后再试"), - Self::VerifyAttemptsExceeded => f.write_str("验证码错误次数过多,请重新获取验证码"), - Self::UserNotFound => f.write_str("用户不存在"), - Self::UserStateMismatch => f.write_str("当前账号状态不允许执行该操作"), - Self::Store(message) | Self::PasswordHash(message) => f.write_str(message), - } - } -} - -impl Error for PhoneAuthError {} - -impl fmt::Display for WechatAuthError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::MissingProfile => f.write_str("缺少微信身份信息"), - Self::StateNotFound => f.write_str("微信登录状态已失效,请重新发起登录"), - Self::StateExpired => f.write_str("微信登录状态已过期,请重新发起登录"), - Self::StateConsumed => f.write_str("微信登录状态已被消费,请重新发起登录"), - Self::UserNotFound => f.write_str("用户不存在"), - Self::MissingWechatIdentity => f.write_str("当前账号缺少微信身份"), - Self::Store(message) | Self::PasswordHash(message) => f.write_str(message), - } - } -} - -impl Error for WechatAuthError {} - -impl fmt::Display for RefreshSessionError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::MissingToken => f.write_str("缺少刷新会话"), - Self::SessionNotFound | Self::SessionExpired | Self::UserNotFound => { - f.write_str("当前登录态已失效,请重新登录") - } - Self::Store(message) => f.write_str(message), - } - } -} - -impl Error for RefreshSessionError {} - -impl fmt::Display for LogoutError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::UserNotFound => f.write_str("当前登录态已失效,请重新登录"), - Self::Store(message) => f.write_str(message), - } - } -} - -impl Error for LogoutError {} - -fn map_password_store_error(error: PasswordEntryError) -> RefreshSessionError { - match error { - PasswordEntryError::Store(message) => RefreshSessionError::Store(message), - PasswordEntryError::InvalidPhoneNumber - | PasswordEntryError::InvalidPasswordLength - | PasswordEntryError::InvalidPublicUserCode - | PasswordEntryError::InvalidCredentials - | PasswordEntryError::UserNotFound - | PasswordEntryError::PasswordHash(_) => { - RefreshSessionError::Store("用户仓储读取失败".to_string()) - } - } -} - -fn map_password_error_to_phone_error(error: PasswordEntryError) -> PhoneAuthError { - match error { - PasswordEntryError::Store(message) => PhoneAuthError::Store(message), - PasswordEntryError::PasswordHash(message) => PhoneAuthError::PasswordHash(message), - PasswordEntryError::InvalidPhoneNumber - | PasswordEntryError::InvalidPasswordLength - | PasswordEntryError::InvalidPublicUserCode - | PasswordEntryError::InvalidCredentials - | PasswordEntryError::UserNotFound => PhoneAuthError::Store("用户仓储读取失败".to_string()), - } -} - -fn map_password_error_to_logout_error(error: PasswordEntryError) -> LogoutError { - match error { - PasswordEntryError::Store(message) => LogoutError::Store(message), - PasswordEntryError::InvalidPhoneNumber - | PasswordEntryError::InvalidPasswordLength - | PasswordEntryError::InvalidPublicUserCode - | PasswordEntryError::InvalidCredentials - | PasswordEntryError::UserNotFound - | PasswordEntryError::PasswordHash(_) => LogoutError::Store("用户仓储读取失败".to_string()), - } -} - fn map_sms_provider_error_to_phone_error(error: SmsProviderError) -> PhoneAuthError { match error { SmsProviderError::InvalidVerifyCode => PhoneAuthError::InvalidVerifyCode, @@ -2266,25 +1795,6 @@ fn map_sms_provider_error_to_phone_error(error: SmsProviderError) -> PhoneAuthEr } } -fn map_refresh_error_to_logout_error(error: RefreshSessionError) -> LogoutError { - match error { - RefreshSessionError::Store(message) => LogoutError::Store(message), - RefreshSessionError::MissingToken - | RefreshSessionError::SessionNotFound - | RefreshSessionError::SessionExpired - | RefreshSessionError::UserNotFound => LogoutError::Store("会话吊销失败".to_string()), - } -} - -fn validate_password(password: &str) -> Result<(), PasswordEntryError> { - let length = password.chars().count(); - if !(PASSWORD_MIN_LENGTH..=PASSWORD_MAX_LENGTH).contains(&length) { - return Err(PasswordEntryError::InvalidPasswordLength); - } - - Ok(()) -} - async fn verify_stored_password_user( existing_user: StoredPasswordUser, password: &str, @@ -2309,51 +1819,6 @@ async fn verify_stored_password_user( }) } -fn verify_sms_code_format(verify_code: &str) -> Result<(), PhoneAuthError> { - let verify_code = verify_code.trim(); - if verify_code.len() != SMS_CODE_LENGTH - || !verify_code - .chars() - .all(|character| character.is_ascii_digit()) - { - return Err(PhoneAuthError::InvalidVerifyCode); - } - - Ok(()) -} - -fn normalize_mainland_china_phone_number( - raw_phone_number: &str, -) -> Result { - let digits = raw_phone_number - .trim() - .chars() - .filter(|character| character.is_ascii_digit()) - .collect::(); - if digits.len() != 11 || !digits.starts_with('1') { - return Err(PhoneAuthError::InvalidPhoneNumber); - } - - Ok(PhoneNumberSnapshot { - e164: format!("+86{digits}"), - masked_national_number: mask_phone_number(&digits), - }) -} - -fn mask_phone_number(phone_number: &str) -> String { - format!("{}****{}", &phone_number[..3], &phone_number[7..11]) -} - -fn build_national_phone_number(e164_phone_number: &str) -> Result { - let digits = e164_phone_number.trim().trim_start_matches('+'); - if let Some(national) = digits.strip_prefix("86") - && national.len() == 11 - { - return Ok(national.to_string()); - } - Err(PhoneAuthError::InvalidPhoneNumber) -} - fn build_random_password_seed() -> String { format!( "seed_{}_{}", @@ -2362,34 +1827,6 @@ fn build_random_password_seed() -> String { ) } -fn build_system_username(prefix: &str, sequence: u64) -> String { - format!("{prefix}_{sequence:08}") -} - -// 公开叙世号是稳定的公开检索键,不替代内部 user_id,仅用于展示、分享与搜索。 -fn build_public_user_code(sequence: u64) -> String { - format!("SY-{sequence:08}") -} - -pub fn normalize_public_user_code(input: &str) -> Result { - let normalized = input - .trim() - .chars() - .filter(|character| character.is_ascii_alphanumeric()) - .collect::() - .to_ascii_uppercase(); - let digits = normalized.strip_prefix("SY").unwrap_or(&normalized); - - if digits.is_empty() - || digits.len() > 8 - || !digits.chars().all(|character| character.is_ascii_digit()) - { - return Err(PasswordEntryError::InvalidPublicUserCode); - } - - Ok(format!("SY-{digits:0>8}")) -} - fn format_rfc3339(value: OffsetDateTime) -> Result { format_shared_rfc3339(value) } @@ -2404,10 +1841,6 @@ fn seconds_until(now: OffsetDateTime, target: OffsetDateTime) -> u64 { u64::try_from(seconds.max(1)).unwrap_or(1) } -fn build_phone_code_key(phone_number: &str, scene: &PhoneAuthScene) -> String { - format!("{}:{}", phone_number.trim(), scene.as_str()) -} - fn create_wechat_state_token() -> String { new_uuid_simple_string() } @@ -2428,26 +1861,6 @@ fn parse_rfc3339_with_context( .map_err(|error| RefreshSessionError::Store(format!("{field_label}解析失败:{error}"))) } -impl PhoneAuthScene { - pub fn as_str(&self) -> &'static str { - match self { - Self::Login => "login", - Self::BindPhone => "bind_phone", - Self::ChangePhone => "change_phone", - Self::ResetPassword => "reset_password", - } - } -} - -impl WechatAuthScene { - pub fn as_str(&self) -> &'static str { - match self { - Self::Desktop => "desktop", - Self::WechatInApp => "wechat_in_app", - } - } -} - #[cfg(test)] mod tests { use platform_auth::{ diff --git a/server-rs/crates/module-big-fish/src/application.rs b/server-rs/crates/module-big-fish/src/application.rs index fad5c0ed..3817db02 100644 --- a/server-rs/crates/module-big-fish/src/application.rs +++ b/server-rs/crates/module-big-fish/src/application.rs @@ -5,11 +5,29 @@ use shared_kernel::normalize_required_string; use crate::{ - BigFishAssetSlotSnapshot, build_asset_coverage, - commands::EvaluateBigFishPublishReadinessCommand, domain::BigFishPublishReadiness, - errors::BigFishApplicationError, events::BigFishDomainEvent, + BIG_FISH_DEFAULT_LEVEL_COUNT, BIG_FISH_TARGET_WILD_COUNT, BigFishAssetSlotSnapshot, + build_asset_coverage, + commands::{ + EvaluateBigFishPublishReadinessCommand, StartBigFishRunCommand, SubmitBigFishInputCommand, + }, + domain::{ + BigFishPublishReadiness, BigFishRunStatus, BigFishRuntimeEntitySnapshot, + BigFishRuntimeSnapshot, BigFishVector2, + }, + errors::BigFishApplicationError, + events::BigFishDomainEvent, }; +const VIEW_WIDTH: f32 = 720.0; +const VIEW_HEIGHT: f32 = 1280.0; +const WORLD_HALF_WIDTH: f32 = 1400.0; +const WORLD_HALF_HEIGHT: f32 = 2400.0; +const DEFAULT_WILD_COUNT: usize = 28; +const LEADER_SPEED: f32 = 210.0; +const FOLLOWER_SPEED: f32 = 170.0; +const WILD_SPEED: f32 = 74.0; +const TICK_SECONDS: f32 = 0.1; + /// 发布门禁应用结果,供 adapter 持久化快照或转换成 API DTO。 #[derive(Clone, Debug, PartialEq, Eq)] pub struct EvaluateBigFishPublishReadinessResult { @@ -17,6 +35,13 @@ pub struct EvaluateBigFishPublishReadinessResult { pub events: Vec, } +/// 运行态推进应用结果。 +#[derive(Clone, Debug, PartialEq)] +pub struct BigFishRuntimeResult { + pub snapshot: BigFishRuntimeSnapshot, + pub events: Vec, +} + /// 评估 Big Fish 作品是否具备发布条件。 /// /// 规则只依赖草稿和资产槽:草稿必须存在,等级主图、基础动作和背景图 @@ -51,6 +76,508 @@ pub fn evaluate_publish_readiness( }) } +/// 开始一局 Big Fish 运行态。 +/// +/// 领域层生成初始实体池,adapter 只负责把快照序列化并写入运行表。 +pub fn start_big_fish_run( + command: StartBigFishRunCommand, +) -> Result { + let run_id = + normalize_required_string(command.run_id).ok_or(BigFishApplicationError::MissingRunId)?; + let session_id = normalize_required_string(command.session_id) + .ok_or(BigFishApplicationError::MissingSessionId)?; + let owner_user_id = normalize_required_string(command.owner_user_id) + .ok_or(BigFishApplicationError::MissingOwnerUserId)?; + let win_level = command + .draft + .as_ref() + .map(|draft| draft.runtime_params.win_level) + .or(command.work_level_count) + .unwrap_or(BIG_FISH_DEFAULT_LEVEL_COUNT) + .clamp(1, 32); + let wild_count = command + .draft + .as_ref() + .map(|draft| draft.runtime_params.spawn_target_count as usize) + .unwrap_or(BIG_FISH_TARGET_WILD_COUNT) + .max(DEFAULT_WILD_COUNT); + let leader = build_entity("owned-1".to_string(), 1, 0.0, 0.0); + let mut wild_entities = vec![ + build_entity("wild-open-1".to_string(), 1, 92.0, 0.0), + build_entity("wild-open-2".to_string(), 1, -118.0, 46.0), + ]; + while wild_entities.len() < wild_count { + wild_entities.push(build_wild_entity( + 0, + wild_entities.len() as u64, + 1, + win_level, + &leader.position, + )); + } + + let snapshot = BigFishRuntimeSnapshot { + run_id: run_id.clone(), + session_id: session_id.clone(), + status: BigFishRunStatus::Running, + tick: 0, + player_level: 1, + win_level, + leader_entity_id: Some(leader.entity_id.clone()), + owned_entities: vec![leader.clone()], + wild_entities, + camera_center: leader.position, + last_input: BigFishVector2 { x: 0.0, y: 0.0 }, + event_log: vec!["开局生成同级可收编目标".to_string()], + updated_at_micros: command.started_at_micros, + }; + + Ok(BigFishRuntimeResult { + snapshot, + events: vec![BigFishDomainEvent::RuntimeRunStarted { + run_id, + session_id, + owner_user_id, + occurred_at_micros: command.started_at_micros, + }], + }) +} + +/// 根据最新输入推进一帧运行态。 +/// +/// 这里是 Big Fish 运行态真相源;前端只能提交输入并渲染返回快照。 +pub fn submit_big_fish_input( + command: SubmitBigFishInputCommand, +) -> Result { + let owner_user_id = normalize_required_string(command.owner_user_id) + .ok_or(BigFishApplicationError::MissingOwnerUserId)?; + if !command.x.is_finite() || !command.y.is_finite() { + return Err(BigFishApplicationError::InvalidRuntimeInput); + } + + let mut snapshot = command.current_snapshot; + if snapshot.status != BigFishRunStatus::Running { + return Ok(BigFishRuntimeResult { + snapshot, + events: Vec::new(), + }); + } + + let next_tick = snapshot.tick.saturating_add(1); + let normalized_input = normalize_vector(command.x, command.y); + let mut sorted_owned = refresh_leader(std::mem::take(&mut snapshot.owned_entities)); + let Some(current_leader) = sorted_owned.first().cloned() else { + snapshot.status = BigFishRunStatus::Failed; + snapshot.event_log = tail_events(vec!["己方实体归零,本局失败".to_string()]); + snapshot.updated_at_micros = command.submitted_at_micros; + return Ok(BigFishRuntimeResult { + events: settlement_events(&snapshot, owner_user_id, command.submitted_at_micros), + snapshot, + }); + }; + + let next_leader = move_leader(¤t_leader, &normalized_input); + let mut owned_entities = vec![next_leader.clone()]; + for (index, follower) in sorted_owned.drain(1..).enumerate() { + owned_entities.push(move_follower(&follower, &next_leader, index + 1)); + } + let mut wild_entities = snapshot + .wild_entities + .into_iter() + .map(|entity| move_wild_entity(&entity, next_tick)) + .collect::>(); + let mut events = snapshot.event_log; + let mut removed_wild = Vec::::new(); + let mut removed_owned = Vec::::new(); + let mut newly_owned = Vec::::new(); + + for owned in &owned_entities { + if removed_owned.contains(&owned.entity_id) { + continue; + } + for wild in &wild_entities { + if removed_wild.contains(&wild.entity_id) { + continue; + } + if distance(owned, wild) > owned.radius + wild.radius { + continue; + } + if owned.level >= wild.level { + removed_wild.push(wild.entity_id.clone()); + newly_owned.push(build_entity( + format!("owned-from-{}-{next_tick}", wild.entity_id), + wild.level, + wild.position.x, + wild.position.y, + )); + events.push(format!("收编 {} 级实体", wild.level)); + } else { + removed_owned.push(owned.entity_id.clone()); + events.push(format!( + "{} 级己方实体被 {} 级野生实体吃掉", + owned.level, wild.level + )); + } + } + } + + owned_entities.retain(|entity| !removed_owned.contains(&entity.entity_id)); + owned_entities.extend(newly_owned); + wild_entities.retain(|entity| !removed_wild.contains(&entity.entity_id)); + + let merge_result = merge_owned_entities(owned_entities, next_tick); + owned_entities = refresh_leader(merge_result.owned_entities); + events.extend(merge_result.events); + + let player_level = owned_entities + .iter() + .map(|entity| entity.level) + .max() + .unwrap_or(0); + let leader = owned_entities.first().cloned(); + let camera_center = leader + .as_ref() + .map(|entity| entity.position.clone()) + .unwrap_or(snapshot.camera_center); + + wild_entities = wild_entities + .into_iter() + .filter_map(|entity| { + let should_cull = entity.level == player_level + || entity.level >= player_level.saturating_add(3) + || entity.level.saturating_add(3) <= player_level; + let offscreen_seconds = if should_cull && is_offscreen(&entity, &camera_center) { + entity.offscreen_seconds + TICK_SECONDS + } else { + 0.0 + }; + (offscreen_seconds < 3.0).then_some(BigFishRuntimeEntitySnapshot { + offscreen_seconds, + ..entity + }) + }) + .collect(); + + while wild_entities.len() < DEFAULT_WILD_COUNT { + wild_entities.push(build_wild_entity( + next_tick, + wild_entities.len() as u64 + next_tick, + player_level.max(1), + snapshot.win_level, + &camera_center, + )); + } + + let status = if owned_entities.is_empty() { + events.push("己方实体归零,本局失败".to_string()); + BigFishRunStatus::Failed + } else if player_level >= snapshot.win_level { + events.push("获得最高等级实体,通关".to_string()); + BigFishRunStatus::Won + } else { + BigFishRunStatus::Running + }; + + let next_snapshot = BigFishRuntimeSnapshot { + run_id: snapshot.run_id, + session_id: snapshot.session_id, + status, + tick: next_tick, + player_level, + win_level: snapshot.win_level, + leader_entity_id: leader.map(|entity| entity.entity_id), + owned_entities, + wild_entities, + camera_center, + last_input: normalized_input, + event_log: tail_events(events), + updated_at_micros: command.submitted_at_micros, + }; + let events = settlement_events(&next_snapshot, owner_user_id, command.submitted_at_micros); + + Ok(BigFishRuntimeResult { + snapshot: next_snapshot, + events, + }) +} + +pub fn serialize_runtime_snapshot( + snapshot: &BigFishRuntimeSnapshot, +) -> Result { + serde_json::to_string(snapshot) +} + +pub fn deserialize_runtime_snapshot( + value: &str, +) -> Result { + serde_json::from_str(value) +} + +fn build_entity(entity_id: String, level: u32, x: f32, y: f32) -> BigFishRuntimeEntitySnapshot { + BigFishRuntimeEntitySnapshot { + entity_id, + level, + position: BigFishVector2 { x, y }, + radius: entity_radius(level), + offscreen_seconds: 0.0, + } +} + +fn entity_radius(level: u32) -> f32 { + 18.0 + level as f32 * 4.0 +} + +fn normalize_vector(x: f32, y: f32) -> BigFishVector2 { + let length = (x * x + y * y).sqrt(); + if length <= 0.001 { + return BigFishVector2 { x: 0.0, y: 0.0 }; + } + let capped = length.min(1.0); + BigFishVector2 { + x: (x / length) * capped, + y: (y / length) * capped, + } +} + +fn distance(first: &BigFishRuntimeEntitySnapshot, second: &BigFishRuntimeEntitySnapshot) -> f32 { + let x = first.position.x - second.position.x; + let y = first.position.y - second.position.y; + (x * x + y * y).sqrt() +} + +fn clamp(value: f32, min: f32, max: f32) -> f32 { + value.max(min).min(max) +} + +fn spawn_level(player_level: u32, win_level: u32, index: u64) -> u32 { + if player_level <= 1 && index % 4 < 2 { + return 1; + } + let deltas = [-2_i32, -1, 1, 2]; + let delta = deltas[(index as usize) % deltas.len()]; + (player_level as i32 + delta).clamp(1, win_level as i32) as u32 +} + +fn spawn_position(center: &BigFishVector2, index: u64) -> BigFishVector2 { + let side = index % 4; + let offset = ((index * 97) % 980) as f32 - 490.0; + match side { + 0 => BigFishVector2 { + x: center.x - VIEW_WIDTH * 0.72, + y: center.y + offset, + }, + 1 => BigFishVector2 { + x: center.x + VIEW_WIDTH * 0.72, + y: center.y + offset, + }, + 2 => BigFishVector2 { + x: center.x + offset, + y: center.y - VIEW_HEIGHT * 0.64, + }, + _ => BigFishVector2 { + x: center.x + offset, + y: center.y + VIEW_HEIGHT * 0.64, + }, + } +} + +fn build_wild_entity( + tick: u64, + index: u64, + player_level: u32, + win_level: u32, + center: &BigFishVector2, +) -> BigFishRuntimeEntitySnapshot { + let level = spawn_level(player_level, win_level, index); + let position = spawn_position(center, index); + build_entity( + format!("wild-{tick}-{index}"), + level, + position.x, + position.y, + ) +} + +fn move_leader( + leader: &BigFishRuntimeEntitySnapshot, + input: &BigFishVector2, +) -> BigFishRuntimeEntitySnapshot { + BigFishRuntimeEntitySnapshot { + position: BigFishVector2 { + x: clamp( + leader.position.x + input.x * LEADER_SPEED * TICK_SECONDS, + -WORLD_HALF_WIDTH, + WORLD_HALF_WIDTH, + ), + y: clamp( + leader.position.y + input.y * LEADER_SPEED * TICK_SECONDS, + -WORLD_HALF_HEIGHT, + WORLD_HALF_HEIGHT, + ), + }, + ..leader.clone() + } +} + +fn move_follower( + follower: &BigFishRuntimeEntitySnapshot, + leader: &BigFishRuntimeEntitySnapshot, + index: usize, +) -> BigFishRuntimeEntitySnapshot { + let slot_y = (index as f32 * 0.7).sin() * 42.0; + let target = BigFishVector2 { + x: leader.position.x - 52.0 - index as f32 * 10.0, + y: leader.position.y + slot_y, + }; + let delta_x = target.x - follower.position.x; + let delta_y = target.y - follower.position.y; + let direction = normalize_vector(delta_x, delta_y); + let step = (FOLLOWER_SPEED * TICK_SECONDS).min((delta_x * delta_x + delta_y * delta_y).sqrt()); + BigFishRuntimeEntitySnapshot { + position: BigFishVector2 { + x: follower.position.x + direction.x * step, + y: follower.position.y + direction.y * step, + }, + ..follower.clone() + } +} + +fn move_wild_entity( + entity: &BigFishRuntimeEntitySnapshot, + tick: u64, +) -> BigFishRuntimeEntitySnapshot { + let phase = + tick as f32 * 0.23 + entity.level as f32 * 0.91 + entity.entity_id.len() as f32 * 0.13; + BigFishRuntimeEntitySnapshot { + position: BigFishVector2 { + x: clamp( + entity.position.x + + phase.cos() * (WILD_SPEED + entity.level as f32 * 3.0) * TICK_SECONDS, + -WORLD_HALF_WIDTH, + WORLD_HALF_WIDTH, + ), + y: clamp( + entity.position.y + + (phase * 0.72).sin() + * (WILD_SPEED + entity.level as f32 * 3.0) + * TICK_SECONDS, + -WORLD_HALF_HEIGHT, + WORLD_HALF_HEIGHT, + ), + }, + ..entity.clone() + } +} + +#[derive(Clone, Debug)] +struct MergeOwnedEntitiesResult { + owned_entities: Vec, + events: Vec, +} + +fn merge_owned_entities( + mut owned_entities: Vec, + tick: u64, +) -> MergeOwnedEntitiesResult { + let mut events = Vec::new(); + let mut changed = true; + while changed { + changed = false; + for level in 1..32 { + let same_level = owned_entities + .iter() + .enumerate() + .filter(|(_, entity)| entity.level == level) + .take(3) + .map(|(index, entity)| (index, entity.clone())) + .collect::>(); + if same_level.len() < 3 { + continue; + } + let center = + same_level + .iter() + .fold(BigFishVector2 { x: 0.0, y: 0.0 }, |acc, (_, entity)| { + BigFishVector2 { + x: acc.x + entity.position.x / 3.0, + y: acc.y + entity.position.y / 3.0, + } + }); + let remove_indices = same_level + .iter() + .map(|(index, _)| *index) + .collect::>(); + owned_entities = owned_entities + .into_iter() + .enumerate() + .filter_map(|(index, entity)| (!remove_indices.contains(&index)).then_some(entity)) + .collect(); + owned_entities.push(build_entity( + format!("owned-merge-{}-{tick}", level + 1), + level + 1, + center.x, + center.y, + )); + events.push(format!("3 个 {level} 级实体合成 {} 级", level + 1)); + changed = true; + break; + } + } + + MergeOwnedEntitiesResult { + owned_entities, + events, + } +} + +fn is_offscreen(entity: &BigFishRuntimeEntitySnapshot, camera_center: &BigFishVector2) -> bool { + entity.position.x + entity.radius < camera_center.x - VIEW_WIDTH / 2.0 + || entity.position.x - entity.radius > camera_center.x + VIEW_WIDTH / 2.0 + || entity.position.y + entity.radius < camera_center.y - VIEW_HEIGHT / 2.0 + || entity.position.y - entity.radius > camera_center.y + VIEW_HEIGHT / 2.0 +} + +fn refresh_leader( + mut owned_entities: Vec, +) -> Vec { + owned_entities.sort_by(|left, right| { + right + .level + .cmp(&left.level) + .then_with(|| left.entity_id.cmp(&right.entity_id)) + }); + owned_entities +} + +fn tail_events(events: Vec) -> Vec { + events + .into_iter() + .rev() + .take(5) + .collect::>() + .into_iter() + .rev() + .collect() +} + +fn settlement_events( + snapshot: &BigFishRuntimeSnapshot, + owner_user_id: String, + occurred_at_micros: i64, +) -> Vec { + if snapshot.status == BigFishRunStatus::Running { + return Vec::new(); + } + + vec![BigFishDomainEvent::RuntimeRunSettled { + run_id: snapshot.run_id.clone(), + session_id: snapshot.session_id.clone(), + owner_user_id, + status: snapshot.status.as_str().to_string(), + occurred_at_micros, + }] +} + #[cfg(test)] mod tests { use super::*; @@ -133,4 +660,63 @@ mod tests { assert!(result.readiness.publish_ready); assert!(result.readiness.blockers.is_empty()); } + + #[test] + fn start_big_fish_run_builds_server_owned_initial_snapshot() { + let draft = compile_default_draft(&infer_anchor_pack("深海", None)); + let result = start_big_fish_run(StartBigFishRunCommand { + run_id: "big-fish-run-1".to_string(), + session_id: "big-fish-session-1".to_string(), + owner_user_id: "user-1".to_string(), + draft: Some(draft), + work_level_count: None, + started_at_micros: 1, + }) + .expect("run"); + + assert_eq!(result.snapshot.status, BigFishRunStatus::Running); + assert_eq!(result.snapshot.player_level, 1); + assert_eq!(result.snapshot.win_level, 8); + assert!(!result.snapshot.wild_entities.is_empty()); + assert_eq!(result.events.len(), 1); + } + + #[test] + fn submit_big_fish_input_advances_and_keeps_runtime_truth_in_domain() { + let mut result = start_big_fish_run(StartBigFishRunCommand { + run_id: "big-fish-run-2".to_string(), + session_id: "big-fish-session-2".to_string(), + owner_user_id: "user-1".to_string(), + draft: None, + work_level_count: Some(3), + started_at_micros: 1, + }) + .expect("run"); + result.snapshot.wild_entities = vec![BigFishRuntimeEntitySnapshot { + entity_id: "wild-touching".to_string(), + level: 1, + position: BigFishVector2 { x: 10.0, y: 0.0 }, + radius: 22.0, + offscreen_seconds: 0.0, + }]; + + let advanced = submit_big_fish_input(SubmitBigFishInputCommand { + owner_user_id: "user-1".to_string(), + x: 0.0, + y: 0.0, + submitted_at_micros: 2, + current_snapshot: result.snapshot, + }) + .expect("advanced"); + + assert_eq!(advanced.snapshot.tick, 1); + assert!(advanced.snapshot.owned_entities.len() >= 2); + assert!( + advanced + .snapshot + .event_log + .iter() + .any(|event| event.contains("收编")) + ); + } } diff --git a/server-rs/crates/module-big-fish/src/commands.rs b/server-rs/crates/module-big-fish/src/commands.rs index 694c3c1a..0da0657c 100644 --- a/server-rs/crates/module-big-fish/src/commands.rs +++ b/server-rs/crates/module-big-fish/src/commands.rs @@ -2,7 +2,7 @@ //! //! 用于表达创建会话、写入消息、更新资产槽和推进运行态等输入。 -use crate::BigFishGameDraft; +use crate::{BigFishGameDraft, domain::BigFishRuntimeSnapshot}; /// 评估作品是否可以发布的纯领域命令。 /// @@ -15,3 +15,24 @@ pub struct EvaluateBigFishPublishReadinessCommand { pub draft: Option, pub evaluated_at_micros: i64, } + +/// 开始一局 Big Fish 运行态的纯领域命令。 +#[derive(Clone, Debug, PartialEq)] +pub struct StartBigFishRunCommand { + pub run_id: String, + pub session_id: String, + pub owner_user_id: String, + pub draft: Option, + pub work_level_count: Option, + pub started_at_micros: i64, +} + +/// 提交方向输入并推进一帧的纯领域命令。 +#[derive(Clone, Debug, PartialEq)] +pub struct SubmitBigFishInputCommand { + pub owner_user_id: String, + pub x: f32, + pub y: f32, + pub submitted_at_micros: i64, + pub current_snapshot: BigFishRuntimeSnapshot, +} diff --git a/server-rs/crates/module-big-fish/src/domain.rs b/server-rs/crates/module-big-fish/src/domain.rs index 60a6b429..7f41fd93 100644 --- a/server-rs/crates/module-big-fish/src/domain.rs +++ b/server-rs/crates/module-big-fish/src/domain.rs @@ -3,6 +3,10 @@ //! 后续迁移创作会话、资产槽和运行态聚合时,只保留玩法状态与规则; //! 图片生成、OSS 与 HTTP handler 均留在 adapter 层。 +use serde::{Deserialize, Serialize}; +#[cfg(feature = "spacetime-types")] +use spacetimedb::SpacetimeType; + /// 发布门禁的领域判定结果。 /// /// 这里不保存外部任务状态,只表达当前聚合快照是否满足发布条件。 @@ -14,3 +18,62 @@ pub struct BigFishPublishReadiness { pub blockers: Vec, pub evaluated_at_micros: i64, } + +/// 运行态一局的状态。 +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub enum BigFishRunStatus { + Running, + Won, + Failed, +} + +/// 运行态二维坐标。 +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct BigFishVector2 { + pub x: f32, + pub y: f32, +} + +/// 运行态实体快照。 +/// +/// 只表达服务端结算后的事实,前端不能据此反推规则并本地裁决。 +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct BigFishRuntimeEntitySnapshot { + pub entity_id: String, + pub level: u32, + pub position: BigFishVector2, + pub radius: f32, + pub offscreen_seconds: f32, +} + +/// 运行态一局快照。 +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct BigFishRuntimeSnapshot { + pub run_id: String, + pub session_id: String, + pub status: BigFishRunStatus, + pub tick: u64, + pub player_level: u32, + pub win_level: u32, + pub leader_entity_id: Option, + pub owned_entities: Vec, + pub wild_entities: Vec, + pub camera_center: BigFishVector2, + pub last_input: BigFishVector2, + pub event_log: Vec, + pub updated_at_micros: i64, +} + +impl BigFishRunStatus { + pub fn as_str(self) -> &'static str { + match self { + Self::Running => "running", + Self::Won => "won", + Self::Failed => "failed", + } + } +} diff --git a/server-rs/crates/module-big-fish/src/errors.rs b/server-rs/crates/module-big-fish/src/errors.rs index ec920f89..49d0afc7 100644 --- a/server-rs/crates/module-big-fish/src/errors.rs +++ b/server-rs/crates/module-big-fish/src/errors.rs @@ -11,6 +11,8 @@ use std::{error::Error, fmt}; pub enum BigFishApplicationError { MissingSessionId, MissingOwnerUserId, + MissingRunId, + InvalidRuntimeInput, } impl fmt::Display for BigFishApplicationError { @@ -18,6 +20,8 @@ impl fmt::Display for BigFishApplicationError { match self { Self::MissingSessionId => f.write_str("big_fish.session_id 不能为空"), Self::MissingOwnerUserId => f.write_str("big_fish.owner_user_id 不能为空"), + Self::MissingRunId => f.write_str("big_fish.run_id 不能为空"), + Self::InvalidRuntimeInput => f.write_str("big_fish.runtime_input 非法"), } } } diff --git a/server-rs/crates/module-big-fish/src/events.rs b/server-rs/crates/module-big-fish/src/events.rs index ba6201d3..17650477 100644 --- a/server-rs/crates/module-big-fish/src/events.rs +++ b/server-rs/crates/module-big-fish/src/events.rs @@ -15,4 +15,17 @@ pub enum BigFishDomainEvent { blockers: Vec, occurred_at_micros: i64, }, + RuntimeRunStarted { + run_id: String, + session_id: String, + owner_user_id: String, + occurred_at_micros: i64, + }, + RuntimeRunSettled { + run_id: String, + session_id: String, + owner_user_id: String, + status: String, + occurred_at_micros: i64, + }, } diff --git a/server-rs/crates/module-big-fish/src/lib.rs b/server-rs/crates/module-big-fish/src/lib.rs index 1b358831..d567c189 100644 --- a/server-rs/crates/module-big-fish/src/lib.rs +++ b/server-rs/crates/module-big-fish/src/lib.rs @@ -4,9 +4,18 @@ mod domain; mod errors; mod events; -pub use application::{EvaluateBigFishPublishReadinessResult, evaluate_publish_readiness}; -pub use commands::EvaluateBigFishPublishReadinessCommand; -pub use domain::BigFishPublishReadiness; +pub use application::{ + BigFishRuntimeResult, EvaluateBigFishPublishReadinessResult, deserialize_runtime_snapshot, + evaluate_publish_readiness, serialize_runtime_snapshot, start_big_fish_run, + submit_big_fish_input, +}; +pub use commands::{ + EvaluateBigFishPublishReadinessCommand, StartBigFishRunCommand, SubmitBigFishInputCommand, +}; +pub use domain::{ + BigFishPublishReadiness, BigFishRunStatus, BigFishRuntimeEntitySnapshot, + BigFishRuntimeSnapshot, BigFishVector2, +}; pub use errors::BigFishApplicationError; pub use events::BigFishDomainEvent; @@ -343,6 +352,40 @@ pub struct BigFishPlayRecordInput { pub played_at_micros: i64, } +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct BigFishRunStartInput { + pub run_id: String, + pub session_id: String, + pub owner_user_id: String, + pub started_at_micros: i64, +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct BigFishRunGetInput { + pub run_id: String, + pub owner_user_id: String, +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct BigFishInputSubmitInput { + pub run_id: String, + pub owner_user_id: String, + pub x: f32, + pub y: f32, + pub submitted_at_micros: i64, +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct BigFishRunProcedureResult { + pub ok: bool, + pub run_json: Option, + pub error_message: Option, +} + #[derive(Clone, Debug, PartialEq, Eq)] pub enum BigFishFieldError { MissingSessionId, @@ -352,6 +395,8 @@ pub enum BigFishFieldError { MissingDraft, InvalidLevel, InvalidAssetKind, + MissingRunId, + InvalidRuntimeInput, } impl BigFishCreationStage { @@ -691,6 +736,39 @@ pub fn validate_play_record_input(input: &BigFishPlayRecordInput) -> Result<(), Ok(()) } +pub fn validate_run_start_input(input: &BigFishRunStartInput) -> Result<(), BigFishFieldError> { + validate_session_owner(&input.session_id, &input.owner_user_id)?; + if normalize_required_string(&input.run_id).is_none() { + return Err(BigFishFieldError::MissingRunId); + } + Ok(()) +} + +pub fn validate_run_get_input(input: &BigFishRunGetInput) -> Result<(), BigFishFieldError> { + if normalize_required_string(&input.run_id).is_none() { + return Err(BigFishFieldError::MissingRunId); + } + if normalize_required_string(&input.owner_user_id).is_none() { + return Err(BigFishFieldError::MissingOwnerUserId); + } + Ok(()) +} + +pub fn validate_input_submit_input( + input: &BigFishInputSubmitInput, +) -> Result<(), BigFishFieldError> { + if normalize_required_string(&input.run_id).is_none() { + return Err(BigFishFieldError::MissingRunId); + } + if normalize_required_string(&input.owner_user_id).is_none() { + return Err(BigFishFieldError::MissingOwnerUserId); + } + if !input.x.is_finite() || !input.y.is_finite() { + return Err(BigFishFieldError::InvalidRuntimeInput); + } + Ok(()) +} + pub fn serialize_anchor_pack(anchor_pack: &BigFishAnchorPack) -> Result { serde_json::to_string(anchor_pack) } @@ -903,6 +981,8 @@ impl fmt::Display for BigFishFieldError { Self::MissingDraft => f.write_str("big_fish.draft 尚未编译"), Self::InvalidLevel => f.write_str("big_fish.level 不在合法等级范围内"), Self::InvalidAssetKind => f.write_str("big_fish.asset_kind 或动作位非法"), + Self::MissingRunId => f.write_str("big_fish.run_id 不能为空"), + Self::InvalidRuntimeInput => f.write_str("big_fish.runtime_input 非法"), } } } diff --git a/server-rs/crates/module-combat/Cargo.toml b/server-rs/crates/module-combat/Cargo.toml index 4954cf4d..30aa15b1 100644 --- a/server-rs/crates/module-combat/Cargo.toml +++ b/server-rs/crates/module-combat/Cargo.toml @@ -6,7 +6,7 @@ license.workspace = true [features] default = [] -spacetime-types = ["dep:spacetimedb"] +spacetime-types = ["dep:spacetimedb", "module-runtime-item/spacetime-types"] [dependencies] module-runtime-item = { path = "../module-runtime-item", default-features = false } diff --git a/server-rs/crates/module-combat/README.md b/server-rs/crates/module-combat/README.md index 98c134f8..04177b97 100644 --- a/server-rs/crates/module-combat/README.md +++ b/server-rs/crates/module-combat/README.md @@ -15,7 +15,7 @@ 当前已经真实落地: -1. `BattleMode / BattleStatus / CombatOutcome` +1. `src/domain.rs` 承接战斗 ID 前缀、版本、伤害、切磋保底生命、旧攻击 function 列表和 `BattleMode / BattleStatus / CombatOutcome` 2. `BattleStateInput / BattleStateSnapshot / BattleStateQueryInput` 3. `ResolveCombatActionInput / ResolveCombatActionResult` 4. `BattleStateProcedureResult / ResolveCombatActionProcedureResult` @@ -34,11 +34,12 @@ 落地依据见: -1. [../../../docs/technical/M4_MODULE_COMBAT_SPACETIMEDB_BASELINE_2026-04-21.md](../../../docs/technical/M4_MODULE_COMBAT_SPACETIMEDB_BASELINE_2026-04-21.md) -2. [../../../docs/technical/M4_MODULE_COMBAT_AXUM_FACADE_DESIGN_2026-04-21.md](../../../docs/technical/M4_MODULE_COMBAT_AXUM_FACADE_DESIGN_2026-04-21.md) -3. [../../../docs/technical/M4_MODULE_COMBAT_STATE_QUERY_DESIGN_2026-04-22.md](../../../docs/technical/M4_MODULE_COMBAT_STATE_QUERY_DESIGN_2026-04-22.md) -4. [../../../docs/technical/M4_PROGRESSION_QUEST_COMBAT_INTEGRATION_2026-04-21.md](../../../docs/technical/M4_PROGRESSION_QUEST_COMBAT_INTEGRATION_2026-04-21.md) -5. [../../../docs/prd/AI_NATIVE_BATTLE_SINGLE_ACTION_FUNCTION_PRD_2026-04-18.md](../../../docs/prd/AI_NATIVE_BATTLE_SINGLE_ACTION_FUNCTION_PRD_2026-04-18.md) +1. [../../../docs/technical/SERVER_RS_DDD_WP_RPG_COMBAT_DOMAIN_ENUM_REHOME_2026-04-29.md](../../../docs/technical/SERVER_RS_DDD_WP_RPG_COMBAT_DOMAIN_ENUM_REHOME_2026-04-29.md) +2. [../../../docs/technical/M4_MODULE_COMBAT_SPACETIMEDB_BASELINE_2026-04-21.md](../../../docs/technical/M4_MODULE_COMBAT_SPACETIMEDB_BASELINE_2026-04-21.md) +3. [../../../docs/technical/M4_MODULE_COMBAT_AXUM_FACADE_DESIGN_2026-04-21.md](../../../docs/technical/M4_MODULE_COMBAT_AXUM_FACADE_DESIGN_2026-04-21.md) +4. [../../../docs/technical/M4_MODULE_COMBAT_STATE_QUERY_DESIGN_2026-04-22.md](../../../docs/technical/M4_MODULE_COMBAT_STATE_QUERY_DESIGN_2026-04-22.md) +5. [../../../docs/technical/M4_PROGRESSION_QUEST_COMBAT_INTEGRATION_2026-04-21.md](../../../docs/technical/M4_PROGRESSION_QUEST_COMBAT_INTEGRATION_2026-04-21.md) +6. [../../../docs/prd/AI_NATIVE_BATTLE_SINGLE_ACTION_FUNCTION_PRD_2026-04-18.md](../../../docs/prd/AI_NATIVE_BATTLE_SINGLE_ACTION_FUNCTION_PRD_2026-04-18.md) ## 4. 边界约束 diff --git a/server-rs/crates/module-combat/src/domain.rs b/server-rs/crates/module-combat/src/domain.rs index b0ea813e..45253f91 100644 --- a/server-rs/crates/module-combat/src/domain.rs +++ b/server-rs/crates/module-combat/src/domain.rs @@ -2,3 +2,84 @@ //! //! 后续迁移 `BattleState` 与行动结算规则时,只保留单聚合内部状态变化; //! 背包奖励、成长记账和任务联动由应用服务或 SpacetimeDB 事务 adapter 编排。 + +use serde::{Deserialize, Serialize}; +#[cfg(feature = "spacetime-types")] +use spacetimedb::SpacetimeType; + +/// 战斗状态 ID 的稳定前缀,由领域层统一持有,避免应用层重复拼接规则。 +pub const BATTLE_STATE_ID_PREFIX: &str = "battle_"; +/// 新建战斗状态的初始版本号,用于乐观更新和快照投影。 +pub const INITIAL_BATTLE_VERSION: u32 = 1; +/// 普通战斗中敌方反击伤害占玩家输出的比例。 +pub const BASIC_FIGHT_COUNTER_RATIO: f32 = 0.14; +/// 普通战斗中敌方反击的最低伤害,保证战斗有稳定消耗。 +pub const MIN_FIGHT_COUNTER_DAMAGE: i32 = 4; +/// 切磋模式保底生命值,避免非生死战把玩家扣到 0。 +pub const SPAR_MIN_HP: i32 = 1; + +/// 旧版战斗动作 function id 白名单,仍由结算规则用于识别攻击类动作。 +pub(crate) const LEGACY_ATTACK_FUNCTION_IDS: [&str; 5] = [ + "battle_all_in_crush", + "battle_guard_break", + "battle_probe_pressure", + "battle_feint_step", + "battle_finisher_window", +]; + +/// 战斗模式,决定结算时是否允许击败玩家。 +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub enum BattleMode { + Fight, + Spar, +} + +impl BattleMode { + pub fn as_str(&self) -> &'static str { + match self { + Self::Fight => "fight", + Self::Spar => "spar", + } + } +} + +/// 战斗状态,用于标记战斗是否仍可继续接收行动。 +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub enum BattleStatus { + Ongoing, + Resolved, + Aborted, +} + +impl BattleStatus { + pub fn as_str(&self) -> &'static str { + match self { + Self::Ongoing => "ongoing", + Self::Resolved => "resolved", + Self::Aborted => "aborted", + } + } +} + +/// 单次战斗行动结算后的领域结果。 +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub enum CombatOutcome { + Ongoing, + Victory, + SparComplete, + Escaped, +} + +impl CombatOutcome { + pub fn as_str(&self) -> &'static str { + match self { + Self::Ongoing => "ongoing", + Self::Victory => "victory", + Self::SparComplete => "spar_complete", + Self::Escaped => "escaped", + } + } +} diff --git a/server-rs/crates/module-combat/src/lib.rs b/server-rs/crates/module-combat/src/lib.rs index 70471778..65ff8e61 100644 --- a/server-rs/crates/module-combat/src/lib.rs +++ b/server-rs/crates/module-combat/src/lib.rs @@ -4,8 +4,11 @@ mod domain; mod errors; mod events; +pub use domain::*; + use std::{error::Error, fmt}; +use crate::domain::LEGACY_ATTACK_FUNCTION_IDS; use module_runtime_item::{ RuntimeItemRewardItemSnapshot, TreasureFieldError, normalize_reward_item_snapshot, }; @@ -14,74 +17,6 @@ use shared_kernel::{build_prefixed_seed_id, normalize_required_string}; #[cfg(feature = "spacetime-types")] use spacetimedb::SpacetimeType; -pub const BATTLE_STATE_ID_PREFIX: &str = "battle_"; -pub const INITIAL_BATTLE_VERSION: u32 = 1; -pub const BASIC_FIGHT_COUNTER_RATIO: f32 = 0.14; -pub const MIN_FIGHT_COUNTER_DAMAGE: i32 = 4; -pub const SPAR_MIN_HP: i32 = 1; - -const LEGACY_ATTACK_FUNCTION_IDS: [&str; 5] = [ - "battle_all_in_crush", - "battle_guard_break", - "battle_probe_pressure", - "battle_feint_step", - "battle_finisher_window", -]; - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub enum BattleMode { - Fight, - Spar, -} - -impl BattleMode { - pub fn as_str(&self) -> &'static str { - match self { - Self::Fight => "fight", - Self::Spar => "spar", - } - } -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub enum BattleStatus { - Ongoing, - Resolved, - Aborted, -} - -impl BattleStatus { - pub fn as_str(&self) -> &'static str { - match self { - Self::Ongoing => "ongoing", - Self::Resolved => "resolved", - Self::Aborted => "aborted", - } - } -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub enum CombatOutcome { - Ongoing, - Victory, - SparComplete, - Escaped, -} - -impl CombatOutcome { - pub fn as_str(&self) -> &'static str { - match self { - Self::Ongoing => "ongoing", - Self::Victory => "victory", - Self::SparComplete => "spar_complete", - Self::Escaped => "escaped", - } - } -} - #[derive(Clone, Debug, PartialEq, Eq)] pub enum CombatFieldError { MissingBattleStateId, diff --git a/server-rs/crates/module-custom-world/README.md b/server-rs/crates/module-custom-world/README.md index b5bd3eae..111d739f 100644 --- a/server-rs/crates/module-custom-world/README.md +++ b/server-rs/crates/module-custom-world/README.md @@ -19,7 +19,7 @@ 当前已落地: 1. 真实 `Cargo.toml` crate scaffold -2. `CustomWorldPublicationStatus`、`CustomWorldThemeMode`、`CustomWorldGenerationMode` +2. `src/domain.rs` 承接 `CustomWorldPublicationStatus`、`CustomWorldThemeMode`、`CustomWorldGenerationMode` 3. `CustomWorldSessionStatus`、`RpgAgentStage` 4. `RpgAgentMessageRole`、`RpgAgentMessageKind` 5. `RpgAgentOperationType`、`RpgAgentOperationStatus` @@ -31,7 +31,7 @@ 当前 crate 仍然只承接: -1. 共享枚举与类型口径 +1. 共享枚举、进度常量与类型口径,基础枚举统一从 `src/domain.rs` 导出 2. 字段校验与字符串归一化 3. published profile compile 的最小编译摘要 contract 4. 后续 `spacetime-module` 聚合表时需要复用的领域边界 @@ -45,10 +45,11 @@ 当前设计依据: -1. [../../../docs/technical/SPACETIMEDB_CUSTOM_WORLD_AGENT_STAGE1_TABLE_DESIGN_2026-04-21.md](../../../docs/technical/SPACETIMEDB_CUSTOM_WORLD_AGENT_STAGE1_TABLE_DESIGN_2026-04-21.md) -2. [../../../backend-rewrite-tasklist/04_M5_CUSTOM_WORLD_AND_AGENT.md](../../../backend-rewrite-tasklist/04_M5_CUSTOM_WORLD_AND_AGENT.md) -3. [../../../docs/technical/SPACETIMEDB_CUSTOM_WORLD_PUBLISHED_PROFILE_COMPILE_STAGE3_DESIGN_2026-04-21.md](../../../docs/technical/SPACETIMEDB_CUSTOM_WORLD_PUBLISHED_PROFILE_COMPILE_STAGE3_DESIGN_2026-04-21.md) -4. [../../../docs/technical/SPACETIMEDB_CUSTOM_WORLD_PUBLISH_WORLD_STAGE4_DESIGN_2026-04-21.md](../../../docs/technical/SPACETIMEDB_CUSTOM_WORLD_PUBLISH_WORLD_STAGE4_DESIGN_2026-04-21.md) +1. [../../../docs/technical/SERVER_RS_DDD_WP_CW_DOMAIN_ENUM_REHOME_2026-04-29.md](../../../docs/technical/SERVER_RS_DDD_WP_CW_DOMAIN_ENUM_REHOME_2026-04-29.md) +2. [../../../docs/technical/SPACETIMEDB_CUSTOM_WORLD_AGENT_STAGE1_TABLE_DESIGN_2026-04-21.md](../../../docs/technical/SPACETIMEDB_CUSTOM_WORLD_AGENT_STAGE1_TABLE_DESIGN_2026-04-21.md) +3. [../../../backend-rewrite-tasklist/04_M5_CUSTOM_WORLD_AND_AGENT.md](../../../backend-rewrite-tasklist/04_M5_CUSTOM_WORLD_AND_AGENT.md) +4. [../../../docs/technical/SPACETIMEDB_CUSTOM_WORLD_PUBLISHED_PROFILE_COMPILE_STAGE3_DESIGN_2026-04-21.md](../../../docs/technical/SPACETIMEDB_CUSTOM_WORLD_PUBLISHED_PROFILE_COMPILE_STAGE3_DESIGN_2026-04-21.md) +5. [../../../docs/technical/SPACETIMEDB_CUSTOM_WORLD_PUBLISH_WORLD_STAGE4_DESIGN_2026-04-21.md](../../../docs/technical/SPACETIMEDB_CUSTOM_WORLD_PUBLISH_WORLD_STAGE4_DESIGN_2026-04-21.md) 后续与本 package 直接相关的任务包括: diff --git a/server-rs/crates/module-custom-world/src/domain.rs b/server-rs/crates/module-custom-world/src/domain.rs index e1ad6d84..4a0f0965 100644 --- a/server-rs/crates/module-custom-world/src/domain.rs +++ b/server-rs/crates/module-custom-world/src/domain.rs @@ -2,3 +2,305 @@ //! //! 后续迁移 profile、Agent 会话、草稿卡、发布门禁和画廊投影规则时, //! 只保留纯领域结构;LLM 推理、SSE 和 OSS 均留在外层 adapter。 + +use serde::{Deserialize, Serialize}; +#[cfg(feature = "spacetime-types")] +use spacetimedb::SpacetimeType; + +pub const MAX_PROGRESS_PERCENT: u32 = 100; + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub enum CustomWorldPublicationStatus { + Draft, + Published, +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub enum CustomWorldThemeMode { + Martial, + Arcane, + Machina, + Tide, + Rift, + Mythic, +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub enum CustomWorldGenerationMode { + Fast, + Full, +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub enum CustomWorldSessionStatus { + Clarifying, + ReadyToGenerate, + Generating, + Completed, + GenerationError, +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub enum RpgAgentStage { + CollectingIntent, + Clarifying, + FoundationReview, + ObjectRefining, + VisualRefining, + LongTailReview, + ReadyToPublish, + Published, + Error, +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub enum RpgAgentMessageRole { + User, + Assistant, + System, +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub enum RpgAgentMessageKind { + Chat, + Clarification, + Summary, + Checkpoint, + Warning, + ActionResult, +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub enum RpgAgentOperationType { + ProcessMessage, + DraftFoundation, + UpdateDraftCard, + SyncResultProfile, + GenerateCharacters, + GenerateLandmarks, + DeleteCharacters, + DeleteLandmarks, + GenerateRoleAssets, + SyncRoleAssets, + GenerateSceneAssets, + SyncSceneAssets, + ExpandLongTail, + PublishWorld, + RevertCheckpoint, +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub enum RpgAgentOperationStatus { + Queued, + Running, + Completed, + Failed, +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub enum RpgAgentDraftCardKind { + World, + Camp, + Faction, + Character, + Landmark, + Thread, + Chapter, + SceneChapter, + Carrier, + SidequestSeed, +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub enum RpgAgentDraftCardStatus { + Suggested, + Confirmed, + Locked, + Warning, +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub enum CustomWorldRoleAssetStatus { + Missing, + VisualReady, + AnimationsReady, + Complete, +} + +impl CustomWorldPublicationStatus { + pub fn as_str(&self) -> &'static str { + match self { + Self::Draft => "draft", + Self::Published => "published", + } + } +} + +impl CustomWorldThemeMode { + pub fn as_str(&self) -> &'static str { + match self { + Self::Martial => "martial", + Self::Arcane => "arcane", + Self::Machina => "machina", + Self::Tide => "tide", + Self::Rift => "rift", + Self::Mythic => "mythic", + } + } + + pub fn from_client_str(value: &str) -> Option { + match value.trim().to_ascii_lowercase().as_str() { + "martial" => Some(Self::Martial), + "arcane" => Some(Self::Arcane), + "machina" => Some(Self::Machina), + "tide" => Some(Self::Tide), + "rift" => Some(Self::Rift), + "mythic" => Some(Self::Mythic), + _ => None, + } + } +} + +impl CustomWorldGenerationMode { + pub fn as_str(&self) -> &'static str { + match self { + Self::Fast => "fast", + Self::Full => "full", + } + } +} + +impl CustomWorldSessionStatus { + pub fn as_str(&self) -> &'static str { + match self { + Self::Clarifying => "clarifying", + Self::ReadyToGenerate => "ready_to_generate", + Self::Generating => "generating", + Self::Completed => "completed", + Self::GenerationError => "generation_error", + } + } +} + +impl RpgAgentStage { + pub fn as_str(&self) -> &'static str { + match self { + Self::CollectingIntent => "collecting_intent", + Self::Clarifying => "clarifying", + Self::FoundationReview => "foundation_review", + Self::ObjectRefining => "object_refining", + Self::VisualRefining => "visual_refining", + Self::LongTailReview => "long_tail_review", + Self::ReadyToPublish => "ready_to_publish", + Self::Published => "published", + Self::Error => "error", + } + } +} + +impl RpgAgentMessageRole { + pub fn as_str(&self) -> &'static str { + match self { + Self::User => "user", + Self::Assistant => "assistant", + Self::System => "system", + } + } +} + +impl RpgAgentMessageKind { + pub fn as_str(&self) -> &'static str { + match self { + Self::Chat => "chat", + Self::Clarification => "clarification", + Self::Summary => "summary", + Self::Checkpoint => "checkpoint", + Self::Warning => "warning", + Self::ActionResult => "action_result", + } + } +} + +impl RpgAgentOperationType { + pub fn as_str(&self) -> &'static str { + match self { + Self::ProcessMessage => "process_message", + Self::DraftFoundation => "draft_foundation", + Self::UpdateDraftCard => "update_draft_card", + Self::SyncResultProfile => "sync_result_profile", + Self::GenerateCharacters => "generate_characters", + Self::GenerateLandmarks => "generate_landmarks", + Self::DeleteCharacters => "delete_characters", + Self::DeleteLandmarks => "delete_landmarks", + Self::GenerateRoleAssets => "generate_role_assets", + Self::SyncRoleAssets => "sync_role_assets", + Self::GenerateSceneAssets => "generate_scene_assets", + Self::SyncSceneAssets => "sync_scene_assets", + Self::ExpandLongTail => "expand_long_tail", + Self::PublishWorld => "publish_world", + Self::RevertCheckpoint => "revert_checkpoint", + } + } +} + +impl RpgAgentOperationStatus { + pub fn as_str(&self) -> &'static str { + match self { + Self::Queued => "queued", + Self::Running => "running", + Self::Completed => "completed", + Self::Failed => "failed", + } + } +} + +impl RpgAgentDraftCardKind { + pub fn as_str(&self) -> &'static str { + match self { + Self::World => "world", + Self::Camp => "camp", + Self::Faction => "faction", + Self::Character => "character", + Self::Landmark => "landmark", + Self::Thread => "thread", + Self::Chapter => "chapter", + Self::SceneChapter => "scene_chapter", + Self::Carrier => "carrier", + Self::SidequestSeed => "sidequest_seed", + } + } +} + +impl RpgAgentDraftCardStatus { + pub fn as_str(&self) -> &'static str { + match self { + Self::Suggested => "suggested", + Self::Confirmed => "confirmed", + Self::Locked => "locked", + Self::Warning => "warning", + } + } +} + +impl CustomWorldRoleAssetStatus { + pub fn as_str(&self) -> &'static str { + match self { + Self::Missing => "missing", + Self::VisualReady => "visual_ready", + Self::AnimationsReady => "animations_ready", + Self::Complete => "complete", + } + } +} diff --git a/server-rs/crates/module-custom-world/src/lib.rs b/server-rs/crates/module-custom-world/src/lib.rs index 405a5aab..88ba4c90 100644 --- a/server-rs/crates/module-custom-world/src/lib.rs +++ b/server-rs/crates/module-custom-world/src/lib.rs @@ -4,6 +4,8 @@ mod domain; mod errors; mod events; +pub use domain::*; + use std::{error::Error, fmt}; use serde::{Deserialize, Serialize}; @@ -11,138 +13,6 @@ use serde_json::{Map, Value}; #[cfg(feature = "spacetime-types")] use spacetimedb::SpacetimeType; -pub const MAX_PROGRESS_PERCENT: u32 = 100; - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub enum CustomWorldPublicationStatus { - Draft, - Published, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub enum CustomWorldThemeMode { - Martial, - Arcane, - Machina, - Tide, - Rift, - Mythic, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub enum CustomWorldGenerationMode { - Fast, - Full, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub enum CustomWorldSessionStatus { - Clarifying, - ReadyToGenerate, - Generating, - Completed, - GenerationError, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub enum RpgAgentStage { - CollectingIntent, - Clarifying, - FoundationReview, - ObjectRefining, - VisualRefining, - LongTailReview, - ReadyToPublish, - Published, - Error, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub enum RpgAgentMessageRole { - User, - Assistant, - System, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub enum RpgAgentMessageKind { - Chat, - Clarification, - Summary, - Checkpoint, - Warning, - ActionResult, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub enum RpgAgentOperationType { - ProcessMessage, - DraftFoundation, - UpdateDraftCard, - SyncResultProfile, - GenerateCharacters, - GenerateLandmarks, - DeleteCharacters, - DeleteLandmarks, - GenerateRoleAssets, - SyncRoleAssets, - GenerateSceneAssets, - SyncSceneAssets, - ExpandLongTail, - PublishWorld, - RevertCheckpoint, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub enum RpgAgentOperationStatus { - Queued, - Running, - Completed, - Failed, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub enum RpgAgentDraftCardKind { - World, - Camp, - Faction, - Character, - Landmark, - Thread, - Chapter, - SceneChapter, - Carrier, - SidequestSeed, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub enum RpgAgentDraftCardStatus { - Suggested, - Confirmed, - Locked, - Warning, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub enum CustomWorldRoleAssetStatus { - Missing, - VisualReady, - AnimationsReady, - Complete, -} - #[derive(Clone, Debug, PartialEq, Eq)] pub enum CustomWorldFieldError { MissingProfileId, @@ -688,172 +558,6 @@ pub struct CustomWorldPublishWorldResult { pub error_message: Option, } -impl CustomWorldPublicationStatus { - pub fn as_str(&self) -> &'static str { - match self { - Self::Draft => "draft", - Self::Published => "published", - } - } -} - -impl CustomWorldThemeMode { - pub fn as_str(&self) -> &'static str { - match self { - Self::Martial => "martial", - Self::Arcane => "arcane", - Self::Machina => "machina", - Self::Tide => "tide", - Self::Rift => "rift", - Self::Mythic => "mythic", - } - } - - pub fn from_client_str(value: &str) -> Option { - match value.trim().to_ascii_lowercase().as_str() { - "martial" => Some(Self::Martial), - "arcane" => Some(Self::Arcane), - "machina" => Some(Self::Machina), - "tide" => Some(Self::Tide), - "rift" => Some(Self::Rift), - "mythic" => Some(Self::Mythic), - _ => None, - } - } -} - -impl CustomWorldGenerationMode { - pub fn as_str(&self) -> &'static str { - match self { - Self::Fast => "fast", - Self::Full => "full", - } - } -} - -impl CustomWorldSessionStatus { - pub fn as_str(&self) -> &'static str { - match self { - Self::Clarifying => "clarifying", - Self::ReadyToGenerate => "ready_to_generate", - Self::Generating => "generating", - Self::Completed => "completed", - Self::GenerationError => "generation_error", - } - } -} - -impl RpgAgentStage { - pub fn as_str(&self) -> &'static str { - match self { - Self::CollectingIntent => "collecting_intent", - Self::Clarifying => "clarifying", - Self::FoundationReview => "foundation_review", - Self::ObjectRefining => "object_refining", - Self::VisualRefining => "visual_refining", - Self::LongTailReview => "long_tail_review", - Self::ReadyToPublish => "ready_to_publish", - Self::Published => "published", - Self::Error => "error", - } - } -} - -impl RpgAgentMessageRole { - pub fn as_str(&self) -> &'static str { - match self { - Self::User => "user", - Self::Assistant => "assistant", - Self::System => "system", - } - } -} - -impl RpgAgentMessageKind { - pub fn as_str(&self) -> &'static str { - match self { - Self::Chat => "chat", - Self::Clarification => "clarification", - Self::Summary => "summary", - Self::Checkpoint => "checkpoint", - Self::Warning => "warning", - Self::ActionResult => "action_result", - } - } -} - -impl RpgAgentOperationType { - pub fn as_str(&self) -> &'static str { - match self { - Self::ProcessMessage => "process_message", - Self::DraftFoundation => "draft_foundation", - Self::UpdateDraftCard => "update_draft_card", - Self::SyncResultProfile => "sync_result_profile", - Self::GenerateCharacters => "generate_characters", - Self::GenerateLandmarks => "generate_landmarks", - Self::DeleteCharacters => "delete_characters", - Self::DeleteLandmarks => "delete_landmarks", - Self::GenerateRoleAssets => "generate_role_assets", - Self::SyncRoleAssets => "sync_role_assets", - Self::GenerateSceneAssets => "generate_scene_assets", - Self::SyncSceneAssets => "sync_scene_assets", - Self::ExpandLongTail => "expand_long_tail", - Self::PublishWorld => "publish_world", - Self::RevertCheckpoint => "revert_checkpoint", - } - } -} - -impl RpgAgentOperationStatus { - pub fn as_str(&self) -> &'static str { - match self { - Self::Queued => "queued", - Self::Running => "running", - Self::Completed => "completed", - Self::Failed => "failed", - } - } -} - -impl RpgAgentDraftCardKind { - pub fn as_str(&self) -> &'static str { - match self { - Self::World => "world", - Self::Camp => "camp", - Self::Faction => "faction", - Self::Character => "character", - Self::Landmark => "landmark", - Self::Thread => "thread", - Self::Chapter => "chapter", - Self::SceneChapter => "scene_chapter", - Self::Carrier => "carrier", - Self::SidequestSeed => "sidequest_seed", - } - } -} - -impl RpgAgentDraftCardStatus { - pub fn as_str(&self) -> &'static str { - match self { - Self::Suggested => "suggested", - Self::Confirmed => "confirmed", - Self::Locked => "locked", - Self::Warning => "warning", - } - } -} - -impl CustomWorldRoleAssetStatus { - pub fn as_str(&self) -> &'static str { - match self { - Self::Missing => "missing", - Self::VisualReady => "visual_ready", - Self::AnimationsReady => "animations_ready", - Self::Complete => "complete", - } - } -} - pub fn validate_custom_world_profile_fields( profile_id: &str, owner_user_id: &str, diff --git a/server-rs/crates/module-puzzle/README.md b/server-rs/crates/module-puzzle/README.md new file mode 100644 index 00000000..ea2067ea --- /dev/null +++ b/server-rs/crates/module-puzzle/README.md @@ -0,0 +1,51 @@ +# module-puzzle 独立模块 package 说明 + +日期:`2026-04-29` + +## 1. package 职责 + +`module-puzzle` 是拼图创作、作品 profile 与运行态规则模块 package,后续负责: + +1. Puzzle Agent 会话、消息、锚点包与结果草稿的纯领域模型。 +2. 拼图作品 profile、发布门禁、标签规则与作品列表投影。 +3. 拼图运行态开局、交换、拖动、合并、拆分、过关和下一关推荐规则。 +4. 与 `spacetime-module` 的拼图表、reducer、procedure 和事件聚合对接。 + +## 2. 当前阶段说明 + +当前阶段已经不再只是目录占位,已先固定拼图领域 contract 与最小规则函数。 + +当前已落地: + +1. `src/domain.rs` 承接 Puzzle 基础 ID 前缀、标签数量、洗牌次数常量、基础枚举、Agent session、message、anchor、result draft、work profile、runtime board/run 等领域类型。 +2. `src/commands.rs` 承接 SpacetimeDB procedure/reducer 写入输入。 +3. `src/application.rs` 承接 procedure 返回包装、标签归一化、草稿编译、发布覆盖、开局、交换、拖动、合并、拆分和下一关推荐的纯规则。 +4. `src/errors.rs` 承接拼图字段错误与中文错误文案。 +5. `src/events.rs` 承接草稿变化、作品发布和运行态推进的最小领域事件。 +6. `spacetime-types` feature 下可供 SpacetimeDB 绑定复用的类型派生。 + +当前 crate 仍然只承接: + +1. 拼图领域常量、枚举、快照类型和纯规则。 +2. 字段校验、标签归一化与运行态规则。 +3. 后续 `spacetime-module` 聚合表时需要复用的领域边界。 + +当前阶段明确不提前进入: + +1. 图片生成、OSS 上传或 AI prompt 编排。 +2. Axum 路由、SSE 或前端展示状态。 +3. SpacetimeDB table、reducer、procedure 的直接定义。 + +当前设计依据: + +1. [../../../docs/technical/SERVER_RS_DDD_WP_PZ_DOMAIN_ENUM_REHOME_2026-04-29.md](../../../docs/technical/SERVER_RS_DDD_WP_PZ_DOMAIN_ENUM_REHOME_2026-04-29.md) +2. [../../../docs/technical/SERVER_RS_DDD_WP_PZ_DOMAIN_SPLIT_2026-04-29.md](../../../docs/technical/SERVER_RS_DDD_WP_PZ_DOMAIN_SPLIT_2026-04-29.md) +3. [../../../docs/technical/PUZZLE_CREATION_AND_RUNTIME_MINIMAL_IMPLEMENTATION_2026-04-22.md](../../../docs/technical/PUZZLE_CREATION_AND_RUNTIME_MINIMAL_IMPLEMENTATION_2026-04-22.md) +4. [../../../docs/technical/PUZZLE_RESULT_AUTOSAVE_AND_TAG_GATE_FIX_2026-04-28.md](../../../docs/technical/PUZZLE_RESULT_AUTOSAVE_AND_TAG_GATE_FIX_2026-04-28.md) + +## 3. 边界约束 + +1. `module-puzzle` 不直接调用图片生成、OSS、HTTP、SSE 或 SpacetimeDB SDK。 +2. 领域函数不依赖前端临时状态,拼图运行态真相最终由 SpacetimeDB 表承载。 +3. `api-server` 负责 LLM、图片生成和请求响应映射;`spacetime-module` 负责表、reducer、procedure 与事件。 +4. 后续迁移必须优先复用现有 `domain.rs`、`commands.rs`、`application.rs`、`events.rs`、`errors.rs` 骨架。 diff --git a/server-rs/crates/module-puzzle/src/application.rs b/server-rs/crates/module-puzzle/src/application.rs index 0d900e85..720584d0 100644 --- a/server-rs/crates/module-puzzle/src/application.rs +++ b/server-rs/crates/module-puzzle/src/application.rs @@ -1,3 +1,1869 @@ -//! 拼图应用编排过渡落位。 +//! 拼图应用编排与纯规则。 //! -//! 这里只组合拼图规则并返回应用结果或领域事件,不直接调用图片服务。 +//! 本层组合 Agent 草稿、作品发布、运行态棋盘和排行榜推荐规则; +//! 图片生成、OSS、HTTP response shape 与 SpacetimeDB 写回由外层 adapter 负责。 + +use std::collections::{BTreeMap, BTreeSet, VecDeque}; + +use serde::{Deserialize, Serialize}; +use shared_kernel::{normalize_required_string, normalize_string_list}; +#[cfg(feature = "spacetime-types")] +use spacetimedb::SpacetimeType; + +use crate::{domain::*, errors::PuzzleFieldError}; + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct PuzzleAgentSessionProcedureResult { + pub ok: bool, + pub session_json: Option, + pub error_message: Option, +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct PuzzleWorksProcedureResult { + pub ok: bool, + pub items_json: Option, + pub error_message: Option, +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct PuzzleWorkProcedureResult { + pub ok: bool, + pub item_json: Option, + pub error_message: Option, +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct PuzzleRunProcedureResult { + pub ok: bool, + pub run_json: Option, + pub error_message: Option, +} + +pub fn empty_anchor_pack() -> PuzzleAnchorPack { + PuzzleAnchorPack { + theme_promise: PuzzleAnchorItem { + key: "themePromise".to_string(), + label: "题材承诺".to_string(), + value: String::new(), + status: PuzzleAnchorStatus::Missing, + }, + visual_subject: PuzzleAnchorItem { + key: "visualSubject".to_string(), + label: "画面主体".to_string(), + value: String::new(), + status: PuzzleAnchorStatus::Missing, + }, + visual_mood: PuzzleAnchorItem { + key: "visualMood".to_string(), + label: "视觉气质".to_string(), + value: String::new(), + status: PuzzleAnchorStatus::Missing, + }, + composition_hooks: PuzzleAnchorItem { + key: "compositionHooks".to_string(), + label: "拼图记忆点".to_string(), + value: String::new(), + status: PuzzleAnchorStatus::Missing, + }, + tags_and_forbidden: PuzzleAnchorItem { + key: "tagsAndForbidden".to_string(), + label: "标签与禁忌".to_string(), + value: String::new(), + status: PuzzleAnchorStatus::Missing, + }, + } +} + +pub fn infer_anchor_pack(seed_text: &str, latest_message: Option<&str>) -> PuzzleAnchorPack { + let source = normalize_required_string(latest_message.unwrap_or(seed_text)) + .or_else(|| normalize_required_string(seed_text)) + .unwrap_or_else(|| "童话森林里的发光猫咪遗迹".to_string()); + let mut pack = empty_anchor_pack(); + pack.theme_promise.value = infer_theme_promise(&source); + pack.theme_promise.status = PuzzleAnchorStatus::Inferred; + pack.visual_subject.value = infer_visual_subject(&source); + pack.visual_subject.status = PuzzleAnchorStatus::Inferred; + pack.visual_mood.value = infer_visual_mood(&source); + pack.visual_mood.status = PuzzleAnchorStatus::Inferred; + pack.composition_hooks.value = infer_composition_hooks(&source); + pack.composition_hooks.status = PuzzleAnchorStatus::Inferred; + pack.tags_and_forbidden.value = infer_tags_and_forbidden(&source); + pack.tags_and_forbidden.status = PuzzleAnchorStatus::Inferred; + pack +} + +pub fn build_creator_intent( + anchor_pack: &PuzzleAnchorPack, + messages: &[PuzzleAgentMessageSnapshot], +) -> PuzzleCreatorIntent { + PuzzleCreatorIntent { + source_mode: "agent_chat".to_string(), + raw_messages_summary: messages + .iter() + .rev() + .take(4) + .map(|entry| entry.text.clone()) + .collect::>() + .join(" / "), + theme_promise: anchor_pack.theme_promise.value.clone(), + visual_subject: anchor_pack.visual_subject.value.clone(), + visual_mood: split_phrase_list(&anchor_pack.visual_mood.value), + composition_hooks: split_phrase_list(&anchor_pack.composition_hooks.value), + theme_tags: split_phrase_list(&anchor_pack.tags_and_forbidden.value) + .into_iter() + .take(PUZZLE_MAX_TAG_COUNT) + .collect(), + forbidden_directives: vec![extract_forbidden_directive( + &anchor_pack.tags_and_forbidden.value, + )], + } +} + +pub fn compile_result_draft( + anchor_pack: &PuzzleAnchorPack, + messages: &[PuzzleAgentMessageSnapshot], +) -> PuzzleResultDraft { + let creator_intent = build_creator_intent(anchor_pack, messages); + let normalized_tags = normalize_theme_tags(creator_intent.theme_tags.clone()); + let level_name = build_level_name(anchor_pack, &normalized_tags); + PuzzleResultDraft { + level_name, + summary: format!( + "{},主体是{},氛围偏{}。", + fallback_text(&anchor_pack.theme_promise.value, "梦幻题材"), + fallback_text(&anchor_pack.visual_subject.value, "画面主体"), + fallback_text(&anchor_pack.visual_mood.value, "温暖") + ), + theme_tags: normalized_tags, + forbidden_directives: creator_intent.forbidden_directives.clone(), + creator_intent: Some(creator_intent), + anchor_pack: anchor_pack.clone(), + candidates: Vec::new(), + selected_candidate_id: None, + cover_image_src: None, + cover_asset_id: None, + generation_status: "idle".to_string(), + } +} + +pub fn build_generated_candidates( + session_id: &str, + prompt_text: Option<&str>, + draft: &PuzzleResultDraft, + candidate_count: u32, + now_micros: i64, +) -> Result, PuzzleFieldError> { + let session_id = + normalize_required_string(session_id).ok_or(PuzzleFieldError::MissingSessionId)?; + let count = candidate_count.max(1).min(1); + let prompt = normalize_required_string(prompt_text.unwrap_or(&draft.summary)) + .unwrap_or_else(|| draft.summary.clone()); + + Ok((0..count) + .map(|index| { + let candidate_seed = now_micros + i64::from(index); + let candidate_id = format!("{session_id}-candidate-{}", index + 1); + PuzzleGeneratedImageCandidate { + candidate_id: candidate_id.clone(), + // 拼图图片的正式持久化由 api-server 上传 OSS;这里仅保留 reducer + // 单测/保底路径构造,前缀必须与 OSS 兼容路由一致,不能再指向 public 目录。 + image_src: format!( + "/generated-puzzle-assets/{session_id}/{candidate_seed}/cover.svg" + ), + asset_id: format!("puzzle-cover-{candidate_seed}"), + prompt: prompt.clone(), + actual_prompt: Some(prompt.clone()), + source_type: "generated".to_string(), + selected: index == 0, + } + }) + .collect()) +} + +pub fn apply_selected_candidate( + mut draft: PuzzleResultDraft, + candidate_id: &str, +) -> Result { + let candidate_id = + normalize_required_string(candidate_id).ok_or(PuzzleFieldError::MissingText)?; + let mut selected_cover_image_src = None; + let mut selected_cover_asset_id = None; + let mut matched = false; + + for candidate in &mut draft.candidates { + candidate.selected = candidate.candidate_id == candidate_id; + if candidate.selected { + matched = true; + selected_cover_image_src = Some(candidate.image_src.clone()); + selected_cover_asset_id = Some(candidate.asset_id.clone()); + } + } + + if !matched { + return Err(PuzzleFieldError::InvalidOperation); + } + + draft.selected_candidate_id = Some(candidate_id); + draft.cover_image_src = selected_cover_image_src; + draft.cover_asset_id = selected_cover_asset_id; + draft.generation_status = "ready".to_string(); + Ok(draft) +} + +pub fn build_result_preview( + draft: &PuzzleResultDraft, + author_display_name: Option<&str>, +) -> PuzzleResultPreviewEnvelope { + let blockers = validate_publish_requirements(draft, author_display_name); + PuzzleResultPreviewEnvelope { + draft: draft.clone(), + blockers, + quality_findings: Vec::new(), + publish_ready: validate_publish_requirements(draft, author_display_name).is_empty(), + } +} + +pub fn validate_publish_requirements( + draft: &PuzzleResultDraft, + author_display_name: Option<&str>, +) -> Vec { + let mut blockers = Vec::new(); + if normalize_required_string(&draft.level_name).is_none() { + blockers.push(PuzzleResultPreviewBlocker { + id: "missing-level-name".to_string(), + code: "MISSING_LEVEL_NAME".to_string(), + message: "关卡名不能为空".to_string(), + }); + } + if draft + .cover_image_src + .as_deref() + .map(str::trim) + .unwrap_or("") + .is_empty() + { + blockers.push(PuzzleResultPreviewBlocker { + id: "missing-cover-image".to_string(), + code: "MISSING_COVER_IMAGE".to_string(), + message: "正式拼图图片尚未确定".to_string(), + }); + } + if draft.theme_tags.len() < PUZZLE_MIN_TAG_COUNT + || draft.theme_tags.len() > PUZZLE_MAX_TAG_COUNT + { + blockers.push(PuzzleResultPreviewBlocker { + id: "invalid-tag-count".to_string(), + code: "INVALID_TAG_COUNT".to_string(), + message: "正式标签数量必须在 3 到 6 之间".to_string(), + }); + } + if normalize_required_string(author_display_name.unwrap_or("")).is_none() { + blockers.push(PuzzleResultPreviewBlocker { + id: "missing-author".to_string(), + code: "MISSING_AUTHOR".to_string(), + message: "作者信息不可读".to_string(), + }); + } + blockers +} + +pub fn create_work_profile( + work_id: String, + profile_id: String, + owner_user_id: String, + source_session_id: Option, + author_display_name: String, + draft: &PuzzleResultDraft, + updated_at_micros: i64, +) -> Result { + let author_display_name = normalize_required_string(author_display_name) + .ok_or(PuzzleFieldError::MissingAuthorDisplayName)?; + let preview = build_result_preview(draft, Some(&author_display_name)); + Ok(PuzzleWorkProfile { + work_id, + profile_id, + owner_user_id, + source_session_id, + author_display_name, + level_name: draft.level_name.clone(), + summary: draft.summary.clone(), + theme_tags: normalize_theme_tags(draft.theme_tags.clone()), + cover_image_src: draft.cover_image_src.clone(), + cover_asset_id: draft.cover_asset_id.clone(), + publication_status: PuzzlePublicationStatus::Draft, + updated_at_micros, + published_at_micros: None, + play_count: 0, + publish_ready: preview.publish_ready, + anchor_pack: draft.anchor_pack.clone(), + }) +} + +pub fn publish_work_profile( + mut profile: PuzzleWorkProfile, + draft: &PuzzleResultDraft, + published_at_micros: i64, +) -> Result { + if !validate_publish_requirements(draft, Some(&profile.author_display_name)).is_empty() { + return Err(PuzzleFieldError::InvalidOperation); + } + profile.level_name = draft.level_name.clone(); + profile.summary = draft.summary.clone(); + profile.theme_tags = normalize_theme_tags(draft.theme_tags.clone()); + profile.cover_image_src = draft.cover_image_src.clone(); + profile.cover_asset_id = draft.cover_asset_id.clone(); + profile.publication_status = PuzzlePublicationStatus::Published; + profile.publish_ready = true; + profile.updated_at_micros = published_at_micros; + profile.published_at_micros = Some(published_at_micros); + Ok(profile) +} + +/// 在发布前把结果页的轻量编辑字段覆盖回草稿真相。 +/// 这里只允许覆盖 PRD 明确要求的关卡名、摘要与标签,不额外扩到更多结果页元数据。 +pub fn apply_publish_overrides_to_draft( + draft: &PuzzleResultDraft, + level_name: Option, + summary: Option, + theme_tags: Option>, +) -> Result { + let mut next_draft = draft.clone(); + + if let Some(next_level_name) = level_name + && let Some(normalized_level_name) = normalize_required_string(&next_level_name) + { + next_draft.level_name = normalized_level_name; + } + + if let Some(next_summary) = summary + && let Some(normalized_summary) = normalize_required_string(&next_summary) + { + next_draft.summary = normalized_summary; + } + + if let Some(next_theme_tags) = theme_tags { + let normalized_theme_tags = normalize_theme_tags(next_theme_tags); + if normalized_theme_tags.len() < PUZZLE_MIN_TAG_COUNT + || normalized_theme_tags.len() > PUZZLE_MAX_TAG_COUNT + { + return Err(PuzzleFieldError::InvalidTagCount); + } + next_draft.theme_tags = normalized_theme_tags; + } + + Ok(next_draft) +} + +pub fn resolve_puzzle_grid_size(cleared_level_count: u32) -> u32 { + if cleared_level_count >= 3 { 4 } else { 3 } +} + +pub fn build_initial_board(grid_size: u32) -> Result { + build_initial_board_with_seed(grid_size, 0) +} + +pub fn build_initial_board_with_seed( + grid_size: u32, + shuffle_seed: u64, +) -> Result { + if !matches!(grid_size, 3 | 4) { + return Err(PuzzleFieldError::InvalidGridSize); + } + + let pieces = build_initial_pieces_without_correct_neighbors(grid_size, shuffle_seed); + + Ok(rebuild_board_snapshot(grid_size, pieces, None)) +} + +pub fn start_run( + run_id: String, + entry_profile: &PuzzleWorkProfile, + cleared_level_count: u32, +) -> Result { + let grid_size = resolve_puzzle_grid_size(cleared_level_count); + let shuffle_seed = puzzle_shuffle_seed( + &run_id, + &entry_profile.profile_id, + cleared_level_count + 1, + grid_size, + ); + start_run_with_shuffle_seed(run_id, entry_profile, cleared_level_count, shuffle_seed) +} + +pub fn start_run_with_shuffle_seed( + run_id: String, + entry_profile: &PuzzleWorkProfile, + cleared_level_count: u32, + shuffle_seed: u64, +) -> Result { + let grid_size = resolve_puzzle_grid_size(cleared_level_count); + let board = build_initial_board_with_seed(grid_size, shuffle_seed)?; + let started_at_ms = current_unix_ms(); + Ok(PuzzleRunSnapshot { + run_id: run_id.clone(), + entry_profile_id: entry_profile.profile_id.clone(), + cleared_level_count, + current_level_index: cleared_level_count + 1, + current_grid_size: grid_size, + played_profile_ids: vec![entry_profile.profile_id.clone()], + previous_level_tags: entry_profile.theme_tags.clone(), + current_level: Some(PuzzleRuntimeLevelSnapshot { + run_id, + level_index: cleared_level_count + 1, + grid_size, + profile_id: entry_profile.profile_id.clone(), + level_name: entry_profile.level_name.clone(), + author_display_name: entry_profile.author_display_name.clone(), + theme_tags: entry_profile.theme_tags.clone(), + cover_image_src: entry_profile.cover_image_src.clone(), + board, + status: PuzzleRuntimeLevelStatus::Playing, + started_at_ms, + cleared_at_ms: None, + elapsed_ms: None, + leaderboard_entries: Vec::new(), + }), + recommended_next_profile_id: None, + leaderboard_entries: Vec::new(), + }) +} + +pub fn swap_pieces( + run: &PuzzleRunSnapshot, + first_piece_id: &str, + second_piece_id: &str, +) -> Result { + let first_piece_id = + normalize_required_string(first_piece_id).ok_or(PuzzleFieldError::MissingPieceId)?; + let second_piece_id = + normalize_required_string(second_piece_id).ok_or(PuzzleFieldError::MissingPieceId)?; + let current_level = run + .current_level + .clone() + .ok_or(PuzzleFieldError::InvalidOperation)?; + if current_level.status == PuzzleRuntimeLevelStatus::Cleared { + return Err(PuzzleFieldError::InvalidOperation); + } + let mut pieces = current_level.board.pieces.clone(); + let first_index = pieces + .iter() + .position(|piece| piece.piece_id == first_piece_id) + .ok_or(PuzzleFieldError::MissingPieceId)?; + let second_index = pieces + .iter() + .position(|piece| piece.piece_id == second_piece_id) + .ok_or(PuzzleFieldError::MissingPieceId)?; + + let (first_row, first_col) = ( + pieces[first_index].current_row, + pieces[first_index].current_col, + ); + let (second_row, second_col) = ( + pieces[second_index].current_row, + pieces[second_index].current_col, + ); + pieces[first_index].current_row = second_row; + pieces[first_index].current_col = second_col; + pieces[second_index].current_row = first_row; + pieces[second_index].current_col = first_col; + + let affected_cells = [ + PuzzleCellPosition { + row: first_row, + col: first_col, + }, + PuzzleCellPosition { + row: second_row, + col: second_col, + }, + ]; + let next_board = rebuild_board_snapshot_for_affected_cells( + current_level.grid_size, + ¤t_level.board, + pieces, + affected_cells, + None, + ); + Ok(with_next_board(run, next_board)) +} + +pub fn drag_piece_or_group( + run: &PuzzleRunSnapshot, + piece_id: &str, + target_row: u32, + target_col: u32, +) -> Result { + let piece_id = normalize_required_string(piece_id).ok_or(PuzzleFieldError::MissingPieceId)?; + let current_level = run + .current_level + .clone() + .ok_or(PuzzleFieldError::InvalidOperation)?; + if current_level.status == PuzzleRuntimeLevelStatus::Cleared { + return Err(PuzzleFieldError::InvalidOperation); + } + let grid_size = current_level.grid_size; + if target_row >= grid_size || target_col >= grid_size { + return Err(PuzzleFieldError::InvalidTargetCell); + } + + let mut pieces = current_level.board.pieces.clone(); + let piece_index = pieces + .iter() + .position(|piece| piece.piece_id == piece_id) + .ok_or(PuzzleFieldError::MissingPieceId)?; + let source_group_id = pieces[piece_index].merged_group_id.clone(); + + let operation_cells = match source_group_id { + Some(group_id) => drag_group(&mut pieces, &group_id, target_row, target_col, grid_size)?, + None => drag_single_piece(&mut pieces, piece_index, target_row, target_col)?, + }; + + let next_board = rebuild_board_snapshot_for_affected_cells( + grid_size, + ¤t_level.board, + pieces, + operation_cells, + None, + ); + Ok(with_next_board(run, next_board)) +} + +pub fn rebuild_board_snapshot_for_affected_cells( + grid_size: u32, + previous_board: &PuzzleBoardSnapshot, + pieces: Vec, + affected_cells: impl IntoIterator, + selected_piece_id: Option, +) -> PuzzleBoardSnapshot { + let affected_scope = expand_affected_cells(grid_size, affected_cells); + if affected_scope.is_empty() || previous_board.merged_groups.is_empty() { + return rebuild_board_snapshot(grid_size, pieces, selected_piece_id); + } + + let mut recalculated_piece_ids = pieces + .iter() + .filter(|piece| affected_scope.contains(&(piece.current_row, piece.current_col))) + .map(|piece| piece.piece_id.clone()) + .collect::>(); + let previous_piece_by_id = previous_board + .pieces + .iter() + .map(|piece| (piece.piece_id.clone(), piece)) + .collect::>(); + + for piece_id in recalculated_piece_ids.clone() { + if let Some(previous_piece) = previous_piece_by_id.get(&piece_id) + && let Some(group_id) = previous_piece.merged_group_id.as_deref() + { + add_previous_group_piece_ids(previous_board, group_id, &mut recalculated_piece_ids); + } + } + + let mut preserved_groups = Vec::new(); + for group in &previous_board.merged_groups { + if group + .piece_ids + .iter() + .any(|piece_id| recalculated_piece_ids.contains(piece_id)) + { + continue; + } + let occupied_cells = group + .piece_ids + .iter() + .filter_map(|piece_id| { + pieces + .iter() + .find(|piece| piece.piece_id == *piece_id) + .map(|piece| PuzzleCellPosition { + row: piece.current_row, + col: piece.current_col, + }) + }) + .collect::>(); + if occupied_cells.len() == group.piece_ids.len() { + preserved_groups.push(PuzzleMergedGroupState { + group_id: group.group_id.clone(), + piece_ids: group.piece_ids.clone(), + occupied_cells, + }); + } + } + + let recalculated_pieces = pieces + .iter() + .filter(|piece| recalculated_piece_ids.contains(&piece.piece_id)) + .cloned() + .collect::>(); + let mut next_groups = preserved_groups; + next_groups.extend(resolve_merged_groups(&recalculated_pieces)); + rebuild_board_snapshot_with_groups(grid_size, pieces, next_groups, selected_piece_id) +} + +pub fn advance_next_level( + run: &PuzzleRunSnapshot, + next_profile: &PuzzleWorkProfile, +) -> Result { + let current_level = run + .current_level + .clone() + .ok_or(PuzzleFieldError::InvalidOperation)?; + if current_level.status != PuzzleRuntimeLevelStatus::Cleared { + return Err(PuzzleFieldError::InvalidOperation); + } + + let next_cleared_count = run.cleared_level_count; + let next_grid_size = resolve_puzzle_grid_size(next_cleared_count); + let shuffle_seed = puzzle_shuffle_seed( + &run.run_id, + &next_profile.profile_id, + run.current_level_index + 1, + next_grid_size, + ); + let next_board = build_initial_board_with_seed(next_grid_size, shuffle_seed)?; + let mut played_profile_ids = run.played_profile_ids.clone(); + played_profile_ids.push(next_profile.profile_id.clone()); + + Ok(PuzzleRunSnapshot { + run_id: run.run_id.clone(), + entry_profile_id: run.entry_profile_id.clone(), + cleared_level_count: next_cleared_count, + current_level_index: run.current_level_index + 1, + current_grid_size: next_grid_size, + played_profile_ids, + previous_level_tags: next_profile.theme_tags.clone(), + current_level: Some(PuzzleRuntimeLevelSnapshot { + run_id: run.run_id.clone(), + level_index: run.current_level_index + 1, + grid_size: next_grid_size, + profile_id: next_profile.profile_id.clone(), + level_name: next_profile.level_name.clone(), + author_display_name: next_profile.author_display_name.clone(), + theme_tags: next_profile.theme_tags.clone(), + cover_image_src: next_profile.cover_image_src.clone(), + board: next_board, + status: PuzzleRuntimeLevelStatus::Playing, + started_at_ms: current_unix_ms(), + cleared_at_ms: None, + elapsed_ms: None, + leaderboard_entries: Vec::new(), + }), + recommended_next_profile_id: None, + leaderboard_entries: Vec::new(), + }) +} + +pub fn select_next_profile<'a>( + current_profile: &PuzzleWorkProfile, + played_profile_ids: &[String], + candidates: &'a [PuzzleWorkProfile], +) -> Option<&'a PuzzleWorkProfile> { + let mut available = candidates + .iter() + .filter(|candidate| { + candidate.publication_status == PuzzlePublicationStatus::Published + && candidate.cover_image_src.is_some() + && !candidate.theme_tags.is_empty() + && candidate.profile_id != current_profile.profile_id + }) + .collect::>(); + + let has_unplayed = available + .iter() + .any(|candidate| !played_profile_ids.contains(&candidate.profile_id)); + + if has_unplayed { + available.retain(|candidate| !played_profile_ids.contains(&candidate.profile_id)); + } else if let Some(last_played) = played_profile_ids.last() { + available.retain(|candidate| candidate.profile_id != *last_played); + } + + available.into_iter().max_by(|left, right| { + let left_score = recommendation_score(current_profile, left); + let right_score = recommendation_score(current_profile, right); + left_score + .partial_cmp(&right_score) + .unwrap_or(std::cmp::Ordering::Equal) + .then_with(|| { + tag_similarity_score(¤t_profile.theme_tags, &left.theme_tags) + .partial_cmp(&tag_similarity_score( + ¤t_profile.theme_tags, + &right.theme_tags, + )) + .unwrap_or(std::cmp::Ordering::Equal) + }) + .then_with(|| right.play_count.cmp(&left.play_count)) + .then_with(|| left.updated_at_micros.cmp(&right.updated_at_micros)) + }) +} + +pub fn recommendation_score( + current_profile: &PuzzleWorkProfile, + candidate: &PuzzleWorkProfile, +) -> f32 { + let tag_similarity = tag_similarity_score(¤t_profile.theme_tags, &candidate.theme_tags); + let same_author_score = if current_profile.owner_user_id == candidate.owner_user_id { + 1.0 + } else { + 0.0 + }; + tag_similarity * 0.7 + same_author_score * 0.3 +} + +pub fn tag_similarity_score(left_tags: &[String], right_tags: &[String]) -> f32 { + let left_set = normalize_theme_tags(left_tags.to_vec()) + .into_iter() + .collect::>(); + let right_set = normalize_theme_tags(right_tags.to_vec()) + .into_iter() + .collect::>(); + if left_set.is_empty() && right_set.is_empty() { + return 0.0; + } + let intersection = left_set.intersection(&right_set).count() as f32; + let union = left_set.union(&right_set).count() as f32; + if union <= f32::EPSILON { + 0.0 + } else { + intersection / union + } +} + +pub fn normalize_theme_tags(tags: Vec) -> Vec { + let alias_map = BTreeMap::from([ + ("蒸汽", "蒸汽城市"), + ("蒸汽朋克", "蒸汽城市"), + ("遗迹", "神庙遗迹"), + ("森林", "童话森林"), + ("夜雨", "雨夜"), + ("发光猫", "猫咪"), + ]); + + let mut normalized = normalize_string_list(tags) + .into_iter() + .flat_map(|value| split_phrase_list(&value)) + .map(|value| { + alias_map + .get(value.as_str()) + .map(|alias| (*alias).to_string()) + .unwrap_or(value) + }) + .collect::>(); + normalized.sort(); + normalized.dedup(); + normalized.into_iter().take(PUZZLE_MAX_TAG_COUNT).collect() +} + +fn infer_theme_promise(source: &str) -> String { + if source.contains("神庙") { + "探索遗迹中的奇幻想象".to_string() + } else if source.contains("雨") { + "在雨夜中寻找视觉线索".to_string() + } else if source.contains("猫") { + "一眼记住可爱的猫咪奇景".to_string() + } else { + "用一张强识别度画面承诺幻想题材".to_string() + } +} + +fn infer_visual_subject(source: &str) -> String { + if source.contains("猫") { + "发光猫咪".to_string() + } else if source.contains("神庙") { + "巨大遗迹入口".to_string() + } else if source.contains("城市") { + "蒸汽城市核心地标".to_string() + } else { + "画面中央的核心主体".to_string() + } +} + +fn infer_visual_mood(source: &str) -> String { + if source.contains("悬疑") { + "悬疑、静谧".to_string() + } else if source.contains("温暖") { + "温暖、柔和".to_string() + } else if source.contains("机械") { + "机械、奇诡".to_string() + } else { + "梦幻、清晰".to_string() + } +} + +fn infer_composition_hooks(source: &str) -> String { + if source.contains("塔") { + "高塔轮廓、纵向构图、亮色焦点".to_string() + } else if source.contains("遗迹") { + "入口轮廓、对称台阶、地标雕像".to_string() + } else { + "主体轮廓、色块分区、地标元素".to_string() + } +} + +fn infer_tags_and_forbidden(source: &str) -> String { + if source.contains("神庙") { + "神庙遗迹、童话森林、雨夜;禁止标题字".to_string() + } else if source.contains("猫") { + "猫咪、童话森林、发光;禁止水印".to_string() + } else { + "蒸汽城市、雨夜、奇幻;禁止按钮".to_string() + } +} + +fn extract_forbidden_directive(source: &str) -> String { + if let Some((_, tail)) = source.split_once(';') { + return normalize_required_string(tail).unwrap_or_else(|| "禁止标题字".to_string()); + } + "禁止标题字".to_string() +} + +fn build_level_name(anchor_pack: &PuzzleAnchorPack, normalized_tags: &[String]) -> String { + if let Some(tag) = normalized_tags.first() { + return format!("{tag}拼图"); + } + if let Some(subject) = normalize_required_string(&anchor_pack.visual_subject.value) { + return subject.chars().take(8).collect::(); + } + "奇景拼图".to_string() +} + +fn fallback_text(value: &str, fallback: &str) -> String { + normalize_required_string(value).unwrap_or_else(|| fallback.to_string()) +} + +fn split_phrase_list(value: &str) -> Vec { + value + .replace(',', ",") + .replace('、', ",") + .replace(';', ",") + .split(',') + .filter_map(normalize_required_string) + .collect() +} + +fn puzzle_shuffle_seed(run_id: &str, profile_id: &str, level_index: u32, grid_size: u32) -> u64 { + let mut hash = 0xcbf2_9ce4_8422_2325_u64; + for byte in run_id + .bytes() + .chain(profile_id.bytes()) + .chain(level_index.to_le_bytes()) + .chain(grid_size.to_le_bytes()) + { + hash ^= u64::from(byte); + hash = hash.wrapping_mul(0x0000_0100_0000_01b3); + } + hash +} + +fn shuffle_positions(positions: &mut [PuzzleCellPosition], seed: u64) { + if positions.len() <= 1 { + return; + } + + let mut state = seed ^ ((positions.len() as u64) << 32) ^ 0x9e37_79b9_7f4a_7c15; + for index in (1..positions.len()).rev() { + state = state + .wrapping_mul(6_364_136_223_846_793_005) + .wrapping_add(1_442_695_040_888_963_407); + let swap_index = (state % ((index + 1) as u64)) as usize; + positions.swap(index, swap_index); + } +} + +fn build_initial_pieces_without_correct_neighbors( + grid_size: u32, + shuffle_seed: u64, +) -> Vec { + let base_positions = build_correct_positions(grid_size); + for attempt in 0..PUZZLE_INITIAL_SHUFFLE_ATTEMPTS { + let mut positions = base_positions.clone(); + shuffle_positions( + &mut positions, + shuffle_seed.wrapping_add(attempt.wrapping_mul(0x9e37_79b9_7f4a_7c15)), + ); + ensure_board_is_not_solved(&mut positions, grid_size); + let pieces = build_pieces_from_positions(grid_size, &positions); + if !has_any_original_neighbor_pair(&pieces) { + return pieces; + } + } + + // 随机尝试耗尽后使用确定性约束搜索兜底,保证开局没有任意一对原图相邻块互相贴边。 + let fallback_pieces = build_original_neighbor_free_pieces(grid_size, shuffle_seed) + .unwrap_or_else(|| build_pieces_from_positions(grid_size, &base_positions)); + debug_assert!(!has_any_original_neighbor_pair(&fallback_pieces)); + fallback_pieces +} + +fn build_correct_positions(grid_size: u32) -> Vec { + let total = grid_size * grid_size; + (0..total) + .map(|index| PuzzleCellPosition { + row: index / grid_size, + col: index % grid_size, + }) + .collect() +} + +fn build_pieces_from_positions( + grid_size: u32, + positions: &[PuzzleCellPosition], +) -> Vec { + positions + .iter() + .enumerate() + .map(|(index, current)| { + let index = index as u32; + PuzzlePieceState { + piece_id: format!("piece-{index}"), + correct_row: index / grid_size, + correct_col: index % grid_size, + current_row: current.row, + current_col: current.col, + merged_group_id: None, + } + }) + .collect() +} + +fn ensure_board_is_not_solved(positions: &mut [PuzzleCellPosition], grid_size: u32) { + if positions.len() <= 1 { + return; + } + + let is_solved = positions.iter().enumerate().all(|(index, position)| { + position.row == index as u32 / grid_size && position.col == index as u32 % grid_size + }); + if is_solved { + positions.rotate_left(1); + } +} + +fn has_any_original_neighbor_pair(pieces: &[PuzzlePieceState]) -> bool { + let pieces_by_cell = pieces + .iter() + .map(|piece| ((piece.current_row, piece.current_col), piece)) + .collect::>(); + + pieces.iter().any(|piece| { + neighbor_cells(piece.current_row, piece.current_col) + .into_iter() + .filter_map(|cell| pieces_by_cell.get(&cell)) + .any(|neighbor| are_original_neighbors(piece, neighbor)) + }) +} + +fn are_original_neighbors(left: &PuzzlePieceState, right: &PuzzlePieceState) -> bool { + left.correct_row.abs_diff(right.correct_row) + left.correct_col.abs_diff(right.correct_col) == 1 +} + +fn build_original_neighbor_free_pieces( + grid_size: u32, + shuffle_seed: u64, +) -> Option> { + let total = (grid_size * grid_size) as usize; + let mut piece_order = (0..total as u32).collect::>(); + sort_indices_by_seed(&mut piece_order, shuffle_seed ^ 0xa076_1d64_78bd_642f); + let mut cell_order = build_correct_positions(grid_size); + sort_cells_by_seed(&mut cell_order, shuffle_seed ^ 0xe703_7ed1_a0b4_28db); + + let mut placements = vec![None; total]; + let mut used_cells = BTreeSet::new(); + if place_neighbor_free_piece( + grid_size, + &piece_order, + &cell_order, + 0, + &mut placements, + &mut used_cells, + ) { + Some( + placements + .into_iter() + .enumerate() + .filter_map(|(index, current)| { + current.map(|current| PuzzlePieceState { + piece_id: format!("piece-{index}"), + correct_row: index as u32 / grid_size, + correct_col: index as u32 % grid_size, + current_row: current.row, + current_col: current.col, + merged_group_id: None, + }) + }) + .collect(), + ) + } else { + None + } +} + +fn place_neighbor_free_piece( + grid_size: u32, + piece_order: &[u32], + cell_order: &[PuzzleCellPosition], + depth: usize, + placements: &mut [Option], + used_cells: &mut BTreeSet<(u32, u32)>, +) -> bool { + let Some(piece_index) = piece_order.get(depth).copied() else { + return true; + }; + + for cell in cell_order { + if used_cells.contains(&(cell.row, cell.col)) { + continue; + } + if cell.row == piece_index / grid_size && cell.col == piece_index % grid_size { + continue; + } + if violates_original_neighbor_free_rule(grid_size, piece_index, cell.clone(), placements) { + continue; + } + + placements[piece_index as usize] = Some(cell.clone()); + used_cells.insert((cell.row, cell.col)); + if place_neighbor_free_piece( + grid_size, + piece_order, + cell_order, + depth + 1, + placements, + used_cells, + ) { + return true; + } + used_cells.remove(&(cell.row, cell.col)); + placements[piece_index as usize] = None; + } + + false +} + +fn violates_original_neighbor_free_rule( + grid_size: u32, + piece_index: u32, + cell: PuzzleCellPosition, + placements: &[Option], +) -> bool { + placements + .iter() + .enumerate() + .filter_map(|(placed_index, placed_cell)| { + placed_cell + .as_ref() + .map(|placed_cell| (placed_index as u32, placed_cell)) + }) + .any(|(placed_index, placed_cell)| { + let original_neighbors = (piece_index / grid_size).abs_diff(placed_index / grid_size) + + (piece_index % grid_size).abs_diff(placed_index % grid_size) + == 1; + let current_neighbors = + cell.row.abs_diff(placed_cell.row) + cell.col.abs_diff(placed_cell.col) == 1; + original_neighbors && current_neighbors + }) +} + +fn sort_indices_by_seed(indices: &mut [u32], seed: u64) { + indices.sort_by_key(|index| seeded_order_key(seed, u64::from(*index))); +} + +fn sort_cells_by_seed(cells: &mut [PuzzleCellPosition], seed: u64) { + cells.sort_by_key(|cell| seeded_order_key(seed, u64::from(cell.row * 16 + cell.col))); +} + +fn seeded_order_key(seed: u64, value: u64) -> u64 { + let mut state = seed ^ value.wrapping_mul(0x9e37_79b9_7f4a_7c15); + state ^= state >> 30; + state = state.wrapping_mul(0xbf58_476d_1ce4_e5b9); + state ^= state >> 27; + state = state.wrapping_mul(0x94d0_49bb_1331_11eb); + state ^ (state >> 31) +} + +fn rebuild_board_snapshot( + grid_size: u32, + pieces: Vec, + selected_piece_id: Option, +) -> PuzzleBoardSnapshot { + let merged_groups = resolve_merged_groups(&pieces); + rebuild_board_snapshot_with_groups(grid_size, pieces, merged_groups, selected_piece_id) +} + +fn rebuild_board_snapshot_with_groups( + grid_size: u32, + mut pieces: Vec, + merged_groups: Vec, + selected_piece_id: Option, +) -> PuzzleBoardSnapshot { + let merged_groups = normalize_group_ids(merged_groups); + let group_by_piece = merged_groups + .iter() + .flat_map(|group| { + group + .piece_ids + .iter() + .cloned() + .map(|piece_id| (piece_id, group.group_id.clone())) + }) + .collect::>(); + + for piece in &mut pieces { + piece.merged_group_id = group_by_piece.get(&piece.piece_id).cloned(); + } + + let all_pieces_in_correct_cells = pieces.iter().all(|piece| { + piece.correct_row == piece.current_row && piece.correct_col == piece.current_col + }); + let all_pieces_merged_into_one_group = merged_groups + .iter() + .any(|group| group.piece_ids.len() == pieces.len() && pieces.len() > 1); + let all_tiles_resolved = all_pieces_in_correct_cells || all_pieces_merged_into_one_group; + + PuzzleBoardSnapshot { + rows: grid_size, + cols: grid_size, + pieces, + merged_groups, + selected_piece_id, + all_tiles_resolved, + } +} + +fn normalize_group_ids(groups: Vec) -> Vec { + groups + .into_iter() + .enumerate() + .map(|(index, group)| PuzzleMergedGroupState { + group_id: format!("group-{}", index + 1), + ..group + }) + .collect() +} + +fn expand_affected_cells( + grid_size: u32, + cells: impl IntoIterator, +) -> BTreeSet<(u32, u32)> { + let mut scope = BTreeSet::new(); + for cell in cells { + if cell.row >= grid_size || cell.col >= grid_size { + continue; + } + scope.insert((cell.row, cell.col)); + for (row, col) in neighbor_cells(cell.row, cell.col) { + if row < grid_size && col < grid_size { + scope.insert((row, col)); + } + } + } + scope +} + +fn add_previous_group_piece_ids( + previous_board: &PuzzleBoardSnapshot, + group_id: &str, + piece_ids: &mut BTreeSet, +) { + if let Some(group) = previous_board + .merged_groups + .iter() + .find(|group| group.group_id == group_id) + { + piece_ids.extend(group.piece_ids.iter().cloned()); + } +} + +fn resolve_merged_groups(pieces: &[PuzzlePieceState]) -> Vec { + let pieces_by_cell = pieces + .iter() + .map(|piece| ((piece.current_row, piece.current_col), piece)) + .collect::>(); + let pieces_by_id = pieces + .iter() + .map(|piece| (piece.piece_id.clone(), piece)) + .collect::>(); + let mut visited = BTreeSet::new(); + let mut groups = Vec::new(); + + for piece in pieces { + if visited.contains(&piece.piece_id) { + continue; + } + + let mut queue = VecDeque::from([piece.piece_id.clone()]); + let mut collected_ids = Vec::new(); + + while let Some(current_piece_id) = queue.pop_front() { + if !visited.insert(current_piece_id.clone()) { + continue; + } + let current_piece = match pieces_by_id.get(¤t_piece_id) { + Some(value) => *value, + None => continue, + }; + collected_ids.push(current_piece_id.clone()); + + for (neighbor_row, neighbor_col) in + neighbor_cells(current_piece.current_row, current_piece.current_col) + { + if let Some(neighbor_piece) = pieces_by_cell.get(&(neighbor_row, neighbor_col)) + && are_correct_neighbors(current_piece, neighbor_piece) + { + queue.push_back(neighbor_piece.piece_id.clone()); + } + } + } + + if collected_ids.len() <= 1 { + continue; + } + + let occupied_cells = collected_ids + .iter() + .filter_map(|piece_id| pieces_by_id.get(piece_id).copied()) + .map(|piece| PuzzleCellPosition { + row: piece.current_row, + col: piece.current_col, + }) + .collect::>(); + + groups.push(PuzzleMergedGroupState { + group_id: format!("group-{}", groups.len() + 1), + piece_ids: collected_ids, + occupied_cells, + }); + } + + groups +} + +fn neighbor_cells(row: u32, col: u32) -> Vec<(u32, u32)> { + let mut neighbors = Vec::new(); + if row > 0 { + neighbors.push((row - 1, col)); + } + neighbors.push((row + 1, col)); + if col > 0 { + neighbors.push((row, col - 1)); + } + neighbors.push((row, col + 1)); + neighbors +} + +fn are_correct_neighbors(left: &PuzzlePieceState, right: &PuzzlePieceState) -> bool { + let current_row_delta = right.current_row as i32 - left.current_row as i32; + let current_col_delta = right.current_col as i32 - left.current_col as i32; + let correct_row_delta = right.correct_row as i32 - left.correct_row as i32; + let correct_col_delta = right.correct_col as i32 - left.correct_col as i32; + (current_row_delta.abs() + current_col_delta.abs()) == 1 + && current_row_delta == correct_row_delta + && current_col_delta == correct_col_delta +} + +fn drag_single_piece( + pieces: &mut [PuzzlePieceState], + piece_index: usize, + target_row: u32, + target_col: u32, +) -> Result, PuzzleFieldError> { + let target_index = pieces + .iter() + .position(|piece| piece.current_row == target_row && piece.current_col == target_col) + .ok_or(PuzzleFieldError::InvalidTargetCell)?; + + let mut affected_cells = vec![ + PuzzleCellPosition { + row: pieces[piece_index].current_row, + col: pieces[piece_index].current_col, + }, + PuzzleCellPosition { + row: target_row, + col: target_col, + }, + ]; + + if let Some(target_group_id) = pieces[target_index].merged_group_id.clone() { + for piece in pieces + .iter_mut() + .filter(|piece| piece.merged_group_id.as_deref() == Some(target_group_id.as_str())) + { + affected_cells.push(PuzzleCellPosition { + row: piece.current_row, + col: piece.current_col, + }); + piece.merged_group_id = None; + } + } + + let (source_row, source_col) = ( + pieces[piece_index].current_row, + pieces[piece_index].current_col, + ); + pieces[piece_index].current_row = target_row; + pieces[piece_index].current_col = target_col; + if target_index != piece_index { + pieces[target_index].current_row = source_row; + pieces[target_index].current_col = source_col; + } + Ok(affected_cells) +} + +fn drag_group( + pieces: &mut [PuzzlePieceState], + group_id: &str, + target_row: u32, + target_col: u32, + grid_size: u32, +) -> Result, PuzzleFieldError> { + let group_indices = pieces + .iter() + .enumerate() + .filter_map(|(index, piece)| { + (piece.merged_group_id.as_deref() == Some(group_id)).then_some(index) + }) + .collect::>(); + if group_indices.is_empty() { + return Err(PuzzleFieldError::InvalidOperation); + } + + let anchor_piece = &pieces[group_indices[0]]; + let row_offset = target_row as i32 - anchor_piece.current_row as i32; + let col_offset = target_col as i32 - anchor_piece.current_col as i32; + let mut target_positions = Vec::new(); + for &index in &group_indices { + let next_row = pieces[index].current_row as i32 + row_offset; + let next_col = pieces[index].current_col as i32 + col_offset; + if next_row < 0 + || next_col < 0 + || next_row >= grid_size as i32 + || next_col >= grid_size as i32 + { + return Err(PuzzleFieldError::InvalidTargetCell); + } + target_positions.push((index, next_row as u32, next_col as u32)); + } + + let moving_piece_ids = group_indices + .iter() + .map(|index| pieces[*index].piece_id.clone()) + .collect::>(); + let source_positions = group_indices + .iter() + .map(|index| (pieces[*index].current_row, pieces[*index].current_col)) + .collect::>(); + let mut affected_cells = source_positions + .iter() + .map(|(row, col)| PuzzleCellPosition { + row: *row, + col: *col, + }) + .collect::>(); + + for (index, next_row, next_col) in &target_positions { + affected_cells.push(PuzzleCellPosition { + row: *next_row, + col: *next_col, + }); + if let Some(target_piece_index) = pieces.iter().position(|piece| { + piece.current_row == *next_row + && piece.current_col == *next_col + && !moving_piece_ids.contains(&piece.piece_id) + }) { + let fallback = source_positions + .iter() + .find(|position| { + !target_positions + .iter() + .any(|(_, row, col)| row == &position.0 && col == &position.1) + }) + .copied() + .ok_or(PuzzleFieldError::InvalidOperation)?; + pieces[target_piece_index].merged_group_id = None; + affected_cells.push(PuzzleCellPosition { + row: pieces[target_piece_index].current_row, + col: pieces[target_piece_index].current_col, + }); + affected_cells.push(PuzzleCellPosition { + row: fallback.0, + col: fallback.1, + }); + pieces[target_piece_index].current_row = fallback.0; + pieces[target_piece_index].current_col = fallback.1; + } + pieces[*index].current_row = *next_row; + pieces[*index].current_col = *next_col; + } + + Ok(affected_cells) +} + +fn with_next_board(run: &PuzzleRunSnapshot, next_board: PuzzleBoardSnapshot) -> PuzzleRunSnapshot { + let mut next_run = run.clone(); + let is_cleared = next_board.all_tiles_resolved; + let next_level_status = if is_cleared { + PuzzleRuntimeLevelStatus::Cleared + } else { + PuzzleRuntimeLevelStatus::Playing + }; + + if let Some(current_level) = next_run.current_level.as_mut() { + current_level.board = next_board; + if current_level.status != PuzzleRuntimeLevelStatus::Cleared && is_cleared { + let cleared_at_ms = current_unix_ms(); + current_level.cleared_at_ms = Some(cleared_at_ms); + current_level.elapsed_ms = Some( + cleared_at_ms + .saturating_sub(current_level.started_at_ms) + .max(1_000), + ); + } + current_level.status = next_level_status; + } + + if is_cleared + && run.current_level.as_ref().map(|level| level.status) + != Some(PuzzleRuntimeLevelStatus::Cleared) + { + next_run.cleared_level_count += 1; + } + next_run +} + +fn current_unix_ms() -> u64 { + use std::time::{SystemTime, UNIX_EPOCH}; + + SystemTime::now() + .duration_since(UNIX_EPOCH) + .map(|value| value.as_millis() as u64) + .unwrap_or(0) +} + +#[cfg(test)] +mod tests { + use super::*; + + fn build_published_profile( + profile_id: &str, + owner_user_id: &str, + tags: Vec<&str>, + ) -> PuzzleWorkProfile { + PuzzleWorkProfile { + work_id: format!("work-{profile_id}"), + profile_id: profile_id.to_string(), + owner_user_id: owner_user_id.to_string(), + source_session_id: None, + author_display_name: "作者".to_string(), + level_name: format!("{profile_id} 关"), + summary: "summary".to_string(), + theme_tags: tags.into_iter().map(|value| value.to_string()).collect(), + cover_image_src: Some("/cover.png".to_string()), + cover_asset_id: Some("asset-1".to_string()), + publication_status: PuzzlePublicationStatus::Published, + updated_at_micros: 100, + published_at_micros: Some(100), + play_count: 0, + publish_ready: true, + anchor_pack: empty_anchor_pack(), + } + } + + #[test] + fn resolve_grid_size_matches_prd() { + assert_eq!(resolve_puzzle_grid_size(0), 3); + assert_eq!(resolve_puzzle_grid_size(2), 3); + assert_eq!(resolve_puzzle_grid_size(3), 4); + } + + #[test] + fn normalize_theme_tags_dedups_aliases() { + assert_eq!( + normalize_theme_tags(vec![ + "蒸汽".to_string(), + "蒸汽朋克".to_string(), + "雨夜".to_string(), + "雨夜".to_string() + ]), + vec!["蒸汽城市".to_string(), "雨夜".to_string()] + ); + } + + #[test] + fn generated_candidate_uses_oss_compatible_prefix_and_single_image() { + let anchor_pack = infer_anchor_pack("雨夜猫咪", Some("雨夜猫咪")); + let draft = compile_result_draft(&anchor_pack, &[]); + let candidates = build_generated_candidates("session-1", None, &draft, 2, 1_000) + .expect("candidates should build"); + + assert_eq!(candidates.len(), 1); + assert!( + candidates[0] + .image_src + .starts_with("/generated-puzzle-assets/session-1/") + ); + let legacy_public_prefix = ["generated-puzzle", "covers"].join("-"); + assert!(!candidates[0].image_src.contains(&legacy_public_prefix)); + } + + #[test] + fn tag_similarity_score_uses_jaccard() { + let score = tag_similarity_score( + &["蒸汽城市".to_string(), "雨夜".to_string()], + &["蒸汽城市".to_string(), "猫咪".to_string()], + ); + assert!((score - 0.3333).abs() < 0.01); + } + + #[test] + fn select_next_profile_prefers_same_tags_and_author() { + let current = build_published_profile("a", "owner-a", vec!["蒸汽城市", "雨夜"]); + let candidates = vec![ + build_published_profile("b", "owner-a", vec!["蒸汽城市", "雨夜"]), + build_published_profile("c", "owner-c", vec!["猫咪", "森林"]), + ]; + let selected = + select_next_profile(¤t, &["a".to_string()], &candidates).expect("should select"); + assert_eq!(selected.profile_id, "b"); + } + + #[test] + fn swap_pieces_marks_cleared_when_back_to_origin() { + let profile = build_published_profile("entry", "owner-a", vec!["蒸汽城市", "雨夜", "猫咪"]); + let run = start_run("run-1".to_string(), &profile, 0).expect("run"); + let current_level = run.current_level.clone().expect("level"); + let first_piece = current_level.board.pieces[0].clone(); + let second_piece = current_level.board.pieces[1].clone(); + let swapped = + swap_pieces(&run, &first_piece.piece_id, &second_piece.piece_id).expect("swap"); + assert_eq!( + swapped + .current_level + .as_ref() + .expect("level") + .board + .pieces + .len(), + 9 + ); + } + + #[test] + fn initial_board_shuffle_changes_by_run_id() { + let profile = build_published_profile("entry", "owner-a", vec!["蒸汽城市", "雨夜", "猫咪"]); + let first = start_run("run-random-a".to_string(), &profile, 0).expect("first run"); + let second = start_run("run-random-b".to_string(), &profile, 0).expect("second run"); + let first_positions = first + .current_level + .expect("first level") + .board + .pieces + .into_iter() + .map(|piece| (piece.current_row, piece.current_col)) + .collect::>(); + let second_positions = second + .current_level + .expect("second level") + .board + .pieces + .into_iter() + .map(|piece| (piece.current_row, piece.current_col)) + .collect::>(); + + assert_ne!(first_positions, second_positions); + } + + #[test] + fn initial_board_has_no_original_neighbor_pairs() { + for grid_size in [3, 4] { + for shuffle_seed in 0..128 { + let board = build_initial_board_with_seed(grid_size, shuffle_seed).expect("board"); + + assert!(board.merged_groups.is_empty()); + assert!( + !has_any_original_neighbor_pair(&board.pieces), + "grid_size={grid_size}, shuffle_seed={shuffle_seed}" + ); + } + } + } + + #[test] + fn correct_neighbors_auto_merge_after_swap() { + let profile = build_published_profile("entry", "owner-a", vec!["蒸汽城市", "雨夜", "猫咪"]); + let mut run = + start_run_with_shuffle_seed("run-merge".to_string(), &profile, 0, 7).expect("run"); + let current_level = run.current_level.as_mut().expect("level"); + current_level.board = rebuild_board_snapshot( + 3, + vec![ + PuzzlePieceState { + piece_id: "piece-0".to_string(), + correct_row: 0, + correct_col: 0, + current_row: 1, + current_col: 1, + merged_group_id: None, + }, + PuzzlePieceState { + piece_id: "piece-1".to_string(), + correct_row: 0, + correct_col: 1, + current_row: 0, + current_col: 1, + merged_group_id: None, + }, + PuzzlePieceState { + piece_id: "piece-2".to_string(), + correct_row: 0, + correct_col: 2, + current_row: 2, + current_col: 2, + merged_group_id: None, + }, + PuzzlePieceState { + piece_id: "piece-3".to_string(), + correct_row: 1, + correct_col: 0, + current_row: 0, + current_col: 2, + merged_group_id: None, + }, + PuzzlePieceState { + piece_id: "piece-4".to_string(), + correct_row: 1, + correct_col: 1, + current_row: 1, + current_col: 0, + merged_group_id: None, + }, + PuzzlePieceState { + piece_id: "piece-5".to_string(), + correct_row: 1, + correct_col: 2, + current_row: 2, + current_col: 0, + merged_group_id: None, + }, + PuzzlePieceState { + piece_id: "piece-6".to_string(), + correct_row: 2, + correct_col: 0, + current_row: 0, + current_col: 0, + merged_group_id: None, + }, + PuzzlePieceState { + piece_id: "piece-7".to_string(), + correct_row: 2, + correct_col: 1, + current_row: 1, + current_col: 2, + merged_group_id: None, + }, + PuzzlePieceState { + piece_id: "piece-8".to_string(), + correct_row: 2, + correct_col: 2, + current_row: 2, + current_col: 1, + merged_group_id: None, + }, + ], + None, + ); + + let swapped = swap_pieces(&run, "piece-0", "piece-6").expect("swap"); + let board = &swapped.current_level.as_ref().expect("level").board; + let group = board + .merged_groups + .iter() + .find(|group| { + group.piece_ids.contains(&"piece-0".to_string()) + && group.piece_ids.contains(&"piece-1".to_string()) + }) + .expect("piece-0 and piece-1 should merge"); + + assert_eq!(group.piece_ids.len(), 2); + } + + #[test] + fn single_piece_dragging_into_group_splits_target_group() { + let profile = build_published_profile("entry", "owner-a", vec!["蒸汽城市", "雨夜", "猫咪"]); + let mut run = + start_run_with_shuffle_seed("run-split".to_string(), &profile, 0, 9).expect("run"); + let current_level = run.current_level.as_mut().expect("level"); + current_level.board = rebuild_board_snapshot( + 3, + vec![ + PuzzlePieceState { + piece_id: "piece-0".to_string(), + correct_row: 0, + correct_col: 0, + current_row: 0, + current_col: 0, + merged_group_id: None, + }, + PuzzlePieceState { + piece_id: "piece-1".to_string(), + correct_row: 0, + correct_col: 1, + current_row: 0, + current_col: 1, + merged_group_id: None, + }, + PuzzlePieceState { + piece_id: "piece-2".to_string(), + correct_row: 0, + correct_col: 2, + current_row: 2, + current_col: 2, + merged_group_id: None, + }, + PuzzlePieceState { + piece_id: "piece-3".to_string(), + correct_row: 1, + correct_col: 0, + current_row: 1, + current_col: 0, + merged_group_id: None, + }, + PuzzlePieceState { + piece_id: "piece-4".to_string(), + correct_row: 1, + correct_col: 1, + current_row: 1, + current_col: 1, + merged_group_id: None, + }, + PuzzlePieceState { + piece_id: "piece-5".to_string(), + correct_row: 1, + correct_col: 2, + current_row: 1, + current_col: 2, + merged_group_id: None, + }, + PuzzlePieceState { + piece_id: "piece-6".to_string(), + correct_row: 2, + correct_col: 0, + current_row: 2, + current_col: 0, + merged_group_id: None, + }, + PuzzlePieceState { + piece_id: "piece-7".to_string(), + correct_row: 2, + correct_col: 1, + current_row: 2, + current_col: 1, + merged_group_id: None, + }, + PuzzlePieceState { + piece_id: "piece-8".to_string(), + correct_row: 2, + correct_col: 2, + current_row: 0, + current_col: 2, + merged_group_id: None, + }, + ], + None, + ); + + let dragged = drag_piece_or_group(&run, "piece-8", 0, 1).expect("drag"); + let board = &dragged.current_level.as_ref().expect("level").board; + + assert_eq!( + board + .pieces + .iter() + .find(|piece| piece.piece_id == "piece-8") + .map(|piece| (piece.current_row, piece.current_col)), + Some((0, 1)) + ); + assert!( + board + .merged_groups + .iter() + .all(|group| !(group.piece_ids.contains(&"piece-0".to_string()) + && group.piece_ids.contains(&"piece-1".to_string()))) + ); + } + + #[test] + fn one_full_board_group_marks_level_cleared() { + let pieces = (0..9) + .map(|index| PuzzlePieceState { + piece_id: format!("piece-{index}"), + correct_row: index / 3, + correct_col: index % 3, + current_row: index / 3, + current_col: (index + 1) % 3, + merged_group_id: None, + }) + .collect::>(); + let board = rebuild_board_snapshot_with_groups( + 3, + pieces, + vec![PuzzleMergedGroupState { + group_id: "group-full".to_string(), + piece_ids: (0..9).map(|index| format!("piece-{index}")).collect(), + occupied_cells: (0..9) + .map(|index| PuzzleCellPosition { + row: index / 3, + col: (index + 1) % 3, + }) + .collect(), + }], + None, + ); + + assert!(board.all_tiles_resolved); + } + + #[test] + fn apply_publish_overrides_updates_draft_truth() { + let anchor_pack = infer_anchor_pack("雨夜猫咪神庙", Some("雨夜猫咪神庙")); + let draft = compile_result_draft(&anchor_pack, &[]); + + let updated = apply_publish_overrides_to_draft( + &draft, + Some("雨夜猫塔".to_string()), + Some("一张更聚焦猫咪塔楼的夜景拼图。".to_string()), + Some(vec![ + "雨夜".to_string(), + "猫咪".to_string(), + "遗迹".to_string(), + ]), + ) + .expect("publish overrides should succeed"); + + assert_eq!(updated.level_name, "雨夜猫塔"); + assert_eq!(updated.summary, "一张更聚焦猫咪塔楼的夜景拼图。"); + assert_eq!( + updated.theme_tags, + vec![ + "猫咪".to_string(), + "神庙遗迹".to_string(), + "雨夜".to_string() + ] + ); + } + + #[test] + fn apply_publish_overrides_rejects_invalid_tag_count() { + let anchor_pack = infer_anchor_pack("蒸汽城市", Some("蒸汽城市")); + let draft = compile_result_draft(&anchor_pack, &[]); + let error = + apply_publish_overrides_to_draft(&draft, None, None, Some(vec!["蒸汽".to_string()])) + .expect_err("invalid tag count should fail"); + + assert_eq!(error, PuzzleFieldError::InvalidTagCount); + } +} diff --git a/server-rs/crates/module-puzzle/src/commands.rs b/server-rs/crates/module-puzzle/src/commands.rs index 1477be89..479b0a2a 100644 --- a/server-rs/crates/module-puzzle/src/commands.rs +++ b/server-rs/crates/module-puzzle/src/commands.rs @@ -1,3 +1,181 @@ -//! 拼图写入命令过渡落位。 +//! 拼图写入命令。 //! -//! 用于表达会话消息、作品更新、发布、开局、交换拼图块和过关推进等输入。 +//! 命令只表达 SpacetimeDB procedure/reducer 需要写入的意图和参数, +//! 不包含 HTTP、前端展示或 SpacetimeDB table 操作。 + +use serde::{Deserialize, Serialize}; +#[cfg(feature = "spacetime-types")] +use spacetimedb::SpacetimeType; + +use crate::domain::PuzzleAgentStage; + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct PuzzleAgentSessionCreateInput { + pub session_id: String, + pub owner_user_id: String, + pub seed_text: String, + pub welcome_message_id: String, + pub welcome_message_text: String, + pub created_at_micros: i64, +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct PuzzleAgentSessionGetInput { + pub session_id: String, + pub owner_user_id: String, +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct PuzzleAgentMessageSubmitInput { + pub session_id: String, + pub owner_user_id: String, + pub user_message_id: String, + pub user_message_text: String, + pub submitted_at_micros: i64, +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct PuzzleAgentMessageFinalizeInput { + pub session_id: String, + pub owner_user_id: String, + pub assistant_message_id: Option, + pub assistant_reply_text: Option, + pub stage: PuzzleAgentStage, + pub progress_percent: u32, + pub anchor_pack_json: String, + pub error_message: Option, + pub updated_at_micros: i64, +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct PuzzleDraftCompileInput { + pub session_id: String, + pub owner_user_id: String, + pub compiled_at_micros: i64, +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct PuzzleGeneratedImagesSaveInput { + pub session_id: String, + pub owner_user_id: String, + pub candidates_json: String, + pub saved_at_micros: i64, +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct PuzzleSelectCoverImageInput { + pub session_id: String, + pub owner_user_id: String, + pub candidate_id: String, + pub selected_at_micros: i64, +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct PuzzlePublishInput { + pub session_id: String, + pub owner_user_id: String, + pub work_id: String, + pub profile_id: String, + pub author_display_name: String, + pub level_name: Option, + pub summary: Option, + pub theme_tags: Option>, + pub published_at_micros: i64, +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct PuzzleWorksListInput { + pub owner_user_id: String, +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct PuzzleWorkGetInput { + pub profile_id: String, +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct PuzzleWorkDeleteInput { + pub profile_id: String, + pub owner_user_id: String, +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct PuzzleWorkUpsertInput { + pub profile_id: String, + pub owner_user_id: String, + pub level_name: String, + pub summary: String, + pub theme_tags: Vec, + pub cover_image_src: Option, + pub cover_asset_id: Option, + pub updated_at_micros: i64, +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct PuzzleRunStartInput { + pub run_id: String, + pub owner_user_id: String, + pub profile_id: String, + pub started_at_micros: i64, +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct PuzzleRunGetInput { + pub run_id: String, + pub owner_user_id: String, +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct PuzzleRunSwapInput { + pub run_id: String, + pub owner_user_id: String, + pub first_piece_id: String, + pub second_piece_id: String, + pub swapped_at_micros: i64, +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct PuzzleRunDragInput { + pub run_id: String, + pub owner_user_id: String, + pub piece_id: String, + pub target_row: u32, + pub target_col: u32, + pub dragged_at_micros: i64, +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct PuzzleRunNextLevelInput { + pub run_id: String, + pub owner_user_id: String, + pub advanced_at_micros: i64, +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct PuzzleLeaderboardSubmitInput { + pub run_id: String, + pub owner_user_id: String, + pub profile_id: String, + pub grid_size: u32, + pub elapsed_ms: u64, + pub nickname: String, + pub submitted_at_micros: i64, +} diff --git a/server-rs/crates/module-puzzle/src/domain.rs b/server-rs/crates/module-puzzle/src/domain.rs index a5dbb2b8..a1aa8251 100644 --- a/server-rs/crates/module-puzzle/src/domain.rs +++ b/server-rs/crates/module-puzzle/src/domain.rs @@ -2,3 +2,353 @@ //! //! 后续迁移拼图 Agent 会话、作品 profile 和运行态聚合时,只保留玩法规则; //! 图片生成、发布 HTTP shape 和排行榜适配留在外层。 + +use serde::{Deserialize, Serialize}; +#[cfg(feature = "spacetime-types")] +use spacetimedb::SpacetimeType; + +pub const PUZZLE_AGENT_SESSION_ID_PREFIX: &str = "puzzle-session-"; +pub const PUZZLE_AGENT_MESSAGE_ID_PREFIX: &str = "puzzle-message-"; +pub const PUZZLE_PROFILE_ID_PREFIX: &str = "puzzle-profile-"; +pub const PUZZLE_RUN_ID_PREFIX: &str = "puzzle-run-"; +pub const PUZZLE_MIN_TAG_COUNT: usize = 3; +pub const PUZZLE_MAX_TAG_COUNT: usize = 6; +pub(crate) const PUZZLE_INITIAL_SHUFFLE_ATTEMPTS: u64 = 64; + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub enum PuzzleAgentStage { + CollectingAnchors, + DraftReady, + ImageRefining, + ReadyToPublish, + Published, +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub enum PuzzleAnchorStatus { + Missing, + Inferred, + Confirmed, + Locked, +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub enum PuzzleAgentMessageRole { + User, + Assistant, + System, +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub enum PuzzleAgentMessageKind { + Chat, + Summary, + ActionResult, + Warning, +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub enum PuzzlePublicationStatus { + Draft, + Published, +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub enum PuzzleRuntimeLevelStatus { + Playing, + Cleared, +} + +impl PuzzleAgentStage { + pub fn as_str(self) -> &'static str { + match self { + Self::CollectingAnchors => "collecting_anchors", + Self::DraftReady => "draft_ready", + Self::ImageRefining => "image_refining", + Self::ReadyToPublish => "ready_to_publish", + Self::Published => "published", + } + } +} + +impl PuzzleAnchorStatus { + pub fn as_str(self) -> &'static str { + match self { + Self::Missing => "missing", + Self::Inferred => "inferred", + Self::Confirmed => "confirmed", + Self::Locked => "locked", + } + } +} + +impl PuzzleAgentMessageRole { + pub fn as_str(self) -> &'static str { + match self { + Self::User => "user", + Self::Assistant => "assistant", + Self::System => "system", + } + } +} + +impl PuzzleAgentMessageKind { + pub fn as_str(self) -> &'static str { + match self { + Self::Chat => "chat", + Self::Summary => "summary", + Self::ActionResult => "action_result", + Self::Warning => "warning", + } + } +} + +impl PuzzlePublicationStatus { + pub fn as_str(self) -> &'static str { + match self { + Self::Draft => "draft", + Self::Published => "published", + } + } +} + +impl PuzzleRuntimeLevelStatus { + pub fn as_str(self) -> &'static str { + match self { + Self::Playing => "playing", + Self::Cleared => "cleared", + } + } +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct PuzzleAnchorItem { + pub key: String, + pub label: String, + pub value: String, + pub status: PuzzleAnchorStatus, +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct PuzzleAnchorPack { + pub theme_promise: PuzzleAnchorItem, + pub visual_subject: PuzzleAnchorItem, + pub visual_mood: PuzzleAnchorItem, + pub composition_hooks: PuzzleAnchorItem, + pub tags_and_forbidden: PuzzleAnchorItem, +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct PuzzleCreatorIntent { + pub source_mode: String, + pub raw_messages_summary: String, + pub theme_promise: String, + pub visual_subject: String, + pub visual_mood: Vec, + pub composition_hooks: Vec, + pub theme_tags: Vec, + pub forbidden_directives: Vec, +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct PuzzleGeneratedImageCandidate { + pub candidate_id: String, + pub image_src: String, + pub asset_id: String, + pub prompt: String, + pub actual_prompt: Option, + pub source_type: String, + pub selected: bool, +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct PuzzleResultDraft { + pub level_name: String, + pub summary: String, + pub theme_tags: Vec, + pub forbidden_directives: Vec, + pub creator_intent: Option, + pub anchor_pack: PuzzleAnchorPack, + pub candidates: Vec, + pub selected_candidate_id: Option, + pub cover_image_src: Option, + pub cover_asset_id: Option, + pub generation_status: String, +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct PuzzleResultPreviewBlocker { + pub id: String, + pub code: String, + pub message: String, +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct PuzzleResultPreviewFinding { + pub id: String, + pub severity: String, + pub code: String, + pub message: String, +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct PuzzleResultPreviewEnvelope { + pub draft: PuzzleResultDraft, + pub blockers: Vec, + pub quality_findings: Vec, + pub publish_ready: bool, +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct PuzzleAgentMessageSnapshot { + pub message_id: String, + pub session_id: String, + pub role: PuzzleAgentMessageRole, + pub kind: PuzzleAgentMessageKind, + pub text: String, + pub created_at_micros: i64, +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct PuzzleAgentSuggestedAction { + pub id: String, + pub action_type: String, + pub label: String, +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct PuzzleAgentSessionSnapshot { + pub session_id: String, + pub owner_user_id: String, + pub seed_text: String, + pub current_turn: u32, + pub progress_percent: u32, + pub stage: PuzzleAgentStage, + pub anchor_pack: PuzzleAnchorPack, + pub draft: Option, + pub messages: Vec, + pub last_assistant_reply: Option, + pub published_profile_id: Option, + pub suggested_actions: Vec, + pub result_preview: Option, + pub created_at_micros: i64, + pub updated_at_micros: i64, +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct PuzzleWorkProfile { + pub work_id: String, + pub profile_id: String, + pub owner_user_id: String, + pub source_session_id: Option, + pub author_display_name: String, + pub level_name: String, + pub summary: String, + pub theme_tags: Vec, + pub cover_image_src: Option, + pub cover_asset_id: Option, + pub publication_status: PuzzlePublicationStatus, + pub updated_at_micros: i64, + pub published_at_micros: Option, + pub play_count: u32, + pub publish_ready: bool, + pub anchor_pack: PuzzleAnchorPack, +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct PuzzleCellPosition { + pub row: u32, + pub col: u32, +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct PuzzlePieceState { + pub piece_id: String, + pub correct_row: u32, + pub correct_col: u32, + pub current_row: u32, + pub current_col: u32, + pub merged_group_id: Option, +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct PuzzleMergedGroupState { + pub group_id: String, + pub piece_ids: Vec, + pub occupied_cells: Vec, +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct PuzzleLeaderboardEntry { + pub rank: u32, + pub nickname: String, + pub elapsed_ms: u64, + pub is_current_player: bool, +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct PuzzleBoardSnapshot { + pub rows: u32, + pub cols: u32, + pub pieces: Vec, + pub merged_groups: Vec, + pub selected_piece_id: Option, + pub all_tiles_resolved: bool, +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct PuzzleRuntimeLevelSnapshot { + pub run_id: String, + pub level_index: u32, + pub grid_size: u32, + pub profile_id: String, + pub level_name: String, + pub author_display_name: String, + pub theme_tags: Vec, + pub cover_image_src: Option, + pub board: PuzzleBoardSnapshot, + pub status: PuzzleRuntimeLevelStatus, + pub started_at_ms: u64, + pub cleared_at_ms: Option, + pub elapsed_ms: Option, + pub leaderboard_entries: Vec, +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct PuzzleRunSnapshot { + pub run_id: String, + pub entry_profile_id: String, + pub cleared_level_count: u32, + pub current_level_index: u32, + pub current_grid_size: u32, + pub played_profile_ids: Vec, + pub previous_level_tags: Vec, + pub current_level: Option, + pub recommended_next_profile_id: Option, + pub leaderboard_entries: Vec, +} diff --git a/server-rs/crates/module-puzzle/src/errors.rs b/server-rs/crates/module-puzzle/src/errors.rs index 0422cf7a..ca014eae 100644 --- a/server-rs/crates/module-puzzle/src/errors.rs +++ b/server-rs/crates/module-puzzle/src/errors.rs @@ -1,3 +1,39 @@ -//! 拼图领域错误过渡落位。 +//! 拼图领域错误。 //! -//! 错误只表达拼图业务失败,例如标签不足、移动非法或运行态不存在。 +//! 错误只表达玩法规则失败,例如标签不足、移动非法或运行态不存在; +//! HTTP 状态码与 SpacetimeDB 字符串错误由 adapter 映射。 + +use std::{error::Error, fmt}; + +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum PuzzleFieldError { + MissingText, + MissingSessionId, + MissingProfileId, + MissingRunId, + MissingPieceId, + MissingAuthorDisplayName, + InvalidTagCount, + InvalidGridSize, + InvalidTargetCell, + InvalidOperation, +} + +impl fmt::Display for PuzzleFieldError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::MissingText => write!(f, "必填文本缺失"), + Self::MissingSessionId => write!(f, "session_id 缺失"), + Self::MissingProfileId => write!(f, "profile_id 缺失"), + Self::MissingRunId => write!(f, "run_id 缺失"), + Self::MissingPieceId => write!(f, "piece_id 缺失"), + Self::MissingAuthorDisplayName => write!(f, "author_display_name 缺失"), + Self::InvalidTagCount => write!(f, "标签数量不合法"), + Self::InvalidGridSize => write!(f, "网格规格不合法"), + Self::InvalidTargetCell => write!(f, "目标格子不合法"), + Self::InvalidOperation => write!(f, "操作不合法"), + } + } +} + +impl Error for PuzzleFieldError {} diff --git a/server-rs/crates/module-puzzle/src/events.rs b/server-rs/crates/module-puzzle/src/events.rs index 04b0aacf..1ed04670 100644 --- a/server-rs/crates/module-puzzle/src/events.rs +++ b/server-rs/crates/module-puzzle/src/events.rs @@ -1,3 +1,27 @@ //! 拼图领域事件过渡落位。 //! //! 用于表达草稿变化、作品发布、运行态推进和排行榜候选产生等事实。 + +/// 拼图领域事件。 +/// +/// 事件只描述已经发生的领域事实,持久化、订阅投影和 HTTP/SSE 通知 +/// 均由 SpacetimeDB adapter 或 BFF 决定。 +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum PuzzleDomainEvent { + DraftChanged { + session_id: String, + owner_user_id: String, + occurred_at_micros: i64, + }, + WorkPublished { + profile_id: String, + owner_user_id: String, + occurred_at_micros: i64, + }, + RunAdvanced { + run_id: String, + owner_user_id: String, + level_index: u32, + occurred_at_micros: i64, + }, +} diff --git a/server-rs/crates/module-puzzle/src/lib.rs b/server-rs/crates/module-puzzle/src/lib.rs index 7b334357..6acdc7c7 100644 --- a/server-rs/crates/module-puzzle/src/lib.rs +++ b/server-rs/crates/module-puzzle/src/lib.rs @@ -4,2419 +4,8 @@ mod domain; mod errors; mod events; -use std::{ - collections::{BTreeMap, BTreeSet, VecDeque}, - error::Error, - fmt, -}; - -use serde::{Deserialize, Serialize}; -use shared_kernel::{normalize_required_string, normalize_string_list}; -#[cfg(feature = "spacetime-types")] -use spacetimedb::SpacetimeType; - -pub const PUZZLE_AGENT_SESSION_ID_PREFIX: &str = "puzzle-session-"; -pub const PUZZLE_AGENT_MESSAGE_ID_PREFIX: &str = "puzzle-message-"; -pub const PUZZLE_PROFILE_ID_PREFIX: &str = "puzzle-profile-"; -pub const PUZZLE_RUN_ID_PREFIX: &str = "puzzle-run-"; -pub const PUZZLE_MIN_TAG_COUNT: usize = 3; -pub const PUZZLE_MAX_TAG_COUNT: usize = 6; -const PUZZLE_INITIAL_SHUFFLE_ATTEMPTS: u64 = 64; - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub enum PuzzleAgentStage { - CollectingAnchors, - DraftReady, - ImageRefining, - ReadyToPublish, - Published, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub enum PuzzleAnchorStatus { - Missing, - Inferred, - Confirmed, - Locked, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub enum PuzzleAgentMessageRole { - User, - Assistant, - System, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub enum PuzzleAgentMessageKind { - Chat, - Summary, - ActionResult, - Warning, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub enum PuzzlePublicationStatus { - Draft, - Published, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub enum PuzzleRuntimeLevelStatus { - Playing, - Cleared, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct PuzzleAnchorItem { - pub key: String, - pub label: String, - pub value: String, - pub status: PuzzleAnchorStatus, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct PuzzleAnchorPack { - pub theme_promise: PuzzleAnchorItem, - pub visual_subject: PuzzleAnchorItem, - pub visual_mood: PuzzleAnchorItem, - pub composition_hooks: PuzzleAnchorItem, - pub tags_and_forbidden: PuzzleAnchorItem, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct PuzzleCreatorIntent { - pub source_mode: String, - pub raw_messages_summary: String, - pub theme_promise: String, - pub visual_subject: String, - pub visual_mood: Vec, - pub composition_hooks: Vec, - pub theme_tags: Vec, - pub forbidden_directives: Vec, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct PuzzleGeneratedImageCandidate { - pub candidate_id: String, - pub image_src: String, - pub asset_id: String, - pub prompt: String, - pub actual_prompt: Option, - pub source_type: String, - pub selected: bool, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct PuzzleResultDraft { - pub level_name: String, - pub summary: String, - pub theme_tags: Vec, - pub forbidden_directives: Vec, - pub creator_intent: Option, - pub anchor_pack: PuzzleAnchorPack, - pub candidates: Vec, - pub selected_candidate_id: Option, - pub cover_image_src: Option, - pub cover_asset_id: Option, - pub generation_status: String, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct PuzzleResultPreviewBlocker { - pub id: String, - pub code: String, - pub message: String, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct PuzzleResultPreviewFinding { - pub id: String, - pub severity: String, - pub code: String, - pub message: String, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct PuzzleResultPreviewEnvelope { - pub draft: PuzzleResultDraft, - pub blockers: Vec, - pub quality_findings: Vec, - pub publish_ready: bool, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct PuzzleAgentMessageSnapshot { - pub message_id: String, - pub session_id: String, - pub role: PuzzleAgentMessageRole, - pub kind: PuzzleAgentMessageKind, - pub text: String, - pub created_at_micros: i64, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct PuzzleAgentSuggestedAction { - pub id: String, - pub action_type: String, - pub label: String, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct PuzzleAgentSessionSnapshot { - pub session_id: String, - pub owner_user_id: String, - pub seed_text: String, - pub current_turn: u32, - pub progress_percent: u32, - pub stage: PuzzleAgentStage, - pub anchor_pack: PuzzleAnchorPack, - pub draft: Option, - pub messages: Vec, - pub last_assistant_reply: Option, - pub published_profile_id: Option, - pub suggested_actions: Vec, - pub result_preview: Option, - pub created_at_micros: i64, - pub updated_at_micros: i64, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct PuzzleWorkProfile { - pub work_id: String, - pub profile_id: String, - pub owner_user_id: String, - pub source_session_id: Option, - pub author_display_name: String, - pub level_name: String, - pub summary: String, - pub theme_tags: Vec, - pub cover_image_src: Option, - pub cover_asset_id: Option, - pub publication_status: PuzzlePublicationStatus, - pub updated_at_micros: i64, - pub published_at_micros: Option, - pub play_count: u32, - pub publish_ready: bool, - pub anchor_pack: PuzzleAnchorPack, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct PuzzleCellPosition { - pub row: u32, - pub col: u32, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct PuzzlePieceState { - pub piece_id: String, - pub correct_row: u32, - pub correct_col: u32, - pub current_row: u32, - pub current_col: u32, - pub merged_group_id: Option, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct PuzzleMergedGroupState { - pub group_id: String, - pub piece_ids: Vec, - pub occupied_cells: Vec, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct PuzzleLeaderboardEntry { - pub rank: u32, - pub nickname: String, - pub elapsed_ms: u64, - pub is_current_player: bool, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct PuzzleBoardSnapshot { - pub rows: u32, - pub cols: u32, - pub pieces: Vec, - pub merged_groups: Vec, - pub selected_piece_id: Option, - pub all_tiles_resolved: bool, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct PuzzleRuntimeLevelSnapshot { - pub run_id: String, - pub level_index: u32, - pub grid_size: u32, - pub profile_id: String, - pub level_name: String, - pub author_display_name: String, - pub theme_tags: Vec, - pub cover_image_src: Option, - pub board: PuzzleBoardSnapshot, - pub status: PuzzleRuntimeLevelStatus, - pub started_at_ms: u64, - pub cleared_at_ms: Option, - pub elapsed_ms: Option, - pub leaderboard_entries: Vec, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct PuzzleRunSnapshot { - pub run_id: String, - pub entry_profile_id: String, - pub cleared_level_count: u32, - pub current_level_index: u32, - pub current_grid_size: u32, - pub played_profile_ids: Vec, - pub previous_level_tags: Vec, - pub current_level: Option, - pub recommended_next_profile_id: Option, - pub leaderboard_entries: Vec, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct PuzzleAgentSessionCreateInput { - pub session_id: String, - pub owner_user_id: String, - pub seed_text: String, - pub welcome_message_id: String, - pub welcome_message_text: String, - pub created_at_micros: i64, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct PuzzleAgentSessionGetInput { - pub session_id: String, - pub owner_user_id: String, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct PuzzleAgentMessageSubmitInput { - pub session_id: String, - pub owner_user_id: String, - pub user_message_id: String, - pub user_message_text: String, - pub submitted_at_micros: i64, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct PuzzleAgentMessageFinalizeInput { - pub session_id: String, - pub owner_user_id: String, - pub assistant_message_id: Option, - pub assistant_reply_text: Option, - pub stage: PuzzleAgentStage, - pub progress_percent: u32, - pub anchor_pack_json: String, - pub error_message: Option, - pub updated_at_micros: i64, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct PuzzleDraftCompileInput { - pub session_id: String, - pub owner_user_id: String, - pub compiled_at_micros: i64, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct PuzzleGeneratedImagesSaveInput { - pub session_id: String, - pub owner_user_id: String, - pub candidates_json: String, - pub saved_at_micros: i64, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct PuzzleSelectCoverImageInput { - pub session_id: String, - pub owner_user_id: String, - pub candidate_id: String, - pub selected_at_micros: i64, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct PuzzlePublishInput { - pub session_id: String, - pub owner_user_id: String, - pub work_id: String, - pub profile_id: String, - pub author_display_name: String, - pub level_name: Option, - pub summary: Option, - pub theme_tags: Option>, - pub published_at_micros: i64, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct PuzzleWorksListInput { - pub owner_user_id: String, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct PuzzleWorkGetInput { - pub profile_id: String, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct PuzzleWorkDeleteInput { - pub profile_id: String, - pub owner_user_id: String, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct PuzzleWorkUpsertInput { - pub profile_id: String, - pub owner_user_id: String, - pub level_name: String, - pub summary: String, - pub theme_tags: Vec, - pub cover_image_src: Option, - pub cover_asset_id: Option, - pub updated_at_micros: i64, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct PuzzleRunStartInput { - pub run_id: String, - pub owner_user_id: String, - pub profile_id: String, - pub started_at_micros: i64, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct PuzzleRunGetInput { - pub run_id: String, - pub owner_user_id: String, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct PuzzleRunSwapInput { - pub run_id: String, - pub owner_user_id: String, - pub first_piece_id: String, - pub second_piece_id: String, - pub swapped_at_micros: i64, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct PuzzleRunDragInput { - pub run_id: String, - pub owner_user_id: String, - pub piece_id: String, - pub target_row: u32, - pub target_col: u32, - pub dragged_at_micros: i64, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct PuzzleRunNextLevelInput { - pub run_id: String, - pub owner_user_id: String, - pub advanced_at_micros: i64, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct PuzzleLeaderboardSubmitInput { - pub run_id: String, - pub owner_user_id: String, - pub profile_id: String, - pub grid_size: u32, - pub elapsed_ms: u64, - pub nickname: String, - pub submitted_at_micros: i64, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct PuzzleAgentSessionProcedureResult { - pub ok: bool, - pub session_json: Option, - pub error_message: Option, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct PuzzleWorksProcedureResult { - pub ok: bool, - pub items_json: Option, - pub error_message: Option, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct PuzzleWorkProcedureResult { - pub ok: bool, - pub item_json: Option, - pub error_message: Option, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct PuzzleRunProcedureResult { - pub ok: bool, - pub run_json: Option, - pub error_message: Option, -} - -#[derive(Clone, Debug, PartialEq, Eq)] -pub enum PuzzleFieldError { - MissingText, - MissingSessionId, - MissingProfileId, - MissingRunId, - MissingPieceId, - MissingAuthorDisplayName, - InvalidTagCount, - InvalidGridSize, - InvalidTargetCell, - InvalidOperation, -} - -impl fmt::Display for PuzzleFieldError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::MissingText => write!(f, "必填文本缺失"), - Self::MissingSessionId => write!(f, "session_id 缺失"), - Self::MissingProfileId => write!(f, "profile_id 缺失"), - Self::MissingRunId => write!(f, "run_id 缺失"), - Self::MissingPieceId => write!(f, "piece_id 缺失"), - Self::MissingAuthorDisplayName => write!(f, "author_display_name 缺失"), - Self::InvalidTagCount => write!(f, "标签数量不合法"), - Self::InvalidGridSize => write!(f, "网格规格不合法"), - Self::InvalidTargetCell => write!(f, "目标格子不合法"), - Self::InvalidOperation => write!(f, "操作不合法"), - } - } -} - -impl Error for PuzzleFieldError {} - -impl PuzzleAgentStage { - pub fn as_str(self) -> &'static str { - match self { - Self::CollectingAnchors => "collecting_anchors", - Self::DraftReady => "draft_ready", - Self::ImageRefining => "image_refining", - Self::ReadyToPublish => "ready_to_publish", - Self::Published => "published", - } - } -} - -impl PuzzleAnchorStatus { - pub fn as_str(self) -> &'static str { - match self { - Self::Missing => "missing", - Self::Inferred => "inferred", - Self::Confirmed => "confirmed", - Self::Locked => "locked", - } - } -} - -impl PuzzleAgentMessageRole { - pub fn as_str(self) -> &'static str { - match self { - Self::User => "user", - Self::Assistant => "assistant", - Self::System => "system", - } - } -} - -impl PuzzleAgentMessageKind { - pub fn as_str(self) -> &'static str { - match self { - Self::Chat => "chat", - Self::Summary => "summary", - Self::ActionResult => "action_result", - Self::Warning => "warning", - } - } -} - -impl PuzzlePublicationStatus { - pub fn as_str(self) -> &'static str { - match self { - Self::Draft => "draft", - Self::Published => "published", - } - } -} - -impl PuzzleRuntimeLevelStatus { - pub fn as_str(self) -> &'static str { - match self { - Self::Playing => "playing", - Self::Cleared => "cleared", - } - } -} - -pub fn empty_anchor_pack() -> PuzzleAnchorPack { - PuzzleAnchorPack { - theme_promise: PuzzleAnchorItem { - key: "themePromise".to_string(), - label: "题材承诺".to_string(), - value: String::new(), - status: PuzzleAnchorStatus::Missing, - }, - visual_subject: PuzzleAnchorItem { - key: "visualSubject".to_string(), - label: "画面主体".to_string(), - value: String::new(), - status: PuzzleAnchorStatus::Missing, - }, - visual_mood: PuzzleAnchorItem { - key: "visualMood".to_string(), - label: "视觉气质".to_string(), - value: String::new(), - status: PuzzleAnchorStatus::Missing, - }, - composition_hooks: PuzzleAnchorItem { - key: "compositionHooks".to_string(), - label: "拼图记忆点".to_string(), - value: String::new(), - status: PuzzleAnchorStatus::Missing, - }, - tags_and_forbidden: PuzzleAnchorItem { - key: "tagsAndForbidden".to_string(), - label: "标签与禁忌".to_string(), - value: String::new(), - status: PuzzleAnchorStatus::Missing, - }, - } -} - -pub fn infer_anchor_pack(seed_text: &str, latest_message: Option<&str>) -> PuzzleAnchorPack { - let source = normalize_required_string(latest_message.unwrap_or(seed_text)) - .or_else(|| normalize_required_string(seed_text)) - .unwrap_or_else(|| "童话森林里的发光猫咪遗迹".to_string()); - let mut pack = empty_anchor_pack(); - pack.theme_promise.value = infer_theme_promise(&source); - pack.theme_promise.status = PuzzleAnchorStatus::Inferred; - pack.visual_subject.value = infer_visual_subject(&source); - pack.visual_subject.status = PuzzleAnchorStatus::Inferred; - pack.visual_mood.value = infer_visual_mood(&source); - pack.visual_mood.status = PuzzleAnchorStatus::Inferred; - pack.composition_hooks.value = infer_composition_hooks(&source); - pack.composition_hooks.status = PuzzleAnchorStatus::Inferred; - pack.tags_and_forbidden.value = infer_tags_and_forbidden(&source); - pack.tags_and_forbidden.status = PuzzleAnchorStatus::Inferred; - pack -} - -pub fn build_creator_intent( - anchor_pack: &PuzzleAnchorPack, - messages: &[PuzzleAgentMessageSnapshot], -) -> PuzzleCreatorIntent { - PuzzleCreatorIntent { - source_mode: "agent_chat".to_string(), - raw_messages_summary: messages - .iter() - .rev() - .take(4) - .map(|entry| entry.text.clone()) - .collect::>() - .join(" / "), - theme_promise: anchor_pack.theme_promise.value.clone(), - visual_subject: anchor_pack.visual_subject.value.clone(), - visual_mood: split_phrase_list(&anchor_pack.visual_mood.value), - composition_hooks: split_phrase_list(&anchor_pack.composition_hooks.value), - theme_tags: split_phrase_list(&anchor_pack.tags_and_forbidden.value) - .into_iter() - .take(PUZZLE_MAX_TAG_COUNT) - .collect(), - forbidden_directives: vec![extract_forbidden_directive( - &anchor_pack.tags_and_forbidden.value, - )], - } -} - -pub fn compile_result_draft( - anchor_pack: &PuzzleAnchorPack, - messages: &[PuzzleAgentMessageSnapshot], -) -> PuzzleResultDraft { - let creator_intent = build_creator_intent(anchor_pack, messages); - let normalized_tags = normalize_theme_tags(creator_intent.theme_tags.clone()); - let level_name = build_level_name(anchor_pack, &normalized_tags); - PuzzleResultDraft { - level_name, - summary: format!( - "{},主体是{},氛围偏{}。", - fallback_text(&anchor_pack.theme_promise.value, "梦幻题材"), - fallback_text(&anchor_pack.visual_subject.value, "画面主体"), - fallback_text(&anchor_pack.visual_mood.value, "温暖") - ), - theme_tags: normalized_tags, - forbidden_directives: creator_intent.forbidden_directives.clone(), - creator_intent: Some(creator_intent), - anchor_pack: anchor_pack.clone(), - candidates: Vec::new(), - selected_candidate_id: None, - cover_image_src: None, - cover_asset_id: None, - generation_status: "idle".to_string(), - } -} - -pub fn build_generated_candidates( - session_id: &str, - prompt_text: Option<&str>, - draft: &PuzzleResultDraft, - candidate_count: u32, - now_micros: i64, -) -> Result, PuzzleFieldError> { - let session_id = - normalize_required_string(session_id).ok_or(PuzzleFieldError::MissingSessionId)?; - let count = candidate_count.max(1).min(1); - let prompt = normalize_required_string(prompt_text.unwrap_or(&draft.summary)) - .unwrap_or_else(|| draft.summary.clone()); - - Ok((0..count) - .map(|index| { - let candidate_seed = now_micros + i64::from(index); - let candidate_id = format!("{session_id}-candidate-{}", index + 1); - PuzzleGeneratedImageCandidate { - candidate_id: candidate_id.clone(), - // 拼图图片的正式持久化由 api-server 上传 OSS;这里仅保留 reducer - // 单测/保底路径构造,前缀必须与 OSS 兼容路由一致,不能再指向 public 目录。 - image_src: format!( - "/generated-puzzle-assets/{session_id}/{candidate_seed}/cover.svg" - ), - asset_id: format!("puzzle-cover-{candidate_seed}"), - prompt: prompt.clone(), - actual_prompt: Some(prompt.clone()), - source_type: "generated".to_string(), - selected: index == 0, - } - }) - .collect()) -} - -pub fn apply_selected_candidate( - mut draft: PuzzleResultDraft, - candidate_id: &str, -) -> Result { - let candidate_id = - normalize_required_string(candidate_id).ok_or(PuzzleFieldError::MissingText)?; - let mut selected_cover_image_src = None; - let mut selected_cover_asset_id = None; - let mut matched = false; - - for candidate in &mut draft.candidates { - candidate.selected = candidate.candidate_id == candidate_id; - if candidate.selected { - matched = true; - selected_cover_image_src = Some(candidate.image_src.clone()); - selected_cover_asset_id = Some(candidate.asset_id.clone()); - } - } - - if !matched { - return Err(PuzzleFieldError::InvalidOperation); - } - - draft.selected_candidate_id = Some(candidate_id); - draft.cover_image_src = selected_cover_image_src; - draft.cover_asset_id = selected_cover_asset_id; - draft.generation_status = "ready".to_string(); - Ok(draft) -} - -pub fn build_result_preview( - draft: &PuzzleResultDraft, - author_display_name: Option<&str>, -) -> PuzzleResultPreviewEnvelope { - let blockers = validate_publish_requirements(draft, author_display_name); - PuzzleResultPreviewEnvelope { - draft: draft.clone(), - blockers, - quality_findings: Vec::new(), - publish_ready: validate_publish_requirements(draft, author_display_name).is_empty(), - } -} - -pub fn validate_publish_requirements( - draft: &PuzzleResultDraft, - author_display_name: Option<&str>, -) -> Vec { - let mut blockers = Vec::new(); - if normalize_required_string(&draft.level_name).is_none() { - blockers.push(PuzzleResultPreviewBlocker { - id: "missing-level-name".to_string(), - code: "MISSING_LEVEL_NAME".to_string(), - message: "关卡名不能为空".to_string(), - }); - } - if draft - .cover_image_src - .as_deref() - .map(str::trim) - .unwrap_or("") - .is_empty() - { - blockers.push(PuzzleResultPreviewBlocker { - id: "missing-cover-image".to_string(), - code: "MISSING_COVER_IMAGE".to_string(), - message: "正式拼图图片尚未确定".to_string(), - }); - } - if draft.theme_tags.len() < PUZZLE_MIN_TAG_COUNT - || draft.theme_tags.len() > PUZZLE_MAX_TAG_COUNT - { - blockers.push(PuzzleResultPreviewBlocker { - id: "invalid-tag-count".to_string(), - code: "INVALID_TAG_COUNT".to_string(), - message: "正式标签数量必须在 3 到 6 之间".to_string(), - }); - } - if normalize_required_string(author_display_name.unwrap_or("")).is_none() { - blockers.push(PuzzleResultPreviewBlocker { - id: "missing-author".to_string(), - code: "MISSING_AUTHOR".to_string(), - message: "作者信息不可读".to_string(), - }); - } - blockers -} - -pub fn create_work_profile( - work_id: String, - profile_id: String, - owner_user_id: String, - source_session_id: Option, - author_display_name: String, - draft: &PuzzleResultDraft, - updated_at_micros: i64, -) -> Result { - let author_display_name = normalize_required_string(author_display_name) - .ok_or(PuzzleFieldError::MissingAuthorDisplayName)?; - let preview = build_result_preview(draft, Some(&author_display_name)); - Ok(PuzzleWorkProfile { - work_id, - profile_id, - owner_user_id, - source_session_id, - author_display_name, - level_name: draft.level_name.clone(), - summary: draft.summary.clone(), - theme_tags: normalize_theme_tags(draft.theme_tags.clone()), - cover_image_src: draft.cover_image_src.clone(), - cover_asset_id: draft.cover_asset_id.clone(), - publication_status: PuzzlePublicationStatus::Draft, - updated_at_micros, - published_at_micros: None, - play_count: 0, - publish_ready: preview.publish_ready, - anchor_pack: draft.anchor_pack.clone(), - }) -} - -pub fn publish_work_profile( - mut profile: PuzzleWorkProfile, - draft: &PuzzleResultDraft, - published_at_micros: i64, -) -> Result { - if !validate_publish_requirements(draft, Some(&profile.author_display_name)).is_empty() { - return Err(PuzzleFieldError::InvalidOperation); - } - profile.level_name = draft.level_name.clone(); - profile.summary = draft.summary.clone(); - profile.theme_tags = normalize_theme_tags(draft.theme_tags.clone()); - profile.cover_image_src = draft.cover_image_src.clone(); - profile.cover_asset_id = draft.cover_asset_id.clone(); - profile.publication_status = PuzzlePublicationStatus::Published; - profile.publish_ready = true; - profile.updated_at_micros = published_at_micros; - profile.published_at_micros = Some(published_at_micros); - Ok(profile) -} - -/// 在发布前把结果页的轻量编辑字段覆盖回草稿真相。 -/// 这里只允许覆盖 PRD 明确要求的关卡名、摘要与标签,不额外扩到更多结果页元数据。 -pub fn apply_publish_overrides_to_draft( - draft: &PuzzleResultDraft, - level_name: Option, - summary: Option, - theme_tags: Option>, -) -> Result { - let mut next_draft = draft.clone(); - - if let Some(next_level_name) = level_name - && let Some(normalized_level_name) = normalize_required_string(&next_level_name) - { - next_draft.level_name = normalized_level_name; - } - - if let Some(next_summary) = summary - && let Some(normalized_summary) = normalize_required_string(&next_summary) - { - next_draft.summary = normalized_summary; - } - - if let Some(next_theme_tags) = theme_tags { - let normalized_theme_tags = normalize_theme_tags(next_theme_tags); - if normalized_theme_tags.len() < PUZZLE_MIN_TAG_COUNT - || normalized_theme_tags.len() > PUZZLE_MAX_TAG_COUNT - { - return Err(PuzzleFieldError::InvalidTagCount); - } - next_draft.theme_tags = normalized_theme_tags; - } - - Ok(next_draft) -} - -pub fn resolve_puzzle_grid_size(cleared_level_count: u32) -> u32 { - if cleared_level_count >= 3 { 4 } else { 3 } -} - -pub fn build_initial_board(grid_size: u32) -> Result { - build_initial_board_with_seed(grid_size, 0) -} - -pub fn build_initial_board_with_seed( - grid_size: u32, - shuffle_seed: u64, -) -> Result { - if !matches!(grid_size, 3 | 4) { - return Err(PuzzleFieldError::InvalidGridSize); - } - - let pieces = build_initial_pieces_without_correct_neighbors(grid_size, shuffle_seed); - - Ok(rebuild_board_snapshot(grid_size, pieces, None)) -} - -pub fn start_run( - run_id: String, - entry_profile: &PuzzleWorkProfile, - cleared_level_count: u32, -) -> Result { - let grid_size = resolve_puzzle_grid_size(cleared_level_count); - let shuffle_seed = puzzle_shuffle_seed( - &run_id, - &entry_profile.profile_id, - cleared_level_count + 1, - grid_size, - ); - start_run_with_shuffle_seed(run_id, entry_profile, cleared_level_count, shuffle_seed) -} - -pub fn start_run_with_shuffle_seed( - run_id: String, - entry_profile: &PuzzleWorkProfile, - cleared_level_count: u32, - shuffle_seed: u64, -) -> Result { - let grid_size = resolve_puzzle_grid_size(cleared_level_count); - let board = build_initial_board_with_seed(grid_size, shuffle_seed)?; - let started_at_ms = current_unix_ms(); - Ok(PuzzleRunSnapshot { - run_id: run_id.clone(), - entry_profile_id: entry_profile.profile_id.clone(), - cleared_level_count, - current_level_index: cleared_level_count + 1, - current_grid_size: grid_size, - played_profile_ids: vec![entry_profile.profile_id.clone()], - previous_level_tags: entry_profile.theme_tags.clone(), - current_level: Some(PuzzleRuntimeLevelSnapshot { - run_id, - level_index: cleared_level_count + 1, - grid_size, - profile_id: entry_profile.profile_id.clone(), - level_name: entry_profile.level_name.clone(), - author_display_name: entry_profile.author_display_name.clone(), - theme_tags: entry_profile.theme_tags.clone(), - cover_image_src: entry_profile.cover_image_src.clone(), - board, - status: PuzzleRuntimeLevelStatus::Playing, - started_at_ms, - cleared_at_ms: None, - elapsed_ms: None, - leaderboard_entries: Vec::new(), - }), - recommended_next_profile_id: None, - leaderboard_entries: Vec::new(), - }) -} - -pub fn swap_pieces( - run: &PuzzleRunSnapshot, - first_piece_id: &str, - second_piece_id: &str, -) -> Result { - let first_piece_id = - normalize_required_string(first_piece_id).ok_or(PuzzleFieldError::MissingPieceId)?; - let second_piece_id = - normalize_required_string(second_piece_id).ok_or(PuzzleFieldError::MissingPieceId)?; - let current_level = run - .current_level - .clone() - .ok_or(PuzzleFieldError::InvalidOperation)?; - if current_level.status == PuzzleRuntimeLevelStatus::Cleared { - return Err(PuzzleFieldError::InvalidOperation); - } - let mut pieces = current_level.board.pieces.clone(); - let first_index = pieces - .iter() - .position(|piece| piece.piece_id == first_piece_id) - .ok_or(PuzzleFieldError::MissingPieceId)?; - let second_index = pieces - .iter() - .position(|piece| piece.piece_id == second_piece_id) - .ok_or(PuzzleFieldError::MissingPieceId)?; - - let (first_row, first_col) = ( - pieces[first_index].current_row, - pieces[first_index].current_col, - ); - let (second_row, second_col) = ( - pieces[second_index].current_row, - pieces[second_index].current_col, - ); - pieces[first_index].current_row = second_row; - pieces[first_index].current_col = second_col; - pieces[second_index].current_row = first_row; - pieces[second_index].current_col = first_col; - - let affected_cells = [ - PuzzleCellPosition { - row: first_row, - col: first_col, - }, - PuzzleCellPosition { - row: second_row, - col: second_col, - }, - ]; - let next_board = rebuild_board_snapshot_for_affected_cells( - current_level.grid_size, - ¤t_level.board, - pieces, - affected_cells, - None, - ); - Ok(with_next_board(run, next_board)) -} - -pub fn drag_piece_or_group( - run: &PuzzleRunSnapshot, - piece_id: &str, - target_row: u32, - target_col: u32, -) -> Result { - let piece_id = normalize_required_string(piece_id).ok_or(PuzzleFieldError::MissingPieceId)?; - let current_level = run - .current_level - .clone() - .ok_or(PuzzleFieldError::InvalidOperation)?; - if current_level.status == PuzzleRuntimeLevelStatus::Cleared { - return Err(PuzzleFieldError::InvalidOperation); - } - let grid_size = current_level.grid_size; - if target_row >= grid_size || target_col >= grid_size { - return Err(PuzzleFieldError::InvalidTargetCell); - } - - let mut pieces = current_level.board.pieces.clone(); - let piece_index = pieces - .iter() - .position(|piece| piece.piece_id == piece_id) - .ok_or(PuzzleFieldError::MissingPieceId)?; - let source_group_id = pieces[piece_index].merged_group_id.clone(); - - let operation_cells = match source_group_id { - Some(group_id) => drag_group(&mut pieces, &group_id, target_row, target_col, grid_size)?, - None => drag_single_piece(&mut pieces, piece_index, target_row, target_col)?, - }; - - let next_board = rebuild_board_snapshot_for_affected_cells( - grid_size, - ¤t_level.board, - pieces, - operation_cells, - None, - ); - Ok(with_next_board(run, next_board)) -} - -pub fn rebuild_board_snapshot_for_affected_cells( - grid_size: u32, - previous_board: &PuzzleBoardSnapshot, - pieces: Vec, - affected_cells: impl IntoIterator, - selected_piece_id: Option, -) -> PuzzleBoardSnapshot { - let affected_scope = expand_affected_cells(grid_size, affected_cells); - if affected_scope.is_empty() || previous_board.merged_groups.is_empty() { - return rebuild_board_snapshot(grid_size, pieces, selected_piece_id); - } - - let mut recalculated_piece_ids = pieces - .iter() - .filter(|piece| affected_scope.contains(&(piece.current_row, piece.current_col))) - .map(|piece| piece.piece_id.clone()) - .collect::>(); - let previous_piece_by_id = previous_board - .pieces - .iter() - .map(|piece| (piece.piece_id.clone(), piece)) - .collect::>(); - - for piece_id in recalculated_piece_ids.clone() { - if let Some(previous_piece) = previous_piece_by_id.get(&piece_id) - && let Some(group_id) = previous_piece.merged_group_id.as_deref() - { - add_previous_group_piece_ids(previous_board, group_id, &mut recalculated_piece_ids); - } - } - - let mut preserved_groups = Vec::new(); - for group in &previous_board.merged_groups { - if group - .piece_ids - .iter() - .any(|piece_id| recalculated_piece_ids.contains(piece_id)) - { - continue; - } - let occupied_cells = group - .piece_ids - .iter() - .filter_map(|piece_id| { - pieces - .iter() - .find(|piece| piece.piece_id == *piece_id) - .map(|piece| PuzzleCellPosition { - row: piece.current_row, - col: piece.current_col, - }) - }) - .collect::>(); - if occupied_cells.len() == group.piece_ids.len() { - preserved_groups.push(PuzzleMergedGroupState { - group_id: group.group_id.clone(), - piece_ids: group.piece_ids.clone(), - occupied_cells, - }); - } - } - - let recalculated_pieces = pieces - .iter() - .filter(|piece| recalculated_piece_ids.contains(&piece.piece_id)) - .cloned() - .collect::>(); - let mut next_groups = preserved_groups; - next_groups.extend(resolve_merged_groups(&recalculated_pieces)); - rebuild_board_snapshot_with_groups(grid_size, pieces, next_groups, selected_piece_id) -} - -pub fn advance_next_level( - run: &PuzzleRunSnapshot, - next_profile: &PuzzleWorkProfile, -) -> Result { - let current_level = run - .current_level - .clone() - .ok_or(PuzzleFieldError::InvalidOperation)?; - if current_level.status != PuzzleRuntimeLevelStatus::Cleared { - return Err(PuzzleFieldError::InvalidOperation); - } - - let next_cleared_count = run.cleared_level_count; - let next_grid_size = resolve_puzzle_grid_size(next_cleared_count); - let shuffle_seed = puzzle_shuffle_seed( - &run.run_id, - &next_profile.profile_id, - run.current_level_index + 1, - next_grid_size, - ); - let next_board = build_initial_board_with_seed(next_grid_size, shuffle_seed)?; - let mut played_profile_ids = run.played_profile_ids.clone(); - played_profile_ids.push(next_profile.profile_id.clone()); - - Ok(PuzzleRunSnapshot { - run_id: run.run_id.clone(), - entry_profile_id: run.entry_profile_id.clone(), - cleared_level_count: next_cleared_count, - current_level_index: run.current_level_index + 1, - current_grid_size: next_grid_size, - played_profile_ids, - previous_level_tags: next_profile.theme_tags.clone(), - current_level: Some(PuzzleRuntimeLevelSnapshot { - run_id: run.run_id.clone(), - level_index: run.current_level_index + 1, - grid_size: next_grid_size, - profile_id: next_profile.profile_id.clone(), - level_name: next_profile.level_name.clone(), - author_display_name: next_profile.author_display_name.clone(), - theme_tags: next_profile.theme_tags.clone(), - cover_image_src: next_profile.cover_image_src.clone(), - board: next_board, - status: PuzzleRuntimeLevelStatus::Playing, - started_at_ms: current_unix_ms(), - cleared_at_ms: None, - elapsed_ms: None, - leaderboard_entries: Vec::new(), - }), - recommended_next_profile_id: None, - leaderboard_entries: Vec::new(), - }) -} - -pub fn select_next_profile<'a>( - current_profile: &PuzzleWorkProfile, - played_profile_ids: &[String], - candidates: &'a [PuzzleWorkProfile], -) -> Option<&'a PuzzleWorkProfile> { - let mut available = candidates - .iter() - .filter(|candidate| { - candidate.publication_status == PuzzlePublicationStatus::Published - && candidate.cover_image_src.is_some() - && !candidate.theme_tags.is_empty() - && candidate.profile_id != current_profile.profile_id - }) - .collect::>(); - - let has_unplayed = available - .iter() - .any(|candidate| !played_profile_ids.contains(&candidate.profile_id)); - - if has_unplayed { - available.retain(|candidate| !played_profile_ids.contains(&candidate.profile_id)); - } else if let Some(last_played) = played_profile_ids.last() { - available.retain(|candidate| candidate.profile_id != *last_played); - } - - available.into_iter().max_by(|left, right| { - let left_score = recommendation_score(current_profile, left); - let right_score = recommendation_score(current_profile, right); - left_score - .partial_cmp(&right_score) - .unwrap_or(std::cmp::Ordering::Equal) - .then_with(|| { - tag_similarity_score(¤t_profile.theme_tags, &left.theme_tags) - .partial_cmp(&tag_similarity_score( - ¤t_profile.theme_tags, - &right.theme_tags, - )) - .unwrap_or(std::cmp::Ordering::Equal) - }) - .then_with(|| right.play_count.cmp(&left.play_count)) - .then_with(|| left.updated_at_micros.cmp(&right.updated_at_micros)) - }) -} - -pub fn recommendation_score( - current_profile: &PuzzleWorkProfile, - candidate: &PuzzleWorkProfile, -) -> f32 { - let tag_similarity = tag_similarity_score(¤t_profile.theme_tags, &candidate.theme_tags); - let same_author_score = if current_profile.owner_user_id == candidate.owner_user_id { - 1.0 - } else { - 0.0 - }; - tag_similarity * 0.7 + same_author_score * 0.3 -} - -pub fn tag_similarity_score(left_tags: &[String], right_tags: &[String]) -> f32 { - let left_set = normalize_theme_tags(left_tags.to_vec()) - .into_iter() - .collect::>(); - let right_set = normalize_theme_tags(right_tags.to_vec()) - .into_iter() - .collect::>(); - if left_set.is_empty() && right_set.is_empty() { - return 0.0; - } - let intersection = left_set.intersection(&right_set).count() as f32; - let union = left_set.union(&right_set).count() as f32; - if union <= f32::EPSILON { - 0.0 - } else { - intersection / union - } -} - -pub fn normalize_theme_tags(tags: Vec) -> Vec { - let alias_map = BTreeMap::from([ - ("蒸汽", "蒸汽城市"), - ("蒸汽朋克", "蒸汽城市"), - ("遗迹", "神庙遗迹"), - ("森林", "童话森林"), - ("夜雨", "雨夜"), - ("发光猫", "猫咪"), - ]); - - let mut normalized = normalize_string_list(tags) - .into_iter() - .flat_map(|value| split_phrase_list(&value)) - .map(|value| { - alias_map - .get(value.as_str()) - .map(|alias| (*alias).to_string()) - .unwrap_or(value) - }) - .collect::>(); - normalized.sort(); - normalized.dedup(); - normalized.into_iter().take(PUZZLE_MAX_TAG_COUNT).collect() -} - -fn infer_theme_promise(source: &str) -> String { - if source.contains("神庙") { - "探索遗迹中的奇幻想象".to_string() - } else if source.contains("雨") { - "在雨夜中寻找视觉线索".to_string() - } else if source.contains("猫") { - "一眼记住可爱的猫咪奇景".to_string() - } else { - "用一张强识别度画面承诺幻想题材".to_string() - } -} - -fn infer_visual_subject(source: &str) -> String { - if source.contains("猫") { - "发光猫咪".to_string() - } else if source.contains("神庙") { - "巨大遗迹入口".to_string() - } else if source.contains("城市") { - "蒸汽城市核心地标".to_string() - } else { - "画面中央的核心主体".to_string() - } -} - -fn infer_visual_mood(source: &str) -> String { - if source.contains("悬疑") { - "悬疑、静谧".to_string() - } else if source.contains("温暖") { - "温暖、柔和".to_string() - } else if source.contains("机械") { - "机械、奇诡".to_string() - } else { - "梦幻、清晰".to_string() - } -} - -fn infer_composition_hooks(source: &str) -> String { - if source.contains("塔") { - "高塔轮廓、纵向构图、亮色焦点".to_string() - } else if source.contains("遗迹") { - "入口轮廓、对称台阶、地标雕像".to_string() - } else { - "主体轮廓、色块分区、地标元素".to_string() - } -} - -fn infer_tags_and_forbidden(source: &str) -> String { - if source.contains("神庙") { - "神庙遗迹、童话森林、雨夜;禁止标题字".to_string() - } else if source.contains("猫") { - "猫咪、童话森林、发光;禁止水印".to_string() - } else { - "蒸汽城市、雨夜、奇幻;禁止按钮".to_string() - } -} - -fn extract_forbidden_directive(source: &str) -> String { - if let Some((_, tail)) = source.split_once(';') { - return normalize_required_string(tail).unwrap_or_else(|| "禁止标题字".to_string()); - } - "禁止标题字".to_string() -} - -fn build_level_name(anchor_pack: &PuzzleAnchorPack, normalized_tags: &[String]) -> String { - if let Some(tag) = normalized_tags.first() { - return format!("{tag}拼图"); - } - if let Some(subject) = normalize_required_string(&anchor_pack.visual_subject.value) { - return subject.chars().take(8).collect::(); - } - "奇景拼图".to_string() -} - -fn fallback_text(value: &str, fallback: &str) -> String { - normalize_required_string(value).unwrap_or_else(|| fallback.to_string()) -} - -fn split_phrase_list(value: &str) -> Vec { - value - .replace(',', ",") - .replace('、', ",") - .replace(';', ",") - .split(',') - .filter_map(normalize_required_string) - .collect() -} - -fn puzzle_shuffle_seed(run_id: &str, profile_id: &str, level_index: u32, grid_size: u32) -> u64 { - let mut hash = 0xcbf2_9ce4_8422_2325_u64; - for byte in run_id - .bytes() - .chain(profile_id.bytes()) - .chain(level_index.to_le_bytes()) - .chain(grid_size.to_le_bytes()) - { - hash ^= u64::from(byte); - hash = hash.wrapping_mul(0x0000_0100_0000_01b3); - } - hash -} - -fn shuffle_positions(positions: &mut [PuzzleCellPosition], seed: u64) { - if positions.len() <= 1 { - return; - } - - let mut state = seed ^ ((positions.len() as u64) << 32) ^ 0x9e37_79b9_7f4a_7c15; - for index in (1..positions.len()).rev() { - state = state - .wrapping_mul(6_364_136_223_846_793_005) - .wrapping_add(1_442_695_040_888_963_407); - let swap_index = (state % ((index + 1) as u64)) as usize; - positions.swap(index, swap_index); - } -} - -fn build_initial_pieces_without_correct_neighbors( - grid_size: u32, - shuffle_seed: u64, -) -> Vec { - let base_positions = build_correct_positions(grid_size); - for attempt in 0..PUZZLE_INITIAL_SHUFFLE_ATTEMPTS { - let mut positions = base_positions.clone(); - shuffle_positions( - &mut positions, - shuffle_seed.wrapping_add(attempt.wrapping_mul(0x9e37_79b9_7f4a_7c15)), - ); - ensure_board_is_not_solved(&mut positions, grid_size); - let pieces = build_pieces_from_positions(grid_size, &positions); - if !has_any_original_neighbor_pair(&pieces) { - return pieces; - } - } - - // 随机尝试耗尽后使用确定性约束搜索兜底,保证开局没有任意一对原图相邻块互相贴边。 - let fallback_pieces = build_original_neighbor_free_pieces(grid_size, shuffle_seed) - .unwrap_or_else(|| build_pieces_from_positions(grid_size, &base_positions)); - debug_assert!(!has_any_original_neighbor_pair(&fallback_pieces)); - fallback_pieces -} - -fn build_correct_positions(grid_size: u32) -> Vec { - let total = grid_size * grid_size; - (0..total) - .map(|index| PuzzleCellPosition { - row: index / grid_size, - col: index % grid_size, - }) - .collect() -} - -fn build_pieces_from_positions( - grid_size: u32, - positions: &[PuzzleCellPosition], -) -> Vec { - positions - .iter() - .enumerate() - .map(|(index, current)| { - let index = index as u32; - PuzzlePieceState { - piece_id: format!("piece-{index}"), - correct_row: index / grid_size, - correct_col: index % grid_size, - current_row: current.row, - current_col: current.col, - merged_group_id: None, - } - }) - .collect() -} - -fn ensure_board_is_not_solved(positions: &mut [PuzzleCellPosition], grid_size: u32) { - if positions.len() <= 1 { - return; - } - - let is_solved = positions.iter().enumerate().all(|(index, position)| { - position.row == index as u32 / grid_size && position.col == index as u32 % grid_size - }); - if is_solved { - positions.rotate_left(1); - } -} - -fn has_any_original_neighbor_pair(pieces: &[PuzzlePieceState]) -> bool { - let pieces_by_cell = pieces - .iter() - .map(|piece| ((piece.current_row, piece.current_col), piece)) - .collect::>(); - - pieces.iter().any(|piece| { - neighbor_cells(piece.current_row, piece.current_col) - .into_iter() - .filter_map(|cell| pieces_by_cell.get(&cell)) - .any(|neighbor| are_original_neighbors(piece, neighbor)) - }) -} - -fn are_original_neighbors(left: &PuzzlePieceState, right: &PuzzlePieceState) -> bool { - left.correct_row.abs_diff(right.correct_row) + left.correct_col.abs_diff(right.correct_col) == 1 -} - -fn build_original_neighbor_free_pieces( - grid_size: u32, - shuffle_seed: u64, -) -> Option> { - let total = (grid_size * grid_size) as usize; - let mut piece_order = (0..total as u32).collect::>(); - sort_indices_by_seed(&mut piece_order, shuffle_seed ^ 0xa076_1d64_78bd_642f); - let mut cell_order = build_correct_positions(grid_size); - sort_cells_by_seed(&mut cell_order, shuffle_seed ^ 0xe703_7ed1_a0b4_28db); - - let mut placements = vec![None; total]; - let mut used_cells = BTreeSet::new(); - if place_neighbor_free_piece( - grid_size, - &piece_order, - &cell_order, - 0, - &mut placements, - &mut used_cells, - ) { - Some( - placements - .into_iter() - .enumerate() - .filter_map(|(index, current)| { - current.map(|current| PuzzlePieceState { - piece_id: format!("piece-{index}"), - correct_row: index as u32 / grid_size, - correct_col: index as u32 % grid_size, - current_row: current.row, - current_col: current.col, - merged_group_id: None, - }) - }) - .collect(), - ) - } else { - None - } -} - -fn place_neighbor_free_piece( - grid_size: u32, - piece_order: &[u32], - cell_order: &[PuzzleCellPosition], - depth: usize, - placements: &mut [Option], - used_cells: &mut BTreeSet<(u32, u32)>, -) -> bool { - let Some(piece_index) = piece_order.get(depth).copied() else { - return true; - }; - - for cell in cell_order { - if used_cells.contains(&(cell.row, cell.col)) { - continue; - } - if cell.row == piece_index / grid_size && cell.col == piece_index % grid_size { - continue; - } - if violates_original_neighbor_free_rule(grid_size, piece_index, cell.clone(), placements) { - continue; - } - - placements[piece_index as usize] = Some(cell.clone()); - used_cells.insert((cell.row, cell.col)); - if place_neighbor_free_piece( - grid_size, - piece_order, - cell_order, - depth + 1, - placements, - used_cells, - ) { - return true; - } - used_cells.remove(&(cell.row, cell.col)); - placements[piece_index as usize] = None; - } - - false -} - -fn violates_original_neighbor_free_rule( - grid_size: u32, - piece_index: u32, - cell: PuzzleCellPosition, - placements: &[Option], -) -> bool { - placements - .iter() - .enumerate() - .filter_map(|(placed_index, placed_cell)| { - placed_cell - .as_ref() - .map(|placed_cell| (placed_index as u32, placed_cell)) - }) - .any(|(placed_index, placed_cell)| { - let original_neighbors = (piece_index / grid_size).abs_diff(placed_index / grid_size) - + (piece_index % grid_size).abs_diff(placed_index % grid_size) - == 1; - let current_neighbors = - cell.row.abs_diff(placed_cell.row) + cell.col.abs_diff(placed_cell.col) == 1; - original_neighbors && current_neighbors - }) -} - -fn sort_indices_by_seed(indices: &mut [u32], seed: u64) { - indices.sort_by_key(|index| seeded_order_key(seed, u64::from(*index))); -} - -fn sort_cells_by_seed(cells: &mut [PuzzleCellPosition], seed: u64) { - cells.sort_by_key(|cell| seeded_order_key(seed, u64::from(cell.row * 16 + cell.col))); -} - -fn seeded_order_key(seed: u64, value: u64) -> u64 { - let mut state = seed ^ value.wrapping_mul(0x9e37_79b9_7f4a_7c15); - state ^= state >> 30; - state = state.wrapping_mul(0xbf58_476d_1ce4_e5b9); - state ^= state >> 27; - state = state.wrapping_mul(0x94d0_49bb_1331_11eb); - state ^ (state >> 31) -} - -fn rebuild_board_snapshot( - grid_size: u32, - pieces: Vec, - selected_piece_id: Option, -) -> PuzzleBoardSnapshot { - let merged_groups = resolve_merged_groups(&pieces); - rebuild_board_snapshot_with_groups(grid_size, pieces, merged_groups, selected_piece_id) -} - -fn rebuild_board_snapshot_with_groups( - grid_size: u32, - mut pieces: Vec, - merged_groups: Vec, - selected_piece_id: Option, -) -> PuzzleBoardSnapshot { - let merged_groups = normalize_group_ids(merged_groups); - let group_by_piece = merged_groups - .iter() - .flat_map(|group| { - group - .piece_ids - .iter() - .cloned() - .map(|piece_id| (piece_id, group.group_id.clone())) - }) - .collect::>(); - - for piece in &mut pieces { - piece.merged_group_id = group_by_piece.get(&piece.piece_id).cloned(); - } - - let all_pieces_in_correct_cells = pieces.iter().all(|piece| { - piece.correct_row == piece.current_row && piece.correct_col == piece.current_col - }); - let all_pieces_merged_into_one_group = merged_groups - .iter() - .any(|group| group.piece_ids.len() == pieces.len() && pieces.len() > 1); - let all_tiles_resolved = all_pieces_in_correct_cells || all_pieces_merged_into_one_group; - - PuzzleBoardSnapshot { - rows: grid_size, - cols: grid_size, - pieces, - merged_groups, - selected_piece_id, - all_tiles_resolved, - } -} - -fn normalize_group_ids(groups: Vec) -> Vec { - groups - .into_iter() - .enumerate() - .map(|(index, group)| PuzzleMergedGroupState { - group_id: format!("group-{}", index + 1), - ..group - }) - .collect() -} - -fn expand_affected_cells( - grid_size: u32, - cells: impl IntoIterator, -) -> BTreeSet<(u32, u32)> { - let mut scope = BTreeSet::new(); - for cell in cells { - if cell.row >= grid_size || cell.col >= grid_size { - continue; - } - scope.insert((cell.row, cell.col)); - for (row, col) in neighbor_cells(cell.row, cell.col) { - if row < grid_size && col < grid_size { - scope.insert((row, col)); - } - } - } - scope -} - -fn add_previous_group_piece_ids( - previous_board: &PuzzleBoardSnapshot, - group_id: &str, - piece_ids: &mut BTreeSet, -) { - if let Some(group) = previous_board - .merged_groups - .iter() - .find(|group| group.group_id == group_id) - { - piece_ids.extend(group.piece_ids.iter().cloned()); - } -} - -fn resolve_merged_groups(pieces: &[PuzzlePieceState]) -> Vec { - let pieces_by_cell = pieces - .iter() - .map(|piece| ((piece.current_row, piece.current_col), piece)) - .collect::>(); - let pieces_by_id = pieces - .iter() - .map(|piece| (piece.piece_id.clone(), piece)) - .collect::>(); - let mut visited = BTreeSet::new(); - let mut groups = Vec::new(); - - for piece in pieces { - if visited.contains(&piece.piece_id) { - continue; - } - - let mut queue = VecDeque::from([piece.piece_id.clone()]); - let mut collected_ids = Vec::new(); - - while let Some(current_piece_id) = queue.pop_front() { - if !visited.insert(current_piece_id.clone()) { - continue; - } - let current_piece = match pieces_by_id.get(¤t_piece_id) { - Some(value) => *value, - None => continue, - }; - collected_ids.push(current_piece_id.clone()); - - for (neighbor_row, neighbor_col) in - neighbor_cells(current_piece.current_row, current_piece.current_col) - { - if let Some(neighbor_piece) = pieces_by_cell.get(&(neighbor_row, neighbor_col)) - && are_correct_neighbors(current_piece, neighbor_piece) - { - queue.push_back(neighbor_piece.piece_id.clone()); - } - } - } - - if collected_ids.len() <= 1 { - continue; - } - - let occupied_cells = collected_ids - .iter() - .filter_map(|piece_id| pieces_by_id.get(piece_id).copied()) - .map(|piece| PuzzleCellPosition { - row: piece.current_row, - col: piece.current_col, - }) - .collect::>(); - - groups.push(PuzzleMergedGroupState { - group_id: format!("group-{}", groups.len() + 1), - piece_ids: collected_ids, - occupied_cells, - }); - } - - groups -} - -fn neighbor_cells(row: u32, col: u32) -> Vec<(u32, u32)> { - let mut neighbors = Vec::new(); - if row > 0 { - neighbors.push((row - 1, col)); - } - neighbors.push((row + 1, col)); - if col > 0 { - neighbors.push((row, col - 1)); - } - neighbors.push((row, col + 1)); - neighbors -} - -fn are_correct_neighbors(left: &PuzzlePieceState, right: &PuzzlePieceState) -> bool { - let current_row_delta = right.current_row as i32 - left.current_row as i32; - let current_col_delta = right.current_col as i32 - left.current_col as i32; - let correct_row_delta = right.correct_row as i32 - left.correct_row as i32; - let correct_col_delta = right.correct_col as i32 - left.correct_col as i32; - (current_row_delta.abs() + current_col_delta.abs()) == 1 - && current_row_delta == correct_row_delta - && current_col_delta == correct_col_delta -} - -fn drag_single_piece( - pieces: &mut [PuzzlePieceState], - piece_index: usize, - target_row: u32, - target_col: u32, -) -> Result, PuzzleFieldError> { - let target_index = pieces - .iter() - .position(|piece| piece.current_row == target_row && piece.current_col == target_col) - .ok_or(PuzzleFieldError::InvalidTargetCell)?; - - let mut affected_cells = vec![ - PuzzleCellPosition { - row: pieces[piece_index].current_row, - col: pieces[piece_index].current_col, - }, - PuzzleCellPosition { - row: target_row, - col: target_col, - }, - ]; - - if let Some(target_group_id) = pieces[target_index].merged_group_id.clone() { - for piece in pieces - .iter_mut() - .filter(|piece| piece.merged_group_id.as_deref() == Some(target_group_id.as_str())) - { - affected_cells.push(PuzzleCellPosition { - row: piece.current_row, - col: piece.current_col, - }); - piece.merged_group_id = None; - } - } - - let (source_row, source_col) = ( - pieces[piece_index].current_row, - pieces[piece_index].current_col, - ); - pieces[piece_index].current_row = target_row; - pieces[piece_index].current_col = target_col; - if target_index != piece_index { - pieces[target_index].current_row = source_row; - pieces[target_index].current_col = source_col; - } - Ok(affected_cells) -} - -fn drag_group( - pieces: &mut [PuzzlePieceState], - group_id: &str, - target_row: u32, - target_col: u32, - grid_size: u32, -) -> Result, PuzzleFieldError> { - let group_indices = pieces - .iter() - .enumerate() - .filter_map(|(index, piece)| { - (piece.merged_group_id.as_deref() == Some(group_id)).then_some(index) - }) - .collect::>(); - if group_indices.is_empty() { - return Err(PuzzleFieldError::InvalidOperation); - } - - let anchor_piece = &pieces[group_indices[0]]; - let row_offset = target_row as i32 - anchor_piece.current_row as i32; - let col_offset = target_col as i32 - anchor_piece.current_col as i32; - let mut target_positions = Vec::new(); - for &index in &group_indices { - let next_row = pieces[index].current_row as i32 + row_offset; - let next_col = pieces[index].current_col as i32 + col_offset; - if next_row < 0 - || next_col < 0 - || next_row >= grid_size as i32 - || next_col >= grid_size as i32 - { - return Err(PuzzleFieldError::InvalidTargetCell); - } - target_positions.push((index, next_row as u32, next_col as u32)); - } - - let moving_piece_ids = group_indices - .iter() - .map(|index| pieces[*index].piece_id.clone()) - .collect::>(); - let source_positions = group_indices - .iter() - .map(|index| (pieces[*index].current_row, pieces[*index].current_col)) - .collect::>(); - let mut affected_cells = source_positions - .iter() - .map(|(row, col)| PuzzleCellPosition { - row: *row, - col: *col, - }) - .collect::>(); - - for (index, next_row, next_col) in &target_positions { - affected_cells.push(PuzzleCellPosition { - row: *next_row, - col: *next_col, - }); - if let Some(target_piece_index) = pieces.iter().position(|piece| { - piece.current_row == *next_row - && piece.current_col == *next_col - && !moving_piece_ids.contains(&piece.piece_id) - }) { - let fallback = source_positions - .iter() - .find(|position| { - !target_positions - .iter() - .any(|(_, row, col)| row == &position.0 && col == &position.1) - }) - .copied() - .ok_or(PuzzleFieldError::InvalidOperation)?; - pieces[target_piece_index].merged_group_id = None; - affected_cells.push(PuzzleCellPosition { - row: pieces[target_piece_index].current_row, - col: pieces[target_piece_index].current_col, - }); - affected_cells.push(PuzzleCellPosition { - row: fallback.0, - col: fallback.1, - }); - pieces[target_piece_index].current_row = fallback.0; - pieces[target_piece_index].current_col = fallback.1; - } - pieces[*index].current_row = *next_row; - pieces[*index].current_col = *next_col; - } - - Ok(affected_cells) -} - -fn with_next_board(run: &PuzzleRunSnapshot, next_board: PuzzleBoardSnapshot) -> PuzzleRunSnapshot { - let mut next_run = run.clone(); - let is_cleared = next_board.all_tiles_resolved; - let next_level_status = if is_cleared { - PuzzleRuntimeLevelStatus::Cleared - } else { - PuzzleRuntimeLevelStatus::Playing - }; - - if let Some(current_level) = next_run.current_level.as_mut() { - current_level.board = next_board; - if current_level.status != PuzzleRuntimeLevelStatus::Cleared && is_cleared { - let cleared_at_ms = current_unix_ms(); - current_level.cleared_at_ms = Some(cleared_at_ms); - current_level.elapsed_ms = Some( - cleared_at_ms - .saturating_sub(current_level.started_at_ms) - .max(1_000), - ); - } - current_level.status = next_level_status; - } - - if is_cleared - && run.current_level.as_ref().map(|level| level.status) - != Some(PuzzleRuntimeLevelStatus::Cleared) - { - next_run.cleared_level_count += 1; - } - next_run -} - -fn current_unix_ms() -> u64 { - use std::time::{SystemTime, UNIX_EPOCH}; - - SystemTime::now() - .duration_since(UNIX_EPOCH) - .map(|value| value.as_millis() as u64) - .unwrap_or(0) -} - -#[cfg(test)] -mod tests { - use super::*; - - fn build_published_profile( - profile_id: &str, - owner_user_id: &str, - tags: Vec<&str>, - ) -> PuzzleWorkProfile { - PuzzleWorkProfile { - work_id: format!("work-{profile_id}"), - profile_id: profile_id.to_string(), - owner_user_id: owner_user_id.to_string(), - source_session_id: None, - author_display_name: "作者".to_string(), - level_name: format!("{profile_id} 关"), - summary: "summary".to_string(), - theme_tags: tags.into_iter().map(|value| value.to_string()).collect(), - cover_image_src: Some("/cover.png".to_string()), - cover_asset_id: Some("asset-1".to_string()), - publication_status: PuzzlePublicationStatus::Published, - updated_at_micros: 100, - published_at_micros: Some(100), - play_count: 0, - publish_ready: true, - anchor_pack: empty_anchor_pack(), - } - } - - #[test] - fn resolve_grid_size_matches_prd() { - assert_eq!(resolve_puzzle_grid_size(0), 3); - assert_eq!(resolve_puzzle_grid_size(2), 3); - assert_eq!(resolve_puzzle_grid_size(3), 4); - } - - #[test] - fn normalize_theme_tags_dedups_aliases() { - assert_eq!( - normalize_theme_tags(vec![ - "蒸汽".to_string(), - "蒸汽朋克".to_string(), - "雨夜".to_string(), - "雨夜".to_string() - ]), - vec!["蒸汽城市".to_string(), "雨夜".to_string()] - ); - } - - #[test] - fn generated_candidate_uses_oss_compatible_prefix_and_single_image() { - let anchor_pack = infer_anchor_pack("雨夜猫咪", Some("雨夜猫咪")); - let draft = compile_result_draft(&anchor_pack, &[]); - let candidates = build_generated_candidates("session-1", None, &draft, 2, 1_000) - .expect("candidates should build"); - - assert_eq!(candidates.len(), 1); - assert!( - candidates[0] - .image_src - .starts_with("/generated-puzzle-assets/session-1/") - ); - let legacy_public_prefix = ["generated-puzzle", "covers"].join("-"); - assert!(!candidates[0].image_src.contains(&legacy_public_prefix)); - } - - #[test] - fn tag_similarity_score_uses_jaccard() { - let score = tag_similarity_score( - &["蒸汽城市".to_string(), "雨夜".to_string()], - &["蒸汽城市".to_string(), "猫咪".to_string()], - ); - assert!((score - 0.3333).abs() < 0.01); - } - - #[test] - fn select_next_profile_prefers_same_tags_and_author() { - let current = build_published_profile("a", "owner-a", vec!["蒸汽城市", "雨夜"]); - let candidates = vec![ - build_published_profile("b", "owner-a", vec!["蒸汽城市", "雨夜"]), - build_published_profile("c", "owner-c", vec!["猫咪", "森林"]), - ]; - let selected = - select_next_profile(¤t, &["a".to_string()], &candidates).expect("should select"); - assert_eq!(selected.profile_id, "b"); - } - - #[test] - fn swap_pieces_marks_cleared_when_back_to_origin() { - let profile = build_published_profile("entry", "owner-a", vec!["蒸汽城市", "雨夜", "猫咪"]); - let run = start_run("run-1".to_string(), &profile, 0).expect("run"); - let current_level = run.current_level.clone().expect("level"); - let first_piece = current_level.board.pieces[0].clone(); - let second_piece = current_level.board.pieces[1].clone(); - let swapped = - swap_pieces(&run, &first_piece.piece_id, &second_piece.piece_id).expect("swap"); - assert_eq!( - swapped - .current_level - .as_ref() - .expect("level") - .board - .pieces - .len(), - 9 - ); - } - - #[test] - fn initial_board_shuffle_changes_by_run_id() { - let profile = build_published_profile("entry", "owner-a", vec!["蒸汽城市", "雨夜", "猫咪"]); - let first = start_run("run-random-a".to_string(), &profile, 0).expect("first run"); - let second = start_run("run-random-b".to_string(), &profile, 0).expect("second run"); - let first_positions = first - .current_level - .expect("first level") - .board - .pieces - .into_iter() - .map(|piece| (piece.current_row, piece.current_col)) - .collect::>(); - let second_positions = second - .current_level - .expect("second level") - .board - .pieces - .into_iter() - .map(|piece| (piece.current_row, piece.current_col)) - .collect::>(); - - assert_ne!(first_positions, second_positions); - } - - #[test] - fn initial_board_has_no_original_neighbor_pairs() { - for grid_size in [3, 4] { - for shuffle_seed in 0..128 { - let board = build_initial_board_with_seed(grid_size, shuffle_seed).expect("board"); - - assert!(board.merged_groups.is_empty()); - assert!( - !has_any_original_neighbor_pair(&board.pieces), - "grid_size={grid_size}, shuffle_seed={shuffle_seed}" - ); - } - } - } - - #[test] - fn correct_neighbors_auto_merge_after_swap() { - let profile = build_published_profile("entry", "owner-a", vec!["蒸汽城市", "雨夜", "猫咪"]); - let mut run = - start_run_with_shuffle_seed("run-merge".to_string(), &profile, 0, 7).expect("run"); - let current_level = run.current_level.as_mut().expect("level"); - current_level.board = rebuild_board_snapshot( - 3, - vec![ - PuzzlePieceState { - piece_id: "piece-0".to_string(), - correct_row: 0, - correct_col: 0, - current_row: 1, - current_col: 1, - merged_group_id: None, - }, - PuzzlePieceState { - piece_id: "piece-1".to_string(), - correct_row: 0, - correct_col: 1, - current_row: 0, - current_col: 1, - merged_group_id: None, - }, - PuzzlePieceState { - piece_id: "piece-2".to_string(), - correct_row: 0, - correct_col: 2, - current_row: 2, - current_col: 2, - merged_group_id: None, - }, - PuzzlePieceState { - piece_id: "piece-3".to_string(), - correct_row: 1, - correct_col: 0, - current_row: 0, - current_col: 2, - merged_group_id: None, - }, - PuzzlePieceState { - piece_id: "piece-4".to_string(), - correct_row: 1, - correct_col: 1, - current_row: 1, - current_col: 0, - merged_group_id: None, - }, - PuzzlePieceState { - piece_id: "piece-5".to_string(), - correct_row: 1, - correct_col: 2, - current_row: 2, - current_col: 0, - merged_group_id: None, - }, - PuzzlePieceState { - piece_id: "piece-6".to_string(), - correct_row: 2, - correct_col: 0, - current_row: 0, - current_col: 0, - merged_group_id: None, - }, - PuzzlePieceState { - piece_id: "piece-7".to_string(), - correct_row: 2, - correct_col: 1, - current_row: 1, - current_col: 2, - merged_group_id: None, - }, - PuzzlePieceState { - piece_id: "piece-8".to_string(), - correct_row: 2, - correct_col: 2, - current_row: 2, - current_col: 1, - merged_group_id: None, - }, - ], - None, - ); - - let swapped = swap_pieces(&run, "piece-0", "piece-6").expect("swap"); - let board = &swapped.current_level.as_ref().expect("level").board; - let group = board - .merged_groups - .iter() - .find(|group| { - group.piece_ids.contains(&"piece-0".to_string()) - && group.piece_ids.contains(&"piece-1".to_string()) - }) - .expect("piece-0 and piece-1 should merge"); - - assert_eq!(group.piece_ids.len(), 2); - } - - #[test] - fn single_piece_dragging_into_group_splits_target_group() { - let profile = build_published_profile("entry", "owner-a", vec!["蒸汽城市", "雨夜", "猫咪"]); - let mut run = - start_run_with_shuffle_seed("run-split".to_string(), &profile, 0, 9).expect("run"); - let current_level = run.current_level.as_mut().expect("level"); - current_level.board = rebuild_board_snapshot( - 3, - vec![ - PuzzlePieceState { - piece_id: "piece-0".to_string(), - correct_row: 0, - correct_col: 0, - current_row: 0, - current_col: 0, - merged_group_id: None, - }, - PuzzlePieceState { - piece_id: "piece-1".to_string(), - correct_row: 0, - correct_col: 1, - current_row: 0, - current_col: 1, - merged_group_id: None, - }, - PuzzlePieceState { - piece_id: "piece-2".to_string(), - correct_row: 0, - correct_col: 2, - current_row: 2, - current_col: 2, - merged_group_id: None, - }, - PuzzlePieceState { - piece_id: "piece-3".to_string(), - correct_row: 1, - correct_col: 0, - current_row: 1, - current_col: 0, - merged_group_id: None, - }, - PuzzlePieceState { - piece_id: "piece-4".to_string(), - correct_row: 1, - correct_col: 1, - current_row: 1, - current_col: 1, - merged_group_id: None, - }, - PuzzlePieceState { - piece_id: "piece-5".to_string(), - correct_row: 1, - correct_col: 2, - current_row: 1, - current_col: 2, - merged_group_id: None, - }, - PuzzlePieceState { - piece_id: "piece-6".to_string(), - correct_row: 2, - correct_col: 0, - current_row: 2, - current_col: 0, - merged_group_id: None, - }, - PuzzlePieceState { - piece_id: "piece-7".to_string(), - correct_row: 2, - correct_col: 1, - current_row: 2, - current_col: 1, - merged_group_id: None, - }, - PuzzlePieceState { - piece_id: "piece-8".to_string(), - correct_row: 2, - correct_col: 2, - current_row: 0, - current_col: 2, - merged_group_id: None, - }, - ], - None, - ); - - let dragged = drag_piece_or_group(&run, "piece-8", 0, 1).expect("drag"); - let board = &dragged.current_level.as_ref().expect("level").board; - - assert_eq!( - board - .pieces - .iter() - .find(|piece| piece.piece_id == "piece-8") - .map(|piece| (piece.current_row, piece.current_col)), - Some((0, 1)) - ); - assert!( - board - .merged_groups - .iter() - .all(|group| !(group.piece_ids.contains(&"piece-0".to_string()) - && group.piece_ids.contains(&"piece-1".to_string()))) - ); - } - - #[test] - fn one_full_board_group_marks_level_cleared() { - let pieces = (0..9) - .map(|index| PuzzlePieceState { - piece_id: format!("piece-{index}"), - correct_row: index / 3, - correct_col: index % 3, - current_row: index / 3, - current_col: (index + 1) % 3, - merged_group_id: None, - }) - .collect::>(); - let board = rebuild_board_snapshot_with_groups( - 3, - pieces, - vec![PuzzleMergedGroupState { - group_id: "group-full".to_string(), - piece_ids: (0..9).map(|index| format!("piece-{index}")).collect(), - occupied_cells: (0..9) - .map(|index| PuzzleCellPosition { - row: index / 3, - col: (index + 1) % 3, - }) - .collect(), - }], - None, - ); - - assert!(board.all_tiles_resolved); - } - - #[test] - fn apply_publish_overrides_updates_draft_truth() { - let anchor_pack = infer_anchor_pack("雨夜猫咪神庙", Some("雨夜猫咪神庙")); - let draft = compile_result_draft(&anchor_pack, &[]); - - let updated = apply_publish_overrides_to_draft( - &draft, - Some("雨夜猫塔".to_string()), - Some("一张更聚焦猫咪塔楼的夜景拼图。".to_string()), - Some(vec![ - "雨夜".to_string(), - "猫咪".to_string(), - "遗迹".to_string(), - ]), - ) - .expect("publish overrides should succeed"); - - assert_eq!(updated.level_name, "雨夜猫塔"); - assert_eq!(updated.summary, "一张更聚焦猫咪塔楼的夜景拼图。"); - assert_eq!( - updated.theme_tags, - vec![ - "猫咪".to_string(), - "神庙遗迹".to_string(), - "雨夜".to_string() - ] - ); - } - - #[test] - fn apply_publish_overrides_rejects_invalid_tag_count() { - let anchor_pack = infer_anchor_pack("蒸汽城市", Some("蒸汽城市")); - let draft = compile_result_draft(&anchor_pack, &[]); - let error = - apply_publish_overrides_to_draft(&draft, None, None, Some(vec!["蒸汽".to_string()])) - .expect_err("invalid tag count should fail"); - - assert_eq!(error, PuzzleFieldError::InvalidTagCount); - } -} +pub use application::*; +pub use commands::*; +pub use domain::*; +pub use errors::*; +pub use events::*; diff --git a/server-rs/crates/module-runtime-story/README.md b/server-rs/crates/module-runtime-story/README.md index 6a286a30..071350f4 100644 --- a/server-rs/crates/module-runtime-story/README.md +++ b/server-rs/crates/module-runtime-story/README.md @@ -2,7 +2,7 @@ `module-runtime-story` 承接 RPG runtime story 的纯领域规则、应用用例、事件和错误模型,不依赖 HTTP / `AppState` / SpacetimeDB。 -当前已经迁入的历史兼容纯逻辑会继续收口为 session scoped 新主链: +当前已经迁入的历史快照态纯逻辑会继续收口为 session scoped 新主链: 1. action 结算结果结构。 2. action response 组装参数结构。 @@ -10,4 +10,4 @@ 4. functionId / 队伍上限常量。 5. 少量只依赖 `serde_json::Value` 与 `shared-contracts` 的纯 helper。 -后续 WP-RS 继续按 battle / forge / NPC / quest / presentation 的顺序,把旧 `/api/runtime/story/*` 兼容桥中剩余纯规则迁入本 crate,并删除运行代码中的 compat 命名。 +后续 WP-RS 继续按 battle / forge / NPC / quest / presentation 的顺序,把旧 `/api/runtime/story/*` 写侧能力迁到 session scoped 新接口,并删除运行代码中的旧入口命名。 diff --git a/server-rs/crates/module-runtime-story/src/battle.rs b/server-rs/crates/module-runtime-story/src/battle.rs index 24a23066..5722c560 100644 --- a/server-rs/crates/module-runtime-story/src/battle.rs +++ b/server-rs/crates/module-runtime-story/src/battle.rs @@ -14,7 +14,7 @@ use crate::{ write_i32_field, write_null_field, write_string_field, }; -/// 战斗 compat 纯结算链已经不依赖 HTTP / AppState。 +/// 战斗纯结算链已经不依赖 HTTP / AppState。 /// /// 这里同时承接 battle action 的状态结算、资源恢复和战斗选项编译, /// 让 `api-server` 只保留 HTTP 外壳与最终响应拼装。 @@ -56,7 +56,7 @@ struct BattleInventoryItemView { use_profile: Option, } -/// 兼容战斗结算的胜负状态。 +/// 战斗结算的胜负状态。 /// /// 这里显式补齐失败分支,避免“玩家已死但敌方也被打空时”被错误归类成胜利。 #[derive(Clone, Copy, Debug, Eq, PartialEq)] diff --git a/server-rs/crates/module-runtime-story/src/commands.rs b/server-rs/crates/module-runtime-story/src/commands.rs index 0a0dd9fe..ce75d7fd 100644 --- a/server-rs/crates/module-runtime-story/src/commands.rs +++ b/server-rs/crates/module-runtime-story/src/commands.rs @@ -1,3 +1,3 @@ -//! runtime story 兼容写入命令过渡落位。 +//! runtime story 写入命令过渡落位。 //! -//! 用于表达旧剧情动作解析、战斗动作、锻造动作和 NPC 互动等输入。 +//! 用于表达剧情动作解析、战斗动作、锻造动作和 NPC 互动等输入。 diff --git a/server-rs/crates/module-runtime-story/src/core.rs b/server-rs/crates/module-runtime-story/src/core.rs index 9662c43a..c7af9164 100644 --- a/server-rs/crates/module-runtime-story/src/core.rs +++ b/server-rs/crates/module-runtime-story/src/core.rs @@ -2,7 +2,7 @@ use serde_json::{Map, Value, json}; use shared_kernel::format_rfc3339; use time::OffsetDateTime; -/// Runtime story compat 的纯 JSON 快照工具层。 +/// Runtime story 的纯 JSON 快照工具层。 /// /// 这里不允许引入 HTTP、AppState 或持久化依赖,保证后续 battle/forge/npc/quest /// 规则迁入独立 crate 时可以继续复用同一批状态读写函数。 diff --git a/server-rs/crates/module-runtime-story/src/domain.rs b/server-rs/crates/module-runtime-story/src/domain.rs index 29770c5d..2144d265 100644 --- a/server-rs/crates/module-runtime-story/src/domain.rs +++ b/server-rs/crates/module-runtime-story/src/domain.rs @@ -1,4 +1,4 @@ -//! runtime story 兼容领域模型过渡落位。 +//! runtime story 领域模型过渡落位。 //! -//! 当前 crate 用于旧运行时剧情桥的纯规则兼容。后续迁移时仍只能保留 JSON 规则、 +//! 当前 crate 用于运行时剧情主链的纯规则收口。后续迁移时仍只能保留 JSON 规则、 //! 选项生成和视图模型转换,不引入 Axum、LLM 或 SpacetimeDB。 diff --git a/server-rs/crates/module-runtime-story/src/errors.rs b/server-rs/crates/module-runtime-story/src/errors.rs index 3a4b536e..8b9f3052 100644 --- a/server-rs/crates/module-runtime-story/src/errors.rs +++ b/server-rs/crates/module-runtime-story/src/errors.rs @@ -1,3 +1,3 @@ -//! runtime story 兼容领域错误过渡落位。 +//! runtime story 领域错误过渡落位。 //! -//! 错误只表达兼容规则失败,不能直接绑定 HTTP 或数据库错误模型。 +//! 错误只表达运行时剧情规则失败,不能直接绑定 HTTP 或数据库错误模型。 diff --git a/server-rs/crates/module-runtime-story/src/events.rs b/server-rs/crates/module-runtime-story/src/events.rs index 4abffdfc..8af5ffe4 100644 --- a/server-rs/crates/module-runtime-story/src/events.rs +++ b/server-rs/crates/module-runtime-story/src/events.rs @@ -1,3 +1,3 @@ -//! runtime story 兼容领域事件过渡落位。 +//! runtime story 领域事件过渡落位。 //! -//! 用于表达旧剧情快照变化、战斗表现变化和物品/成长待同步等事实。 +//! 用于表达剧情快照变化、战斗表现变化和物品/成长待同步等事实。 diff --git a/server-rs/crates/module-runtime-story/src/forge.rs b/server-rs/crates/module-runtime-story/src/forge.rs index 536f110a..3f3a25ee 100644 --- a/server-rs/crates/module-runtime-story/src/forge.rs +++ b/server-rs/crates/module-runtime-story/src/forge.rs @@ -6,7 +6,7 @@ use crate::{ remove_inventory_item_from_list, resolve_equipment_slot_for_item, }; -/// 这批定义只服务 runtime story compat 的确定性锻造链。 +/// 这批定义只服务 runtime story 的确定性锻造链。 /// /// 当前仍然保持旧快照态结算口径,不引入 HTTP / AppState / 持久化边界。 pub(crate) struct ForgeRequirementDefinition { diff --git a/server-rs/crates/module-runtime-story/src/forge_actions.rs b/server-rs/crates/module-runtime-story/src/forge_actions.rs index ba2cde78..b2d52d65 100644 --- a/server-rs/crates/module-runtime-story/src/forge_actions.rs +++ b/server-rs/crates/module-runtime-story/src/forge_actions.rs @@ -18,7 +18,7 @@ use super::forge::{ /// 锻造动作编排已经不再依赖 `api-server` 的 HTTP 边界。 /// -/// 这里继续沿用 compat 快照态结算,后续可直接被 `api-server` 外壳或真相态桥接层复用。 +/// 这里继续沿用快照态结算,后续可直接被 `api-server` 外壳或真相态桥接层复用。 pub fn resolve_forge_craft_action( game_state: &mut Value, request: &RuntimeStoryActionRequest, diff --git a/server-rs/crates/module-runtime-story/src/game_state.rs b/server-rs/crates/module-runtime-story/src/game_state.rs index 3cb9283f..6cd7142f 100644 --- a/server-rs/crates/module-runtime-story/src/game_state.rs +++ b/server-rs/crates/module-runtime-story/src/game_state.rs @@ -5,7 +5,7 @@ use crate::{ read_field, read_i32_field, read_object_field, read_optional_string_field, write_i32_field, }; -/// 这批 helper 只负责 runtime story compat 的纯快照读写。 +/// 这批 helper 只负责 runtime story 的纯快照读写。 /// /// 目标是先把 encounter / inventory / equipment 的基础状态工具从 `api-server` /// 边界模块里收口出来,后续 battle / forge / equipment 规则迁移时直接复用。 @@ -186,7 +186,7 @@ pub fn normalize_equipment_slot_id(slot_id: &str) -> Option<&'static str> { "armor" => Some("armor"), "relic" | "accessory" => Some("relic"), _ => { - // 兼容旧 payload 里直接传中文槽位名或物品类别文案的情况。 + // 支持历史 payload 里直接传中文槽位名或物品类别文案的情况。 if slot_id.contains("武器") || slot_id.contains('剑') || slot_id.contains('弓') diff --git a/server-rs/crates/module-runtime-story/src/npc_support.rs b/server-rs/crates/module-runtime-story/src/npc_support.rs index 75a54fff..02df938b 100644 --- a/server-rs/crates/module-runtime-story/src/npc_support.rs +++ b/server-rs/crates/module-runtime-story/src/npc_support.rs @@ -365,7 +365,7 @@ fn remove_companion_by_npc_id(game_state: &mut Value, npc_id: &str) -> Option RuntimeSettingsRecord { + RuntimeSettingsRecord { + user_id: snapshot.user_id, + music_volume: snapshot.music_volume, + platform_theme: snapshot.platform_theme, + created_at_micros: snapshot.created_at_micros, + updated_at_micros: snapshot.updated_at_micros, + } +} + +pub fn build_runtime_browse_history_record( + snapshot: RuntimeBrowseHistorySnapshot, +) -> RuntimeBrowseHistoryRecord { + RuntimeBrowseHistoryRecord { + browse_history_id: snapshot.browse_history_id, + user_id: snapshot.user_id, + owner_user_id: snapshot.owner_user_id, + profile_id: snapshot.profile_id, + world_name: snapshot.world_name, + subtitle: snapshot.subtitle, + summary_text: snapshot.summary_text, + cover_image_src: snapshot.cover_image_src, + theme_mode: snapshot.theme_mode, + author_display_name: snapshot.author_display_name, + visited_at: format_utc_micros(snapshot.visited_at_micros), + visited_at_micros: snapshot.visited_at_micros, + created_at_micros: snapshot.created_at_micros, + updated_at_micros: snapshot.updated_at_micros, + } +} + +pub fn build_runtime_profile_dashboard_record( + snapshot: RuntimeProfileDashboardSnapshot, +) -> RuntimeProfileDashboardRecord { + RuntimeProfileDashboardRecord { + user_id: snapshot.user_id, + wallet_balance: snapshot.wallet_balance, + total_play_time_ms: snapshot.total_play_time_ms, + played_world_count: snapshot.played_world_count, + updated_at: snapshot.updated_at_micros.map(format_utc_micros), + updated_at_micros: snapshot.updated_at_micros, + } +} + +pub fn build_runtime_profile_wallet_ledger_entry_record( + snapshot: RuntimeProfileWalletLedgerEntrySnapshot, +) -> RuntimeProfileWalletLedgerEntryRecord { + RuntimeProfileWalletLedgerEntryRecord { + wallet_ledger_id: snapshot.wallet_ledger_id, + user_id: snapshot.user_id, + amount_delta: snapshot.amount_delta, + balance_after: snapshot.balance_after, + source_type: snapshot.source_type, + created_at: format_utc_micros(snapshot.created_at_micros), + created_at_micros: snapshot.created_at_micros, + } +} + +pub fn build_runtime_profile_recharge_center_record( + snapshot: RuntimeProfileRechargeCenterSnapshot, +) -> RuntimeProfileRechargeCenterRecord { + RuntimeProfileRechargeCenterRecord { + user_id: snapshot.user_id, + wallet_balance: snapshot.wallet_balance, + membership: build_runtime_profile_membership_record(snapshot.membership), + point_products: snapshot + .point_products + .into_iter() + .map(build_runtime_profile_recharge_product_record) + .collect(), + membership_products: snapshot + .membership_products + .into_iter() + .map(build_runtime_profile_recharge_product_record) + .collect(), + benefits: snapshot + .benefits + .into_iter() + .map(build_runtime_profile_membership_benefit_record) + .collect(), + latest_order: snapshot + .latest_order + .map(build_runtime_profile_recharge_order_record), + has_points_recharged: snapshot.has_points_recharged, + } +} + +pub fn build_runtime_profile_recharge_product_record( + snapshot: RuntimeProfileRechargeProductSnapshot, +) -> RuntimeProfileRechargeProductRecord { + RuntimeProfileRechargeProductRecord { + product_id: snapshot.product_id, + title: snapshot.title, + price_cents: snapshot.price_cents, + kind: snapshot.kind, + points_amount: snapshot.points_amount, + bonus_points: snapshot.bonus_points, + duration_days: snapshot.duration_days, + badge_label: snapshot.badge_label, + description: snapshot.description, + tier: snapshot.tier, + } +} + +pub fn build_runtime_profile_membership_benefit_record( + snapshot: RuntimeProfileMembershipBenefitSnapshot, +) -> RuntimeProfileMembershipBenefitRecord { + RuntimeProfileMembershipBenefitRecord { + benefit_name: snapshot.benefit_name, + normal_value: snapshot.normal_value, + month_value: snapshot.month_value, + season_value: snapshot.season_value, + year_value: snapshot.year_value, + } +} + +pub fn build_runtime_profile_membership_record( + snapshot: RuntimeProfileMembershipSnapshot, +) -> RuntimeProfileMembershipRecord { + RuntimeProfileMembershipRecord { + user_id: snapshot.user_id, + status: snapshot.status, + tier: snapshot.tier, + started_at: snapshot.started_at_micros.map(format_utc_micros), + started_at_micros: snapshot.started_at_micros, + expires_at: snapshot.expires_at_micros.map(format_utc_micros), + expires_at_micros: snapshot.expires_at_micros, + updated_at: snapshot.updated_at_micros.map(format_utc_micros), + updated_at_micros: snapshot.updated_at_micros, + } +} + +pub fn build_runtime_profile_recharge_order_record( + snapshot: RuntimeProfileRechargeOrderSnapshot, +) -> RuntimeProfileRechargeOrderRecord { + RuntimeProfileRechargeOrderRecord { + order_id: snapshot.order_id, + user_id: snapshot.user_id, + product_id: snapshot.product_id, + product_title: snapshot.product_title, + kind: snapshot.kind, + amount_cents: snapshot.amount_cents, + status: snapshot.status, + payment_channel: snapshot.payment_channel, + paid_at: format_utc_micros(snapshot.paid_at_micros), + paid_at_micros: snapshot.paid_at_micros, + created_at: format_utc_micros(snapshot.created_at_micros), + created_at_micros: snapshot.created_at_micros, + points_delta: snapshot.points_delta, + membership_expires_at: snapshot.membership_expires_at_micros.map(format_utc_micros), + membership_expires_at_micros: snapshot.membership_expires_at_micros, + } +} + +pub fn build_runtime_referral_invite_center_record( + snapshot: RuntimeReferralInviteCenterSnapshot, +) -> RuntimeReferralInviteCenterRecord { + RuntimeReferralInviteCenterRecord { + user_id: snapshot.user_id, + invite_code: snapshot.invite_code, + invite_link_path: snapshot.invite_link_path, + invited_count: snapshot.invited_count, + rewarded_invite_count: snapshot.rewarded_invite_count, + today_inviter_reward_count: snapshot.today_inviter_reward_count, + today_inviter_reward_remaining: snapshot.today_inviter_reward_remaining, + reward_points: snapshot.reward_points, + has_redeemed_code: snapshot.has_redeemed_code, + bound_inviter_user_id: snapshot.bound_inviter_user_id, + bound_at: snapshot.bound_at_micros.map(format_utc_micros), + bound_at_micros: snapshot.bound_at_micros, + updated_at: format_utc_micros(snapshot.updated_at_micros), + updated_at_micros: snapshot.updated_at_micros, + } +} + +pub fn build_runtime_referral_redeem_record( + snapshot: RuntimeReferralRedeemSnapshot, +) -> RuntimeReferralRedeemRecord { + RuntimeReferralRedeemRecord { + center: build_runtime_referral_invite_center_record(snapshot.center), + invitee_reward_granted: snapshot.invitee_reward_granted, + inviter_reward_granted: snapshot.inviter_reward_granted, + invitee_balance_after: snapshot.invitee_balance_after, + inviter_balance_after: snapshot.inviter_balance_after, + } +} + +pub fn build_runtime_profile_reward_code_redeem_record( + snapshot: RuntimeProfileRewardCodeRedeemSnapshot, +) -> RuntimeProfileRewardCodeRedeemRecord { + RuntimeProfileRewardCodeRedeemRecord { + wallet_balance: snapshot.wallet_balance, + amount_granted: snapshot.amount_granted, + ledger_entry: build_runtime_profile_wallet_ledger_entry_record(snapshot.ledger_entry), + } +} + +pub fn build_runtime_profile_redeem_code_record( + snapshot: RuntimeProfileRedeemCodeSnapshot, +) -> RuntimeProfileRedeemCodeRecord { + RuntimeProfileRedeemCodeRecord { + code: snapshot.code, + mode: snapshot.mode, + reward_points: snapshot.reward_points, + max_uses: snapshot.max_uses, + global_used_count: snapshot.global_used_count, + enabled: snapshot.enabled, + allowed_user_ids: snapshot.allowed_user_ids, + created_by: snapshot.created_by, + created_at: format_utc_micros(snapshot.created_at_micros), + created_at_micros: snapshot.created_at_micros, + updated_at: format_utc_micros(snapshot.updated_at_micros), + updated_at_micros: snapshot.updated_at_micros, + } +} + +pub fn build_runtime_profile_played_world_record( + snapshot: RuntimeProfilePlayedWorldSnapshot, +) -> RuntimeProfilePlayedWorldRecord { + RuntimeProfilePlayedWorldRecord { + played_world_id: snapshot.played_world_id, + user_id: snapshot.user_id, + world_key: snapshot.world_key, + owner_user_id: snapshot.owner_user_id, + profile_id: snapshot.profile_id, + world_type: snapshot.world_type, + world_title: snapshot.world_title, + world_subtitle: snapshot.world_subtitle, + first_played_at: format_utc_micros(snapshot.first_played_at_micros), + first_played_at_micros: snapshot.first_played_at_micros, + last_played_at: format_utc_micros(snapshot.last_played_at_micros), + last_played_at_micros: snapshot.last_played_at_micros, + last_observed_play_time_ms: snapshot.last_observed_play_time_ms, + } +} + +pub fn build_runtime_profile_play_stats_record( + snapshot: RuntimeProfilePlayStatsSnapshot, +) -> RuntimeProfilePlayStatsRecord { + RuntimeProfilePlayStatsRecord { + user_id: snapshot.user_id, + total_play_time_ms: snapshot.total_play_time_ms, + played_works: snapshot + .played_works + .into_iter() + .map(build_runtime_profile_played_world_record) + .collect(), + updated_at: snapshot.updated_at_micros.map(format_utc_micros), + updated_at_micros: snapshot.updated_at_micros, + } +} + +pub fn build_runtime_snapshot_record( + snapshot: RuntimeSnapshot, +) -> Result { + let game_state = serde_json::from_str::(&snapshot.game_state_json) + .map_err(|_| RuntimeProfileFieldError::InvalidGameStateJson)?; + let current_story = parse_optional_json_value( + snapshot.current_story_json.as_deref(), + RuntimeProfileFieldError::InvalidCurrentStoryJson, + )?; + + Ok(RuntimeSnapshotRecord { + user_id: snapshot.user_id, + version: snapshot.version, + saved_at: format_utc_micros(snapshot.saved_at_micros), + saved_at_micros: snapshot.saved_at_micros, + bottom_tab: snapshot.bottom_tab, + game_state, + current_story, + game_state_json: snapshot.game_state_json, + current_story_json: snapshot.current_story_json, + created_at_micros: snapshot.created_at_micros, + updated_at_micros: snapshot.updated_at_micros, + }) +} + +pub fn build_runtime_profile_save_archive_record( + snapshot: RuntimeProfileSaveArchiveSnapshot, +) -> Result { + let game_state = serde_json::from_str::(&snapshot.game_state_json) + .map_err(|_| RuntimeProfileFieldError::InvalidGameStateJson)?; + let current_story = parse_optional_json_value( + snapshot.current_story_json.as_deref(), + RuntimeProfileFieldError::InvalidCurrentStoryJson, + )?; + + Ok(RuntimeProfileSaveArchiveRecord { + archive_id: snapshot.archive_id, + user_id: snapshot.user_id, + world_key: snapshot.world_key, + owner_user_id: snapshot.owner_user_id, + profile_id: snapshot.profile_id, + world_type: snapshot.world_type, + world_name: snapshot.world_name, + subtitle: snapshot.subtitle, + summary_text: snapshot.summary_text, + cover_image_src: snapshot.cover_image_src, + saved_at: format_utc_micros(snapshot.saved_at_micros), + saved_at_micros: snapshot.saved_at_micros, + bottom_tab: snapshot.bottom_tab, + game_state, + current_story, + game_state_json: snapshot.game_state_json, + current_story_json: snapshot.current_story_json, + created_at_micros: snapshot.created_at_micros, + updated_at_micros: snapshot.updated_at_micros, + }) +} + +pub fn build_runtime_save_checkpoint_update( + input: RuntimeSaveCheckpointInput, + existing: RuntimeSnapshotRecord, +) -> Result { + if is_non_persistent_runtime_snapshot(&existing.game_state) { + return Err(RuntimeProfileFieldError::NonPersistentRuntimeSnapshot); + } + + let persisted_session_id = + read_runtime_json_string_field(&existing.game_state, "runtimeSessionId") + .ok_or(RuntimeProfileFieldError::MissingRuntimeSessionId)?; + if persisted_session_id != input.session_id { + return Err(RuntimeProfileFieldError::RuntimeSessionMismatch { + expected_session_id: persisted_session_id, + actual_session_id: input.session_id, + }); + } + + Ok(RuntimeSaveCheckpointSnapshotUpdate { + saved_at_micros: input.saved_at_micros, + bottom_tab: input.bottom_tab, + game_state: refresh_runtime_snapshot_play_time( + existing.game_state, + input.updated_at_micros, + ), + current_story: existing.current_story, + updated_at_micros: input.updated_at_micros, + }) +} + +pub fn build_runtime_profile_played_world_id(user_id: &str, world_key: &str) -> String { + format!("{}:{}", user_id.trim(), world_key.trim()) +} + +pub fn build_runtime_profile_snapshot_wallet_ledger_id( + user_id: &str, + saved_at_micros: i64, + next_wallet_balance: u64, +) -> String { + format!( + "{}:{}:{}", + user_id.trim(), + saved_at_micros, + next_wallet_balance + ) +} + +pub fn build_runtime_profile_save_archive_id(user_id: &str, world_key: &str) -> String { + format!("{}:{}", user_id.trim(), world_key.trim()) +} + +pub fn build_runtime_profile_recharge_wallet_ledger_id( + user_id: &str, + created_at_micros: i64, + product_id: &str, +) -> String { + format!( + "{}:{}:{}", + user_id.trim(), + created_at_micros, + product_id.trim() + ) +} + +pub fn build_runtime_profile_recharge_order_id( + user_id: &str, + created_at_micros: i64, + product_id: &str, +) -> String { + format!( + "recharge:{}", + build_runtime_profile_recharge_wallet_ledger_id(user_id, created_at_micros, product_id) + ) +} + +pub fn resolve_runtime_profile_points_recharge_delta( + product: &RuntimeProfileRechargeProductSnapshot, + has_points_recharged: bool, +) -> u64 { + let bonus_points = if has_points_recharged { + 0 + } else { + product.bonus_points + }; + product.points_amount.saturating_add(bonus_points) +} + +pub fn resolve_runtime_profile_membership_purchase_update( + current_started_at_micros: Option, + current_expires_at_micros: Option, + purchased_at_micros: i64, + duration_days: u32, +) -> RuntimeProfileMembershipPurchaseUpdate { + let start_at_micros = current_expires_at_micros + .filter(|expires_at_micros| *expires_at_micros > purchased_at_micros) + .unwrap_or(purchased_at_micros); + let expires_at_micros = start_at_micros + .saturating_add(i64::from(duration_days).saturating_mul(PROFILE_RUNTIME_DAY_MICROS)); + + RuntimeProfileMembershipPurchaseUpdate { + started_at_micros: current_started_at_micros.unwrap_or(purchased_at_micros), + expires_at_micros, + } +} + +pub fn build_runtime_profile_invite_code(user_id: &str, salt: u32) -> String { + let mut hash = 14_695_981_039_346_656_037u64; + for byte in user_id.as_bytes().iter().copied().chain(salt.to_le_bytes()) { + hash ^= byte as u64; + hash = hash.wrapping_mul(1_099_511_628_211); + } + format!("SY{:08X}", hash as u32) +} + +pub fn build_runtime_profile_invite_link_path(invite_code: &str) -> String { + format!("/?inviteCode={}", invite_code.trim()) +} + +pub fn runtime_profile_day_start_micros(now_micros: i64) -> i64 { + now_micros.div_euclid(PROFILE_RUNTIME_DAY_MICROS) * PROFILE_RUNTIME_DAY_MICROS +} + +pub fn should_grant_runtime_profile_inviter_reward(today_inviter_reward_count: u32) -> bool { + today_inviter_reward_count < PROFILE_REFERRAL_DAILY_INVITER_REWARD_LIMIT +} + +pub fn build_runtime_profile_referral_invitee_ledger_id( + invitee_user_id: &str, + updated_at_micros: i64, +) -> String { + format!("invitee:{}:{}", invitee_user_id.trim(), updated_at_micros) +} + +pub fn build_runtime_profile_referral_inviter_ledger_id( + inviter_user_id: &str, + updated_at_micros: i64, +) -> String { + format!("inviter:{}:{}", inviter_user_id.trim(), updated_at_micros) +} + +pub fn validate_runtime_profile_redeem_code_usage( + code: &RuntimeProfileRedeemCodeSnapshot, + user_id: &str, + user_used_count: u32, +) -> Result<(), RuntimeProfileFieldError> { + if !code.enabled { + return Err(RuntimeProfileFieldError::RedeemCodeDisabled); + } + if code.reward_points == 0 { + return Err(RuntimeProfileFieldError::InvalidRedeemCodeReward); + } + + match code.mode { + RuntimeProfileRedeemCodeMode::Public if user_used_count >= code.max_uses => { + Err(RuntimeProfileFieldError::RedeemCodeUsesExhausted) + } + RuntimeProfileRedeemCodeMode::Unique if code.global_used_count >= code.max_uses => { + Err(RuntimeProfileFieldError::RedeemCodeUsesExhausted) + } + RuntimeProfileRedeemCodeMode::Private => { + if !code.allowed_user_ids.iter().any(|item| item == user_id) { + return Err(RuntimeProfileFieldError::RedeemCodeNotAllowedForUser); + } + if code.global_used_count >= code.max_uses { + return Err(RuntimeProfileFieldError::RedeemCodeUsesExhausted); + } + Ok(()) + } + _ => Ok(()), + } +} + +pub fn build_runtime_profile_redeem_code_usage_id( + code: &str, + user_id: &str, + redeemed_at_micros: i64, + sequence: u32, +) -> String { + format!( + "redeem:{}:{}:{}:{}", + code.trim(), + user_id.trim(), + redeemed_at_micros, + sequence + ) +} + +pub fn build_runtime_profile_redeem_code_ledger_id(usage_id: &str) -> String { + format!("{}:ledger", usage_id.trim()) +} + +pub fn convert_runtime_profile_wallet_unsigned_delta( + amount_delta: u64, +) -> Result { + i64::try_from(amount_delta).map_err(|_| RuntimeProfileFieldError::WalletAmountOverflow) +} + +pub fn calculate_runtime_profile_wallet_balance( + previous_balance: u64, + amount_delta: i64, +) -> Result { + if amount_delta >= 0 { + previous_balance + .checked_add(amount_delta as u64) + .ok_or(RuntimeProfileFieldError::WalletBalanceOverflow) + } else { + previous_balance + .checked_sub(amount_delta.unsigned_abs()) + .ok_or(RuntimeProfileFieldError::InsufficientWalletBalance) + } +} + +pub fn refresh_runtime_snapshot_play_time(mut game_state: Value, now_micros: i64) -> Value { + let Some(game_state_object) = game_state.as_object_mut() else { + return game_state; + }; + let now_text = format_utc_micros(now_micros); + let Some(runtime_stats) = game_state_object + .get_mut("runtimeStats") + .and_then(Value::as_object_mut) + else { + game_state_object.insert( + "runtimeStats".to_string(), + serde_json::json!({ + "playTimeMs": 0, + "lastPlayTickAt": now_text, + "hostileNpcsDefeated": 0, + "questsAccepted": 0, + "itemsUsed": 0, + "scenesTraveled": 0, + }), + ); + return game_state; + }; + + let current_play_time = runtime_stats + .get("playTimeMs") + .and_then(Value::as_f64) + .filter(|value| value.is_finite() && *value >= 0.0) + .unwrap_or(0.0); + let elapsed_ms = runtime_stats + .get("lastPlayTickAt") + .and_then(Value::as_str) + .and_then(|last_tick| parse_rfc3339(last_tick).ok()) + .map(offset_datetime_to_unix_micros) + .map(|last_tick_micros| now_micros.saturating_sub(last_tick_micros).max(0) as f64 / 1000.0) + .unwrap_or(0.0); + let next_play_time = (current_play_time + elapsed_ms).floor().max(0.0); + + // checkpoint 只刷新服务端已有 runtimeStats 的时间水位,不接收浏览器上传的剧情、背包或战斗真相。 + runtime_stats.insert("playTimeMs".to_string(), Value::from(next_play_time as i64)); + runtime_stats.insert("lastPlayTickAt".to_string(), Value::String(now_text)); + game_state +} + +pub fn is_non_persistent_runtime_snapshot(game_state: &Value) -> bool { + let Some(game_state) = game_state.as_object() else { + return false; + }; + + if game_state + .get("runtimePersistenceDisabled") + .and_then(Value::as_bool) + .unwrap_or(false) + { + return true; + } + + matches!( + read_runtime_json_string_field_from_map(game_state, "runtimeMode").as_deref(), + Some("preview") | Some("test") + ) +} + +pub fn resolve_runtime_profile_world_snapshot_meta( + game_state: Option<&serde_json::Map>, +) -> Option { + let game_state = game_state?; + let custom_world_profile = game_state + .get("customWorldProfile") + .and_then(Value::as_object); + + if let Some(custom_world_profile) = custom_world_profile { + let profile_id = read_runtime_json_string_field_from_map(custom_world_profile, "id"); + let world_title = read_runtime_json_string_field_from_map(custom_world_profile, "name") + .or_else(|| read_runtime_json_string_field_from_map(custom_world_profile, "title")); + if profile_id.is_some() || world_title.is_some() { + let world_title = world_title.unwrap_or_else(|| "自定义世界".to_string()); + return Some(RuntimeProfileWorldSnapshotMeta { + world_key: profile_id + .as_ref() + .map(|profile_id| format!("custom:{profile_id}")) + .unwrap_or_else(|| format!("custom:{world_title}")), + owner_user_id: None, + profile_id, + world_type: Some("CUSTOM".to_string()), + world_title, + world_subtitle: read_runtime_json_string_field_from_map( + custom_world_profile, + "summary", + ) + .or_else(|| { + read_runtime_json_string_field_from_map(custom_world_profile, "settingText") + }) + .unwrap_or_default(), + }); + } + } + + let world_type = read_runtime_json_string_field_from_map(game_state, "worldType")?; + let current_scene_preset = game_state + .get("currentScenePreset") + .and_then(Value::as_object); + + Some(RuntimeProfileWorldSnapshotMeta { + world_key: format!("builtin:{world_type}"), + owner_user_id: None, + profile_id: None, + world_type: Some(world_type.clone()), + world_title: current_scene_preset + .and_then(|preset| read_runtime_json_string_field_from_map(preset, "name")) + .unwrap_or_else(|| build_runtime_builtin_world_title(&world_type)), + world_subtitle: current_scene_preset + .and_then(|preset| { + read_runtime_json_string_field_from_map(preset, "summary") + .or_else(|| read_runtime_json_string_field_from_map(preset, "description")) + }) + .unwrap_or_default(), + }) +} + +pub fn resolve_runtime_profile_save_archive_meta( + game_state: &Value, + current_story_json: Option<&str>, +) -> Option { + if is_non_persistent_runtime_snapshot(game_state) { + return None; + } + + let game_state_object = game_state.as_object(); + let world_meta = resolve_runtime_profile_world_snapshot_meta(game_state_object)?; + let story_engine_memory = game_state_object + .and_then(|state| state.get("storyEngineMemory")) + .and_then(Value::as_object); + let continue_game_digest = story_engine_memory + .and_then(|memory| read_runtime_json_string_field_from_map(memory, "continueGameDigest")); + let current_story_text = parse_optional_json_value( + current_story_json, + RuntimeProfileFieldError::InvalidCurrentStoryJson, + ) + .ok() + .flatten() + .and_then(|story| story.as_object().cloned()) + .and_then(|story| read_runtime_json_string_field_from_map(&story, "text")); + let custom_world_profile = game_state_object + .and_then(|state| state.get("customWorldProfile")) + .and_then(Value::as_object); + + if let Some(custom_world_profile) = custom_world_profile { + let world_name = read_runtime_json_string_field_from_map(custom_world_profile, "name") + .or_else(|| read_runtime_json_string_field_from_map(custom_world_profile, "title")) + .unwrap_or_else(|| world_meta.world_title.clone()); + let subtitle = read_runtime_json_string_field_from_map(custom_world_profile, "summary") + .or_else(|| { + read_runtime_json_string_field_from_map(custom_world_profile, "settingText") + }) + .unwrap_or_else(|| world_meta.world_subtitle.clone()); + let summary_text = continue_game_digest + .or(current_story_text) + .or_else(|| { + if subtitle.is_empty() { + None + } else { + Some(subtitle.clone()) + } + }) + .unwrap_or_else(|| DEFAULT_SAVE_ARCHIVE_SUMMARY_TEXT.to_string()); + + return Some(RuntimeProfileSaveArchiveMeta { + world_key: world_meta.world_key, + owner_user_id: world_meta.owner_user_id, + profile_id: world_meta.profile_id, + world_type: world_meta.world_type, + world_name, + subtitle, + summary_text, + cover_image_src: read_runtime_json_string_field_from_map( + custom_world_profile, + "coverImageSrc", + ), + }); + } + + let summary_text = continue_game_digest + .or(current_story_text) + .or_else(|| { + if world_meta.world_subtitle.is_empty() { + None + } else { + Some(world_meta.world_subtitle.clone()) + } + }) + .unwrap_or_else(|| DEFAULT_SAVE_ARCHIVE_SUMMARY_TEXT.to_string()); + let current_scene_preset = game_state_object + .and_then(|state| state.get("currentScenePreset")) + .and_then(Value::as_object); + + Some(RuntimeProfileSaveArchiveMeta { + world_key: world_meta.world_key, + owner_user_id: world_meta.owner_user_id, + profile_id: world_meta.profile_id, + world_type: world_meta.world_type, + world_name: world_meta.world_title, + subtitle: world_meta.world_subtitle, + summary_text, + cover_image_src: current_scene_preset + .and_then(|preset| read_runtime_json_string_field_from_map(preset, "imageSrc")), + }) +} + +pub fn read_runtime_json_non_negative_u64(value: Option<&Value>) -> u64 { + match value { + Some(Value::Number(number)) => { + if let Some(raw) = number.as_u64() { + raw + } else if let Some(raw) = number.as_i64() { + raw.max(0) as u64 + } else if let Some(raw) = number.as_f64() { + if raw.is_finite() && raw > 0.0 { + raw.floor() as u64 + } else { + 0 + } + } else { + 0 + } + } + Some(Value::String(raw)) => raw.trim().parse::().ok().unwrap_or(0), + _ => 0, + } +} + +pub fn read_runtime_json_string_field(value: &Value, field: &str) -> Option { + read_runtime_json_string_field_from_map(value.as_object()?, field) +} + +pub fn read_runtime_json_string_field_from_map( + value: &serde_json::Map, + field: &str, +) -> Option { + value + .get(field)? + .as_str() + .map(str::trim) + .filter(|value| !value.is_empty()) + .map(ToOwned::to_owned) +} + +pub fn build_runtime_builtin_world_title(world_type: &str) -> String { + match world_type { + "WUXIA" => "武侠世界".to_string(), + "XIANXIA" => "仙侠世界".to_string(), + _ => "叙事世界".to_string(), + } +} + +fn parse_optional_json_value( + raw: Option<&str>, + error: RuntimeProfileFieldError, +) -> Result, RuntimeProfileFieldError> { + match raw.map(str::trim).filter(|value| !value.is_empty()) { + Some(value) => serde_json::from_str::(value) + .map(Some) + .map_err(|_| error), + None => Ok(None), + } +} diff --git a/server-rs/crates/module-runtime/src/commands.rs b/server-rs/crates/module-runtime/src/commands.rs index 54d9ecfb..61ee2d63 100644 --- a/server-rs/crates/module-runtime/src/commands.rs +++ b/server-rs/crates/module-runtime/src/commands.rs @@ -1,3 +1,472 @@ //! 运行时写入命令过渡落位。 //! //! 用于表达保存快照、更新设置、写入浏览历史、调整钱包和保存存档等输入。 + +use std::collections::HashSet; + +use serde_json::Value; +use shared_kernel::{ + normalize_optional_string, normalize_required_string, parse_rfc3339 as parse_shared_rfc3339, +}; + +use crate::domain::*; +use crate::errors::*; +use crate::{format_utc_micros, runtime_profile_recharge_product_by_id}; + +// 统一把共享必填字符串归一化映射到 runtime 各自的字段错误,避免输入构造函数重复 trim + 判空。 +fn normalize_runtime_settings_user_id( + user_id: String, +) -> Result { + normalize_required_string(user_id).ok_or(RuntimeSettingsFieldError::MissingUserId) +} + +fn normalize_runtime_browse_history_user_id( + user_id: String, +) -> Result { + normalize_required_string(user_id).ok_or(RuntimeBrowseHistoryFieldError::MissingUserId) +} + +fn normalize_runtime_profile_user_id(user_id: String) -> Result { + normalize_required_string(user_id).ok_or(RuntimeProfileFieldError::MissingUserId) +} + +pub fn build_runtime_setting_get_input( + user_id: String, +) -> Result { + let user_id = normalize_runtime_settings_user_id(user_id)?; + Ok(RuntimeSettingGetInput { user_id }) +} + +pub fn build_runtime_setting_upsert_input( + user_id: String, + music_volume: f32, + platform_theme: RuntimePlatformTheme, + updated_at_micros: i64, +) -> Result { + let user_id = normalize_runtime_settings_user_id(user_id)?; + let normalized = RuntimeSettings::normalized(music_volume, platform_theme); + + Ok(RuntimeSettingUpsertInput { + user_id, + music_volume: normalized.music_volume, + platform_theme: normalized.platform_theme, + updated_at_micros, + }) +} + +pub fn build_runtime_browse_history_list_input( + user_id: String, +) -> Result { + let user_id = normalize_runtime_browse_history_user_id(user_id)?; + Ok(RuntimeBrowseHistoryListInput { user_id }) +} + +pub fn build_runtime_profile_dashboard_get_input( + user_id: String, +) -> Result { + let user_id = normalize_runtime_profile_user_id(user_id)?; + Ok(RuntimeProfileDashboardGetInput { user_id }) +} + +pub fn build_runtime_profile_wallet_ledger_list_input( + user_id: String, +) -> Result { + let user_id = normalize_runtime_profile_user_id(user_id)?; + Ok(RuntimeProfileWalletLedgerListInput { user_id }) +} + +pub fn build_runtime_profile_wallet_adjustment_input( + user_id: String, + amount: u64, + ledger_id: String, + created_at_micros: i64, +) -> Result { + let user_id = normalize_runtime_profile_user_id(user_id)?; + let ledger_id = + normalize_required_string(ledger_id).ok_or(RuntimeProfileFieldError::MissingLedgerId)?; + if amount == 0 || amount > i64::MAX as u64 { + return Err(RuntimeProfileFieldError::InvalidWalletAmount); + } + Ok(RuntimeProfileWalletAdjustmentInput { + user_id, + amount, + ledger_id, + created_at_micros, + }) +} + +pub fn build_runtime_profile_recharge_center_get_input( + user_id: String, +) -> Result { + let user_id = normalize_runtime_profile_user_id(user_id)?; + Ok(RuntimeProfileRechargeCenterGetInput { user_id }) +} + +pub fn build_runtime_profile_recharge_order_create_input( + user_id: String, + product_id: String, + payment_channel: String, + created_at_micros: i64, +) -> Result { + let user_id = normalize_runtime_profile_user_id(user_id)?; + let product_id = + normalize_required_string(product_id).ok_or(RuntimeProfileFieldError::MissingProductId)?; + if runtime_profile_recharge_product_by_id(&product_id).is_none() { + return Err(RuntimeProfileFieldError::UnknownRechargeProduct); + } + let payment_channel = normalize_required_string(payment_channel) + .unwrap_or_else(|| PROFILE_RECHARGE_PAYMENT_CHANNEL_MOCK.to_string()); + + Ok(RuntimeProfileRechargeOrderCreateInput { + user_id, + product_id, + payment_channel, + created_at_micros, + }) +} + +pub fn build_runtime_referral_invite_center_get_input( + user_id: String, +) -> Result { + let user_id = normalize_runtime_profile_user_id(user_id)?; + Ok(RuntimeReferralInviteCenterGetInput { user_id }) +} + +pub fn build_runtime_referral_redeem_input( + user_id: String, + invite_code: String, + updated_at_micros: i64, +) -> Result { + let user_id = normalize_runtime_profile_user_id(user_id)?; + let invite_code = + normalize_invite_code(invite_code).ok_or(RuntimeProfileFieldError::MissingInviteCode)?; + Ok(RuntimeReferralRedeemInput { + user_id, + invite_code, + updated_at_micros, + }) +} + +pub fn build_runtime_profile_reward_code_redeem_input( + user_id: String, + code: String, + redeemed_at_micros: i64, +) -> Result { + let user_id = normalize_runtime_profile_user_id(user_id)?; + let code = normalize_redeem_code(code).ok_or(RuntimeProfileFieldError::MissingRedeemCode)?; + Ok(RuntimeProfileRewardCodeRedeemInput { + user_id, + code, + redeemed_at_micros, + }) +} + +pub fn build_runtime_profile_redeem_code_admin_upsert_input( + admin_user_id: String, + code: String, + mode: RuntimeProfileRedeemCodeMode, + reward_points: u64, + max_uses: u32, + enabled: bool, + allowed_user_ids: Vec, + allowed_public_user_codes: Vec, + updated_at_micros: i64, +) -> Result { + let admin_user_id = normalize_runtime_profile_user_id(admin_user_id)?; + let code = normalize_redeem_code(code).ok_or(RuntimeProfileFieldError::MissingRedeemCode)?; + if reward_points == 0 { + return Err(RuntimeProfileFieldError::InvalidRedeemCodeReward); + } + if max_uses == 0 { + return Err(RuntimeProfileFieldError::InvalidRedeemCodeMaxUses); + } + + Ok(RuntimeProfileRedeemCodeAdminUpsertInput { + admin_user_id, + code, + mode, + reward_points, + max_uses, + enabled, + allowed_user_ids: allowed_user_ids + .into_iter() + .filter_map(|value| normalize_optional_string(Some(value))) + .collect(), + allowed_public_user_codes: allowed_public_user_codes + .into_iter() + .filter_map(|value| normalize_optional_string(Some(value))) + .collect(), + updated_at_micros, + }) +} + +pub fn build_runtime_profile_redeem_code_admin_disable_input( + admin_user_id: String, + code: String, + updated_at_micros: i64, +) -> Result { + let admin_user_id = normalize_runtime_profile_user_id(admin_user_id)?; + let code = normalize_redeem_code(code).ok_or(RuntimeProfileFieldError::MissingRedeemCode)?; + Ok(RuntimeProfileRedeemCodeAdminDisableInput { + admin_user_id, + code, + updated_at_micros, + }) +} + +pub fn build_runtime_profile_play_stats_get_input( + user_id: String, +) -> Result { + let user_id = normalize_runtime_profile_user_id(user_id)?; + Ok(RuntimeProfilePlayStatsGetInput { user_id }) +} + +pub fn build_runtime_snapshot_get_input( + user_id: String, +) -> Result { + let user_id = normalize_runtime_profile_user_id(user_id)?; + Ok(RuntimeSnapshotGetInput { user_id }) +} + +pub fn build_runtime_snapshot_delete_input( + user_id: String, +) -> Result { + let user_id = normalize_runtime_profile_user_id(user_id)?; + Ok(RuntimeSnapshotDeleteInput { user_id }) +} + +pub fn build_runtime_profile_save_archive_list_input( + user_id: String, +) -> Result { + let user_id = normalize_runtime_profile_user_id(user_id)?; + Ok(RuntimeProfileSaveArchiveListInput { user_id }) +} + +pub fn build_runtime_profile_save_archive_resume_input( + user_id: String, + world_key: String, +) -> Result { + let user_id = normalize_runtime_profile_user_id(user_id)?; + let world_key = + normalize_required_string(world_key).ok_or(RuntimeProfileFieldError::MissingWorldKey)?; + Ok(RuntimeProfileSaveArchiveResumeInput { user_id, world_key }) +} + +pub fn build_runtime_save_checkpoint_input( + session_id: String, + bottom_tab: String, + saved_at_micros: i64, + updated_at_micros: i64, +) -> Result { + let session_id = normalize_required_string(session_id) + .ok_or(RuntimeProfileFieldError::MissingCheckpointSessionId)?; + let bottom_tab = + normalize_bottom_tab(bottom_tab).ok_or(RuntimeProfileFieldError::MissingBottomTab)?; + + Ok(RuntimeSaveCheckpointInput { + session_id, + bottom_tab, + saved_at_micros, + updated_at_micros, + }) +} + +pub fn build_runtime_browse_history_clear_input( + user_id: String, +) -> Result { + let user_id = normalize_runtime_browse_history_user_id(user_id)?; + Ok(RuntimeBrowseHistoryClearInput { user_id }) +} + +pub fn build_runtime_snapshot_upsert_input( + user_id: String, + saved_at_micros: i64, + bottom_tab: String, + game_state: Value, + current_story: Option, + updated_at_micros: i64, +) -> Result { + let user_id = normalize_runtime_profile_user_id(user_id)?; + let bottom_tab = + normalize_bottom_tab(bottom_tab).ok_or(RuntimeProfileFieldError::MissingBottomTab)?; + let game_state_json = serde_json::to_string(&game_state) + .map_err(|_| RuntimeProfileFieldError::InvalidGameStateJson)?; + let current_story_json = normalize_current_story_json(current_story)?; + + Ok(RuntimeSnapshotUpsertInput { + user_id, + saved_at_micros, + bottom_tab, + game_state_json, + current_story_json, + updated_at_micros, + }) +} + +pub fn build_runtime_browse_history_sync_input( + user_id: String, + entries: Vec, + updated_at_micros: i64, +) -> Result { + let user_id = normalize_runtime_browse_history_user_id(user_id)?; + if entries.len() > MAX_BROWSE_HISTORY_BATCH_SIZE { + return Err(RuntimeBrowseHistoryFieldError::TooManyEntries); + } + + let mut normalized_entries = Vec::with_capacity(entries.len()); + for entry in entries { + let Some(owner_user_id) = normalize_required_string(entry.owner_user_id) else { + continue; + }; + let Some(profile_id) = normalize_required_string(entry.profile_id) else { + continue; + }; + let Some(world_name) = normalize_required_string(entry.world_name) else { + continue; + }; + // 与旧 Node 仓储保持一致:单条缺少关键字段时静默过滤,不让整批请求失败。 + let visited_at_micros = entry + .visited_at + .as_deref() + .and_then(parse_utc_rfc3339_to_micros) + .unwrap_or(updated_at_micros); + + normalized_entries.push(RuntimeBrowseHistoryWriteInput { + owner_user_id, + profile_id, + world_name, + subtitle: normalize_optional_string(entry.subtitle), + summary_text: normalize_optional_string(entry.summary_text), + cover_image_src: normalize_optional_string(entry.cover_image_src), + theme_mode: normalize_optional_string(entry.theme_mode), + author_display_name: normalize_optional_string(entry.author_display_name), + // 统一把 visitedAt 收口成 RFC3339,避免后续排序与回包格式继续漂移。 + visited_at: Some(format_utc_micros(visited_at_micros)), + }); + } + + Ok(RuntimeBrowseHistorySyncInput { + user_id, + entries: normalized_entries, + updated_at_micros, + }) +} + +pub fn prepare_runtime_browse_history_entries( + input: RuntimeBrowseHistorySyncInput, +) -> Result, RuntimeBrowseHistoryFieldError> { + let validated_input = build_runtime_browse_history_sync_input( + input.user_id, + input.entries, + input.updated_at_micros, + )?; + let mut prepared_entries = validated_input + .entries + .into_iter() + .map(|entry| { + let visited_at_micros = entry + .visited_at + .as_deref() + .and_then(parse_utc_rfc3339_to_micros) + .unwrap_or(validated_input.updated_at_micros); + + RuntimeBrowseHistoryPreparedEntry { + browse_history_id: build_runtime_browse_history_id( + &validated_input.user_id, + &entry.owner_user_id, + &entry.profile_id, + ), + user_id: validated_input.user_id.clone(), + owner_user_id: entry.owner_user_id, + profile_id: entry.profile_id, + world_name: entry.world_name, + subtitle: entry.subtitle.unwrap_or_default(), + summary_text: entry.summary_text.unwrap_or_default(), + cover_image_src: entry.cover_image_src, + theme_mode: RuntimeBrowseHistoryThemeMode::from_client_str( + entry.theme_mode.as_deref().unwrap_or("mythic"), + ), + author_display_name: entry + .author_display_name + .unwrap_or_else(|| DEFAULT_BROWSE_HISTORY_AUTHOR_DISPLAY_NAME.to_string()), + visited_at_micros, + updated_at_micros: validated_input.updated_at_micros, + } + }) + .collect::>(); + + // 与旧 Node 仓储保持一致:先按 visitedAt 倒序,再按 owner/profile 去重,只保留最近一次访问。 + prepared_entries.sort_by(|left, right| { + right + .visited_at_micros + .cmp(&left.visited_at_micros) + .then_with(|| left.browse_history_id.cmp(&right.browse_history_id)) + }); + + let mut seen_ids = HashSet::new(); + prepared_entries.retain(|entry| seen_ids.insert(entry.browse_history_id.clone())); + + Ok(prepared_entries) +} + +pub fn build_runtime_browse_history_id( + user_id: &str, + owner_user_id: &str, + profile_id: &str, +) -> String { + format!("{user_id}:{owner_user_id}:{profile_id}") +} + +fn parse_utc_rfc3339_to_micros(value: &str) -> Option { + let trimmed = value.trim(); + if trimmed.is_empty() { + return None; + } + + let nanos = parse_shared_rfc3339(trimmed).ok()?.unix_timestamp_nanos(); + i64::try_from(nanos / 1_000).ok() +} + +fn normalize_bottom_tab(value: String) -> Option { + let trimmed = normalize_required_string(value)?; + let normalized = match trimmed.as_str() { + "character" | "inventory" => trimmed, + _ => "adventure".to_string(), + }; + + Some(normalized) +} + +fn normalize_current_story_json( + current_story: Option, +) -> Result, RuntimeProfileFieldError> { + let Some(current_story) = current_story else { + return Ok(None); + }; + if !current_story.is_object() { + return Ok(None); + } + + serde_json::to_string(¤t_story) + .map(Some) + .map_err(|_| RuntimeProfileFieldError::InvalidCurrentStoryJson) +} + +pub fn normalize_invite_code(value: String) -> Option { + let normalized = value + .trim() + .chars() + .filter(|character| character.is_ascii_alphanumeric()) + .map(|character| character.to_ascii_uppercase()) + .collect::(); + + if normalized.is_empty() { + None + } else { + Some(normalized) + } +} + +pub fn normalize_redeem_code(value: String) -> Option { + normalize_invite_code(value) +} diff --git a/server-rs/crates/module-runtime/src/domain.rs b/server-rs/crates/module-runtime/src/domain.rs index 47ae17c6..2b0bbd8d 100644 --- a/server-rs/crates/module-runtime/src/domain.rs +++ b/server-rs/crates/module-runtime/src/domain.rs @@ -1,4 +1,1013 @@ -//! 运行时领域模型过渡落位。 +//! 运行时领域模型。 //! -//! 后续迁移设置、快照、个人页状态、钱包流水、存档和浏览历史规则时, -//! 只保留纯运行时数据变化;SpacetimeDB client 与 HTTP response 不进入本文件。 +//! 这里只保留运行时设置、快照、个人页状态、钱包流水、存档和浏览历史等 +//! 纯领域事实与值对象;SpacetimeDB client 与 HTTP response 不进入本文件。 + +use serde::{Deserialize, Serialize}; +use serde_json::Value; + +#[cfg(feature = "spacetime-types")] +use spacetimedb::SpacetimeType; + +pub const DEFAULT_MUSIC_VOLUME: f32 = 0.42; +pub const DEFAULT_PLATFORM_THEME: RuntimePlatformTheme = RuntimePlatformTheme::Light; +pub const DEFAULT_BROWSE_HISTORY_AUTHOR_DISPLAY_NAME: &str = "玩家"; +pub const MAX_BROWSE_HISTORY_BATCH_SIZE: usize = 100; +pub const PROFILE_WALLET_LEDGER_LIST_LIMIT: usize = 50; +pub const PROFILE_REFERRAL_REWARD_POINTS: u64 = 30; +pub const PROFILE_REFERRAL_DAILY_INVITER_REWARD_LIMIT: u32 = 10; +pub const PROFILE_RUNTIME_DAY_MICROS: i64 = 86_400_000_000; +pub const SAVE_SNAPSHOT_VERSION: u32 = 2; +pub const DEFAULT_SAVE_ARCHIVE_SUMMARY_TEXT: &str = "继续推进上一次保存的故事。"; +pub const PROFILE_RECHARGE_PAYMENT_CHANNEL_MOCK: &str = "mock"; + +/// 运行时平台主题。 +/// +/// 当前只冻结 light/dark 两种主题,避免各层散落字符串字面量。 +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub enum RuntimePlatformTheme { + Light, + Dark, +} + +impl RuntimePlatformTheme { + pub fn as_str(&self) -> &'static str { + match self { + Self::Light => "light", + Self::Dark => "dark", + } + } + + pub fn from_client_str(value: &str) -> Self { + if value.trim().eq_ignore_ascii_case("dark") { + Self::Dark + } else { + Self::Light + } + } +} + +/// 运行时设置聚合。 +#[derive(Clone, Debug, PartialEq)] +pub struct RuntimeSettings { + pub music_volume: f32, + pub platform_theme: RuntimePlatformTheme, +} + +impl RuntimeSettings { + pub fn defaults() -> Self { + Self { + music_volume: DEFAULT_MUSIC_VOLUME, + platform_theme: DEFAULT_PLATFORM_THEME, + } + } + + /// 与旧 Node 仓储保持一致:音量 clamp 到 0~1,主题除 dark 外统一回退到 light。 + pub fn normalized(music_volume: f32, platform_theme: RuntimePlatformTheme) -> Self { + Self { + music_volume: music_volume.clamp(0.0, 1.0), + platform_theme, + } + } +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct RuntimeSnapshot { + pub user_id: String, + pub version: u32, + pub saved_at_micros: i64, + pub bottom_tab: String, + pub game_state_json: String, + pub current_story_json: Option, + pub created_at_micros: i64, + pub updated_at_micros: i64, +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct RuntimeSnapshotProcedureResult { + pub ok: bool, + pub record: Option, + pub error_message: Option, +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct RuntimeSnapshotGetInput { + pub user_id: String, +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct RuntimeSnapshotUpsertInput { + pub user_id: String, + pub saved_at_micros: i64, + pub bottom_tab: String, + pub game_state_json: String, + pub current_story_json: Option, + pub updated_at_micros: i64, +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct RuntimeSnapshotDeleteInput { + pub user_id: String, +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct RuntimeProfileSaveArchiveSnapshot { + pub archive_id: String, + pub user_id: String, + pub world_key: String, + pub owner_user_id: Option, + pub profile_id: Option, + pub world_type: Option, + pub world_name: String, + pub subtitle: String, + pub summary_text: String, + pub cover_image_src: Option, + pub saved_at_micros: i64, + pub bottom_tab: String, + pub game_state_json: String, + pub current_story_json: Option, + pub created_at_micros: i64, + pub updated_at_micros: i64, +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct RuntimeProfileSaveArchiveProcedureResult { + pub ok: bool, + pub entries: Vec, + pub record: Option, + pub current_snapshot: Option, + pub error_message: Option, +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct RuntimeProfileSaveArchiveListInput { + pub user_id: String, +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct RuntimeProfileSaveArchiveResumeInput { + pub user_id: String, + pub world_key: String, +} + +#[derive(Clone, Debug, PartialEq)] +pub struct RuntimeSaveCheckpointInput { + pub session_id: String, + pub bottom_tab: String, + pub saved_at_micros: i64, + pub updated_at_micros: i64, +} + +#[derive(Clone, Debug, PartialEq)] +pub struct RuntimeSaveCheckpointSnapshotUpdate { + pub saved_at_micros: i64, + pub bottom_tab: String, + pub game_state: Value, + pub current_story: Option, + pub updated_at_micros: i64, +} + +/// 浏览历史沿用平台已有的六种世界主题,但独立冻结在 runtime 领域内,避免反向耦合创作域 crate。 +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub enum RuntimeBrowseHistoryThemeMode { + Martial, + Arcane, + Machina, + Tide, + Rift, + Mythic, +} + +impl RuntimeBrowseHistoryThemeMode { + pub fn as_str(&self) -> &'static str { + match self { + Self::Martial => "martial", + Self::Arcane => "arcane", + Self::Machina => "machina", + Self::Tide => "tide", + Self::Rift => "rift", + Self::Mythic => "mythic", + } + } + + /// 浏览历史主题沿用旧 Node 逻辑:不做严格校验,未知值统一回退到 mythic。 + pub fn from_client_str(value: &str) -> Self { + match value.trim().to_ascii_lowercase().as_str() { + "martial" => Self::Martial, + "arcane" => Self::Arcane, + "machina" => Self::Machina, + "tide" => Self::Tide, + "rift" => Self::Rift, + _ => Self::Mythic, + } + } +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct RuntimeSettingSnapshot { + pub user_id: String, + pub music_volume: f32, + pub platform_theme: RuntimePlatformTheme, + pub created_at_micros: i64, + pub updated_at_micros: i64, +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct RuntimeSettingProcedureResult { + pub ok: bool, + pub record: Option, + pub error_message: Option, +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct RuntimeSettingGetInput { + pub user_id: String, +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct RuntimeSettingUpsertInput { + pub user_id: String, + pub music_volume: f32, + pub platform_theme: RuntimePlatformTheme, + pub updated_at_micros: i64, +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct RuntimeBrowseHistorySnapshot { + pub browse_history_id: String, + pub user_id: String, + pub owner_user_id: String, + pub profile_id: String, + pub world_name: String, + pub subtitle: String, + pub summary_text: String, + pub cover_image_src: Option, + pub theme_mode: RuntimeBrowseHistoryThemeMode, + pub author_display_name: String, + pub visited_at_micros: i64, + pub created_at_micros: i64, + pub updated_at_micros: i64, +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct RuntimeBrowseHistoryProcedureResult { + pub ok: bool, + pub entries: Vec, + pub error_message: Option, +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct RuntimeBrowseHistoryListInput { + pub user_id: String, +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct RuntimeBrowseHistoryClearInput { + pub user_id: String, +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct RuntimeBrowseHistoryWriteInput { + pub owner_user_id: String, + pub profile_id: String, + pub world_name: String, + pub subtitle: Option, + pub summary_text: Option, + pub cover_image_src: Option, + pub theme_mode: Option, + pub author_display_name: Option, + pub visited_at: Option, +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct RuntimeBrowseHistorySyncInput { + pub user_id: String, + pub entries: Vec, + pub updated_at_micros: i64, +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct RuntimeProfileDashboardSnapshot { + pub user_id: String, + pub wallet_balance: u64, + pub total_play_time_ms: u64, + pub played_world_count: u32, + pub updated_at_micros: Option, +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct RuntimeProfileDashboardProcedureResult { + pub ok: bool, + pub record: Option, + pub error_message: Option, +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct RuntimeProfileDashboardGetInput { + pub user_id: String, +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub enum RuntimeProfileWalletLedgerSourceType { + SnapshotSync, + InviteInviterReward, + InviteInviteeReward, + PointsRecharge, + AssetOperationConsume, + AssetOperationRefund, + RedeemCodeReward, +} + +impl RuntimeProfileWalletLedgerSourceType { + pub fn as_str(&self) -> &'static str { + match self { + Self::SnapshotSync => "snapshot_sync", + Self::InviteInviterReward => "invite_inviter_reward", + Self::InviteInviteeReward => "invite_invitee_reward", + Self::PointsRecharge => "points_recharge", + Self::AssetOperationConsume => "asset_operation_consume", + Self::AssetOperationRefund => "asset_operation_refund", + Self::RedeemCodeReward => "redeem_code_reward", + } + } +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub enum RuntimeProfileRedeemCodeMode { + Public, + Unique, + Private, +} + +impl RuntimeProfileRedeemCodeMode { + pub fn as_str(&self) -> &'static str { + match self { + Self::Public => "public", + Self::Unique => "unique", + Self::Private => "private", + } + } +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub enum RuntimeProfileRechargeProductKind { + Points, + Membership, +} + +impl RuntimeProfileRechargeProductKind { + pub fn as_str(&self) -> &'static str { + match self { + Self::Points => "points", + Self::Membership => "membership", + } + } +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub enum RuntimeProfileMembershipStatus { + Normal, + Active, +} + +impl RuntimeProfileMembershipStatus { + pub fn as_str(&self) -> &'static str { + match self { + Self::Normal => "normal", + Self::Active => "active", + } + } +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub enum RuntimeProfileMembershipTier { + Normal, + Month, + Season, + Year, +} + +impl RuntimeProfileMembershipTier { + pub fn as_str(&self) -> &'static str { + match self { + Self::Normal => "normal", + Self::Month => "month", + Self::Season => "season", + Self::Year => "year", + } + } +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub enum RuntimeProfileRechargeOrderStatus { + Paid, +} + +impl RuntimeProfileRechargeOrderStatus { + pub fn as_str(&self) -> &'static str { + match self { + Self::Paid => "paid", + } + } +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct RuntimeProfileRechargeProductSnapshot { + pub product_id: String, + pub title: String, + pub price_cents: u64, + pub kind: RuntimeProfileRechargeProductKind, + pub points_amount: u64, + pub bonus_points: u64, + pub duration_days: u32, + pub badge_label: String, + pub description: String, + pub tier: RuntimeProfileMembershipTier, +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct RuntimeProfileMembershipBenefitSnapshot { + pub benefit_name: String, + pub normal_value: String, + pub month_value: String, + pub season_value: String, + pub year_value: String, +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct RuntimeProfileMembershipSnapshot { + pub user_id: String, + pub status: RuntimeProfileMembershipStatus, + pub tier: RuntimeProfileMembershipTier, + pub started_at_micros: Option, + pub expires_at_micros: Option, + pub updated_at_micros: Option, +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct RuntimeProfileRechargeOrderSnapshot { + pub order_id: String, + pub user_id: String, + pub product_id: String, + pub product_title: String, + pub kind: RuntimeProfileRechargeProductKind, + pub amount_cents: u64, + pub status: RuntimeProfileRechargeOrderStatus, + pub payment_channel: String, + pub paid_at_micros: i64, + pub created_at_micros: i64, + pub points_delta: i64, + pub membership_expires_at_micros: Option, +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct RuntimeProfileRechargeCenterSnapshot { + pub user_id: String, + pub wallet_balance: u64, + pub membership: RuntimeProfileMembershipSnapshot, + pub point_products: Vec, + pub membership_products: Vec, + pub benefits: Vec, + pub latest_order: Option, + pub has_points_recharged: bool, +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct RuntimeProfileMembershipPurchaseUpdate { + pub started_at_micros: i64, + pub expires_at_micros: i64, +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct RuntimeProfileRechargeCenterProcedureResult { + pub ok: bool, + pub record: Option, + pub order: Option, + pub error_message: Option, +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct RuntimeProfileRechargeCenterGetInput { + pub user_id: String, +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct RuntimeProfileRechargeOrderCreateInput { + pub user_id: String, + pub product_id: String, + pub payment_channel: String, + pub created_at_micros: i64, +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct RuntimeProfileWalletLedgerEntrySnapshot { + pub wallet_ledger_id: String, + pub user_id: String, + pub amount_delta: i64, + pub balance_after: u64, + pub source_type: RuntimeProfileWalletLedgerSourceType, + pub created_at_micros: i64, +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct RuntimeProfileWalletLedgerProcedureResult { + pub ok: bool, + pub entries: Vec, + pub error_message: Option, +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct RuntimeProfileWalletAdjustmentProcedureResult { + pub ok: bool, + pub record: Option, + pub error_message: Option, +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct RuntimeProfileWalletLedgerListInput { + pub user_id: String, +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct RuntimeProfileWalletAdjustmentInput { + pub user_id: String, + pub amount: u64, + pub ledger_id: String, + pub created_at_micros: i64, +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct RuntimeProfileRewardCodeRedeemInput { + pub user_id: String, + pub code: String, + pub redeemed_at_micros: i64, +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct RuntimeProfileRewardCodeRedeemSnapshot { + pub wallet_balance: u64, + pub amount_granted: u64, + pub ledger_entry: RuntimeProfileWalletLedgerEntrySnapshot, +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct RuntimeProfileRewardCodeRedeemProcedureResult { + pub ok: bool, + pub record: Option, + pub error_message: Option, +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct RuntimeProfileRedeemCodeAdminUpsertInput { + pub admin_user_id: String, + pub code: String, + pub mode: RuntimeProfileRedeemCodeMode, + pub reward_points: u64, + pub max_uses: u32, + pub enabled: bool, + pub allowed_user_ids: Vec, + pub allowed_public_user_codes: Vec, + pub updated_at_micros: i64, +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct RuntimeProfileRedeemCodeAdminDisableInput { + pub admin_user_id: String, + pub code: String, + pub updated_at_micros: i64, +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct RuntimeProfileRedeemCodeSnapshot { + pub code: String, + pub mode: RuntimeProfileRedeemCodeMode, + pub reward_points: u64, + pub max_uses: u32, + pub global_used_count: u32, + pub enabled: bool, + pub allowed_user_ids: Vec, + pub created_by: String, + pub created_at_micros: i64, + pub updated_at_micros: i64, +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct RuntimeProfileRedeemCodeAdminProcedureResult { + pub ok: bool, + pub record: Option, + pub error_message: Option, +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct RuntimeReferralInviteCenterSnapshot { + pub user_id: String, + pub invite_code: String, + pub invite_link_path: String, + pub invited_count: u32, + pub rewarded_invite_count: u32, + pub today_inviter_reward_count: u32, + pub today_inviter_reward_remaining: u32, + pub reward_points: u64, + pub has_redeemed_code: bool, + pub bound_inviter_user_id: Option, + pub bound_at_micros: Option, + pub updated_at_micros: i64, +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct RuntimeReferralInviteCenterProcedureResult { + pub ok: bool, + pub record: Option, + pub error_message: Option, +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct RuntimeReferralInviteCenterGetInput { + pub user_id: String, +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct RuntimeReferralRedeemInput { + pub user_id: String, + pub invite_code: String, + pub updated_at_micros: i64, +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct RuntimeReferralRedeemSnapshot { + pub center: RuntimeReferralInviteCenterSnapshot, + pub invitee_reward_granted: bool, + pub inviter_reward_granted: bool, + pub invitee_balance_after: u64, + pub inviter_balance_after: u64, +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct RuntimeReferralRedeemProcedureResult { + pub ok: bool, + pub record: Option, + pub error_message: Option, +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct RuntimeProfilePlayedWorldSnapshot { + pub played_world_id: String, + pub user_id: String, + pub world_key: String, + pub owner_user_id: Option, + pub profile_id: Option, + pub world_type: Option, + pub world_title: String, + pub world_subtitle: String, + pub first_played_at_micros: i64, + pub last_played_at_micros: i64, + pub last_observed_play_time_ms: u64, +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct RuntimeProfilePlayStatsSnapshot { + pub user_id: String, + pub total_play_time_ms: u64, + pub played_works: Vec, + pub updated_at_micros: Option, +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct RuntimeProfilePlayStatsProcedureResult { + pub ok: bool, + pub record: Option, + pub error_message: Option, +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct RuntimeProfilePlayStatsGetInput { + pub user_id: String, +} + +#[derive(Clone, Debug, PartialEq)] +pub struct RuntimeBrowseHistoryPreparedEntry { + pub browse_history_id: String, + pub user_id: String, + pub owner_user_id: String, + pub profile_id: String, + pub world_name: String, + pub subtitle: String, + pub summary_text: String, + pub cover_image_src: Option, + pub theme_mode: RuntimeBrowseHistoryThemeMode, + pub author_display_name: String, + pub visited_at_micros: i64, + pub updated_at_micros: i64, +} + +#[derive(Clone, Debug, PartialEq)] +pub struct RuntimeBrowseHistoryRecord { + pub browse_history_id: String, + pub user_id: String, + pub owner_user_id: String, + pub profile_id: String, + pub world_name: String, + pub subtitle: String, + pub summary_text: String, + pub cover_image_src: Option, + pub theme_mode: RuntimeBrowseHistoryThemeMode, + pub author_display_name: String, + pub visited_at: String, + pub visited_at_micros: i64, + pub created_at_micros: i64, + pub updated_at_micros: i64, +} + +#[derive(Clone, Debug, PartialEq)] +pub struct RuntimeSettingsRecord { + pub user_id: String, + pub music_volume: f32, + pub platform_theme: RuntimePlatformTheme, + pub created_at_micros: i64, + pub updated_at_micros: i64, +} + +#[derive(Clone, Debug, PartialEq)] +pub struct RuntimeProfileDashboardRecord { + pub user_id: String, + pub wallet_balance: u64, + pub total_play_time_ms: u64, + pub played_world_count: u32, + pub updated_at: Option, + pub updated_at_micros: Option, +} + +#[derive(Clone, Debug, PartialEq)] +pub struct RuntimeProfileWalletLedgerEntryRecord { + pub wallet_ledger_id: String, + pub user_id: String, + pub amount_delta: i64, + pub balance_after: u64, + pub source_type: RuntimeProfileWalletLedgerSourceType, + pub created_at: String, + pub created_at_micros: i64, +} + +#[derive(Clone, Debug, PartialEq)] +pub struct RuntimeProfilePlayedWorldRecord { + pub played_world_id: String, + pub user_id: String, + pub world_key: String, + pub owner_user_id: Option, + pub profile_id: Option, + pub world_type: Option, + pub world_title: String, + pub world_subtitle: String, + pub first_played_at: String, + pub first_played_at_micros: i64, + pub last_played_at: String, + pub last_played_at_micros: i64, + pub last_observed_play_time_ms: u64, +} + +#[derive(Clone, Debug, PartialEq)] +pub struct RuntimeProfilePlayStatsRecord { + pub user_id: String, + pub total_play_time_ms: u64, + pub played_works: Vec, + pub updated_at: Option, + pub updated_at_micros: Option, +} + +#[derive(Clone, Debug, PartialEq)] +pub struct RuntimeProfileRechargeProductRecord { + pub product_id: String, + pub title: String, + pub price_cents: u64, + pub kind: RuntimeProfileRechargeProductKind, + pub points_amount: u64, + pub bonus_points: u64, + pub duration_days: u32, + pub badge_label: String, + pub description: String, + pub tier: RuntimeProfileMembershipTier, +} + +#[derive(Clone, Debug, PartialEq)] +pub struct RuntimeProfileMembershipBenefitRecord { + pub benefit_name: String, + pub normal_value: String, + pub month_value: String, + pub season_value: String, + pub year_value: String, +} + +#[derive(Clone, Debug, PartialEq)] +pub struct RuntimeProfileMembershipRecord { + pub user_id: String, + pub status: RuntimeProfileMembershipStatus, + pub tier: RuntimeProfileMembershipTier, + pub started_at: Option, + pub started_at_micros: Option, + pub expires_at: Option, + pub expires_at_micros: Option, + pub updated_at: Option, + pub updated_at_micros: Option, +} + +#[derive(Clone, Debug, PartialEq)] +pub struct RuntimeProfileRechargeOrderRecord { + pub order_id: String, + pub user_id: String, + pub product_id: String, + pub product_title: String, + pub kind: RuntimeProfileRechargeProductKind, + pub amount_cents: u64, + pub status: RuntimeProfileRechargeOrderStatus, + pub payment_channel: String, + pub paid_at: String, + pub paid_at_micros: i64, + pub created_at: String, + pub created_at_micros: i64, + pub points_delta: i64, + pub membership_expires_at: Option, + pub membership_expires_at_micros: Option, +} + +#[derive(Clone, Debug, PartialEq)] +pub struct RuntimeProfileRechargeCenterRecord { + pub user_id: String, + pub wallet_balance: u64, + pub membership: RuntimeProfileMembershipRecord, + pub point_products: Vec, + pub membership_products: Vec, + pub benefits: Vec, + pub latest_order: Option, + pub has_points_recharged: bool, +} + +#[derive(Clone, Debug, PartialEq)] +pub struct RuntimeProfileRewardCodeRedeemRecord { + pub wallet_balance: u64, + pub amount_granted: u64, + pub ledger_entry: RuntimeProfileWalletLedgerEntryRecord, +} + +#[derive(Clone, Debug, PartialEq)] +pub struct RuntimeProfileRedeemCodeRecord { + pub code: String, + pub mode: RuntimeProfileRedeemCodeMode, + pub reward_points: u64, + pub max_uses: u32, + pub global_used_count: u32, + pub enabled: bool, + pub allowed_user_ids: Vec, + pub created_by: String, + pub created_at: String, + pub created_at_micros: i64, + pub updated_at: String, + pub updated_at_micros: i64, +} + +#[derive(Clone, Debug, PartialEq)] +pub struct RuntimeReferralInviteCenterRecord { + pub user_id: String, + pub invite_code: String, + pub invite_link_path: String, + pub invited_count: u32, + pub rewarded_invite_count: u32, + pub today_inviter_reward_count: u32, + pub today_inviter_reward_remaining: u32, + pub reward_points: u64, + pub has_redeemed_code: bool, + pub bound_inviter_user_id: Option, + pub bound_at: Option, + pub bound_at_micros: Option, + pub updated_at: String, + pub updated_at_micros: i64, +} + +#[derive(Clone, Debug, PartialEq)] +pub struct RuntimeReferralRedeemRecord { + pub center: RuntimeReferralInviteCenterRecord, + pub invitee_reward_granted: bool, + pub inviter_reward_granted: bool, + pub invitee_balance_after: u64, + pub inviter_balance_after: u64, +} + +#[derive(Clone, Debug, PartialEq)] +pub struct RuntimeSnapshotRecord { + pub user_id: String, + pub version: u32, + pub saved_at: String, + pub saved_at_micros: i64, + pub bottom_tab: String, + pub game_state: Value, + pub current_story: Option, + pub game_state_json: String, + pub current_story_json: Option, + pub created_at_micros: i64, + pub updated_at_micros: i64, +} + +#[derive(Clone, Debug, PartialEq)] +pub struct RuntimeProfileSaveArchiveRecord { + pub archive_id: String, + pub user_id: String, + pub world_key: String, + pub owner_user_id: Option, + pub profile_id: Option, + pub world_type: Option, + pub world_name: String, + pub subtitle: String, + pub summary_text: String, + pub cover_image_src: Option, + pub saved_at: String, + pub saved_at_micros: i64, + pub bottom_tab: String, + pub game_state: Value, + pub current_story: Option, + pub game_state_json: String, + pub current_story_json: Option, + pub created_at_micros: i64, + pub updated_at_micros: i64, +} + +#[derive(Clone, Debug, PartialEq)] +pub struct RuntimeProfileWorldSnapshotMeta { + pub world_key: String, + pub owner_user_id: Option, + pub profile_id: Option, + pub world_type: Option, + pub world_title: String, + pub world_subtitle: String, +} + +#[derive(Clone, Debug, PartialEq)] +pub struct RuntimeProfileSaveArchiveMeta { + pub world_key: String, + pub owner_user_id: Option, + pub profile_id: Option, + pub world_type: Option, + pub world_name: String, + pub subtitle: String, + pub summary_text: String, + pub cover_image_src: Option, +} diff --git a/server-rs/crates/module-runtime/src/errors.rs b/server-rs/crates/module-runtime/src/errors.rs index 3607fe24..037f8e9f 100644 --- a/server-rs/crates/module-runtime/src/errors.rs +++ b/server-rs/crates/module-runtime/src/errors.rs @@ -1,3 +1,107 @@ //! 运行时领域错误过渡落位。 //! //! 错误保持运行时业务语义,例如快照版本非法、兑换码不可用或钱包余额不足。 + +use crate::MAX_BROWSE_HISTORY_BATCH_SIZE; + +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum RuntimeSettingsFieldError { + MissingUserId, +} + +impl std::fmt::Display for RuntimeSettingsFieldError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::MissingUserId => f.write_str("runtime_setting.user_id 不能为空"), + } + } +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum RuntimeBrowseHistoryFieldError { + MissingUserId, + TooManyEntries, +} + +impl std::fmt::Display for RuntimeBrowseHistoryFieldError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::MissingUserId => f.write_str("browse_history.user_id 不能为空"), + Self::TooManyEntries => write!( + f, + "browse_history.entries 单次最多只允许 {} 条", + MAX_BROWSE_HISTORY_BATCH_SIZE + ), + } + } +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum RuntimeProfileFieldError { + MissingUserId, + MissingLedgerId, + InvalidWalletAmount, + WalletAmountOverflow, + WalletBalanceOverflow, + InsufficientWalletBalance, + MissingInviteCode, + MissingRedeemCode, + RedeemCodeDisabled, + RedeemCodeUsesExhausted, + RedeemCodeNotAllowedForUser, + InvalidRedeemCodeReward, + InvalidRedeemCodeMaxUses, + MissingProductId, + MissingWorldKey, + MissingBottomTab, + MissingCheckpointSessionId, + UnknownRechargeProduct, + InvalidGameStateJson, + InvalidCurrentStoryJson, + MissingRuntimeSessionId, + RuntimeSessionMismatch { + expected_session_id: String, + actual_session_id: String, + }, + NonPersistentRuntimeSnapshot, +} + +impl std::fmt::Display for RuntimeProfileFieldError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::MissingUserId => f.write_str("profile.user_id 不能为空"), + Self::MissingLedgerId => f.write_str("profile.wallet_ledger_id 不能为空"), + Self::InvalidWalletAmount => f.write_str("profile.wallet_amount 必须大于 0"), + Self::WalletAmountOverflow => f.write_str("profile.wallet_amount 超出上限"), + Self::WalletBalanceOverflow => f.write_str("profile.wallet_balance 超出上限"), + Self::InsufficientWalletBalance => f.write_str("叙世币余额不足"), + Self::MissingInviteCode => f.write_str("referral.invite_code 不能为空"), + Self::MissingRedeemCode => f.write_str("兑换码不能为空"), + Self::RedeemCodeDisabled => f.write_str("兑换码已停用"), + Self::RedeemCodeUsesExhausted => f.write_str("兑换次数已用完"), + Self::RedeemCodeNotAllowedForUser => f.write_str("该兑换码不适用于当前账号"), + Self::InvalidRedeemCodeReward => f.write_str("兑换码奖励无效"), + Self::InvalidRedeemCodeMaxUses => f.write_str("兑换次数必须大于 0"), + Self::MissingProductId => f.write_str("recharge.product_id 不能为空"), + Self::MissingWorldKey => f.write_str("profile.world_key 不能为空"), + Self::MissingBottomTab => f.write_str("runtime_snapshot.bottom_tab 不能为空"), + Self::MissingCheckpointSessionId => f.write_str("checkpoint.session_id 不能为空"), + Self::UnknownRechargeProduct => f.write_str("recharge.product_id 不存在"), + Self::InvalidGameStateJson => { + f.write_str("runtime_snapshot.game_state 必须是合法 JSON") + } + Self::InvalidCurrentStoryJson => { + f.write_str("runtime_snapshot.current_story 必须是合法 JSON object 或 null") + } + Self::MissingRuntimeSessionId => { + f.write_str("服务端运行时快照缺少 runtimeSessionId,无法创建 checkpoint") + } + Self::RuntimeSessionMismatch { .. } => { + f.write_str("checkpoint sessionId 与服务端运行时快照不一致") + } + Self::NonPersistentRuntimeSnapshot => { + f.write_str("预览或测试运行态不能创建正式 checkpoint") + } + } + } +} diff --git a/server-rs/crates/module-runtime/src/lib.rs b/server-rs/crates/module-runtime/src/lib.rs index 63cbfeb6..f40ab84a 100644 --- a/server-rs/crates/module-runtime/src/lib.rs +++ b/server-rs/crates/module-runtime/src/lib.rs @@ -4,1768 +4,20 @@ mod domain; mod errors; mod events; -use std::collections::HashSet; +pub use application::*; +pub use commands::*; +pub use domain::*; +pub use errors::*; -use serde::{Deserialize, Serialize}; -use serde_json::Value; -use shared_kernel::{ - format_rfc3339 as format_shared_rfc3339, normalize_optional_string, normalize_required_string, - parse_rfc3339 as parse_shared_rfc3339, -}; -#[cfg(feature = "spacetime-types")] -use spacetimedb::SpacetimeType; +use shared_kernel::format_rfc3339 as format_shared_rfc3339; use time::OffsetDateTime; -pub const DEFAULT_MUSIC_VOLUME: f32 = 0.42; -pub const DEFAULT_PLATFORM_THEME: RuntimePlatformTheme = RuntimePlatformTheme::Light; -pub const DEFAULT_BROWSE_HISTORY_AUTHOR_DISPLAY_NAME: &str = "玩家"; -pub const MAX_BROWSE_HISTORY_BATCH_SIZE: usize = 100; -pub const PROFILE_WALLET_LEDGER_LIST_LIMIT: usize = 50; -pub const PROFILE_REFERRAL_REWARD_POINTS: u64 = 30; -pub const PROFILE_REFERRAL_DAILY_INVITER_REWARD_LIMIT: u32 = 10; -pub const SAVE_SNAPSHOT_VERSION: u32 = 2; -pub const DEFAULT_SAVE_ARCHIVE_SUMMARY_TEXT: &str = "继续推进上一次保存的故事。"; -pub const PROFILE_RECHARGE_PAYMENT_CHANNEL_MOCK: &str = "mock"; - -// 运行时设置目前只冻结 light/dark 两种主题,避免各层散落字符串字面量。 -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub enum RuntimePlatformTheme { - Light, - Dark, -} - -#[derive(Clone, Debug, PartialEq)] -pub struct RuntimeSettings { - pub music_volume: f32, - pub platform_theme: RuntimePlatformTheme, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] -pub struct RuntimeSnapshot { - pub user_id: String, - pub version: u32, - pub saved_at_micros: i64, - pub bottom_tab: String, - pub game_state_json: String, - pub current_story_json: Option, - pub created_at_micros: i64, - pub updated_at_micros: i64, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] -pub struct RuntimeSnapshotProcedureResult { - pub ok: bool, - pub record: Option, - pub error_message: Option, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] -pub struct RuntimeSnapshotGetInput { - pub user_id: String, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] -pub struct RuntimeSnapshotUpsertInput { - pub user_id: String, - pub saved_at_micros: i64, - pub bottom_tab: String, - pub game_state_json: String, - pub current_story_json: Option, - pub updated_at_micros: i64, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] -pub struct RuntimeSnapshotDeleteInput { - pub user_id: String, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] -pub struct RuntimeProfileSaveArchiveSnapshot { - pub archive_id: String, - pub user_id: String, - pub world_key: String, - pub owner_user_id: Option, - pub profile_id: Option, - pub world_type: Option, - pub world_name: String, - pub subtitle: String, - pub summary_text: String, - pub cover_image_src: Option, - pub saved_at_micros: i64, - pub bottom_tab: String, - pub game_state_json: String, - pub current_story_json: Option, - pub created_at_micros: i64, - pub updated_at_micros: i64, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] -pub struct RuntimeProfileSaveArchiveProcedureResult { - pub ok: bool, - pub entries: Vec, - pub record: Option, - pub current_snapshot: Option, - pub error_message: Option, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] -pub struct RuntimeProfileSaveArchiveListInput { - pub user_id: String, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] -pub struct RuntimeProfileSaveArchiveResumeInput { - pub user_id: String, - pub world_key: String, -} - -// 浏览历史沿用平台已有的六种世界主题,但独立冻结在 runtime 领域内,避免反向耦合创作域 crate。 -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub enum RuntimeBrowseHistoryThemeMode { - Martial, - Arcane, - Machina, - Tide, - Rift, - Mythic, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] -pub struct RuntimeSettingSnapshot { - pub user_id: String, - pub music_volume: f32, - pub platform_theme: RuntimePlatformTheme, - pub created_at_micros: i64, - pub updated_at_micros: i64, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] -pub struct RuntimeSettingProcedureResult { - pub ok: bool, - pub record: Option, - pub error_message: Option, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] -pub struct RuntimeSettingGetInput { - pub user_id: String, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] -pub struct RuntimeSettingUpsertInput { - pub user_id: String, - pub music_volume: f32, - pub platform_theme: RuntimePlatformTheme, - pub updated_at_micros: i64, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] -pub struct RuntimeBrowseHistorySnapshot { - pub browse_history_id: String, - pub user_id: String, - pub owner_user_id: String, - pub profile_id: String, - pub world_name: String, - pub subtitle: String, - pub summary_text: String, - pub cover_image_src: Option, - pub theme_mode: RuntimeBrowseHistoryThemeMode, - pub author_display_name: String, - pub visited_at_micros: i64, - pub created_at_micros: i64, - pub updated_at_micros: i64, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] -pub struct RuntimeBrowseHistoryProcedureResult { - pub ok: bool, - pub entries: Vec, - pub error_message: Option, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] -pub struct RuntimeBrowseHistoryListInput { - pub user_id: String, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] -pub struct RuntimeBrowseHistoryClearInput { - pub user_id: String, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] -pub struct RuntimeBrowseHistoryWriteInput { - pub owner_user_id: String, - pub profile_id: String, - pub world_name: String, - pub subtitle: Option, - pub summary_text: Option, - pub cover_image_src: Option, - pub theme_mode: Option, - pub author_display_name: Option, - pub visited_at: Option, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] -pub struct RuntimeBrowseHistorySyncInput { - pub user_id: String, - pub entries: Vec, - pub updated_at_micros: i64, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] -pub struct RuntimeProfileDashboardSnapshot { - pub user_id: String, - pub wallet_balance: u64, - pub total_play_time_ms: u64, - pub played_world_count: u32, - pub updated_at_micros: Option, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] -pub struct RuntimeProfileDashboardProcedureResult { - pub ok: bool, - pub record: Option, - pub error_message: Option, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] -pub struct RuntimeProfileDashboardGetInput { - pub user_id: String, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub enum RuntimeProfileWalletLedgerSourceType { - SnapshotSync, - InviteInviterReward, - InviteInviteeReward, - PointsRecharge, - AssetOperationConsume, - AssetOperationRefund, - RedeemCodeReward, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub enum RuntimeProfileRedeemCodeMode { - Public, - Unique, - Private, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub enum RuntimeProfileRechargeProductKind { - Points, - Membership, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub enum RuntimeProfileMembershipStatus { - Normal, - Active, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub enum RuntimeProfileMembershipTier { - Normal, - Month, - Season, - Year, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub enum RuntimeProfileRechargeOrderStatus { - Paid, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] -pub struct RuntimeProfileRechargeProductSnapshot { - pub product_id: String, - pub title: String, - pub price_cents: u64, - pub kind: RuntimeProfileRechargeProductKind, - pub points_amount: u64, - pub bonus_points: u64, - pub duration_days: u32, - pub badge_label: String, - pub description: String, - pub tier: RuntimeProfileMembershipTier, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] -pub struct RuntimeProfileMembershipBenefitSnapshot { - pub benefit_name: String, - pub normal_value: String, - pub month_value: String, - pub season_value: String, - pub year_value: String, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] -pub struct RuntimeProfileMembershipSnapshot { - pub user_id: String, - pub status: RuntimeProfileMembershipStatus, - pub tier: RuntimeProfileMembershipTier, - pub started_at_micros: Option, - pub expires_at_micros: Option, - pub updated_at_micros: Option, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] -pub struct RuntimeProfileRechargeOrderSnapshot { - pub order_id: String, - pub user_id: String, - pub product_id: String, - pub product_title: String, - pub kind: RuntimeProfileRechargeProductKind, - pub amount_cents: u64, - pub status: RuntimeProfileRechargeOrderStatus, - pub payment_channel: String, - pub paid_at_micros: i64, - pub created_at_micros: i64, - pub points_delta: i64, - pub membership_expires_at_micros: Option, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] -pub struct RuntimeProfileRechargeCenterSnapshot { - pub user_id: String, - pub wallet_balance: u64, - pub membership: RuntimeProfileMembershipSnapshot, - pub point_products: Vec, - pub membership_products: Vec, - pub benefits: Vec, - pub latest_order: Option, - pub has_points_recharged: bool, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] -pub struct RuntimeProfileRechargeCenterProcedureResult { - pub ok: bool, - pub record: Option, - pub order: Option, - pub error_message: Option, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] -pub struct RuntimeProfileRechargeCenterGetInput { - pub user_id: String, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] -pub struct RuntimeProfileRechargeOrderCreateInput { - pub user_id: String, - pub product_id: String, - pub payment_channel: String, - pub created_at_micros: i64, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] -pub struct RuntimeProfileWalletLedgerEntrySnapshot { - pub wallet_ledger_id: String, - pub user_id: String, - pub amount_delta: i64, - pub balance_after: u64, - pub source_type: RuntimeProfileWalletLedgerSourceType, - pub created_at_micros: i64, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] -pub struct RuntimeProfileWalletLedgerProcedureResult { - pub ok: bool, - pub entries: Vec, - pub error_message: Option, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] -pub struct RuntimeProfileWalletAdjustmentProcedureResult { - pub ok: bool, - pub record: Option, - pub error_message: Option, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] -pub struct RuntimeProfileWalletLedgerListInput { - pub user_id: String, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] -pub struct RuntimeProfileWalletAdjustmentInput { - pub user_id: String, - pub amount: u64, - pub ledger_id: String, - pub created_at_micros: i64, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] -pub struct RuntimeProfileRewardCodeRedeemInput { - pub user_id: String, - pub code: String, - pub redeemed_at_micros: i64, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] -pub struct RuntimeProfileRewardCodeRedeemSnapshot { - pub wallet_balance: u64, - pub amount_granted: u64, - pub ledger_entry: RuntimeProfileWalletLedgerEntrySnapshot, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] -pub struct RuntimeProfileRewardCodeRedeemProcedureResult { - pub ok: bool, - pub record: Option, - pub error_message: Option, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] -pub struct RuntimeProfileRedeemCodeAdminUpsertInput { - pub admin_user_id: String, - pub code: String, - pub mode: RuntimeProfileRedeemCodeMode, - pub reward_points: u64, - pub max_uses: u32, - pub enabled: bool, - pub allowed_user_ids: Vec, - pub allowed_public_user_codes: Vec, - pub updated_at_micros: i64, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] -pub struct RuntimeProfileRedeemCodeAdminDisableInput { - pub admin_user_id: String, - pub code: String, - pub updated_at_micros: i64, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] -pub struct RuntimeProfileRedeemCodeSnapshot { - pub code: String, - pub mode: RuntimeProfileRedeemCodeMode, - pub reward_points: u64, - pub max_uses: u32, - pub global_used_count: u32, - pub enabled: bool, - pub allowed_user_ids: Vec, - pub created_by: String, - pub created_at_micros: i64, - pub updated_at_micros: i64, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] -pub struct RuntimeProfileRedeemCodeAdminProcedureResult { - pub ok: bool, - pub record: Option, - pub error_message: Option, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] -pub struct RuntimeReferralInviteCenterSnapshot { - pub user_id: String, - pub invite_code: String, - pub invite_link_path: String, - pub invited_count: u32, - pub rewarded_invite_count: u32, - pub today_inviter_reward_count: u32, - pub today_inviter_reward_remaining: u32, - pub reward_points: u64, - pub has_redeemed_code: bool, - pub bound_inviter_user_id: Option, - pub bound_at_micros: Option, - pub updated_at_micros: i64, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] -pub struct RuntimeReferralInviteCenterProcedureResult { - pub ok: bool, - pub record: Option, - pub error_message: Option, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] -pub struct RuntimeReferralInviteCenterGetInput { - pub user_id: String, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] -pub struct RuntimeReferralRedeemInput { - pub user_id: String, - pub invite_code: String, - pub updated_at_micros: i64, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] -pub struct RuntimeReferralRedeemSnapshot { - pub center: RuntimeReferralInviteCenterSnapshot, - pub invitee_reward_granted: bool, - pub inviter_reward_granted: bool, - pub invitee_balance_after: u64, - pub inviter_balance_after: u64, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] -pub struct RuntimeReferralRedeemProcedureResult { - pub ok: bool, - pub record: Option, - pub error_message: Option, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] -pub struct RuntimeProfilePlayedWorldSnapshot { - pub played_world_id: String, - pub user_id: String, - pub world_key: String, - pub owner_user_id: Option, - pub profile_id: Option, - pub world_type: Option, - pub world_title: String, - pub world_subtitle: String, - pub first_played_at_micros: i64, - pub last_played_at_micros: i64, - pub last_observed_play_time_ms: u64, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] -pub struct RuntimeProfilePlayStatsSnapshot { - pub user_id: String, - pub total_play_time_ms: u64, - pub played_works: Vec, - pub updated_at_micros: Option, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] -pub struct RuntimeProfilePlayStatsProcedureResult { - pub ok: bool, - pub record: Option, - pub error_message: Option, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] -pub struct RuntimeProfilePlayStatsGetInput { - pub user_id: String, -} - -#[derive(Clone, Debug, PartialEq, Eq)] -pub enum RuntimeSettingsFieldError { - MissingUserId, -} - -#[derive(Clone, Debug, PartialEq, Eq)] -pub enum RuntimeBrowseHistoryFieldError { - MissingUserId, - TooManyEntries, -} - -#[derive(Clone, Debug, PartialEq, Eq)] -pub enum RuntimeProfileFieldError { - MissingUserId, - MissingLedgerId, - InvalidWalletAmount, - MissingInviteCode, - MissingRedeemCode, - InvalidRedeemCodeReward, - InvalidRedeemCodeMaxUses, - MissingProductId, - MissingWorldKey, - MissingBottomTab, - UnknownRechargeProduct, - InvalidGameStateJson, - InvalidCurrentStoryJson, -} - -#[derive(Clone, Debug, PartialEq)] -pub struct RuntimeBrowseHistoryPreparedEntry { - pub browse_history_id: String, - pub user_id: String, - pub owner_user_id: String, - pub profile_id: String, - pub world_name: String, - pub subtitle: String, - pub summary_text: String, - pub cover_image_src: Option, - pub theme_mode: RuntimeBrowseHistoryThemeMode, - pub author_display_name: String, - pub visited_at_micros: i64, - pub updated_at_micros: i64, -} - -#[derive(Clone, Debug, PartialEq)] -pub struct RuntimeBrowseHistoryRecord { - pub browse_history_id: String, - pub user_id: String, - pub owner_user_id: String, - pub profile_id: String, - pub world_name: String, - pub subtitle: String, - pub summary_text: String, - pub cover_image_src: Option, - pub theme_mode: RuntimeBrowseHistoryThemeMode, - pub author_display_name: String, - pub visited_at: String, - pub visited_at_micros: i64, - pub created_at_micros: i64, - pub updated_at_micros: i64, -} - -impl RuntimePlatformTheme { - pub fn as_str(&self) -> &'static str { - match self { - Self::Light => "light", - Self::Dark => "dark", - } - } - - pub fn from_client_str(value: &str) -> Self { - if value.trim().eq_ignore_ascii_case("dark") { - Self::Dark - } else { - Self::Light - } - } -} - -impl RuntimeBrowseHistoryThemeMode { - pub fn as_str(&self) -> &'static str { - match self { - Self::Martial => "martial", - Self::Arcane => "arcane", - Self::Machina => "machina", - Self::Tide => "tide", - Self::Rift => "rift", - Self::Mythic => "mythic", - } - } - - // 浏览历史主题沿用旧 Node 逻辑:不做严格校验,未知值统一回退到 mythic。 - pub fn from_client_str(value: &str) -> Self { - match value.trim().to_ascii_lowercase().as_str() { - "martial" => Self::Martial, - "arcane" => Self::Arcane, - "machina" => Self::Machina, - "tide" => Self::Tide, - "rift" => Self::Rift, - _ => Self::Mythic, - } - } -} - -impl RuntimeSettings { - pub fn defaults() -> Self { - Self { - music_volume: DEFAULT_MUSIC_VOLUME, - platform_theme: DEFAULT_PLATFORM_THEME, - } - } - - // 与旧 Node 仓储保持一致:音量 clamp 到 0~1,主题除 dark 外统一回退到 light。 - pub fn normalized(music_volume: f32, platform_theme: RuntimePlatformTheme) -> Self { - Self { - music_volume: music_volume.clamp(0.0, 1.0), - platform_theme, - } - } -} - -// 统一把共享必填字符串归一化映射到 runtime 各自的字段错误,避免输入构造函数重复 trim + 判空。 -fn normalize_runtime_settings_user_id( - user_id: String, -) -> Result { - normalize_required_string(user_id).ok_or(RuntimeSettingsFieldError::MissingUserId) -} - -fn normalize_runtime_browse_history_user_id( - user_id: String, -) -> Result { - normalize_required_string(user_id).ok_or(RuntimeBrowseHistoryFieldError::MissingUserId) -} - -fn normalize_runtime_profile_user_id(user_id: String) -> Result { - normalize_required_string(user_id).ok_or(RuntimeProfileFieldError::MissingUserId) -} - -pub fn build_runtime_setting_get_input( - user_id: String, -) -> Result { - let user_id = normalize_runtime_settings_user_id(user_id)?; - Ok(RuntimeSettingGetInput { user_id }) -} - -pub fn build_runtime_setting_upsert_input( - user_id: String, - music_volume: f32, - platform_theme: RuntimePlatformTheme, - updated_at_micros: i64, -) -> Result { - let user_id = normalize_runtime_settings_user_id(user_id)?; - let normalized = RuntimeSettings::normalized(music_volume, platform_theme); - - Ok(RuntimeSettingUpsertInput { - user_id, - music_volume: normalized.music_volume, - platform_theme: normalized.platform_theme, - updated_at_micros, - }) -} - -pub fn build_runtime_setting_record(snapshot: RuntimeSettingSnapshot) -> RuntimeSettingsRecord { - RuntimeSettingsRecord { - user_id: snapshot.user_id, - music_volume: snapshot.music_volume, - platform_theme: snapshot.platform_theme, - created_at_micros: snapshot.created_at_micros, - updated_at_micros: snapshot.updated_at_micros, - } -} - -#[derive(Clone, Debug, PartialEq)] -pub struct RuntimeSettingsRecord { - pub user_id: String, - pub music_volume: f32, - pub platform_theme: RuntimePlatformTheme, - pub created_at_micros: i64, - pub updated_at_micros: i64, -} - -#[derive(Clone, Debug, PartialEq)] -pub struct RuntimeProfileDashboardRecord { - pub user_id: String, - pub wallet_balance: u64, - pub total_play_time_ms: u64, - pub played_world_count: u32, - pub updated_at: Option, - pub updated_at_micros: Option, -} - -#[derive(Clone, Debug, PartialEq)] -pub struct RuntimeProfileWalletLedgerEntryRecord { - pub wallet_ledger_id: String, - pub user_id: String, - pub amount_delta: i64, - pub balance_after: u64, - pub source_type: RuntimeProfileWalletLedgerSourceType, - pub created_at: String, - pub created_at_micros: i64, -} - -#[derive(Clone, Debug, PartialEq)] -pub struct RuntimeProfilePlayedWorldRecord { - pub played_world_id: String, - pub user_id: String, - pub world_key: String, - pub owner_user_id: Option, - pub profile_id: Option, - pub world_type: Option, - pub world_title: String, - pub world_subtitle: String, - pub first_played_at: String, - pub first_played_at_micros: i64, - pub last_played_at: String, - pub last_played_at_micros: i64, - pub last_observed_play_time_ms: u64, -} - -#[derive(Clone, Debug, PartialEq)] -pub struct RuntimeProfilePlayStatsRecord { - pub user_id: String, - pub total_play_time_ms: u64, - pub played_works: Vec, - pub updated_at: Option, - pub updated_at_micros: Option, -} - -#[derive(Clone, Debug, PartialEq)] -pub struct RuntimeProfileRechargeProductRecord { - pub product_id: String, - pub title: String, - pub price_cents: u64, - pub kind: RuntimeProfileRechargeProductKind, - pub points_amount: u64, - pub bonus_points: u64, - pub duration_days: u32, - pub badge_label: String, - pub description: String, - pub tier: RuntimeProfileMembershipTier, -} - -#[derive(Clone, Debug, PartialEq)] -pub struct RuntimeProfileMembershipBenefitRecord { - pub benefit_name: String, - pub normal_value: String, - pub month_value: String, - pub season_value: String, - pub year_value: String, -} - -#[derive(Clone, Debug, PartialEq)] -pub struct RuntimeProfileMembershipRecord { - pub user_id: String, - pub status: RuntimeProfileMembershipStatus, - pub tier: RuntimeProfileMembershipTier, - pub started_at: Option, - pub started_at_micros: Option, - pub expires_at: Option, - pub expires_at_micros: Option, - pub updated_at: Option, - pub updated_at_micros: Option, -} - -#[derive(Clone, Debug, PartialEq)] -pub struct RuntimeProfileRechargeOrderRecord { - pub order_id: String, - pub user_id: String, - pub product_id: String, - pub product_title: String, - pub kind: RuntimeProfileRechargeProductKind, - pub amount_cents: u64, - pub status: RuntimeProfileRechargeOrderStatus, - pub payment_channel: String, - pub paid_at: String, - pub paid_at_micros: i64, - pub created_at: String, - pub created_at_micros: i64, - pub points_delta: i64, - pub membership_expires_at: Option, - pub membership_expires_at_micros: Option, -} - -#[derive(Clone, Debug, PartialEq)] -pub struct RuntimeProfileRechargeCenterRecord { - pub user_id: String, - pub wallet_balance: u64, - pub membership: RuntimeProfileMembershipRecord, - pub point_products: Vec, - pub membership_products: Vec, - pub benefits: Vec, - pub latest_order: Option, - pub has_points_recharged: bool, -} - -#[derive(Clone, Debug, PartialEq)] -pub struct RuntimeProfileRewardCodeRedeemRecord { - pub wallet_balance: u64, - pub amount_granted: u64, - pub ledger_entry: RuntimeProfileWalletLedgerEntryRecord, -} - -#[derive(Clone, Debug, PartialEq)] -pub struct RuntimeProfileRedeemCodeRecord { - pub code: String, - pub mode: RuntimeProfileRedeemCodeMode, - pub reward_points: u64, - pub max_uses: u32, - pub global_used_count: u32, - pub enabled: bool, - pub allowed_user_ids: Vec, - pub created_by: String, - pub created_at: String, - pub created_at_micros: i64, - pub updated_at: String, - pub updated_at_micros: i64, -} - -#[derive(Clone, Debug, PartialEq)] -pub struct RuntimeReferralInviteCenterRecord { - pub user_id: String, - pub invite_code: String, - pub invite_link_path: String, - pub invited_count: u32, - pub rewarded_invite_count: u32, - pub today_inviter_reward_count: u32, - pub today_inviter_reward_remaining: u32, - pub reward_points: u64, - pub has_redeemed_code: bool, - pub bound_inviter_user_id: Option, - pub bound_at: Option, - pub bound_at_micros: Option, - pub updated_at: String, - pub updated_at_micros: i64, -} - -#[derive(Clone, Debug, PartialEq)] -pub struct RuntimeReferralRedeemRecord { - pub center: RuntimeReferralInviteCenterRecord, - pub invitee_reward_granted: bool, - pub inviter_reward_granted: bool, - pub invitee_balance_after: u64, - pub inviter_balance_after: u64, -} - -#[derive(Clone, Debug, PartialEq)] -pub struct RuntimeSnapshotRecord { - pub user_id: String, - pub version: u32, - pub saved_at: String, - pub saved_at_micros: i64, - pub bottom_tab: String, - pub game_state: Value, - pub current_story: Option, - pub game_state_json: String, - pub current_story_json: Option, - pub created_at_micros: i64, - pub updated_at_micros: i64, -} - -#[derive(Clone, Debug, PartialEq)] -pub struct RuntimeProfileSaveArchiveRecord { - pub archive_id: String, - pub user_id: String, - pub world_key: String, - pub owner_user_id: Option, - pub profile_id: Option, - pub world_type: Option, - pub world_name: String, - pub subtitle: String, - pub summary_text: String, - pub cover_image_src: Option, - pub saved_at: String, - pub saved_at_micros: i64, - pub bottom_tab: String, - pub game_state: Value, - pub current_story: Option, - pub game_state_json: String, - pub current_story_json: Option, - pub created_at_micros: i64, - pub updated_at_micros: i64, -} - -pub fn build_runtime_browse_history_list_input( - user_id: String, -) -> Result { - let user_id = normalize_runtime_browse_history_user_id(user_id)?; - Ok(RuntimeBrowseHistoryListInput { user_id }) -} - -pub fn build_runtime_profile_dashboard_get_input( - user_id: String, -) -> Result { - let user_id = normalize_runtime_profile_user_id(user_id)?; - Ok(RuntimeProfileDashboardGetInput { user_id }) -} - -pub fn build_runtime_profile_wallet_ledger_list_input( - user_id: String, -) -> Result { - let user_id = normalize_runtime_profile_user_id(user_id)?; - Ok(RuntimeProfileWalletLedgerListInput { user_id }) -} - -pub fn build_runtime_profile_wallet_adjustment_input( - user_id: String, - amount: u64, - ledger_id: String, - created_at_micros: i64, -) -> Result { - let user_id = normalize_runtime_profile_user_id(user_id)?; - let ledger_id = - normalize_required_string(ledger_id).ok_or(RuntimeProfileFieldError::MissingLedgerId)?; - if amount == 0 || amount > i64::MAX as u64 { - return Err(RuntimeProfileFieldError::InvalidWalletAmount); - } - Ok(RuntimeProfileWalletAdjustmentInput { - user_id, - amount, - ledger_id, - created_at_micros, - }) -} - -pub fn build_runtime_profile_recharge_center_get_input( - user_id: String, -) -> Result { - let user_id = normalize_runtime_profile_user_id(user_id)?; - Ok(RuntimeProfileRechargeCenterGetInput { user_id }) -} - -pub fn build_runtime_profile_recharge_order_create_input( - user_id: String, - product_id: String, - payment_channel: String, - created_at_micros: i64, -) -> Result { - let user_id = normalize_runtime_profile_user_id(user_id)?; - let product_id = - normalize_required_string(product_id).ok_or(RuntimeProfileFieldError::MissingProductId)?; - if runtime_profile_recharge_product_by_id(&product_id).is_none() { - return Err(RuntimeProfileFieldError::UnknownRechargeProduct); - } - let payment_channel = normalize_required_string(payment_channel) - .unwrap_or_else(|| PROFILE_RECHARGE_PAYMENT_CHANNEL_MOCK.to_string()); - - Ok(RuntimeProfileRechargeOrderCreateInput { - user_id, - product_id, - payment_channel, - created_at_micros, - }) -} - -pub fn build_runtime_referral_invite_center_get_input( - user_id: String, -) -> Result { - let user_id = normalize_runtime_profile_user_id(user_id)?; - Ok(RuntimeReferralInviteCenterGetInput { user_id }) -} - -pub fn build_runtime_referral_redeem_input( - user_id: String, - invite_code: String, - updated_at_micros: i64, -) -> Result { - let user_id = normalize_runtime_profile_user_id(user_id)?; - let invite_code = - normalize_invite_code(invite_code).ok_or(RuntimeProfileFieldError::MissingInviteCode)?; - Ok(RuntimeReferralRedeemInput { - user_id, - invite_code, - updated_at_micros, - }) -} - -pub fn build_runtime_profile_reward_code_redeem_input( - user_id: String, - code: String, - redeemed_at_micros: i64, -) -> Result { - let user_id = normalize_runtime_profile_user_id(user_id)?; - let code = normalize_redeem_code(code).ok_or(RuntimeProfileFieldError::MissingRedeemCode)?; - Ok(RuntimeProfileRewardCodeRedeemInput { - user_id, - code, - redeemed_at_micros, - }) -} - -pub fn build_runtime_profile_redeem_code_admin_upsert_input( - admin_user_id: String, - code: String, - mode: RuntimeProfileRedeemCodeMode, - reward_points: u64, - max_uses: u32, - enabled: bool, - allowed_user_ids: Vec, - allowed_public_user_codes: Vec, - updated_at_micros: i64, -) -> Result { - let admin_user_id = normalize_runtime_profile_user_id(admin_user_id)?; - let code = normalize_redeem_code(code).ok_or(RuntimeProfileFieldError::MissingRedeemCode)?; - if reward_points == 0 { - return Err(RuntimeProfileFieldError::InvalidRedeemCodeReward); - } - if max_uses == 0 { - return Err(RuntimeProfileFieldError::InvalidRedeemCodeMaxUses); - } - - Ok(RuntimeProfileRedeemCodeAdminUpsertInput { - admin_user_id, - code, - mode, - reward_points, - max_uses, - enabled, - allowed_user_ids: allowed_user_ids - .into_iter() - .filter_map(|value| normalize_optional_string(Some(value))) - .collect(), - allowed_public_user_codes: allowed_public_user_codes - .into_iter() - .filter_map(|value| normalize_optional_string(Some(value))) - .collect(), - updated_at_micros, - }) -} - -pub fn build_runtime_profile_redeem_code_admin_disable_input( - admin_user_id: String, - code: String, - updated_at_micros: i64, -) -> Result { - let admin_user_id = normalize_runtime_profile_user_id(admin_user_id)?; - let code = normalize_redeem_code(code).ok_or(RuntimeProfileFieldError::MissingRedeemCode)?; - Ok(RuntimeProfileRedeemCodeAdminDisableInput { - admin_user_id, - code, - updated_at_micros, - }) -} - -pub fn build_runtime_profile_play_stats_get_input( - user_id: String, -) -> Result { - let user_id = normalize_runtime_profile_user_id(user_id)?; - Ok(RuntimeProfilePlayStatsGetInput { user_id }) -} - -pub fn build_runtime_snapshot_get_input( - user_id: String, -) -> Result { - let user_id = normalize_runtime_profile_user_id(user_id)?; - Ok(RuntimeSnapshotGetInput { user_id }) -} - -pub fn build_runtime_snapshot_delete_input( - user_id: String, -) -> Result { - let user_id = normalize_runtime_profile_user_id(user_id)?; - Ok(RuntimeSnapshotDeleteInput { user_id }) -} - -pub fn build_runtime_profile_save_archive_list_input( - user_id: String, -) -> Result { - let user_id = normalize_runtime_profile_user_id(user_id)?; - Ok(RuntimeProfileSaveArchiveListInput { user_id }) -} - -pub fn build_runtime_profile_save_archive_resume_input( - user_id: String, - world_key: String, -) -> Result { - let user_id = normalize_runtime_profile_user_id(user_id)?; - let world_key = - normalize_required_string(world_key).ok_or(RuntimeProfileFieldError::MissingWorldKey)?; - Ok(RuntimeProfileSaveArchiveResumeInput { user_id, world_key }) -} - -pub fn build_runtime_browse_history_clear_input( - user_id: String, -) -> Result { - let user_id = normalize_runtime_browse_history_user_id(user_id)?; - Ok(RuntimeBrowseHistoryClearInput { user_id }) -} - -pub fn build_runtime_snapshot_upsert_input( - user_id: String, - saved_at_micros: i64, - bottom_tab: String, - game_state: Value, - current_story: Option, - updated_at_micros: i64, -) -> Result { - let user_id = normalize_runtime_profile_user_id(user_id)?; - let bottom_tab = - normalize_bottom_tab(bottom_tab).ok_or(RuntimeProfileFieldError::MissingBottomTab)?; - let game_state_json = serde_json::to_string(&game_state) - .map_err(|_| RuntimeProfileFieldError::InvalidGameStateJson)?; - let current_story_json = normalize_current_story_json(current_story)?; - - Ok(RuntimeSnapshotUpsertInput { - user_id, - saved_at_micros, - bottom_tab, - game_state_json, - current_story_json, - updated_at_micros, - }) -} - -pub fn build_runtime_browse_history_sync_input( - user_id: String, - entries: Vec, - updated_at_micros: i64, -) -> Result { - let user_id = normalize_runtime_browse_history_user_id(user_id)?; - if entries.len() > MAX_BROWSE_HISTORY_BATCH_SIZE { - return Err(RuntimeBrowseHistoryFieldError::TooManyEntries); - } - - let mut normalized_entries = Vec::with_capacity(entries.len()); - for entry in entries { - let Some(owner_user_id) = normalize_required_string(entry.owner_user_id) else { - continue; - }; - let Some(profile_id) = normalize_required_string(entry.profile_id) else { - continue; - }; - let Some(world_name) = normalize_required_string(entry.world_name) else { - continue; - }; - // 与旧 Node 仓储保持一致:单条缺少关键字段时静默过滤,不让整批请求失败。 - let visited_at_micros = entry - .visited_at - .as_deref() - .and_then(parse_utc_rfc3339_to_micros) - .unwrap_or(updated_at_micros); - - normalized_entries.push(RuntimeBrowseHistoryWriteInput { - owner_user_id, - profile_id, - world_name, - subtitle: normalize_optional_string(entry.subtitle), - summary_text: normalize_optional_string(entry.summary_text), - cover_image_src: normalize_optional_string(entry.cover_image_src), - theme_mode: normalize_optional_string(entry.theme_mode), - author_display_name: normalize_optional_string(entry.author_display_name), - // 统一把 visitedAt 收口成 RFC3339,避免后续排序与回包格式继续漂移。 - visited_at: Some(format_utc_micros(visited_at_micros)), - }); - } - - Ok(RuntimeBrowseHistorySyncInput { - user_id, - entries: normalized_entries, - updated_at_micros, - }) -} - -pub fn prepare_runtime_browse_history_entries( - input: RuntimeBrowseHistorySyncInput, -) -> Result, RuntimeBrowseHistoryFieldError> { - let validated_input = build_runtime_browse_history_sync_input( - input.user_id, - input.entries, - input.updated_at_micros, - )?; - let mut prepared_entries = validated_input - .entries - .into_iter() - .map(|entry| { - let visited_at_micros = entry - .visited_at - .as_deref() - .and_then(parse_utc_rfc3339_to_micros) - .unwrap_or(validated_input.updated_at_micros); - - RuntimeBrowseHistoryPreparedEntry { - browse_history_id: build_runtime_browse_history_id( - &validated_input.user_id, - &entry.owner_user_id, - &entry.profile_id, - ), - user_id: validated_input.user_id.clone(), - owner_user_id: entry.owner_user_id, - profile_id: entry.profile_id, - world_name: entry.world_name, - subtitle: entry.subtitle.unwrap_or_default(), - summary_text: entry.summary_text.unwrap_or_default(), - cover_image_src: entry.cover_image_src, - theme_mode: RuntimeBrowseHistoryThemeMode::from_client_str( - entry.theme_mode.as_deref().unwrap_or("mythic"), - ), - author_display_name: entry - .author_display_name - .unwrap_or_else(|| DEFAULT_BROWSE_HISTORY_AUTHOR_DISPLAY_NAME.to_string()), - visited_at_micros, - updated_at_micros: validated_input.updated_at_micros, - } - }) - .collect::>(); - - // 与旧 Node 仓储保持一致:先按 visitedAt 倒序,再按 owner/profile 去重,只保留最近一次访问。 - prepared_entries.sort_by(|left, right| { - right - .visited_at_micros - .cmp(&left.visited_at_micros) - .then_with(|| left.browse_history_id.cmp(&right.browse_history_id)) - }); - - let mut seen_ids = HashSet::new(); - prepared_entries.retain(|entry| seen_ids.insert(entry.browse_history_id.clone())); - - Ok(prepared_entries) -} - -pub fn build_runtime_browse_history_record( - snapshot: RuntimeBrowseHistorySnapshot, -) -> RuntimeBrowseHistoryRecord { - RuntimeBrowseHistoryRecord { - browse_history_id: snapshot.browse_history_id, - user_id: snapshot.user_id, - owner_user_id: snapshot.owner_user_id, - profile_id: snapshot.profile_id, - world_name: snapshot.world_name, - subtitle: snapshot.subtitle, - summary_text: snapshot.summary_text, - cover_image_src: snapshot.cover_image_src, - theme_mode: snapshot.theme_mode, - author_display_name: snapshot.author_display_name, - visited_at: format_utc_micros(snapshot.visited_at_micros), - visited_at_micros: snapshot.visited_at_micros, - created_at_micros: snapshot.created_at_micros, - updated_at_micros: snapshot.updated_at_micros, - } -} - -pub fn build_runtime_profile_dashboard_record( - snapshot: RuntimeProfileDashboardSnapshot, -) -> RuntimeProfileDashboardRecord { - RuntimeProfileDashboardRecord { - user_id: snapshot.user_id, - wallet_balance: snapshot.wallet_balance, - total_play_time_ms: snapshot.total_play_time_ms, - played_world_count: snapshot.played_world_count, - updated_at: snapshot.updated_at_micros.map(format_utc_micros), - updated_at_micros: snapshot.updated_at_micros, - } -} - -pub fn build_runtime_profile_wallet_ledger_entry_record( - snapshot: RuntimeProfileWalletLedgerEntrySnapshot, -) -> RuntimeProfileWalletLedgerEntryRecord { - RuntimeProfileWalletLedgerEntryRecord { - wallet_ledger_id: snapshot.wallet_ledger_id, - user_id: snapshot.user_id, - amount_delta: snapshot.amount_delta, - balance_after: snapshot.balance_after, - source_type: snapshot.source_type, - created_at: format_utc_micros(snapshot.created_at_micros), - created_at_micros: snapshot.created_at_micros, - } -} - -pub fn build_runtime_profile_recharge_center_record( - snapshot: RuntimeProfileRechargeCenterSnapshot, -) -> RuntimeProfileRechargeCenterRecord { - RuntimeProfileRechargeCenterRecord { - user_id: snapshot.user_id, - wallet_balance: snapshot.wallet_balance, - membership: build_runtime_profile_membership_record(snapshot.membership), - point_products: snapshot - .point_products - .into_iter() - .map(build_runtime_profile_recharge_product_record) - .collect(), - membership_products: snapshot - .membership_products - .into_iter() - .map(build_runtime_profile_recharge_product_record) - .collect(), - benefits: snapshot - .benefits - .into_iter() - .map(build_runtime_profile_membership_benefit_record) - .collect(), - latest_order: snapshot - .latest_order - .map(build_runtime_profile_recharge_order_record), - has_points_recharged: snapshot.has_points_recharged, - } -} - -pub fn build_runtime_profile_recharge_product_record( - snapshot: RuntimeProfileRechargeProductSnapshot, -) -> RuntimeProfileRechargeProductRecord { - RuntimeProfileRechargeProductRecord { - product_id: snapshot.product_id, - title: snapshot.title, - price_cents: snapshot.price_cents, - kind: snapshot.kind, - points_amount: snapshot.points_amount, - bonus_points: snapshot.bonus_points, - duration_days: snapshot.duration_days, - badge_label: snapshot.badge_label, - description: snapshot.description, - tier: snapshot.tier, - } -} - -pub fn build_runtime_profile_membership_benefit_record( - snapshot: RuntimeProfileMembershipBenefitSnapshot, -) -> RuntimeProfileMembershipBenefitRecord { - RuntimeProfileMembershipBenefitRecord { - benefit_name: snapshot.benefit_name, - normal_value: snapshot.normal_value, - month_value: snapshot.month_value, - season_value: snapshot.season_value, - year_value: snapshot.year_value, - } -} - -pub fn build_runtime_profile_membership_record( - snapshot: RuntimeProfileMembershipSnapshot, -) -> RuntimeProfileMembershipRecord { - RuntimeProfileMembershipRecord { - user_id: snapshot.user_id, - status: snapshot.status, - tier: snapshot.tier, - started_at: snapshot.started_at_micros.map(format_utc_micros), - started_at_micros: snapshot.started_at_micros, - expires_at: snapshot.expires_at_micros.map(format_utc_micros), - expires_at_micros: snapshot.expires_at_micros, - updated_at: snapshot.updated_at_micros.map(format_utc_micros), - updated_at_micros: snapshot.updated_at_micros, - } -} - -pub fn build_runtime_profile_recharge_order_record( - snapshot: RuntimeProfileRechargeOrderSnapshot, -) -> RuntimeProfileRechargeOrderRecord { - RuntimeProfileRechargeOrderRecord { - order_id: snapshot.order_id, - user_id: snapshot.user_id, - product_id: snapshot.product_id, - product_title: snapshot.product_title, - kind: snapshot.kind, - amount_cents: snapshot.amount_cents, - status: snapshot.status, - payment_channel: snapshot.payment_channel, - paid_at: format_utc_micros(snapshot.paid_at_micros), - paid_at_micros: snapshot.paid_at_micros, - created_at: format_utc_micros(snapshot.created_at_micros), - created_at_micros: snapshot.created_at_micros, - points_delta: snapshot.points_delta, - membership_expires_at: snapshot.membership_expires_at_micros.map(format_utc_micros), - membership_expires_at_micros: snapshot.membership_expires_at_micros, - } -} - -pub fn build_runtime_referral_invite_center_record( - snapshot: RuntimeReferralInviteCenterSnapshot, -) -> RuntimeReferralInviteCenterRecord { - RuntimeReferralInviteCenterRecord { - user_id: snapshot.user_id, - invite_code: snapshot.invite_code, - invite_link_path: snapshot.invite_link_path, - invited_count: snapshot.invited_count, - rewarded_invite_count: snapshot.rewarded_invite_count, - today_inviter_reward_count: snapshot.today_inviter_reward_count, - today_inviter_reward_remaining: snapshot.today_inviter_reward_remaining, - reward_points: snapshot.reward_points, - has_redeemed_code: snapshot.has_redeemed_code, - bound_inviter_user_id: snapshot.bound_inviter_user_id, - bound_at: snapshot.bound_at_micros.map(format_utc_micros), - bound_at_micros: snapshot.bound_at_micros, - updated_at: format_utc_micros(snapshot.updated_at_micros), - updated_at_micros: snapshot.updated_at_micros, - } -} - -pub fn build_runtime_referral_redeem_record( - snapshot: RuntimeReferralRedeemSnapshot, -) -> RuntimeReferralRedeemRecord { - RuntimeReferralRedeemRecord { - center: build_runtime_referral_invite_center_record(snapshot.center), - invitee_reward_granted: snapshot.invitee_reward_granted, - inviter_reward_granted: snapshot.inviter_reward_granted, - invitee_balance_after: snapshot.invitee_balance_after, - inviter_balance_after: snapshot.inviter_balance_after, - } -} - -pub fn build_runtime_profile_reward_code_redeem_record( - snapshot: RuntimeProfileRewardCodeRedeemSnapshot, -) -> RuntimeProfileRewardCodeRedeemRecord { - RuntimeProfileRewardCodeRedeemRecord { - wallet_balance: snapshot.wallet_balance, - amount_granted: snapshot.amount_granted, - ledger_entry: build_runtime_profile_wallet_ledger_entry_record(snapshot.ledger_entry), - } -} - -pub fn build_runtime_profile_redeem_code_record( - snapshot: RuntimeProfileRedeemCodeSnapshot, -) -> RuntimeProfileRedeemCodeRecord { - RuntimeProfileRedeemCodeRecord { - code: snapshot.code, - mode: snapshot.mode, - reward_points: snapshot.reward_points, - max_uses: snapshot.max_uses, - global_used_count: snapshot.global_used_count, - enabled: snapshot.enabled, - allowed_user_ids: snapshot.allowed_user_ids, - created_by: snapshot.created_by, - created_at: format_utc_micros(snapshot.created_at_micros), - created_at_micros: snapshot.created_at_micros, - updated_at: format_utc_micros(snapshot.updated_at_micros), - updated_at_micros: snapshot.updated_at_micros, - } -} - -pub fn build_runtime_profile_played_world_record( - snapshot: RuntimeProfilePlayedWorldSnapshot, -) -> RuntimeProfilePlayedWorldRecord { - RuntimeProfilePlayedWorldRecord { - played_world_id: snapshot.played_world_id, - user_id: snapshot.user_id, - world_key: snapshot.world_key, - owner_user_id: snapshot.owner_user_id, - profile_id: snapshot.profile_id, - world_type: snapshot.world_type, - world_title: snapshot.world_title, - world_subtitle: snapshot.world_subtitle, - first_played_at: format_utc_micros(snapshot.first_played_at_micros), - first_played_at_micros: snapshot.first_played_at_micros, - last_played_at: format_utc_micros(snapshot.last_played_at_micros), - last_played_at_micros: snapshot.last_played_at_micros, - last_observed_play_time_ms: snapshot.last_observed_play_time_ms, - } -} - -pub fn build_runtime_profile_play_stats_record( - snapshot: RuntimeProfilePlayStatsSnapshot, -) -> RuntimeProfilePlayStatsRecord { - RuntimeProfilePlayStatsRecord { - user_id: snapshot.user_id, - total_play_time_ms: snapshot.total_play_time_ms, - played_works: snapshot - .played_works - .into_iter() - .map(build_runtime_profile_played_world_record) - .collect(), - updated_at: snapshot.updated_at_micros.map(format_utc_micros), - updated_at_micros: snapshot.updated_at_micros, - } -} - -pub fn build_runtime_snapshot_record( - snapshot: RuntimeSnapshot, -) -> Result { - let game_state = serde_json::from_str::(&snapshot.game_state_json) - .map_err(|_| RuntimeProfileFieldError::InvalidGameStateJson)?; - let current_story = parse_optional_json_value( - snapshot.current_story_json.as_deref(), - RuntimeProfileFieldError::InvalidCurrentStoryJson, - )?; - - Ok(RuntimeSnapshotRecord { - user_id: snapshot.user_id, - version: snapshot.version, - saved_at: format_utc_micros(snapshot.saved_at_micros), - saved_at_micros: snapshot.saved_at_micros, - bottom_tab: snapshot.bottom_tab, - game_state, - current_story, - game_state_json: snapshot.game_state_json, - current_story_json: snapshot.current_story_json, - created_at_micros: snapshot.created_at_micros, - updated_at_micros: snapshot.updated_at_micros, - }) -} - -pub fn build_runtime_profile_save_archive_record( - snapshot: RuntimeProfileSaveArchiveSnapshot, -) -> Result { - let game_state = serde_json::from_str::(&snapshot.game_state_json) - .map_err(|_| RuntimeProfileFieldError::InvalidGameStateJson)?; - let current_story = parse_optional_json_value( - snapshot.current_story_json.as_deref(), - RuntimeProfileFieldError::InvalidCurrentStoryJson, - )?; - - Ok(RuntimeProfileSaveArchiveRecord { - archive_id: snapshot.archive_id, - user_id: snapshot.user_id, - world_key: snapshot.world_key, - owner_user_id: snapshot.owner_user_id, - profile_id: snapshot.profile_id, - world_type: snapshot.world_type, - world_name: snapshot.world_name, - subtitle: snapshot.subtitle, - summary_text: snapshot.summary_text, - cover_image_src: snapshot.cover_image_src, - saved_at: format_utc_micros(snapshot.saved_at_micros), - saved_at_micros: snapshot.saved_at_micros, - bottom_tab: snapshot.bottom_tab, - game_state, - current_story, - game_state_json: snapshot.game_state_json, - current_story_json: snapshot.current_story_json, - created_at_micros: snapshot.created_at_micros, - updated_at_micros: snapshot.updated_at_micros, - }) -} - -pub fn build_runtime_browse_history_id( - user_id: &str, - owner_user_id: &str, - profile_id: &str, -) -> String { - format!("{user_id}:{owner_user_id}:{profile_id}") -} - pub fn format_utc_micros(micros: i64) -> String { let timestamp = OffsetDateTime::from_unix_timestamp_nanos(i128::from(micros) * 1_000) .unwrap_or(OffsetDateTime::UNIX_EPOCH); format_shared_rfc3339(timestamp).unwrap_or_else(|_| "1970-01-01T00:00:00Z".to_string()) } -fn parse_utc_rfc3339_to_micros(value: &str) -> Option { - let trimmed = value.trim(); - if trimmed.is_empty() { - return None; - } - - let nanos = parse_shared_rfc3339(trimmed).ok()?.unix_timestamp_nanos(); - i64::try_from(nanos / 1_000).ok() -} - -fn normalize_bottom_tab(value: String) -> Option { - let trimmed = normalize_required_string(value)?; - let normalized = match trimmed.as_str() { - "character" | "inventory" => trimmed, - _ => "adventure".to_string(), - }; - - Some(normalized) -} - -fn normalize_current_story_json( - current_story: Option, -) -> Result, RuntimeProfileFieldError> { - let Some(current_story) = current_story else { - return Ok(None); - }; - if !current_story.is_object() { - return Ok(None); - } - - serde_json::to_string(¤t_story) - .map(Some) - .map_err(|_| RuntimeProfileFieldError::InvalidCurrentStoryJson) -} - -fn parse_optional_json_value( - raw: Option<&str>, - error: RuntimeProfileFieldError, -) -> Result, RuntimeProfileFieldError> { - match raw.map(str::trim).filter(|value| !value.is_empty()) { - Some(value) => serde_json::from_str::(value) - .map(Some) - .map_err(|_| error), - None => Ok(None), - } -} - -impl std::fmt::Display for RuntimeSettingsFieldError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Self::MissingUserId => f.write_str("runtime_setting.user_id 不能为空"), - } - } -} - -impl std::fmt::Display for RuntimeBrowseHistoryFieldError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Self::MissingUserId => f.write_str("browse_history.user_id 不能为空"), - Self::TooManyEntries => write!( - f, - "browse_history.entries 单次最多只允许 {} 条", - MAX_BROWSE_HISTORY_BATCH_SIZE - ), - } - } -} - -impl RuntimeProfileWalletLedgerSourceType { - pub fn as_str(&self) -> &'static str { - match self { - Self::SnapshotSync => "snapshot_sync", - Self::InviteInviterReward => "invite_inviter_reward", - Self::InviteInviteeReward => "invite_invitee_reward", - Self::PointsRecharge => "points_recharge", - Self::AssetOperationConsume => "asset_operation_consume", - Self::AssetOperationRefund => "asset_operation_refund", - Self::RedeemCodeReward => "redeem_code_reward", - } - } -} - -impl RuntimeProfileRedeemCodeMode { - pub fn as_str(&self) -> &'static str { - match self { - Self::Public => "public", - Self::Unique => "unique", - Self::Private => "private", - } - } -} - -impl RuntimeProfileRechargeProductKind { - pub fn as_str(&self) -> &'static str { - match self { - Self::Points => "points", - Self::Membership => "membership", - } - } -} - -impl RuntimeProfileMembershipStatus { - pub fn as_str(&self) -> &'static str { - match self { - Self::Normal => "normal", - Self::Active => "active", - } - } -} - -impl RuntimeProfileMembershipTier { - pub fn as_str(&self) -> &'static str { - match self { - Self::Normal => "normal", - Self::Month => "month", - Self::Season => "season", - Self::Year => "year", - } - } -} - -impl RuntimeProfileRechargeOrderStatus { - pub fn as_str(&self) -> &'static str { - match self { - Self::Paid => "paid", - } - } -} - pub fn runtime_profile_recharge_point_products() -> Vec { vec![ build_points_recharge_product( @@ -1938,49 +190,6 @@ fn build_membership_recharge_product( } } -pub fn normalize_invite_code(value: String) -> Option { - let normalized = value - .trim() - .chars() - .filter(|character| character.is_ascii_alphanumeric()) - .map(|character| character.to_ascii_uppercase()) - .collect::(); - - if normalized.is_empty() { - None - } else { - Some(normalized) - } -} - -pub fn normalize_redeem_code(value: String) -> Option { - normalize_invite_code(value) -} - -impl std::fmt::Display for RuntimeProfileFieldError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Self::MissingUserId => f.write_str("profile.user_id 不能为空"), - Self::MissingLedgerId => f.write_str("profile.wallet_ledger_id 不能为空"), - Self::InvalidWalletAmount => f.write_str("profile.wallet_amount 必须大于 0"), - Self::MissingInviteCode => f.write_str("referral.invite_code 不能为空"), - Self::MissingRedeemCode => f.write_str("兑换码不能为空"), - Self::InvalidRedeemCodeReward => f.write_str("兑换码奖励无效"), - Self::InvalidRedeemCodeMaxUses => f.write_str("兑换次数必须大于 0"), - Self::MissingProductId => f.write_str("recharge.product_id 不能为空"), - Self::MissingWorldKey => f.write_str("profile.world_key 不能为空"), - Self::MissingBottomTab => f.write_str("runtime_snapshot.bottom_tab 不能为空"), - Self::UnknownRechargeProduct => f.write_str("recharge.product_id 不存在"), - Self::InvalidGameStateJson => { - f.write_str("runtime_snapshot.game_state 必须是合法 JSON") - } - Self::InvalidCurrentStoryJson => { - f.write_str("runtime_snapshot.current_story 必须是合法 JSON object 或 null") - } - } - } -} - #[cfg(test)] mod tests { use super::*; @@ -2281,4 +490,161 @@ mod tests { assert_eq!(error, RuntimeProfileFieldError::UnknownRechargeProduct); } + + #[test] + fn runtime_profile_identity_helpers_keep_existing_key_shape() { + assert_eq!( + build_runtime_profile_played_world_id(" user-1 ", " custom:world "), + "user-1:custom:world" + ); + assert_eq!( + build_runtime_profile_snapshot_wallet_ledger_id(" user-1 ", 100, 30), + "user-1:100:30" + ); + assert_eq!( + build_runtime_profile_recharge_wallet_ledger_id("user-1", 200, "points_60"), + "user-1:200:points_60" + ); + assert_eq!( + build_runtime_profile_recharge_order_id("user-1", 200, "points_60"), + "recharge:user-1:200:points_60" + ); + assert_eq!( + build_runtime_profile_redeem_code_usage_id("GIFT", "user-1", 300, 2), + "redeem:GIFT:user-1:300:2" + ); + assert_eq!( + build_runtime_profile_redeem_code_ledger_id("redeem:GIFT:user-1:300:2"), + "redeem:GIFT:user-1:300:2:ledger" + ); + } + + #[test] + fn runtime_profile_membership_purchase_extends_from_active_expiry() { + let update = + resolve_runtime_profile_membership_purchase_update(Some(10), Some(200), 100, 30); + + assert_eq!(update.started_at_micros, 10); + assert_eq!( + update.expires_at_micros, + 200 + 30 * PROFILE_RUNTIME_DAY_MICROS + ); + + let expired_update = + resolve_runtime_profile_membership_purchase_update(Some(10), Some(80), 100, 1); + assert_eq!(expired_update.started_at_micros, 10); + assert_eq!( + expired_update.expires_at_micros, + 100 + PROFILE_RUNTIME_DAY_MICROS + ); + } + + #[test] + fn runtime_profile_wallet_balance_calculation_guards_edges() { + assert_eq!( + convert_runtime_profile_wallet_unsigned_delta(8).expect("small amount should convert"), + 8 + ); + assert_eq!( + convert_runtime_profile_wallet_unsigned_delta(i64::MAX as u64 + 1) + .expect_err("oversized amount should fail"), + RuntimeProfileFieldError::WalletAmountOverflow + ); + assert_eq!( + calculate_runtime_profile_wallet_balance(10, 5).expect("positive delta should add"), + 15 + ); + assert_eq!( + calculate_runtime_profile_wallet_balance(10, -4) + .expect("negative delta should subtract"), + 6 + ); + assert_eq!( + calculate_runtime_profile_wallet_balance(3, -4).expect_err("overspend should fail"), + RuntimeProfileFieldError::InsufficientWalletBalance + ); + } + + #[test] + fn runtime_profile_redeem_code_usage_validation_matches_modes() { + let base = RuntimeProfileRedeemCodeSnapshot { + code: "GIFT".to_string(), + mode: RuntimeProfileRedeemCodeMode::Public, + reward_points: 30, + max_uses: 2, + global_used_count: 0, + enabled: true, + allowed_user_ids: Vec::new(), + created_by: "admin".to_string(), + created_at_micros: 1, + updated_at_micros: 1, + }; + + validate_runtime_profile_redeem_code_usage(&base, "user-1", 1) + .expect("public code under per-user limit should pass"); + assert_eq!( + validate_runtime_profile_redeem_code_usage(&base, "user-1", 2) + .expect_err("public code over per-user limit should fail"), + RuntimeProfileFieldError::RedeemCodeUsesExhausted + ); + + let private = RuntimeProfileRedeemCodeSnapshot { + mode: RuntimeProfileRedeemCodeMode::Private, + allowed_user_ids: vec!["user-2".to_string()], + ..base.clone() + }; + assert_eq!( + validate_runtime_profile_redeem_code_usage(&private, "user-1", 0) + .expect_err("private code should check allow list"), + RuntimeProfileFieldError::RedeemCodeNotAllowedForUser + ); + + let disabled = RuntimeProfileRedeemCodeSnapshot { + enabled: false, + ..base + }; + assert_eq!( + validate_runtime_profile_redeem_code_usage(&disabled, "user-1", 0) + .expect_err("disabled code should fail"), + RuntimeProfileFieldError::RedeemCodeDisabled + ); + } + + #[test] + fn runtime_save_checkpoint_update_rejects_session_mismatch() { + let existing = RuntimeSnapshotRecord { + user_id: "user-1".to_string(), + version: SAVE_SNAPSHOT_VERSION, + saved_at: "2026-04-29T00:00:00Z".to_string(), + saved_at_micros: 1, + bottom_tab: "story".to_string(), + game_state: serde_json::json!({ + "runtimeSessionId": "session-old", + "runtimeStats": { + "playTimeMs": 10, + "lastPlayTickAt": "2026-04-29T00:00:00Z" + } + }), + current_story: None, + game_state_json: "{}".to_string(), + current_story_json: None, + created_at_micros: 1, + updated_at_micros: 1, + }; + let input = RuntimeSaveCheckpointInput { + session_id: "session-new".to_string(), + bottom_tab: "story".to_string(), + saved_at_micros: 2, + updated_at_micros: 3, + }; + + assert_eq!( + build_runtime_save_checkpoint_update(input, existing) + .expect_err("mismatched session should fail"), + RuntimeProfileFieldError::RuntimeSessionMismatch { + expected_session_id: "session-old".to_string(), + actual_session_id: "session-new".to_string(), + } + ); + } } diff --git a/server-rs/crates/module-story/README.md b/server-rs/crates/module-story/README.md index adeaac2f..4a764259 100644 --- a/server-rs/crates/module-story/README.md +++ b/server-rs/crates/module-story/README.md @@ -1,30 +1,33 @@ -# module-story 独立模块 package 占位说明 +# module-story 剧情领域模块说明 -日期:`2026-04-20` +日期:`2026-04-29` ## 1. package 职责 -`module-story` 是故事主循环模块 package,后续负责: +`module-story` 是 RPG story session 的纯领域模块,当前负责: -1. `story_session`、`story_event` 等故事会话状态模型 -2. story action 主循环与状态推进规则 -3. `currentStory`、story state、兼容视图模型的模块级拼装 -4. 与 `apps/api-server` 的 story facade 与 SSE 输出对接 -5. 与 `apps/spacetime-module` 的 story 表、reducer、view 聚合对接 +1. `StorySessionSnapshot`、`StoryEventSnapshot` 等故事会话与事件快照。 +2. `begin / continue / state query` 相关输入命令的字段归一化与基础校验。 +3. 剧情会话开局、续写事件追加、版本推进和只读记录投影。 +4. 给 `spacetime-module`、`spacetime-client` 与 `api-server` 提供可复用的纯 Rust 规则。 ## 2. 当前阶段说明 -当前提交仅完成目录占位,不提前进入 reducer、view 与 SSE 兼容实现。 +当前模块已经完成 DDD 薄层物理拆分,不再是目录占位: -后续与本 package 直接相关的任务包括: +1. `src/domain.rs`:会话领域快照、状态和值对象。 +2. `src/commands.rs`:story session scoped 输入命令与校验。 +3. `src/events.rs`:剧情事件类型、事件快照和事件 ID 生成。 +4. `src/application.rs`:快照构造、续写应用服务和读模型记录映射。 +5. `src/errors.rs`:剧情字段错误与中文错误文案。 +6. `src/lib.rs`:只保留模块公开导出,保持 `module_story::*` 对外 API 稳定。 -1. 设计 `story_session`、`story_event` -2. 设计 `resolve_story_action`、`continue_story`、`begin_story_session` -3. 对齐 `RuntimeStoryActionResponse`、`RuntimeStoryOptionView` -4. 落地 `/api/runtime/story/*` 兼容链路 +当前仍未扩到完整运行态动作结算。`inventory action`、NPC interaction、forge、battle、quest 等写侧闭环继续归入 `WP-RS Runtime Story 去兼容层` 和 `WP-RPG Gameplay 域` 后续切片。 ## 3. 边界约束 -1. `module-story` 负责故事状态真相与主循环规则,不把外部 LLM、OSS、短信、微信等副作用塞进模块内部。 -2. 流式文本输出与 HTTP 协议兼容由 `apps/api-server` 暴露,但阶段状态与故事真相必须回写到 `apps/spacetime-module` 聚合的状态模型中。 -3. 跨模块联动通过明确的 reducer 与模块边界协作,不回到单大 service 直接改整包 JSON 的旧实现方式。 +1. `module-story` 不调用 LLM、OSS、HTTP、SpacetimeDB client 或旧 Node 服务。 +2. `module-story` 不恢复旧 `/api/runtime/story/*` 兼容路由;HTTP 主链固定走 G1 冻结的 `/api/story/*` session scoped route。 +3. SpacetimeDB 表、reducer、procedure 和 row mapper 只在 `spacetime-module` adapter 中落地。 +4. `api-server` 只负责 BFF、鉴权、SSE、DTO 映射和平台能力编排,不复制本模块的领域规则。 +5. 前端只消费后端投影和新 contract,不在 UI 或 hooks 中重建 story session 真相。 diff --git a/server-rs/crates/module-story/src/application.rs b/server-rs/crates/module-story/src/application.rs index bc78bf8a..d3492e3b 100644 --- a/server-rs/crates/module-story/src/application.rs +++ b/server-rs/crates/module-story/src/application.rs @@ -1,3 +1,167 @@ -//! 剧情应用编排过渡落位。 +//! 剧情应用服务与读模型映射。 //! -//! 这里只返回剧情快照、事件和待投影结果,不直接调用模型或数据库。 +//! 应用层负责把命令变成快照、事件和前端可消费记录;它不直接调用模型、HTTP、 +//! SpacetimeDB 或旧 Node 兼容服务。 + +use crate::commands::{StoryContinueInput, StorySessionInput, normalize_optional_value}; +use crate::domain::{INITIAL_STORY_SESSION_VERSION, StorySessionSnapshot, StorySessionStatus}; +use crate::errors::StorySessionFieldError; +use crate::events::{StoryEventKind, StoryEventSnapshot}; +use serde::{Deserialize, Serialize}; +use shared_kernel::format_timestamp_micros; +#[cfg(feature = "spacetime-types")] +use spacetimedb::SpacetimeType; + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct StorySessionProcedureResult { + pub ok: bool, + pub session: Option, + pub event: Option, + pub error_message: Option, +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct StorySessionStateProcedureResult { + pub ok: bool, + pub session: Option, + pub events: Vec, + pub error_message: Option, +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct StorySessionRecord { + pub story_session_id: String, + pub runtime_session_id: String, + pub actor_user_id: String, + pub world_profile_id: String, + pub initial_prompt: String, + pub opening_summary: Option, + pub latest_narrative_text: String, + pub latest_choice_function_id: Option, + pub status: String, + pub version: u32, + pub created_at: String, + pub updated_at: String, +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct StoryEventRecord { + pub event_id: String, + pub story_session_id: String, + pub event_kind: String, + pub narrative_text: String, + pub choice_function_id: Option, + pub created_at: String, +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct StorySessionResultRecord { + pub session: StorySessionRecord, + pub event: StoryEventRecord, +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct StorySessionStateRecord { + pub session: StorySessionRecord, + pub events: Vec, +} + +pub fn build_story_session_snapshot(input: StorySessionInput) -> StorySessionSnapshot { + StorySessionSnapshot { + story_session_id: input.story_session_id, + runtime_session_id: input.runtime_session_id, + actor_user_id: input.actor_user_id, + world_profile_id: input.world_profile_id, + initial_prompt: input.initial_prompt, + opening_summary: normalize_optional_value(input.opening_summary), + latest_narrative_text: String::new(), + latest_choice_function_id: None, + status: StorySessionStatus::Active, + version: INITIAL_STORY_SESSION_VERSION, + created_at_micros: input.created_at_micros, + updated_at_micros: input.created_at_micros, + } +} + +pub fn apply_story_continue( + current: StorySessionSnapshot, + input: StoryContinueInput, +) -> Result<(StorySessionSnapshot, StoryEventSnapshot), StorySessionFieldError> { + crate::commands::validate_story_continue_input(&input)?; + + if current.version == 0 { + return Err(StorySessionFieldError::InvalidVersion); + } + + let event = StoryEventSnapshot { + event_id: input.event_id, + story_session_id: current.story_session_id.clone(), + event_kind: StoryEventKind::StoryContinued, + narrative_text: input.narrative_text.clone(), + choice_function_id: normalize_optional_value(input.choice_function_id), + created_at_micros: input.updated_at_micros, + }; + + let next = StorySessionSnapshot { + latest_narrative_text: input.narrative_text, + latest_choice_function_id: event.choice_function_id.clone(), + version: current.version + 1, + updated_at_micros: input.updated_at_micros, + ..current + }; + + Ok((next, event)) +} + +pub fn build_story_session_record(snapshot: StorySessionSnapshot) -> StorySessionRecord { + StorySessionRecord { + story_session_id: snapshot.story_session_id, + runtime_session_id: snapshot.runtime_session_id, + actor_user_id: snapshot.actor_user_id, + world_profile_id: snapshot.world_profile_id, + initial_prompt: snapshot.initial_prompt, + opening_summary: snapshot.opening_summary, + latest_narrative_text: snapshot.latest_narrative_text, + latest_choice_function_id: snapshot.latest_choice_function_id, + status: snapshot.status.as_str().to_string(), + version: snapshot.version, + created_at: format_timestamp_micros(snapshot.created_at_micros), + updated_at: format_timestamp_micros(snapshot.updated_at_micros), + } +} + +pub fn build_story_event_record(snapshot: StoryEventSnapshot) -> StoryEventRecord { + StoryEventRecord { + event_id: snapshot.event_id, + story_session_id: snapshot.story_session_id, + event_kind: snapshot.event_kind.as_str().to_string(), + narrative_text: snapshot.narrative_text, + choice_function_id: snapshot.choice_function_id, + created_at: format_timestamp_micros(snapshot.created_at_micros), + } +} + +pub fn build_story_session_result_record( + session: StorySessionSnapshot, + event: StoryEventSnapshot, +) -> StorySessionResultRecord { + StorySessionResultRecord { + session: build_story_session_record(session), + event: build_story_event_record(event), + } +} + +pub fn build_story_session_state_record( + session: StorySessionSnapshot, + events: Vec, +) -> StorySessionStateRecord { + StorySessionStateRecord { + session: build_story_session_record(session), + events: events + .into_iter() + .map(build_story_event_record) + .collect::>(), + } +} diff --git a/server-rs/crates/module-story/src/commands.rs b/server-rs/crates/module-story/src/commands.rs index 68630c5f..a28fc6d7 100644 --- a/server-rs/crates/module-story/src/commands.rs +++ b/server-rs/crates/module-story/src/commands.rs @@ -1,3 +1,148 @@ -//! 剧情写入命令过渡落位。 +//! 剧情写入命令与输入归一化。 //! -//! 用于表达开启剧情会话、继续剧情和归档会话等输入。 +//! 命令层只处理 story session scoped 的输入结构、字段裁剪和基础校验,不读取数据库, +//! 也不兼容旧 `/api/runtime/story/*` 总入口。 + +use crate::errors::StorySessionFieldError; +use serde::{Deserialize, Serialize}; +use shared_kernel::{ + normalize_optional_string as normalize_shared_optional_string, normalize_required_string, +}; +#[cfg(feature = "spacetime-types")] +use spacetimedb::SpacetimeType; + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct StorySessionInput { + pub story_session_id: String, + pub runtime_session_id: String, + pub actor_user_id: String, + pub world_profile_id: String, + pub initial_prompt: String, + pub opening_summary: Option, + pub created_at_micros: i64, +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct StoryContinueInput { + pub story_session_id: String, + pub event_id: String, + pub narrative_text: String, + pub choice_function_id: Option, + pub updated_at_micros: i64, +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct StorySessionStateInput { + pub story_session_id: String, +} + +pub fn build_story_session_input( + story_session_id: String, + runtime_session_id: String, + actor_user_id: String, + world_profile_id: String, + initial_prompt: String, + opening_summary: Option, + created_at_micros: i64, +) -> Result { + let input = StorySessionInput { + story_session_id: normalize_required_string(story_session_id).unwrap_or_default(), + runtime_session_id: normalize_required_string(runtime_session_id).unwrap_or_default(), + actor_user_id: normalize_required_string(actor_user_id).unwrap_or_default(), + world_profile_id: normalize_required_string(world_profile_id).unwrap_or_default(), + initial_prompt: normalize_required_string(initial_prompt).unwrap_or_default(), + opening_summary: normalize_optional_value(opening_summary), + created_at_micros, + }; + + validate_story_session_input(&input)?; + + Ok(input) +} + +pub fn build_story_session_state_input( + story_session_id: String, +) -> Result { + let input = StorySessionStateInput { + story_session_id: normalize_required_string(story_session_id).unwrap_or_default(), + }; + + validate_story_session_state_input(&input)?; + + Ok(input) +} + +pub fn build_story_continue_input( + story_session_id: String, + event_id: String, + narrative_text: String, + choice_function_id: Option, + updated_at_micros: i64, +) -> Result { + let input = StoryContinueInput { + story_session_id: normalize_required_string(story_session_id).unwrap_or_default(), + event_id: normalize_required_string(event_id).unwrap_or_default(), + narrative_text: normalize_required_string(narrative_text).unwrap_or_default(), + choice_function_id: normalize_optional_value(choice_function_id), + updated_at_micros, + }; + + validate_story_continue_input(&input)?; + + Ok(input) +} + +pub fn validate_story_session_input( + input: &StorySessionInput, +) -> Result<(), StorySessionFieldError> { + if normalize_required_string(&input.story_session_id).is_none() { + return Err(StorySessionFieldError::MissingSessionId); + } + if normalize_required_string(&input.runtime_session_id).is_none() { + return Err(StorySessionFieldError::MissingRuntimeSessionId); + } + if normalize_required_string(&input.actor_user_id).is_none() { + return Err(StorySessionFieldError::MissingActorUserId); + } + if normalize_required_string(&input.world_profile_id).is_none() { + return Err(StorySessionFieldError::MissingWorldProfileId); + } + if normalize_required_string(&input.initial_prompt).is_none() { + return Err(StorySessionFieldError::MissingInitialPrompt); + } + + Ok(()) +} + +pub fn validate_story_session_state_input( + input: &StorySessionStateInput, +) -> Result<(), StorySessionFieldError> { + if normalize_required_string(&input.story_session_id).is_none() { + return Err(StorySessionFieldError::MissingSessionId); + } + + Ok(()) +} + +pub fn validate_story_continue_input( + input: &StoryContinueInput, +) -> Result<(), StorySessionFieldError> { + if normalize_required_string(&input.story_session_id).is_none() { + return Err(StorySessionFieldError::MissingSessionId); + } + if normalize_required_string(&input.event_id).is_none() { + return Err(StorySessionFieldError::MissingEventId); + } + if normalize_required_string(&input.narrative_text).is_none() { + return Err(StorySessionFieldError::MissingNarrativeText); + } + + Ok(()) +} + +pub fn normalize_optional_value(value: Option) -> Option { + normalize_shared_optional_string(value) +} diff --git a/server-rs/crates/module-story/src/domain.rs b/server-rs/crates/module-story/src/domain.rs index 3e032785..5217e298 100644 --- a/server-rs/crates/module-story/src/domain.rs +++ b/server-rs/crates/module-story/src/domain.rs @@ -1,4 +1,55 @@ -//! 剧情领域模型过渡落位。 +//! 剧情会话领域模型。 //! -//! 后续迁移 `StorySession`、`StoryEvent` 和剧情推进规则时,只保留剧情聚合内部变化; -//! LLM 生成和 SpacetimeDB 写回由外层 adapter 处理。 +//! 这里仅保存 RPG story session 聚合内的稳定状态和值对象;LLM 生成、HTTP 回包和 +//! SpacetimeDB 写回都留给外层 adapter,不在领域模型里直接发生副作用。 + +use serde::{Deserialize, Serialize}; +use shared_kernel::build_prefixed_seed_id; +#[cfg(feature = "spacetime-types")] +use spacetimedb::SpacetimeType; + +/// 剧情会话 ID 的稳定前缀,统一放在领域层,避免 adapter 重复拼接。 +pub const STORY_SESSION_ID_PREFIX: &str = "storysess_"; +/// 新建剧情会话快照的初始版本号。 +pub const INITIAL_STORY_SESSION_VERSION: u32 = 1; + +/// 剧情会话状态,用于判断后续 story command 是否还能继续推进。 +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub enum StorySessionStatus { + Active, + Completed, + Archived, +} + +impl StorySessionStatus { + pub fn as_str(&self) -> &'static str { + match self { + Self::Active => "active", + Self::Completed => "completed", + Self::Archived => "archived", + } + } +} + +/// story session 的领域快照,SpacetimeDB row 与 HTTP DTO 都从它映射。 +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct StorySessionSnapshot { + pub story_session_id: String, + pub runtime_session_id: String, + pub actor_user_id: String, + pub world_profile_id: String, + pub initial_prompt: String, + pub opening_summary: Option, + pub latest_narrative_text: String, + pub latest_choice_function_id: Option, + pub status: StorySessionStatus, + pub version: u32, + pub created_at_micros: i64, + pub updated_at_micros: i64, +} + +pub fn generate_story_session_id(seed_micros: i64) -> String { + build_prefixed_seed_id(STORY_SESSION_ID_PREFIX, seed_micros) +} diff --git a/server-rs/crates/module-story/src/errors.rs b/server-rs/crates/module-story/src/errors.rs index a73a95a1..0f9849f5 100644 --- a/server-rs/crates/module-story/src/errors.rs +++ b/server-rs/crates/module-story/src/errors.rs @@ -1,3 +1,36 @@ -//! 剧情领域错误过渡落位。 +//! 剧情领域错误。 //! -//! 错误保持纯剧情规则语义,例如会话不存在、状态不允许或输入为空。 +//! 错误保持纯 story session 规则语义,例如会话字段缺失、事件内容为空或版本非法。 + +use std::{error::Error, fmt}; + +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum StorySessionFieldError { + MissingSessionId, + MissingRuntimeSessionId, + MissingActorUserId, + MissingWorldProfileId, + MissingInitialPrompt, + MissingNarrativeText, + MissingEventId, + InvalidVersion, +} + +impl fmt::Display for StorySessionFieldError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::MissingSessionId => f.write_str("story_session.story_session_id 不能为空"), + Self::MissingRuntimeSessionId => { + f.write_str("story_session.runtime_session_id 不能为空") + } + Self::MissingActorUserId => f.write_str("story_session.actor_user_id 不能为空"), + Self::MissingWorldProfileId => f.write_str("story_session.world_profile_id 不能为空"), + Self::MissingInitialPrompt => f.write_str("story_session.initial_prompt 不能为空"), + Self::MissingNarrativeText => f.write_str("story_event.narrative_text 不能为空"), + Self::MissingEventId => f.write_str("story_event.event_id 不能为空"), + Self::InvalidVersion => f.write_str("story_session.version 必须大于 0"), + } + } +} + +impl Error for StorySessionFieldError {} diff --git a/server-rs/crates/module-story/src/events.rs b/server-rs/crates/module-story/src/events.rs index 6e7fd89f..7581afd3 100644 --- a/server-rs/crates/module-story/src/events.rs +++ b/server-rs/crates/module-story/src/events.rs @@ -1,3 +1,59 @@ -//! 剧情领域事件过渡落位。 +//! 剧情领域事件。 //! -//! 用于表达剧情会话已开启、剧情已推进和剧情事件已追加等事实。 +//! 事件只表达 story session 已经发生的领域事实;是否写入 SpacetimeDB event table 或 +//! 映射成 HTTP/SSE 输出,由外层 adapter 决定。 + +use crate::domain::StorySessionSnapshot; +use serde::{Deserialize, Serialize}; +use shared_kernel::build_prefixed_seed_id; +#[cfg(feature = "spacetime-types")] +use spacetimedb::SpacetimeType; + +/// 剧情事件 ID 的稳定前缀。 +pub const STORY_EVENT_ID_PREFIX: &str = "storyevt_"; + +/// 剧情事件类型,当前覆盖开局和续写两条最小主链。 +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub enum StoryEventKind { + SessionStarted, + StoryContinued, +} + +impl StoryEventKind { + pub fn as_str(&self) -> &'static str { + match self { + Self::SessionStarted => "session_started", + Self::StoryContinued => "story_continued", + } + } +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct StoryEventSnapshot { + pub event_id: String, + pub story_session_id: String, + pub event_kind: StoryEventKind, + pub narrative_text: String, + pub choice_function_id: Option, + pub created_at_micros: i64, +} + +pub fn build_story_started_event(snapshot: &StorySessionSnapshot) -> StoryEventSnapshot { + StoryEventSnapshot { + event_id: generate_story_event_id(snapshot.created_at_micros), + story_session_id: snapshot.story_session_id.clone(), + event_kind: StoryEventKind::SessionStarted, + narrative_text: snapshot + .opening_summary + .clone() + .unwrap_or_else(|| snapshot.initial_prompt.clone()), + choice_function_id: None, + created_at_micros: snapshot.created_at_micros, + } +} + +pub fn generate_story_event_id(seed_micros: i64) -> String { + build_prefixed_seed_id(STORY_EVENT_ID_PREFIX, seed_micros) +} diff --git a/server-rs/crates/module-story/src/lib.rs b/server-rs/crates/module-story/src/lib.rs index 1b639d90..5be48ea4 100644 --- a/server-rs/crates/module-story/src/lib.rs +++ b/server-rs/crates/module-story/src/lib.rs @@ -4,424 +4,11 @@ mod domain; mod errors; mod events; -use std::{error::Error, fmt}; - -use serde::{Deserialize, Serialize}; -use shared_kernel::{ - build_prefixed_seed_id, format_timestamp_micros, - normalize_optional_string as normalize_shared_optional_string, normalize_required_string, -}; -#[cfg(feature = "spacetime-types")] -use spacetimedb::SpacetimeType; - -pub const STORY_SESSION_ID_PREFIX: &str = "storysess_"; -pub const STORY_EVENT_ID_PREFIX: &str = "storyevt_"; -pub const INITIAL_STORY_SESSION_VERSION: u32 = 1; - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub enum StorySessionStatus { - Active, - Completed, - Archived, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub enum StoryEventKind { - SessionStarted, - StoryContinued, -} - -impl StorySessionStatus { - pub fn as_str(&self) -> &'static str { - match self { - Self::Active => "active", - Self::Completed => "completed", - Self::Archived => "archived", - } - } -} - -impl StoryEventKind { - pub fn as_str(&self) -> &'static str { - match self { - Self::SessionStarted => "session_started", - Self::StoryContinued => "story_continued", - } - } -} - -#[derive(Clone, Debug, PartialEq, Eq)] -pub enum StorySessionFieldError { - MissingSessionId, - MissingRuntimeSessionId, - MissingActorUserId, - MissingWorldProfileId, - MissingInitialPrompt, - MissingNarrativeText, - MissingEventId, - InvalidVersion, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct StorySessionInput { - pub story_session_id: String, - pub runtime_session_id: String, - pub actor_user_id: String, - pub world_profile_id: String, - pub initial_prompt: String, - pub opening_summary: Option, - pub created_at_micros: i64, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct StorySessionSnapshot { - pub story_session_id: String, - pub runtime_session_id: String, - pub actor_user_id: String, - pub world_profile_id: String, - pub initial_prompt: String, - pub opening_summary: Option, - pub latest_narrative_text: String, - pub latest_choice_function_id: Option, - pub status: StorySessionStatus, - pub version: u32, - pub created_at_micros: i64, - pub updated_at_micros: i64, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct StoryContinueInput { - pub story_session_id: String, - pub event_id: String, - pub narrative_text: String, - pub choice_function_id: Option, - pub updated_at_micros: i64, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct StorySessionStateInput { - pub story_session_id: String, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct StoryEventSnapshot { - pub event_id: String, - pub story_session_id: String, - pub event_kind: StoryEventKind, - pub narrative_text: String, - pub choice_function_id: Option, - pub created_at_micros: i64, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct StorySessionProcedureResult { - pub ok: bool, - pub session: Option, - pub event: Option, - pub error_message: Option, -} - -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct StorySessionStateProcedureResult { - pub ok: bool, - pub session: Option, - pub events: Vec, - pub error_message: Option, -} - -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct StorySessionRecord { - pub story_session_id: String, - pub runtime_session_id: String, - pub actor_user_id: String, - pub world_profile_id: String, - pub initial_prompt: String, - pub opening_summary: Option, - pub latest_narrative_text: String, - pub latest_choice_function_id: Option, - pub status: String, - pub version: u32, - pub created_at: String, - pub updated_at: String, -} - -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct StoryEventRecord { - pub event_id: String, - pub story_session_id: String, - pub event_kind: String, - pub narrative_text: String, - pub choice_function_id: Option, - pub created_at: String, -} - -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct StorySessionResultRecord { - pub session: StorySessionRecord, - pub event: StoryEventRecord, -} - -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct StorySessionStateRecord { - pub session: StorySessionRecord, - pub events: Vec, -} - -pub fn build_story_session_input( - story_session_id: String, - runtime_session_id: String, - actor_user_id: String, - world_profile_id: String, - initial_prompt: String, - opening_summary: Option, - created_at_micros: i64, -) -> Result { - let input = StorySessionInput { - story_session_id: normalize_required_string(story_session_id).unwrap_or_default(), - runtime_session_id: normalize_required_string(runtime_session_id).unwrap_or_default(), - actor_user_id: normalize_required_string(actor_user_id).unwrap_or_default(), - world_profile_id: normalize_required_string(world_profile_id).unwrap_or_default(), - initial_prompt: normalize_required_string(initial_prompt).unwrap_or_default(), - opening_summary: normalize_optional_value(opening_summary), - created_at_micros, - }; - - validate_story_session_input(&input)?; - - Ok(input) -} - -pub fn build_story_session_state_input( - story_session_id: String, -) -> Result { - let input = StorySessionStateInput { - story_session_id: normalize_required_string(story_session_id).unwrap_or_default(), - }; - - validate_story_session_state_input(&input)?; - - Ok(input) -} - -pub fn build_story_continue_input( - story_session_id: String, - event_id: String, - narrative_text: String, - choice_function_id: Option, - updated_at_micros: i64, -) -> Result { - let input = StoryContinueInput { - story_session_id: normalize_required_string(story_session_id).unwrap_or_default(), - event_id: normalize_required_string(event_id).unwrap_or_default(), - narrative_text: normalize_required_string(narrative_text).unwrap_or_default(), - choice_function_id: normalize_optional_value(choice_function_id), - updated_at_micros, - }; - - validate_story_continue_input(&input)?; - - Ok(input) -} - -pub fn validate_story_session_input( - input: &StorySessionInput, -) -> Result<(), StorySessionFieldError> { - if normalize_required_string(&input.story_session_id).is_none() { - return Err(StorySessionFieldError::MissingSessionId); - } - if normalize_required_string(&input.runtime_session_id).is_none() { - return Err(StorySessionFieldError::MissingRuntimeSessionId); - } - if normalize_required_string(&input.actor_user_id).is_none() { - return Err(StorySessionFieldError::MissingActorUserId); - } - if normalize_required_string(&input.world_profile_id).is_none() { - return Err(StorySessionFieldError::MissingWorldProfileId); - } - if normalize_required_string(&input.initial_prompt).is_none() { - return Err(StorySessionFieldError::MissingInitialPrompt); - } - - Ok(()) -} - -pub fn validate_story_session_state_input( - input: &StorySessionStateInput, -) -> Result<(), StorySessionFieldError> { - if normalize_required_string(&input.story_session_id).is_none() { - return Err(StorySessionFieldError::MissingSessionId); - } - - Ok(()) -} - -pub fn validate_story_continue_input( - input: &StoryContinueInput, -) -> Result<(), StorySessionFieldError> { - if normalize_required_string(&input.story_session_id).is_none() { - return Err(StorySessionFieldError::MissingSessionId); - } - if normalize_required_string(&input.event_id).is_none() { - return Err(StorySessionFieldError::MissingEventId); - } - if normalize_required_string(&input.narrative_text).is_none() { - return Err(StorySessionFieldError::MissingNarrativeText); - } - - Ok(()) -} - -pub fn build_story_session_snapshot(input: StorySessionInput) -> StorySessionSnapshot { - StorySessionSnapshot { - story_session_id: input.story_session_id, - runtime_session_id: input.runtime_session_id, - actor_user_id: input.actor_user_id, - world_profile_id: input.world_profile_id, - initial_prompt: input.initial_prompt, - opening_summary: normalize_optional_value(input.opening_summary), - latest_narrative_text: String::new(), - latest_choice_function_id: None, - status: StorySessionStatus::Active, - version: INITIAL_STORY_SESSION_VERSION, - created_at_micros: input.created_at_micros, - updated_at_micros: input.created_at_micros, - } -} - -pub fn build_story_started_event(snapshot: &StorySessionSnapshot) -> StoryEventSnapshot { - StoryEventSnapshot { - event_id: generate_story_event_id(snapshot.created_at_micros), - story_session_id: snapshot.story_session_id.clone(), - event_kind: StoryEventKind::SessionStarted, - narrative_text: snapshot - .opening_summary - .clone() - .unwrap_or_else(|| snapshot.initial_prompt.clone()), - choice_function_id: None, - created_at_micros: snapshot.created_at_micros, - } -} - -pub fn apply_story_continue( - current: StorySessionSnapshot, - input: StoryContinueInput, -) -> Result<(StorySessionSnapshot, StoryEventSnapshot), StorySessionFieldError> { - validate_story_continue_input(&input)?; - - if current.version == 0 { - return Err(StorySessionFieldError::InvalidVersion); - } - - let event = StoryEventSnapshot { - event_id: input.event_id, - story_session_id: current.story_session_id.clone(), - event_kind: StoryEventKind::StoryContinued, - narrative_text: input.narrative_text.clone(), - choice_function_id: normalize_optional_value(input.choice_function_id), - created_at_micros: input.updated_at_micros, - }; - - let next = StorySessionSnapshot { - latest_narrative_text: input.narrative_text, - latest_choice_function_id: event.choice_function_id.clone(), - version: current.version + 1, - updated_at_micros: input.updated_at_micros, - ..current - }; - - Ok((next, event)) -} - -pub fn generate_story_session_id(seed_micros: i64) -> String { - build_prefixed_seed_id(STORY_SESSION_ID_PREFIX, seed_micros) -} - -pub fn generate_story_event_id(seed_micros: i64) -> String { - build_prefixed_seed_id(STORY_EVENT_ID_PREFIX, seed_micros) -} - -pub fn build_story_session_record(snapshot: StorySessionSnapshot) -> StorySessionRecord { - StorySessionRecord { - story_session_id: snapshot.story_session_id, - runtime_session_id: snapshot.runtime_session_id, - actor_user_id: snapshot.actor_user_id, - world_profile_id: snapshot.world_profile_id, - initial_prompt: snapshot.initial_prompt, - opening_summary: snapshot.opening_summary, - latest_narrative_text: snapshot.latest_narrative_text, - latest_choice_function_id: snapshot.latest_choice_function_id, - status: snapshot.status.as_str().to_string(), - version: snapshot.version, - created_at: format_timestamp_micros(snapshot.created_at_micros), - updated_at: format_timestamp_micros(snapshot.updated_at_micros), - } -} - -pub fn build_story_event_record(snapshot: StoryEventSnapshot) -> StoryEventRecord { - StoryEventRecord { - event_id: snapshot.event_id, - story_session_id: snapshot.story_session_id, - event_kind: snapshot.event_kind.as_str().to_string(), - narrative_text: snapshot.narrative_text, - choice_function_id: snapshot.choice_function_id, - created_at: format_timestamp_micros(snapshot.created_at_micros), - } -} - -pub fn build_story_session_result_record( - session: StorySessionSnapshot, - event: StoryEventSnapshot, -) -> StorySessionResultRecord { - StorySessionResultRecord { - session: build_story_session_record(session), - event: build_story_event_record(event), - } -} - -pub fn build_story_session_state_record( - session: StorySessionSnapshot, - events: Vec, -) -> StorySessionStateRecord { - StorySessionStateRecord { - session: build_story_session_record(session), - events: events - .into_iter() - .map(build_story_event_record) - .collect::>(), - } -} - -pub fn normalize_optional_value(value: Option) -> Option { - normalize_shared_optional_string(value) -} - -impl fmt::Display for StorySessionFieldError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::MissingSessionId => f.write_str("story_session.story_session_id 不能为空"), - Self::MissingRuntimeSessionId => { - f.write_str("story_session.runtime_session_id 不能为空") - } - Self::MissingActorUserId => f.write_str("story_session.actor_user_id 不能为空"), - Self::MissingWorldProfileId => f.write_str("story_session.world_profile_id 不能为空"), - Self::MissingInitialPrompt => f.write_str("story_session.initial_prompt 不能为空"), - Self::MissingNarrativeText => f.write_str("story_event.narrative_text 不能为空"), - Self::MissingEventId => f.write_str("story_event.event_id 不能为空"), - Self::InvalidVersion => f.write_str("story_session.version 必须大于 0"), - } - } -} - -impl Error for StorySessionFieldError {} +pub use application::*; +pub use commands::*; +pub use domain::*; +pub use errors::*; +pub use events::*; #[cfg(test)] mod tests { diff --git a/server-rs/crates/platform-auth/Cargo.toml b/server-rs/crates/platform-auth/Cargo.toml index 0a607b50..38ac0380 100644 --- a/server-rs/crates/platform-auth/Cargo.toml +++ b/server-rs/crates/platform-auth/Cargo.toml @@ -18,6 +18,7 @@ serde = { version = "1", features = ["derive"] } shared-kernel = { path = "../shared-kernel" } time = { version = "0.3", features = ["std"] } tracing = "0.1" +url = "2" urlencoding = "2" uuid = { version = "1", features = ["v4"] } diff --git a/server-rs/crates/platform-auth/src/lib.rs b/server-rs/crates/platform-auth/src/lib.rs index 214c91fb..6f05894d 100644 --- a/server-rs/crates/platform-auth/src/lib.rs +++ b/server-rs/crates/platform-auth/src/lib.rs @@ -19,6 +19,7 @@ use sha2::{Digest, Sha256}; use shared_kernel::{new_uuid_simple_string, normalize_optional_string, normalize_required_string}; use time::{Duration, OffsetDateTime}; use tracing::{info, warn}; +use url::Url; pub const ACCESS_TOKEN_ALGORITHM: Algorithm = Algorithm::HS256; pub const DEFAULT_ACCESS_TOKEN_TTL_SECONDS: u64 = 2 * 60 * 60; @@ -35,6 +36,12 @@ pub const DEFAULT_SMS_VALID_TIME_SECONDS: u64 = 300; pub const DEFAULT_SMS_INTERVAL_SECONDS: u64 = 60; pub const DEFAULT_SMS_DUPLICATE_POLICY: u8 = 1; pub const DEFAULT_SMS_CASE_AUTH_POLICY: u8 = 1; +pub const DEFAULT_WECHAT_AUTHORIZE_ENDPOINT: &str = "https://open.weixin.qq.com/connect/qrconnect"; +pub const DEFAULT_WECHAT_IN_APP_AUTHORIZE_ENDPOINT: &str = + "https://open.weixin.qq.com/connect/oauth2/authorize"; +pub const DEFAULT_WECHAT_ACCESS_TOKEN_ENDPOINT: &str = + "https://api.weixin.qq.com/sns/oauth2/access_token"; +pub const DEFAULT_WECHAT_USER_INFO_ENDPOINT: &str = "https://api.weixin.qq.com/sns/userinfo"; type HmacSha1 = Hmac; @@ -159,6 +166,60 @@ pub struct SmsVerifyCodeRequest { pub provider_out_id: Option, } +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum WechatAuthScene { + Desktop, + WechatInApp, +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct WechatAuthConfig { + pub enabled: bool, + pub provider: String, + pub app_id: Option, + pub app_secret: Option, + pub authorize_endpoint: String, + pub access_token_endpoint: String, + pub user_info_endpoint: String, + pub mock_user_id: String, + pub mock_union_id: Option, + pub mock_display_name: String, + pub mock_avatar_url: Option, +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct WechatIdentityProfile { + pub provider_uid: String, + pub provider_union_id: Option, + pub display_name: Option, + pub avatar_url: Option, +} + +#[derive(Clone, Debug)] +pub enum WechatProvider { + Disabled, + Mock(MockWechatProvider), + Real(RealWechatProvider), +} + +#[derive(Clone, Debug)] +pub struct MockWechatProvider { + mock_user_id: String, + mock_union_id: Option, + mock_display_name: String, + mock_avatar_url: Option, +} + +#[derive(Clone, Debug)] +pub struct RealWechatProvider { + client: Client, + app_id: String, + app_secret: String, + authorize_endpoint: String, + access_token_endpoint: String, + user_info_endpoint: String, +} + #[derive(Clone, Debug)] pub enum SmsAuthProvider { Mock(MockSmsAuthProvider), @@ -202,6 +263,54 @@ pub enum SmsProviderError { Upstream(String), } +#[derive(Debug, PartialEq, Eq)] +pub enum WechatProviderError { + Disabled, + MissingCode, + InvalidConfig(String), + InvalidCallback(String), + RequestFailed(String), + DeserializeFailed(String), + Upstream(String), + MissingProfile(String), +} + +// 鉴权平台错误统一先归类,api-server 再决定 HTTP status 和错误 envelope。 +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum AuthPlatformErrorKind { + InvalidConfig, + InvalidClaims, + SignFailed, + VerifyFailed, + CookieConfig, + HashFailed, + InvalidVerifyCode, + Disabled, + MissingCode, + InvalidCallback, + RequestFailed, + DeserializeFailed, + MissingProfile, + Upstream, +} + +#[derive(Debug, Deserialize)] +struct WechatAccessTokenResponse { + access_token: Option, + openid: Option, + unionid: Option, + errmsg: Option, +} + +#[derive(Debug, Deserialize)] +struct WechatUserInfoResponse { + openid: Option, + unionid: Option, + nickname: Option, + headimgurl: Option, + errmsg: Option, +} + #[derive(Debug, Deserialize)] struct AliyunSendSmsVerifyCodeResponse { // 阿里云 RPC 原始 JSON 使用首字母大写字段名,这里必须显式映射,避免把成功响应误判成空值。 @@ -512,6 +621,257 @@ impl SmsAuthProvider { } } +impl WechatAuthConfig { + #[allow(clippy::too_many_arguments)] + pub fn new( + enabled: bool, + provider: String, + app_id: Option, + app_secret: Option, + authorize_endpoint: String, + access_token_endpoint: String, + user_info_endpoint: String, + mock_user_id: String, + mock_union_id: Option, + mock_display_name: String, + mock_avatar_url: Option, + ) -> Self { + Self { + enabled, + provider, + app_id, + app_secret, + authorize_endpoint, + access_token_endpoint, + user_info_endpoint, + mock_user_id, + mock_union_id, + mock_display_name, + mock_avatar_url, + } + } +} + +impl WechatProvider { + pub fn new(config: WechatAuthConfig) -> Self { + if !config.enabled { + return Self::Disabled; + } + + if config.provider.trim().eq_ignore_ascii_case("mock") { + return Self::Mock(MockWechatProvider { + mock_user_id: config.mock_user_id, + mock_union_id: config.mock_union_id, + mock_display_name: config.mock_display_name, + mock_avatar_url: config.mock_avatar_url, + }); + } + + let Some(app_id) = config.app_id else { + return Self::Disabled; + }; + let Some(app_secret) = config.app_secret else { + return Self::Disabled; + }; + + Self::Real(RealWechatProvider { + client: Client::new(), + app_id, + app_secret, + authorize_endpoint: config.authorize_endpoint, + access_token_endpoint: config.access_token_endpoint, + user_info_endpoint: config.user_info_endpoint, + }) + } + + pub fn build_authorization_url( + &self, + callback_url: &str, + state: &str, + scene: &WechatAuthScene, + ) -> Result { + match self { + Self::Disabled => Err(WechatProviderError::Disabled), + Self::Mock(_) => build_mock_wechat_authorization_url(callback_url, state), + Self::Real(provider) => provider.build_authorization_url(callback_url, state, scene), + } + } + + pub async fn resolve_callback_profile( + &self, + code: Option<&str>, + mock_code: Option<&str>, + ) -> Result { + match self { + Self::Disabled => Err(WechatProviderError::Disabled), + Self::Mock(provider) => Ok(provider.resolve_callback_profile(mock_code)), + Self::Real(provider) => provider.resolve_callback_profile(code).await, + } + } +} + +impl MockWechatProvider { + fn resolve_callback_profile(&self, mock_code: Option<&str>) -> WechatIdentityProfile { + let provider_uid = mock_code + .map(str::trim) + .filter(|value| !value.is_empty()) + .unwrap_or(self.mock_user_id.as_str()) + .to_string(); + WechatIdentityProfile { + provider_uid, + provider_union_id: self.mock_union_id.clone(), + display_name: Some(self.mock_display_name.clone()), + avatar_url: self.mock_avatar_url.clone(), + } + } +} + +impl RealWechatProvider { + fn build_authorization_url( + &self, + callback_url: &str, + state: &str, + scene: &WechatAuthScene, + ) -> Result { + let endpoint = match scene { + WechatAuthScene::Desktop => &self.authorize_endpoint, + WechatAuthScene::WechatInApp => DEFAULT_WECHAT_IN_APP_AUTHORIZE_ENDPOINT, + }; + let mut url = Url::parse(endpoint).map_err(|error| { + WechatProviderError::InvalidConfig(format!("微信授权地址非法:{error}")) + })?; + url.query_pairs_mut() + .append_pair("appid", &self.app_id) + .append_pair("redirect_uri", callback_url) + .append_pair("response_type", "code") + .append_pair( + "scope", + match scene { + WechatAuthScene::Desktop => "snsapi_login", + WechatAuthScene::WechatInApp => "snsapi_userinfo", + }, + ) + .append_pair("state", state); + Ok(format!("{url}#wechat_redirect")) + } + + async fn resolve_callback_profile( + &self, + code: Option<&str>, + ) -> Result { + let code = code + .map(str::trim) + .filter(|value| !value.is_empty()) + .ok_or(WechatProviderError::MissingCode)?; + + let mut access_token_url = Url::parse(&self.access_token_endpoint).map_err(|error| { + WechatProviderError::InvalidConfig(format!("微信 access_token 地址非法:{error}")) + })?; + access_token_url + .query_pairs_mut() + .append_pair("appid", &self.app_id) + .append_pair("secret", &self.app_secret) + .append_pair("code", code) + .append_pair("grant_type", "authorization_code"); + + let access_token_payload = self + .client + .get(access_token_url.as_str()) + .send() + .await + .map_err(|error| { + warn!(error = %error, "微信 access_token 请求失败"); + WechatProviderError::RequestFailed( + "微信登录失败:access_token 请求失败".to_string(), + ) + })? + .json::() + .await + .map_err(|error| { + warn!(error = %error, "微信 access_token 响应解析失败"); + WechatProviderError::DeserializeFailed( + "微信登录失败:access_token 响应非法".to_string(), + ) + })?; + + let access_token = access_token_payload + .access_token + .filter(|value| !value.trim().is_empty()) + .ok_or_else(|| { + WechatProviderError::Upstream(format!( + "微信登录失败:{}", + access_token_payload + .errmsg + .unwrap_or_else(|| "缺少 access_token".to_string()) + )) + })?; + let openid = access_token_payload + .openid + .filter(|value| !value.trim().is_empty()) + .ok_or_else(|| { + WechatProviderError::MissingProfile("微信登录失败:缺少 openid".to_string()) + })?; + + let mut user_info_url = Url::parse(&self.user_info_endpoint).map_err(|error| { + WechatProviderError::InvalidConfig(format!("微信用户信息地址非法:{error}")) + })?; + user_info_url + .query_pairs_mut() + .append_pair("access_token", &access_token) + .append_pair("openid", &openid) + .append_pair("lang", "zh_CN"); + + let user_info_payload = self + .client + .get(user_info_url.as_str()) + .send() + .await + .map_err(|error| { + warn!(error = %error, "微信用户信息请求失败"); + WechatProviderError::RequestFailed("微信登录失败:用户信息请求失败".to_string()) + })? + .json::() + .await + .map_err(|error| { + warn!(error = %error, "微信用户信息响应解析失败"); + WechatProviderError::DeserializeFailed("微信登录失败:用户信息响应非法".to_string()) + })?; + + let provider_uid = user_info_payload + .openid + .filter(|value| !value.trim().is_empty()) + .ok_or_else(|| { + WechatProviderError::Upstream(format!( + "微信登录失败:{}", + user_info_payload + .errmsg + .unwrap_or_else(|| "缺少 openid".to_string()) + )) + })?; + + Ok(WechatIdentityProfile { + provider_uid, + provider_union_id: user_info_payload.unionid.or(access_token_payload.unionid), + display_name: user_info_payload.nickname, + avatar_url: user_info_payload.headimgurl, + }) + } +} + +fn build_mock_wechat_authorization_url( + callback_url: &str, + state: &str, +) -> Result { + let mut callback = Url::parse(callback_url).map_err(|error| { + WechatProviderError::InvalidCallback(format!("微信回调地址非法:{error}")) + })?; + callback + .query_pairs_mut() + .append_pair("mock_code", "wx-mock-code") + .append_pair("state", state); + Ok(callback.to_string()) +} + impl MockSmsAuthProvider { async fn send_code( &self, @@ -1262,10 +1622,154 @@ impl fmt::Display for SmsProviderError { impl Error for SmsProviderError {} +impl fmt::Display for WechatProviderError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Disabled => f.write_str("微信登录暂未启用"), + Self::MissingCode => f.write_str("缺少微信授权 code"), + Self::InvalidConfig(message) + | Self::InvalidCallback(message) + | Self::RequestFailed(message) + | Self::DeserializeFailed(message) + | Self::Upstream(message) + | Self::MissingProfile(message) => f.write_str(message), + } + } +} + +impl Error for WechatProviderError {} + +impl JwtError { + pub fn kind(&self) -> AuthPlatformErrorKind { + match self { + Self::InvalidConfig(_) => AuthPlatformErrorKind::InvalidConfig, + Self::InvalidClaims(_) => AuthPlatformErrorKind::InvalidClaims, + Self::SignFailed(_) => AuthPlatformErrorKind::SignFailed, + Self::VerifyFailed(_) => AuthPlatformErrorKind::VerifyFailed, + } + } +} + +impl RefreshCookieError { + pub fn kind(&self) -> AuthPlatformErrorKind { + match self { + Self::InvalidConfig(_) => AuthPlatformErrorKind::CookieConfig, + } + } +} + +impl PasswordHashError { + pub fn kind(&self) -> AuthPlatformErrorKind { + match self { + Self::HashFailed(_) => AuthPlatformErrorKind::HashFailed, + Self::VerifyFailed(_) => AuthPlatformErrorKind::VerifyFailed, + } + } +} + +impl SmsProviderError { + pub fn kind(&self) -> AuthPlatformErrorKind { + match self { + Self::InvalidConfig(_) => AuthPlatformErrorKind::InvalidConfig, + Self::InvalidVerifyCode => AuthPlatformErrorKind::InvalidVerifyCode, + Self::Upstream(_) => AuthPlatformErrorKind::Upstream, + } + } +} + +impl WechatProviderError { + pub fn kind(&self) -> AuthPlatformErrorKind { + match self { + Self::Disabled => AuthPlatformErrorKind::Disabled, + Self::MissingCode => AuthPlatformErrorKind::MissingCode, + Self::InvalidConfig(_) => AuthPlatformErrorKind::InvalidConfig, + Self::InvalidCallback(_) => AuthPlatformErrorKind::InvalidCallback, + Self::RequestFailed(_) => AuthPlatformErrorKind::RequestFailed, + Self::DeserializeFailed(_) => AuthPlatformErrorKind::DeserializeFailed, + Self::Upstream(_) => AuthPlatformErrorKind::Upstream, + Self::MissingProfile(_) => AuthPlatformErrorKind::MissingProfile, + } + } +} + #[cfg(test)] mod tests { use super::*; + #[test] + fn auth_platform_error_kind_is_stable_for_adapter_mapping() { + assert_eq!( + JwtError::InvalidClaims("JWT roles 至少包含一个角色").kind(), + AuthPlatformErrorKind::InvalidClaims + ); + assert_eq!( + PasswordHashError::VerifyFailed("密码校验失败".to_string()).kind(), + AuthPlatformErrorKind::VerifyFailed + ); + assert_eq!( + SmsProviderError::InvalidVerifyCode.kind(), + AuthPlatformErrorKind::InvalidVerifyCode + ); + assert_eq!( + WechatProviderError::MissingCode.kind(), + AuthPlatformErrorKind::MissingCode + ); + } + + #[test] + fn mock_wechat_provider_builds_callback_authorization_url() { + let provider = WechatProvider::new(WechatAuthConfig::new( + true, + "mock".to_string(), + None, + None, + DEFAULT_WECHAT_AUTHORIZE_ENDPOINT.to_string(), + DEFAULT_WECHAT_ACCESS_TOKEN_ENDPOINT.to_string(), + DEFAULT_WECHAT_USER_INFO_ENDPOINT.to_string(), + "wx-user-001".to_string(), + Some("wx-union-001".to_string()), + "微信测试用户".to_string(), + Some("https://example.test/avatar.png".to_string()), + )); + + let authorization_url = provider + .build_authorization_url( + "http://127.0.0.1:3000/api/auth/wechat/callback", + "state_001", + &WechatAuthScene::Desktop, + ) + .expect("mock authorization url should build"); + + assert!(authorization_url.contains("mock_code=wx-mock-code")); + assert!(authorization_url.contains("state=state_001")); + } + + #[tokio::test] + async fn mock_wechat_provider_resolves_identity_profile() { + let provider = WechatProvider::new(WechatAuthConfig::new( + true, + "mock".to_string(), + None, + None, + DEFAULT_WECHAT_AUTHORIZE_ENDPOINT.to_string(), + DEFAULT_WECHAT_ACCESS_TOKEN_ENDPOINT.to_string(), + DEFAULT_WECHAT_USER_INFO_ENDPOINT.to_string(), + "wx-user-001".to_string(), + Some("wx-union-001".to_string()), + "微信测试用户".to_string(), + None, + )); + + let profile = provider + .resolve_callback_profile(None, Some("wx-code-001")) + .await + .expect("mock profile should resolve"); + + assert_eq!(profile.provider_uid, "wx-code-001"); + assert_eq!(profile.provider_union_id.as_deref(), Some("wx-union-001")); + assert_eq!(profile.display_name.as_deref(), Some("微信测试用户")); + } + fn build_jwt_config() -> JwtConfig { JwtConfig::new( "https://auth.genarrative.local".to_string(), diff --git a/server-rs/crates/platform-llm/src/lib.rs b/server-rs/crates/platform-llm/src/lib.rs index 32f263b2..c52208c9 100644 --- a/server-rs/crates/platform-llm/src/lib.rs +++ b/server-rs/crates/platform-llm/src/lib.rs @@ -109,6 +109,20 @@ pub enum LlmError { Deserialize(String), } +// 平台层只暴露稳定错误分类,HTTP status 和业务文案由 api-server 再映射。 +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum LlmErrorKind { + InvalidConfig, + InvalidRequest, + Timeout, + Connectivity, + Upstream, + StreamUnavailable, + EmptyResponse, + Transport, + Deserialize, +} + // 统一 OpenAI 兼容文本网关 client。 #[derive(Clone, Debug)] pub struct LlmClient { @@ -397,6 +411,22 @@ impl fmt::Display for LlmError { impl Error for LlmError {} +impl LlmError { + pub fn kind(&self) -> LlmErrorKind { + match self { + Self::InvalidConfig(_) => LlmErrorKind::InvalidConfig, + Self::InvalidRequest(_) => LlmErrorKind::InvalidRequest, + Self::Timeout { .. } => LlmErrorKind::Timeout, + Self::Connectivity { .. } => LlmErrorKind::Connectivity, + Self::Upstream { .. } => LlmErrorKind::Upstream, + Self::StreamUnavailable => LlmErrorKind::StreamUnavailable, + Self::EmptyResponse => LlmErrorKind::EmptyResponse, + Self::Transport(_) => LlmErrorKind::Transport, + Self::Deserialize(_) => LlmErrorKind::Deserialize, + } + } +} + impl LlmClient { pub fn new(config: LlmConfig) -> Result { let http_client = Client::builder().build().map_err(|error| { @@ -1108,6 +1138,23 @@ mod tests { use super::*; + #[test] + fn llm_error_kind_is_stable_for_adapter_mapping() { + assert_eq!( + LlmError::InvalidConfig("bad config".to_string()).kind(), + LlmErrorKind::InvalidConfig + ); + assert_eq!( + LlmError::Upstream { + status_code: 429, + message: "too many requests".to_string(), + } + .kind(), + LlmErrorKind::Upstream + ); + assert_eq!(LlmError::EmptyResponse.kind(), LlmErrorKind::EmptyResponse); + } + struct MockResponse { status_line: &'static str, content_type: &'static str, diff --git a/server-rs/crates/platform-oss/src/lib.rs b/server-rs/crates/platform-oss/src/lib.rs index b719a1d6..c116e19b 100644 --- a/server-rs/crates/platform-oss/src/lib.rs +++ b/server-rs/crates/platform-oss/src/lib.rs @@ -196,6 +196,17 @@ pub enum OssError { Sign(String), } +// 平台 OSS 错误只先归类,不在 platform 层绑定 HTTP status。 +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum OssErrorKind { + InvalidConfig, + InvalidRequest, + ObjectNotFound, + Request, + SerializePolicy, + Sign, +} + impl LegacyAssetPrefix { pub fn parse(raw: &str) -> Option { let normalized = raw @@ -620,6 +631,19 @@ impl fmt::Display for OssError { impl Error for OssError {} +impl OssError { + pub fn kind(&self) -> OssErrorKind { + match self { + Self::InvalidConfig(_) => OssErrorKind::InvalidConfig, + Self::InvalidRequest(_) => OssErrorKind::InvalidRequest, + Self::ObjectNotFound(_) => OssErrorKind::ObjectNotFound, + Self::Request(_) => OssErrorKind::Request, + Self::SerializePolicy(_) => OssErrorKind::SerializePolicy, + Self::Sign(_) => OssErrorKind::Sign, + } + } +} + fn build_policy_json( bucket: &str, object_key: &str, @@ -1011,6 +1035,22 @@ fn encode_url_query_value(value: &str) -> String { mod tests { use super::*; + #[test] + fn oss_error_kind_is_stable_for_adapter_mapping() { + assert_eq!( + OssError::InvalidConfig("bad config".to_string()).kind(), + OssErrorKind::InvalidConfig + ); + assert_eq!( + OssError::ObjectNotFound("missing".to_string()).kind(), + OssErrorKind::ObjectNotFound + ); + assert_eq!( + OssError::Request("network".to_string()).kind(), + OssErrorKind::Request + ); + } + fn build_client() -> OssClient { OssClient::new( OssConfig::new( diff --git a/server-rs/crates/shared-contracts/src/big_fish.rs b/server-rs/crates/shared-contracts/src/big_fish.rs index d1d52f7b..ea8f2a43 100644 --- a/server-rs/crates/shared-contracts/src/big_fish.rs +++ b/server-rs/crates/shared-contracts/src/big_fish.rs @@ -33,6 +33,13 @@ pub struct RecordBigFishPlayRequest { pub elapsed_ms: Option, } +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] +#[serde(rename_all = "camelCase")] +pub struct SubmitBigFishInputRequest { + pub x: f32, + pub y: f32, +} + #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] #[serde(rename_all = "camelCase")] pub struct BigFishAnchorItemResponse { @@ -173,6 +180,47 @@ pub struct BigFishActionResponse { pub session: BigFishSessionSnapshotResponse, } +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] +#[serde(rename_all = "camelCase")] +pub struct BigFishVector2Response { + pub x: f32, + pub y: f32, +} + +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] +#[serde(rename_all = "camelCase")] +pub struct BigFishRuntimeEntityResponse { + pub entity_id: String, + pub level: u32, + pub position: BigFishVector2Response, + pub radius: f32, + pub offscreen_seconds: f32, +} + +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] +#[serde(rename_all = "camelCase")] +pub struct BigFishRuntimeSnapshotResponse { + pub run_id: String, + pub session_id: String, + pub status: String, + pub tick: u64, + pub player_level: u32, + pub win_level: u32, + pub leader_entity_id: Option, + pub owned_entities: Vec, + pub wild_entities: Vec, + pub camera_center: BigFishVector2Response, + pub last_input: BigFishVector2Response, + pub event_log: Vec, + pub updated_at: String, +} + +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] +#[serde(rename_all = "camelCase")] +pub struct BigFishRunResponse { + pub run: BigFishRuntimeSnapshotResponse, +} + #[cfg(test)] mod tests { use super::*; diff --git a/server-rs/crates/spacetime-client/src/big_fish.rs b/server-rs/crates/spacetime-client/src/big_fish.rs index 5d4106d4..316a27be 100644 --- a/server-rs/crates/spacetime-client/src/big_fish.rs +++ b/server-rs/crates/spacetime-client/src/big_fish.rs @@ -1,7 +1,10 @@ use super::*; use crate::mapper::*; use crate::module_bindings::delete_big_fish_work_procedure::delete_big_fish_work; +use crate::module_bindings::get_big_fish_run_procedure::get_big_fish_run; use crate::module_bindings::record_big_fish_play_procedure::record_big_fish_play; +use crate::module_bindings::start_big_fish_run_procedure::start_big_fish_run; +use crate::module_bindings::submit_big_fish_input_procedure::submit_big_fish_input; impl SpacetimeClient { pub async fn create_big_fish_session( @@ -290,4 +293,77 @@ impl SpacetimeClient { }) .await } + + pub async fn start_big_fish_run( + &self, + input: BigFishRunStartRecordInput, + ) -> Result { + let procedure_input = BigFishRunStartInput { + run_id: input.run_id, + session_id: input.session_id, + owner_user_id: input.owner_user_id, + started_at_micros: input.started_at_micros, + }; + + self.call_after_connect(move |connection, sender| { + connection + .procedures() + .start_big_fish_run_then(procedure_input, move |_, result| { + let mapped = result + .map_err(SpacetimeClientError::from_sdk_error) + .and_then(map_big_fish_run_procedure_result); + send_once(&sender, mapped); + }); + }) + .await + } + + pub async fn get_big_fish_run( + &self, + run_id: String, + owner_user_id: String, + ) -> Result { + let procedure_input = BigFishRunGetInput { + run_id, + owner_user_id, + }; + + self.call_after_connect(move |connection, sender| { + connection + .procedures() + .get_big_fish_run_then(procedure_input, move |_, result| { + let mapped = result + .map_err(SpacetimeClientError::from_sdk_error) + .and_then(map_big_fish_run_procedure_result); + send_once(&sender, mapped); + }); + }) + .await + } + + pub async fn submit_big_fish_input( + &self, + input: BigFishInputSubmitRecordInput, + ) -> Result { + let procedure_input = BigFishInputSubmitInput { + run_id: input.run_id, + owner_user_id: input.owner_user_id, + x: input.x, + y: input.y, + submitted_at_micros: input.submitted_at_micros, + }; + + self.call_after_connect(move |connection, sender| { + connection.procedures().submit_big_fish_input_then( + procedure_input, + move |_, result| { + let mapped = result + .map_err(SpacetimeClientError::from_sdk_error) + .and_then(map_big_fish_run_procedure_result); + send_once(&sender, mapped); + }, + ); + }) + .await + } } diff --git a/server-rs/crates/spacetime-client/src/lib.rs b/server-rs/crates/spacetime-client/src/lib.rs index b7d56ff7..b9776fca 100644 --- a/server-rs/crates/spacetime-client/src/lib.rs +++ b/server-rs/crates/spacetime-client/src/lib.rs @@ -9,9 +9,11 @@ pub use mapper::{ AiTextChunkRecord, BattleStateRecord, BigFishAgentMessageRecord, BigFishAnchorItemRecord, BigFishAnchorPackRecord, BigFishAssetCoverageRecord, BigFishAssetGenerateRecordInput, BigFishAssetSlotRecord, BigFishBackgroundBlueprintRecord, BigFishDraftCompileRecordInput, - BigFishGameDraftRecord, BigFishLevelBlueprintRecord, BigFishMessageFinalizeRecordInput, - BigFishMessageSubmitRecordInput, BigFishPlayReportRecordInput, BigFishRuntimeParamsRecord, - BigFishSessionCreateRecordInput, BigFishSessionRecord, BigFishWorkSummaryRecord, + BigFishGameDraftRecord, BigFishInputSubmitRecordInput, BigFishLevelBlueprintRecord, + BigFishMessageFinalizeRecordInput, BigFishMessageSubmitRecordInput, + BigFishPlayReportRecordInput, BigFishRunStartRecordInput, BigFishRuntimeEntityRecord, + BigFishRuntimeParamsRecord, BigFishRuntimeRunRecord, BigFishSessionCreateRecordInput, + BigFishSessionRecord, BigFishVector2Record, BigFishWorkSummaryRecord, CustomWorldAgentActionExecuteRecord, CustomWorldAgentActionExecuteRecordInput, CustomWorldAgentCheckpointRecord, CustomWorldAgentMessageFinalizeRecordInput, CustomWorldAgentMessageRecord, CustomWorldAgentMessageSubmitRecordInput, diff --git a/server-rs/crates/spacetime-client/src/mapper.rs b/server-rs/crates/spacetime-client/src/mapper.rs index 7d84ff90..162c6c4e 100644 --- a/server-rs/crates/spacetime-client/src/mapper.rs +++ b/server-rs/crates/spacetime-client/src/mapper.rs @@ -1169,6 +1169,7 @@ pub(crate) fn map_big_fish_works_procedure_result( result: BigFishWorksProcedureResult, fallback_owner_user_id: Option<&str>, ) -> Result, SpacetimeClientError> { + let _ = fallback_owner_user_id; if !result.ok { return Err(SpacetimeClientError::procedure_failed(result.error_message)); } @@ -1176,15 +1177,26 @@ pub(crate) fn map_big_fish_works_procedure_result( let items_json = result .items_json .ok_or_else(|| SpacetimeClientError::missing_snapshot("big fish works 快照"))?; - let items = serde_json::from_str::>(&items_json) - .map_err(|error| { - SpacetimeClientError::Runtime(format!("big fish works items_json 非法: {error}")) - })?; + serde_json::from_str::>(&items_json).map_err(|error| { + SpacetimeClientError::Runtime(format!("big fish works items_json 非法: {error}")) + }) +} - Ok(items - .into_iter() - .map(|item| item.into_record(fallback_owner_user_id)) - .collect()) +pub(crate) fn map_big_fish_run_procedure_result( + result: BigFishRunProcedureResult, +) -> Result { + if !result.ok { + return Err(SpacetimeClientError::procedure_failed(result.error_message)); + } + + let run_json = result + .run_json + .ok_or_else(|| SpacetimeClientError::missing_snapshot("big fish run 快照"))?; + let run: module_big_fish::BigFishRuntimeSnapshot = + serde_json::from_str(&run_json).map_err(|error| { + SpacetimeClientError::Runtime(format!("big fish run run_json 非法: {error}")) + })?; + Ok(map_big_fish_runtime_snapshot(run)) } pub(crate) fn map_story_session_procedure_result( @@ -2396,6 +2408,53 @@ pub(crate) fn map_big_fish_agent_message_snapshot( } } +pub(crate) fn map_big_fish_runtime_snapshot( + snapshot: module_big_fish::BigFishRuntimeSnapshot, +) -> BigFishRuntimeRunRecord { + BigFishRuntimeRunRecord { + run_id: snapshot.run_id, + session_id: snapshot.session_id, + status: snapshot.status.as_str().to_string(), + tick: snapshot.tick, + player_level: snapshot.player_level, + win_level: snapshot.win_level, + leader_entity_id: snapshot.leader_entity_id, + owned_entities: snapshot + .owned_entities + .into_iter() + .map(map_big_fish_runtime_entity_snapshot) + .collect(), + wild_entities: snapshot + .wild_entities + .into_iter() + .map(map_big_fish_runtime_entity_snapshot) + .collect(), + camera_center: map_big_fish_vector2(snapshot.camera_center), + last_input: map_big_fish_vector2(snapshot.last_input), + event_log: snapshot.event_log, + updated_at: format_timestamp_micros(snapshot.updated_at_micros), + } +} + +fn map_big_fish_runtime_entity_snapshot( + snapshot: module_big_fish::BigFishRuntimeEntitySnapshot, +) -> BigFishRuntimeEntityRecord { + BigFishRuntimeEntityRecord { + entity_id: snapshot.entity_id, + level: snapshot.level, + position: map_big_fish_vector2(snapshot.position), + radius: snapshot.radius, + offscreen_seconds: snapshot.offscreen_seconds, + } +} + +fn map_big_fish_vector2(snapshot: module_big_fish::BigFishVector2) -> BigFishVector2Record { + BigFishVector2Record { + x: snapshot.x, + y: snapshot.y, + } +} + pub(crate) fn map_story_session_snapshot(snapshot: StorySessionSnapshot) -> StorySessionRecord { StorySessionRecord { story_session_id: snapshot.story_session_id, @@ -4143,6 +4202,23 @@ pub struct BigFishPlayReportRecordInput { pub reported_at_micros: i64, } +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct BigFishRunStartRecordInput { + pub run_id: String, + pub session_id: String, + pub owner_user_id: String, + pub started_at_micros: i64, +} + +#[derive(Clone, Debug, PartialEq)] +pub struct BigFishInputSubmitRecordInput { + pub run_id: String, + pub owner_user_id: String, + pub x: f32, + pub y: f32, + pub submitted_at_micros: i64, +} + #[derive(Clone, Debug, PartialEq, Eq)] pub struct PuzzleAnchorItemRecord { pub key: String, @@ -4527,6 +4603,38 @@ pub struct BigFishSessionRecord { pub updated_at: String, } +#[derive(Clone, Debug, PartialEq)] +pub struct BigFishVector2Record { + pub x: f32, + pub y: f32, +} + +#[derive(Clone, Debug, PartialEq)] +pub struct BigFishRuntimeEntityRecord { + pub entity_id: String, + pub level: u32, + pub position: BigFishVector2Record, + pub radius: f32, + pub offscreen_seconds: f32, +} + +#[derive(Clone, Debug, PartialEq)] +pub struct BigFishRuntimeRunRecord { + pub run_id: String, + pub session_id: String, + pub status: String, + pub tick: u64, + pub player_level: u32, + pub win_level: u32, + pub leader_entity_id: Option, + pub owned_entities: Vec, + pub wild_entities: Vec, + pub camera_center: BigFishVector2Record, + pub last_input: BigFishVector2Record, + pub event_log: Vec, + pub updated_at: String, +} + #[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)] pub struct BigFishWorkSummaryRecord { pub work_id: String, @@ -4546,123 +4654,6 @@ pub struct BigFishWorkSummaryRecord { pub play_count: u32, } -#[derive(Clone, Debug, PartialEq, Eq, serde::Deserialize)] -struct CompatibleBigFishWorkSummaryRecord { - work_id: String, - source_session_id: String, - #[serde(default)] - owner_user_id: Option, - title: String, - subtitle: String, - summary: String, - cover_image_src: Option, - status: String, - updated_at_micros: i64, - publish_ready: bool, - level_count: u32, - level_main_image_ready_count: u32, - level_motion_ready_count: u32, - background_ready: bool, - #[serde(default)] - play_count: u32, -} - -impl CompatibleBigFishWorkSummaryRecord { - fn into_record(self, fallback_owner_user_id: Option<&str>) -> BigFishWorkSummaryRecord { - BigFishWorkSummaryRecord { - work_id: self.work_id, - source_session_id: self.source_session_id, - // 中文注释:兼容旧 works JSON 没有 owner_user_id 的历史数据,避免一次字段升级把整个作品列表打崩。 - owner_user_id: self.owner_user_id.unwrap_or_else(|| { - fallback_owner_user_id - .map(str::to_string) - .unwrap_or_default() - }), - title: self.title, - subtitle: self.subtitle, - summary: self.summary, - cover_image_src: self.cover_image_src, - status: self.status, - updated_at_micros: self.updated_at_micros, - publish_ready: self.publish_ready, - level_count: self.level_count, - level_main_image_ready_count: self.level_main_image_ready_count, - level_motion_ready_count: self.level_motion_ready_count, - background_ready: self.background_ready, - play_count: self.play_count, - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn big_fish_works_mapper_backfills_missing_owner_user_id_for_private_lists() { - let result = BigFishWorksProcedureResult { - ok: true, - items_json: Some( - r#"[{ - "work_id":"big-fish-work-session-1", - "source_session_id":"session-1", - "title":"深海草稿", - "subtitle":"副标题", - "summary":"摘要", - "cover_image_src":null, - "status":"draft", - "updated_at_micros":123, - "publish_ready":false, - "level_count":8, - "level_main_image_ready_count":0, - "level_motion_ready_count":0, - "background_ready":false - }]"# - .to_string(), - ), - error_message: None, - }; - - let items = map_big_fish_works_procedure_result(result, Some("user-1")) - .expect("旧 works JSON 应能被兼容解析"); - - assert_eq!(items.len(), 1); - assert_eq!(items[0].owner_user_id, "user-1"); - } - - #[test] - fn big_fish_works_mapper_keeps_empty_owner_when_gallery_legacy_json_lacks_field() { - let result = BigFishWorksProcedureResult { - ok: true, - items_json: Some( - r#"[{ - "work_id":"big-fish-work-session-2", - "source_session_id":"session-2", - "title":"公开作品", - "subtitle":"副标题", - "summary":"摘要", - "cover_image_src":null, - "status":"published", - "updated_at_micros":456, - "publish_ready":true, - "level_count":8, - "level_main_image_ready_count":8, - "level_motion_ready_count":16, - "background_ready":true - }]"# - .to_string(), - ), - error_message: None, - }; - - let items = map_big_fish_works_procedure_result(result, None) - .expect("公开 works 旧 JSON 也不应因缺字段报错"); - - assert_eq!(items.len(), 1); - assert!(items[0].owner_user_id.is_empty()); - } -} - #[derive(Clone, Debug, PartialEq, Eq)] pub struct ResolveNpcBattleInteractionInput { pub npc_interaction: DomainResolveNpcInteractionInput, diff --git a/server-rs/crates/spacetime-client/src/module_bindings/accept_quest_reducer.rs b/server-rs/crates/spacetime-client/src/module_bindings/accept_quest_reducer.rs index dfebf903..b9e670d6 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/accept_quest_reducer.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/accept_quest_reducer.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::quest_record_input_type::QuestRecordInput; @@ -14,8 +19,10 @@ pub(super) struct AcceptQuestArgs { impl From for super::Reducer { fn from(args: AcceptQuestArgs) -> Self { - Self::AcceptQuest { input: args.input } - } + Self::AcceptQuest { + input: args.input, +} +} } impl __sdk::InModule for AcceptQuestArgs { @@ -33,8 +40,9 @@ pub trait accept_quest { /// The reducer will run asynchronously in the future, /// and this method provides no way to listen for its completion status. /// /// Use [`accept_quest:accept_quest_then`] to run a callback after the reducer completes. - fn accept_quest(&self, input: QuestRecordInput) -> __sdk::Result<()> { - self.accept_quest_then(input, |_, _| {}) + fn accept_quest(&self, input: QuestRecordInput, +) -> __sdk::Result<()> { + self.accept_quest_then(input, |_, _| {}) } /// Request that the remote module invoke the reducer `accept_quest` to run as soon as possible, @@ -62,7 +70,7 @@ impl accept_quest for super::RemoteReducers { + Send + 'static, ) -> __sdk::Result<()> { - self.imp - .invoke_reducer_with_callback(AcceptQuestArgs { input }, callback) + self.imp.invoke_reducer_with_callback(AcceptQuestArgs { input, }, callback) } } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/acknowledge_quest_completion_reducer.rs b/server-rs/crates/spacetime-client/src/module_bindings/acknowledge_quest_completion_reducer.rs index 6ae2fd10..6027c9f6 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/acknowledge_quest_completion_reducer.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/acknowledge_quest_completion_reducer.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::quest_completion_ack_input_type::QuestCompletionAckInput; @@ -14,8 +19,10 @@ pub(super) struct AcknowledgeQuestCompletionArgs { impl From for super::Reducer { fn from(args: AcknowledgeQuestCompletionArgs) -> Self { - Self::AcknowledgeQuestCompletion { input: args.input } - } + Self::AcknowledgeQuestCompletion { + input: args.input, +} +} } impl __sdk::InModule for AcknowledgeQuestCompletionArgs { @@ -33,8 +40,9 @@ pub trait acknowledge_quest_completion { /// The reducer will run asynchronously in the future, /// and this method provides no way to listen for its completion status. /// /// Use [`acknowledge_quest_completion:acknowledge_quest_completion_then`] to run a callback after the reducer completes. - fn acknowledge_quest_completion(&self, input: QuestCompletionAckInput) -> __sdk::Result<()> { - self.acknowledge_quest_completion_then(input, |_, _| {}) + fn acknowledge_quest_completion(&self, input: QuestCompletionAckInput, +) -> __sdk::Result<()> { + self.acknowledge_quest_completion_then(input, |_, _| {}) } /// Request that the remote module invoke the reducer `acknowledge_quest_completion` to run as soon as possible, @@ -62,7 +70,7 @@ impl acknowledge_quest_completion for super::RemoteReducers { + Send + 'static, ) -> __sdk::Result<()> { - self.imp - .invoke_reducer_with_callback(AcknowledgeQuestCompletionArgs { input }, callback) + self.imp.invoke_reducer_with_callback(AcknowledgeQuestCompletionArgs { input, }, callback) } } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/admin_disable_profile_redeem_code_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/admin_disable_profile_redeem_code_procedure.rs index bbdaab4f..ca3cf7df 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/admin_disable_profile_redeem_code_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/admin_disable_profile_redeem_code_procedure.rs @@ -2,17 +2,23 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::runtime_profile_redeem_code_admin_disable_input_type::RuntimeProfileRedeemCodeAdminDisableInput; use super::runtime_profile_redeem_code_admin_procedure_result_type::RuntimeProfileRedeemCodeAdminProcedureResult; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] -struct AdminDisableProfileRedeemCodeArgs { + struct AdminDisableProfileRedeemCodeArgs { pub input: RuntimeProfileRedeemCodeAdminDisableInput, } + impl __sdk::InModule for AdminDisableProfileRedeemCodeArgs { type Module = super::RemoteModule; } @@ -22,19 +28,16 @@ impl __sdk::InModule for AdminDisableProfileRedeemCodeArgs { /// /// Implemented for [`super::RemoteProcedures`]. pub trait admin_disable_profile_redeem_code { - fn admin_disable_profile_redeem_code(&self, input: RuntimeProfileRedeemCodeAdminDisableInput) { - self.admin_disable_profile_redeem_code_then(input, |_, _| {}); + fn admin_disable_profile_redeem_code(&self, input: RuntimeProfileRedeemCodeAdminDisableInput, +) { + self.admin_disable_profile_redeem_code_then(input, |_, _| {}); } fn admin_disable_profile_redeem_code_then( &self, input: RuntimeProfileRedeemCodeAdminDisableInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ); } @@ -43,17 +46,13 @@ impl admin_disable_profile_redeem_code for super::RemoteProcedures { &self, input: RuntimeProfileRedeemCodeAdminDisableInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ) { - self.imp - .invoke_procedure_with_callback::<_, RuntimeProfileRedeemCodeAdminProcedureResult>( - "admin_disable_profile_redeem_code", - AdminDisableProfileRedeemCodeArgs { input }, - __callback, - ); + self.imp.invoke_procedure_with_callback::<_, RuntimeProfileRedeemCodeAdminProcedureResult>( + "admin_disable_profile_redeem_code", + AdminDisableProfileRedeemCodeArgs { input, }, + __callback, + ); } } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/admin_upsert_profile_redeem_code_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/admin_upsert_profile_redeem_code_procedure.rs index 7e918220..d58eeb31 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/admin_upsert_profile_redeem_code_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/admin_upsert_profile_redeem_code_procedure.rs @@ -2,17 +2,23 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::runtime_profile_redeem_code_admin_procedure_result_type::RuntimeProfileRedeemCodeAdminProcedureResult; use super::runtime_profile_redeem_code_admin_upsert_input_type::RuntimeProfileRedeemCodeAdminUpsertInput; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] -struct AdminUpsertProfileRedeemCodeArgs { + struct AdminUpsertProfileRedeemCodeArgs { pub input: RuntimeProfileRedeemCodeAdminUpsertInput, } + impl __sdk::InModule for AdminUpsertProfileRedeemCodeArgs { type Module = super::RemoteModule; } @@ -22,19 +28,16 @@ impl __sdk::InModule for AdminUpsertProfileRedeemCodeArgs { /// /// Implemented for [`super::RemoteProcedures`]. pub trait admin_upsert_profile_redeem_code { - fn admin_upsert_profile_redeem_code(&self, input: RuntimeProfileRedeemCodeAdminUpsertInput) { - self.admin_upsert_profile_redeem_code_then(input, |_, _| {}); + fn admin_upsert_profile_redeem_code(&self, input: RuntimeProfileRedeemCodeAdminUpsertInput, +) { + self.admin_upsert_profile_redeem_code_then(input, |_, _| {}); } fn admin_upsert_profile_redeem_code_then( &self, input: RuntimeProfileRedeemCodeAdminUpsertInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ); } @@ -43,17 +46,13 @@ impl admin_upsert_profile_redeem_code for super::RemoteProcedures { &self, input: RuntimeProfileRedeemCodeAdminUpsertInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ) { - self.imp - .invoke_procedure_with_callback::<_, RuntimeProfileRedeemCodeAdminProcedureResult>( - "admin_upsert_profile_redeem_code", - AdminUpsertProfileRedeemCodeArgs { input }, - __callback, - ); + self.imp.invoke_procedure_with_callback::<_, RuntimeProfileRedeemCodeAdminProcedureResult>( + "admin_upsert_profile_redeem_code", + AdminUpsertProfileRedeemCodeArgs { input, }, + __callback, + ); } } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/advance_puzzle_next_level_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/advance_puzzle_next_level_procedure.rs index 6d3e9f79..4a7804c7 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/advance_puzzle_next_level_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/advance_puzzle_next_level_procedure.rs @@ -2,17 +2,23 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::puzzle_run_next_level_input_type::PuzzleRunNextLevelInput; use super::puzzle_run_procedure_result_type::PuzzleRunProcedureResult; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] -struct AdvancePuzzleNextLevelArgs { + struct AdvancePuzzleNextLevelArgs { pub input: PuzzleRunNextLevelInput, } + impl __sdk::InModule for AdvancePuzzleNextLevelArgs { type Module = super::RemoteModule; } @@ -22,19 +28,16 @@ impl __sdk::InModule for AdvancePuzzleNextLevelArgs { /// /// Implemented for [`super::RemoteProcedures`]. pub trait advance_puzzle_next_level { - fn advance_puzzle_next_level(&self, input: PuzzleRunNextLevelInput) { - self.advance_puzzle_next_level_then(input, |_, _| {}); + fn advance_puzzle_next_level(&self, input: PuzzleRunNextLevelInput, +) { + self.advance_puzzle_next_level_then(input, |_, _| {}); } fn advance_puzzle_next_level_then( &self, input: PuzzleRunNextLevelInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ); } @@ -43,17 +46,13 @@ impl advance_puzzle_next_level for super::RemoteProcedures { &self, input: PuzzleRunNextLevelInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ) { - self.imp - .invoke_procedure_with_callback::<_, PuzzleRunProcedureResult>( - "advance_puzzle_next_level", - AdvancePuzzleNextLevelArgs { input }, - __callback, - ); + self.imp.invoke_procedure_with_callback::<_, PuzzleRunProcedureResult>( + "advance_puzzle_next_level", + AdvancePuzzleNextLevelArgs { input, }, + __callback, + ); } } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/ai_result_reference_input_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/ai_result_reference_input_type.rs index b63aad41..c22d39dd 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/ai_result_reference_input_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/ai_result_reference_input_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::ai_result_reference_kind_type::AiResultReferenceKind; @@ -12,10 +17,12 @@ pub struct AiResultReferenceInput { pub task_id: String, pub reference_kind: AiResultReferenceKind, pub reference_id: String, - pub label: Option, + pub label: Option::, pub created_at_micros: i64, } + impl __sdk::InModule for AiResultReferenceInput { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/ai_result_reference_kind_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/ai_result_reference_kind_type.rs index 0e5f045b..9b9a5a43 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/ai_result_reference_kind_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/ai_result_reference_kind_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -19,8 +24,12 @@ pub enum AiResultReferenceKind { RuntimeItemRecord, AssetObject, + } + + impl __sdk::InModule for AiResultReferenceKind { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/ai_result_reference_snapshot_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/ai_result_reference_snapshot_type.rs index f949db7f..7522ac4c 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/ai_result_reference_snapshot_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/ai_result_reference_snapshot_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::ai_result_reference_kind_type::AiResultReferenceKind; @@ -13,10 +18,12 @@ pub struct AiResultReferenceSnapshot { pub task_id: String, pub reference_kind: AiResultReferenceKind, pub reference_id: String, - pub label: Option, + pub label: Option::, pub created_at_micros: i64, } + impl __sdk::InModule for AiResultReferenceSnapshot { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/ai_result_reference_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/ai_result_reference_type.rs index 3b1121b0..d9e32eaf 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/ai_result_reference_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/ai_result_reference_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::ai_result_reference_kind_type::AiResultReferenceKind; @@ -14,14 +19,16 @@ pub struct AiResultReference { pub task_id: String, pub reference_kind: AiResultReferenceKind, pub reference_id: String, - pub label: Option, + pub label: Option::, pub created_at: __sdk::Timestamp, } + impl __sdk::InModule for AiResultReference { type Module = super::RemoteModule; } + /// Column accessor struct for the table `AiResultReference`. /// /// Provides typed access to columns for query building. @@ -31,7 +38,7 @@ pub struct AiResultReferenceCols { pub task_id: __sdk::__query_builder::Col, pub reference_kind: __sdk::__query_builder::Col, pub reference_id: __sdk::__query_builder::Col, - pub label: __sdk::__query_builder::Col>, + pub label: __sdk::__query_builder::Col>, pub created_at: __sdk::__query_builder::Col, } @@ -39,16 +46,14 @@ impl __sdk::__query_builder::HasCols for AiResultReference { type Cols = AiResultReferenceCols; fn cols(table_name: &'static str) -> Self::Cols { AiResultReferenceCols { - result_reference_row_id: __sdk::__query_builder::Col::new( - table_name, - "result_reference_row_id", - ), + result_reference_row_id: __sdk::__query_builder::Col::new(table_name, "result_reference_row_id"), result_ref_id: __sdk::__query_builder::Col::new(table_name, "result_ref_id"), task_id: __sdk::__query_builder::Col::new(table_name, "task_id"), reference_kind: __sdk::__query_builder::Col::new(table_name, "reference_kind"), reference_id: __sdk::__query_builder::Col::new(table_name, "reference_id"), label: __sdk::__query_builder::Col::new(table_name, "label"), created_at: __sdk::__query_builder::Col::new(table_name, "created_at"), + } } } @@ -65,13 +70,12 @@ impl __sdk::__query_builder::HasIxCols for AiResultReference { type IxCols = AiResultReferenceIxCols; fn ix_cols(table_name: &'static str) -> Self::IxCols { AiResultReferenceIxCols { - result_reference_row_id: __sdk::__query_builder::IxCol::new( - table_name, - "result_reference_row_id", - ), + result_reference_row_id: __sdk::__query_builder::IxCol::new(table_name, "result_reference_row_id"), task_id: __sdk::__query_builder::IxCol::new(table_name, "task_id"), + } } } impl __sdk::__query_builder::CanBeLookupTable for AiResultReference {} + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/ai_stage_completion_input_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/ai_stage_completion_input_type.rs index 7ce33006..c165fc55 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/ai_stage_completion_input_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/ai_stage_completion_input_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::ai_task_stage_kind_type::AiTaskStageKind; @@ -11,12 +16,14 @@ use super::ai_task_stage_kind_type::AiTaskStageKind; pub struct AiStageCompletionInput { pub task_id: String, pub stage_kind: AiTaskStageKind, - pub text_output: Option, - pub structured_payload_json: Option, - pub warning_messages: Vec, + pub text_output: Option::, + pub structured_payload_json: Option::, + pub warning_messages: Vec::, pub completed_at_micros: i64, } + impl __sdk::InModule for AiStageCompletionInput { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/ai_task_cancel_input_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/ai_task_cancel_input_type.rs index 93b697a6..1c5af5fa 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/ai_task_cancel_input_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/ai_task_cancel_input_type.rs @@ -2,7 +2,13 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; + #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -11,6 +17,8 @@ pub struct AiTaskCancelInput { pub completed_at_micros: i64, } + impl __sdk::InModule for AiTaskCancelInput { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/ai_task_create_input_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/ai_task_create_input_type.rs index 95b0506a..2d53ad93 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/ai_task_create_input_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/ai_task_create_input_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::ai_task_kind_type::AiTaskKind; use super::ai_task_stage_blueprint_type::AiTaskStageBlueprint; @@ -15,12 +20,14 @@ pub struct AiTaskCreateInput { pub owner_user_id: String, pub request_label: String, pub source_module: String, - pub source_entity_id: Option, - pub request_payload_json: Option, - pub stages: Vec, + pub source_entity_id: Option::, + pub request_payload_json: Option::, + pub stages: Vec::, pub created_at_micros: i64, } + impl __sdk::InModule for AiTaskCreateInput { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/ai_task_event_kind_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/ai_task_event_kind_type.rs new file mode 100644 index 00000000..c3929b3c --- /dev/null +++ b/server-rs/crates/spacetime-client/src/module_bindings/ai_task_event_kind_type.rs @@ -0,0 +1,35 @@ +// 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)] +#[derive(Copy, Eq, Hash)] +pub enum AiTaskEventKind { + TaskCreated, + + TaskStatusChanged, + + StageStarted, + + StageCompleted, + + TextChunkAppended, + + ResultReferenceAttached, + +} + + + +impl __sdk::InModule for AiTaskEventKind { + type Module = super::RemoteModule; +} + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/ai_task_event_table.rs b/server-rs/crates/spacetime-client/src/module_bindings/ai_task_event_table.rs new file mode 100644 index 00000000..897cd68d --- /dev/null +++ b/server-rs/crates/spacetime-client/src/module_bindings/ai_task_event_table.rs @@ -0,0 +1,105 @@ +// 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::ai_task_event_type::AiTaskEvent; +use super::ai_task_status_type::AiTaskStatus; +use super::ai_task_event_kind_type::AiTaskEventKind; +use super::ai_task_stage_kind_type::AiTaskStageKind; + +/// Table handle for the table `ai_task_event`. +/// +/// Obtain a handle from the [`AiTaskEventTableAccess::ai_task_event`] method on [`super::RemoteTables`], +/// like `ctx.db.ai_task_event()`. +/// +/// Users are encouraged not to explicitly reference this type, +/// but to directly chain method calls, +/// like `ctx.db.ai_task_event().on_insert(...)`. +pub struct AiTaskEventTableHandle<'ctx> { + imp: __sdk::TableHandle, + ctx: std::marker::PhantomData<&'ctx super::RemoteTables>, +} + +#[allow(non_camel_case_types)] +/// Extension trait for access to the table `ai_task_event`. +/// +/// Implemented for [`super::RemoteTables`]. +pub trait AiTaskEventTableAccess { + #[allow(non_snake_case)] + /// Obtain a [`AiTaskEventTableHandle`], which mediates access to the table `ai_task_event`. + fn ai_task_event(&self) -> AiTaskEventTableHandle<'_>; +} + +impl AiTaskEventTableAccess for super::RemoteTables { + fn ai_task_event(&self) -> AiTaskEventTableHandle<'_> { + AiTaskEventTableHandle { + imp: self.imp.get_table::("ai_task_event"), + ctx: std::marker::PhantomData, + } + } +} + +pub struct AiTaskEventInsertCallbackId(__sdk::CallbackId); + +impl<'ctx> __sdk::EventTable for AiTaskEventTableHandle<'ctx> { + type Row = AiTaskEvent; + type EventContext = super::EventContext; + + fn count(&self) -> u64 { self.imp.count() } + fn iter(&self) -> impl Iterator + '_ { self.imp.iter() } + + type InsertCallbackId = AiTaskEventInsertCallbackId; + + fn on_insert( + &self, + callback: impl FnMut(&Self::EventContext, &Self::Row) + Send + 'static, + ) -> AiTaskEventInsertCallbackId { + AiTaskEventInsertCallbackId(self.imp.on_insert(Box::new(callback))) + } + + fn remove_on_insert(&self, callback: AiTaskEventInsertCallbackId) { + self.imp.remove_on_insert(callback.0) + } +} + +#[doc(hidden)] +pub(super) fn register_table(client_cache: &mut __sdk::ClientCache) { + + let _table = client_cache.get_or_make_table::("ai_task_event"); + _table.add_unique_constraint::("event_id", |row| &row.event_id); +} + +#[doc(hidden)] +pub(super) fn parse_table_update( + raw_updates: __ws::v2::TableUpdate, +) -> __sdk::Result<__sdk::TableUpdate> { + __sdk::TableUpdate::parse_table_update(raw_updates).map_err(|e| { + __sdk::InternalError::failed_parse( + "TableUpdate", + "TableUpdate", + ).with_cause(e).into() + }) +} + + #[allow(non_camel_case_types)] + /// Extension trait for query builder access to the table `AiTaskEvent`. + /// + /// Implemented for [`__sdk::QueryTableAccessor`]. + pub trait ai_task_eventQueryTableAccess { + #[allow(non_snake_case)] + /// Get a query builder for the table `AiTaskEvent`. + fn ai_task_event(&self) -> __sdk::__query_builder::Table; + } + + impl ai_task_eventQueryTableAccess for __sdk::QueryTableAccessor { + fn ai_task_event(&self) -> __sdk::__query_builder::Table { + __sdk::__query_builder::Table::new("ai_task_event") + } + } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/ai_task_event_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/ai_task_event_type.rs new file mode 100644 index 00000000..74497662 --- /dev/null +++ b/server-rs/crates/spacetime-client/src/module_bindings/ai_task_event_type.rs @@ -0,0 +1,89 @@ +// 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::ai_task_status_type::AiTaskStatus; +use super::ai_task_event_kind_type::AiTaskEventKind; +use super::ai_task_stage_kind_type::AiTaskStageKind; + +#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] +#[sats(crate = __lib)] +pub struct AiTaskEvent { + pub event_id: String, + pub task_id: String, + pub owner_user_id: String, + pub event_kind: AiTaskEventKind, + pub task_status: Option::, + pub stage_kind: Option::, + pub text_chunk_row_id: Option::, + pub result_reference_row_id: Option::, + pub occurred_at: __sdk::Timestamp, +} + + +impl __sdk::InModule for AiTaskEvent { + type Module = super::RemoteModule; +} + + +/// Column accessor struct for the table `AiTaskEvent`. +/// +/// Provides typed access to columns for query building. +pub struct AiTaskEventCols { + pub event_id: __sdk::__query_builder::Col, + pub task_id: __sdk::__query_builder::Col, + pub owner_user_id: __sdk::__query_builder::Col, + pub event_kind: __sdk::__query_builder::Col, + pub task_status: __sdk::__query_builder::Col>, + pub stage_kind: __sdk::__query_builder::Col>, + pub text_chunk_row_id: __sdk::__query_builder::Col>, + pub result_reference_row_id: __sdk::__query_builder::Col>, + pub occurred_at: __sdk::__query_builder::Col, +} + +impl __sdk::__query_builder::HasCols for AiTaskEvent { + type Cols = AiTaskEventCols; + fn cols(table_name: &'static str) -> Self::Cols { + AiTaskEventCols { + event_id: __sdk::__query_builder::Col::new(table_name, "event_id"), + task_id: __sdk::__query_builder::Col::new(table_name, "task_id"), + owner_user_id: __sdk::__query_builder::Col::new(table_name, "owner_user_id"), + event_kind: __sdk::__query_builder::Col::new(table_name, "event_kind"), + task_status: __sdk::__query_builder::Col::new(table_name, "task_status"), + stage_kind: __sdk::__query_builder::Col::new(table_name, "stage_kind"), + text_chunk_row_id: __sdk::__query_builder::Col::new(table_name, "text_chunk_row_id"), + result_reference_row_id: __sdk::__query_builder::Col::new(table_name, "result_reference_row_id"), + occurred_at: __sdk::__query_builder::Col::new(table_name, "occurred_at"), + + } + } +} + +/// Indexed column accessor struct for the table `AiTaskEvent`. +/// +/// Provides typed access to indexed columns for query building. +pub struct AiTaskEventIxCols { + pub event_id: __sdk::__query_builder::IxCol, + pub owner_user_id: __sdk::__query_builder::IxCol, + pub task_id: __sdk::__query_builder::IxCol, +} + +impl __sdk::__query_builder::HasIxCols for AiTaskEvent { + type IxCols = AiTaskEventIxCols; + fn ix_cols(table_name: &'static str) -> Self::IxCols { + AiTaskEventIxCols { + event_id: __sdk::__query_builder::IxCol::new(table_name, "event_id"), + owner_user_id: __sdk::__query_builder::IxCol::new(table_name, "owner_user_id"), + task_id: __sdk::__query_builder::IxCol::new(table_name, "task_id"), + + } + } +} + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/ai_task_failure_input_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/ai_task_failure_input_type.rs index 91a87cf5..dfdd6ee6 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/ai_task_failure_input_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/ai_task_failure_input_type.rs @@ -2,7 +2,13 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; + #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -12,6 +18,8 @@ pub struct AiTaskFailureInput { pub completed_at_micros: i64, } + impl __sdk::InModule for AiTaskFailureInput { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/ai_task_finish_input_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/ai_task_finish_input_type.rs index d8fe6713..f7bc9735 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/ai_task_finish_input_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/ai_task_finish_input_type.rs @@ -2,7 +2,13 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; + #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -11,6 +17,8 @@ pub struct AiTaskFinishInput { pub completed_at_micros: i64, } + impl __sdk::InModule for AiTaskFinishInput { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/ai_task_kind_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/ai_task_kind_type.rs index 9468bc95..7724e312 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/ai_task_kind_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/ai_task_kind_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -19,8 +24,12 @@ pub enum AiTaskKind { QuestIntent, RuntimeItemIntent, + } + + impl __sdk::InModule for AiTaskKind { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/ai_task_procedure_result_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/ai_task_procedure_result_type.rs index 57728f6e..f0457945 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/ai_task_procedure_result_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/ai_task_procedure_result_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::ai_task_snapshot_type::AiTaskSnapshot; use super::ai_text_chunk_snapshot_type::AiTextChunkSnapshot; @@ -11,11 +16,13 @@ use super::ai_text_chunk_snapshot_type::AiTextChunkSnapshot; #[sats(crate = __lib)] pub struct AiTaskProcedureResult { pub ok: bool, - pub task: Option, - pub text_chunk: Option, - pub error_message: Option, + pub task: Option::, + pub text_chunk: Option::, + pub error_message: Option::, } + impl __sdk::InModule for AiTaskProcedureResult { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/ai_task_snapshot_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/ai_task_snapshot_type.rs index 66fa9a15..c0259393 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/ai_task_snapshot_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/ai_task_snapshot_type.rs @@ -2,12 +2,17 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; -use super::ai_result_reference_snapshot_type::AiResultReferenceSnapshot; use super::ai_task_kind_type::AiTaskKind; -use super::ai_task_stage_snapshot_type::AiTaskStageSnapshot; use super::ai_task_status_type::AiTaskStatus; +use super::ai_task_stage_snapshot_type::AiTaskStageSnapshot; +use super::ai_result_reference_snapshot_type::AiResultReferenceSnapshot; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -17,21 +22,23 @@ pub struct AiTaskSnapshot { pub owner_user_id: String, pub request_label: String, pub source_module: String, - pub source_entity_id: Option, - pub request_payload_json: Option, + pub source_entity_id: Option::, + pub request_payload_json: Option::, pub status: AiTaskStatus, - pub failure_message: Option, - pub stages: Vec, - pub result_references: Vec, - pub latest_text_output: Option, - pub latest_structured_payload_json: Option, + pub failure_message: Option::, + pub stages: Vec::, + pub result_references: Vec::, + pub latest_text_output: Option::, + pub latest_structured_payload_json: Option::, pub version: u32, pub created_at_micros: i64, - pub started_at_micros: Option, - pub completed_at_micros: Option, + pub started_at_micros: Option::, + pub completed_at_micros: Option::, pub updated_at_micros: i64, } + impl __sdk::InModule for AiTaskSnapshot { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/ai_task_stage_blueprint_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/ai_task_stage_blueprint_type.rs index 8ed04d04..366ca586 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/ai_task_stage_blueprint_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/ai_task_stage_blueprint_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::ai_task_stage_kind_type::AiTaskStageKind; @@ -15,6 +20,8 @@ pub struct AiTaskStageBlueprint { pub order: u32, } + impl __sdk::InModule for AiTaskStageBlueprint { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/ai_task_stage_kind_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/ai_task_stage_kind_type.rs index 1d7405de..679241ae 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/ai_task_stage_kind_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/ai_task_stage_kind_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -17,8 +22,12 @@ pub enum AiTaskStageKind { NormalizeResult, PersistResult, + } + + impl __sdk::InModule for AiTaskStageKind { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/ai_task_stage_snapshot_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/ai_task_stage_snapshot_type.rs index 30cdb845..9d449d38 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/ai_task_stage_snapshot_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/ai_task_stage_snapshot_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::ai_task_stage_kind_type::AiTaskStageKind; use super::ai_task_stage_status_type::AiTaskStageStatus; @@ -15,13 +20,15 @@ pub struct AiTaskStageSnapshot { pub detail: String, pub order: u32, pub status: AiTaskStageStatus, - pub text_output: Option, - pub structured_payload_json: Option, - pub warning_messages: Vec, - pub started_at_micros: Option, - pub completed_at_micros: Option, + pub text_output: Option::, + pub structured_payload_json: Option::, + pub warning_messages: Vec::, + pub started_at_micros: Option::, + pub completed_at_micros: Option::, } + impl __sdk::InModule for AiTaskStageSnapshot { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/ai_task_stage_start_input_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/ai_task_stage_start_input_type.rs index 956fff88..8571c6d8 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/ai_task_stage_start_input_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/ai_task_stage_start_input_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::ai_task_stage_kind_type::AiTaskStageKind; @@ -14,6 +19,8 @@ pub struct AiTaskStageStartInput { pub started_at_micros: i64, } + impl __sdk::InModule for AiTaskStageStartInput { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/ai_task_stage_status_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/ai_task_stage_status_type.rs index e1a28d7c..3f60897b 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/ai_task_stage_status_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/ai_task_stage_status_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -15,8 +20,12 @@ pub enum AiTaskStageStatus { Completed, Skipped, + } + + impl __sdk::InModule for AiTaskStageStatus { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/ai_task_stage_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/ai_task_stage_type.rs index f4c902de..56a4094e 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/ai_task_stage_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/ai_task_stage_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::ai_task_stage_kind_type::AiTaskStageKind; use super::ai_task_stage_status_type::AiTaskStageStatus; @@ -17,17 +22,19 @@ pub struct AiTaskStage { pub detail: String, pub stage_order: u32, pub status: AiTaskStageStatus, - pub text_output: Option, - pub structured_payload_json: Option, - pub warning_messages: Vec, - pub started_at: Option<__sdk::Timestamp>, - pub completed_at: Option<__sdk::Timestamp>, + pub text_output: Option::, + pub structured_payload_json: Option::, + pub warning_messages: Vec::, + pub started_at: Option::<__sdk::Timestamp>, + pub completed_at: Option::<__sdk::Timestamp>, } + impl __sdk::InModule for AiTaskStage { type Module = super::RemoteModule; } + /// Column accessor struct for the table `AiTaskStage`. /// /// Provides typed access to columns for query building. @@ -39,11 +46,11 @@ pub struct AiTaskStageCols { pub detail: __sdk::__query_builder::Col, pub stage_order: __sdk::__query_builder::Col, pub status: __sdk::__query_builder::Col, - pub text_output: __sdk::__query_builder::Col>, - pub structured_payload_json: __sdk::__query_builder::Col>, - pub warning_messages: __sdk::__query_builder::Col>, - pub started_at: __sdk::__query_builder::Col>, - pub completed_at: __sdk::__query_builder::Col>, + pub text_output: __sdk::__query_builder::Col>, + pub structured_payload_json: __sdk::__query_builder::Col>, + pub warning_messages: __sdk::__query_builder::Col>, + pub started_at: __sdk::__query_builder::Col>, + pub completed_at: __sdk::__query_builder::Col>, } impl __sdk::__query_builder::HasCols for AiTaskStage { @@ -58,13 +65,11 @@ impl __sdk::__query_builder::HasCols for AiTaskStage { stage_order: __sdk::__query_builder::Col::new(table_name, "stage_order"), status: __sdk::__query_builder::Col::new(table_name, "status"), text_output: __sdk::__query_builder::Col::new(table_name, "text_output"), - structured_payload_json: __sdk::__query_builder::Col::new( - table_name, - "structured_payload_json", - ), + structured_payload_json: __sdk::__query_builder::Col::new(table_name, "structured_payload_json"), warning_messages: __sdk::__query_builder::Col::new(table_name, "warning_messages"), started_at: __sdk::__query_builder::Col::new(table_name, "started_at"), completed_at: __sdk::__query_builder::Col::new(table_name, "completed_at"), + } } } @@ -83,8 +88,10 @@ impl __sdk::__query_builder::HasIxCols for AiTaskStage { AiTaskStageIxCols { task_id: __sdk::__query_builder::IxCol::new(table_name, "task_id"), task_stage_id: __sdk::__query_builder::IxCol::new(table_name, "task_stage_id"), + } } } impl __sdk::__query_builder::CanBeLookupTable for AiTaskStage {} + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/ai_task_start_input_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/ai_task_start_input_type.rs index d2145e81..14fa5acb 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/ai_task_start_input_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/ai_task_start_input_type.rs @@ -2,7 +2,13 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; + #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -11,6 +17,8 @@ pub struct AiTaskStartInput { pub started_at_micros: i64, } + impl __sdk::InModule for AiTaskStartInput { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/ai_task_status_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/ai_task_status_type.rs index 3c59cd6a..58f1f7a2 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/ai_task_status_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/ai_task_status_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -17,8 +22,12 @@ pub enum AiTaskStatus { Failed, Cancelled, + } + + impl __sdk::InModule for AiTaskStatus { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/ai_task_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/ai_task_type.rs index 6b2845fc..96d4077f 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/ai_task_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/ai_task_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::ai_task_kind_type::AiTaskKind; use super::ai_task_status_type::AiTaskStatus; @@ -15,23 +20,25 @@ pub struct AiTask { pub owner_user_id: String, pub request_label: String, pub source_module: String, - pub source_entity_id: Option, - pub request_payload_json: Option, + pub source_entity_id: Option::, + pub request_payload_json: Option::, pub status: AiTaskStatus, - pub failure_message: Option, - pub latest_text_output: Option, - pub latest_structured_payload_json: Option, + pub failure_message: Option::, + pub latest_text_output: Option::, + pub latest_structured_payload_json: Option::, pub version: u32, pub created_at: __sdk::Timestamp, - pub started_at: Option<__sdk::Timestamp>, - pub completed_at: Option<__sdk::Timestamp>, + pub started_at: Option::<__sdk::Timestamp>, + pub completed_at: Option::<__sdk::Timestamp>, pub updated_at: __sdk::Timestamp, } + impl __sdk::InModule for AiTask { type Module = super::RemoteModule; } + /// Column accessor struct for the table `AiTask`. /// /// Provides typed access to columns for query building. @@ -41,16 +48,16 @@ pub struct AiTaskCols { pub owner_user_id: __sdk::__query_builder::Col, pub request_label: __sdk::__query_builder::Col, pub source_module: __sdk::__query_builder::Col, - pub source_entity_id: __sdk::__query_builder::Col>, - pub request_payload_json: __sdk::__query_builder::Col>, + pub source_entity_id: __sdk::__query_builder::Col>, + pub request_payload_json: __sdk::__query_builder::Col>, pub status: __sdk::__query_builder::Col, - pub failure_message: __sdk::__query_builder::Col>, - pub latest_text_output: __sdk::__query_builder::Col>, - pub latest_structured_payload_json: __sdk::__query_builder::Col>, + pub failure_message: __sdk::__query_builder::Col>, + pub latest_text_output: __sdk::__query_builder::Col>, + pub latest_structured_payload_json: __sdk::__query_builder::Col>, pub version: __sdk::__query_builder::Col, pub created_at: __sdk::__query_builder::Col, - pub started_at: __sdk::__query_builder::Col>, - pub completed_at: __sdk::__query_builder::Col>, + pub started_at: __sdk::__query_builder::Col>, + pub completed_at: __sdk::__query_builder::Col>, pub updated_at: __sdk::__query_builder::Col, } @@ -64,22 +71,17 @@ impl __sdk::__query_builder::HasCols for AiTask { request_label: __sdk::__query_builder::Col::new(table_name, "request_label"), source_module: __sdk::__query_builder::Col::new(table_name, "source_module"), source_entity_id: __sdk::__query_builder::Col::new(table_name, "source_entity_id"), - request_payload_json: __sdk::__query_builder::Col::new( - table_name, - "request_payload_json", - ), + request_payload_json: __sdk::__query_builder::Col::new(table_name, "request_payload_json"), status: __sdk::__query_builder::Col::new(table_name, "status"), failure_message: __sdk::__query_builder::Col::new(table_name, "failure_message"), latest_text_output: __sdk::__query_builder::Col::new(table_name, "latest_text_output"), - latest_structured_payload_json: __sdk::__query_builder::Col::new( - table_name, - "latest_structured_payload_json", - ), + latest_structured_payload_json: __sdk::__query_builder::Col::new(table_name, "latest_structured_payload_json"), version: __sdk::__query_builder::Col::new(table_name, "version"), created_at: __sdk::__query_builder::Col::new(table_name, "created_at"), started_at: __sdk::__query_builder::Col::new(table_name, "started_at"), completed_at: __sdk::__query_builder::Col::new(table_name, "completed_at"), updated_at: __sdk::__query_builder::Col::new(table_name, "updated_at"), + } } } @@ -102,8 +104,10 @@ impl __sdk::__query_builder::HasIxCols for AiTask { status: __sdk::__query_builder::IxCol::new(table_name, "status"), task_id: __sdk::__query_builder::IxCol::new(table_name, "task_id"), task_kind: __sdk::__query_builder::IxCol::new(table_name, "task_kind"), + } } } impl __sdk::__query_builder::CanBeLookupTable for AiTask {} + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/ai_text_chunk_append_input_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/ai_text_chunk_append_input_type.rs index 0f462690..11b5da68 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/ai_text_chunk_append_input_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/ai_text_chunk_append_input_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::ai_task_stage_kind_type::AiTaskStageKind; @@ -16,6 +21,8 @@ pub struct AiTextChunkAppendInput { pub created_at_micros: i64, } + impl __sdk::InModule for AiTextChunkAppendInput { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/ai_text_chunk_snapshot_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/ai_text_chunk_snapshot_type.rs index f386071c..81cb2ce9 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/ai_text_chunk_snapshot_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/ai_text_chunk_snapshot_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::ai_task_stage_kind_type::AiTaskStageKind; @@ -17,6 +22,8 @@ pub struct AiTextChunkSnapshot { pub created_at_micros: i64, } + impl __sdk::InModule for AiTextChunkSnapshot { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/ai_text_chunk_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/ai_text_chunk_type.rs index 8a1a8c93..df60c3a7 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/ai_text_chunk_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/ai_text_chunk_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::ai_task_stage_kind_type::AiTaskStageKind; @@ -18,10 +23,12 @@ pub struct AiTextChunk { pub created_at: __sdk::Timestamp, } + impl __sdk::InModule for AiTextChunk { type Module = super::RemoteModule; } + /// Column accessor struct for the table `AiTextChunk`. /// /// Provides typed access to columns for query building. @@ -46,6 +53,7 @@ impl __sdk::__query_builder::HasCols for AiTextChunk { sequence: __sdk::__query_builder::Col::new(table_name, "sequence"), delta_text: __sdk::__query_builder::Col::new(table_name, "delta_text"), created_at: __sdk::__query_builder::Col::new(table_name, "created_at"), + } } } @@ -64,8 +72,10 @@ impl __sdk::__query_builder::HasIxCols for AiTextChunk { AiTextChunkIxCols { task_id: __sdk::__query_builder::IxCol::new(table_name, "task_id"), text_chunk_row_id: __sdk::__query_builder::IxCol::new(table_name, "text_chunk_row_id"), + } } } impl __sdk::__query_builder::CanBeLookupTable for AiTextChunk {} + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/append_ai_text_chunk_and_return_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/append_ai_text_chunk_and_return_procedure.rs index 11323392..2edd4cbc 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/append_ai_text_chunk_and_return_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/append_ai_text_chunk_and_return_procedure.rs @@ -2,17 +2,23 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; -use super::ai_task_procedure_result_type::AiTaskProcedureResult; use super::ai_text_chunk_append_input_type::AiTextChunkAppendInput; +use super::ai_task_procedure_result_type::AiTaskProcedureResult; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] -struct AppendAiTextChunkAndReturnArgs { + struct AppendAiTextChunkAndReturnArgs { pub input: AiTextChunkAppendInput, } + impl __sdk::InModule for AppendAiTextChunkAndReturnArgs { type Module = super::RemoteModule; } @@ -22,19 +28,16 @@ impl __sdk::InModule for AppendAiTextChunkAndReturnArgs { /// /// Implemented for [`super::RemoteProcedures`]. pub trait append_ai_text_chunk_and_return { - fn append_ai_text_chunk_and_return(&self, input: AiTextChunkAppendInput) { - self.append_ai_text_chunk_and_return_then(input, |_, _| {}); + fn append_ai_text_chunk_and_return(&self, input: AiTextChunkAppendInput, +) { + self.append_ai_text_chunk_and_return_then(input, |_, _| {}); } fn append_ai_text_chunk_and_return_then( &self, input: AiTextChunkAppendInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ); } @@ -43,17 +46,13 @@ impl append_ai_text_chunk_and_return for super::RemoteProcedures { &self, input: AiTextChunkAppendInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ) { - self.imp - .invoke_procedure_with_callback::<_, AiTaskProcedureResult>( - "append_ai_text_chunk_and_return", - AppendAiTextChunkAndReturnArgs { input }, - __callback, - ); + self.imp.invoke_procedure_with_callback::<_, AiTaskProcedureResult>( + "append_ai_text_chunk_and_return", + AppendAiTextChunkAndReturnArgs { input, }, + __callback, + ); } } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/apply_chapter_progression_ledger_entry_and_return_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/apply_chapter_progression_ledger_entry_and_return_procedure.rs index bba4c841..e76eac6e 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/apply_chapter_progression_ledger_entry_and_return_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/apply_chapter_progression_ledger_entry_and_return_procedure.rs @@ -2,17 +2,23 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::chapter_progression_ledger_input_type::ChapterProgressionLedgerInput; use super::chapter_progression_procedure_result_type::ChapterProgressionProcedureResult; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] -struct ApplyChapterProgressionLedgerEntryAndReturnArgs { + struct ApplyChapterProgressionLedgerEntryAndReturnArgs { pub input: ChapterProgressionLedgerInput, } + impl __sdk::InModule for ApplyChapterProgressionLedgerEntryAndReturnArgs { type Module = super::RemoteModule; } @@ -22,22 +28,16 @@ impl __sdk::InModule for ApplyChapterProgressionLedgerEntryAndReturnArgs { /// /// Implemented for [`super::RemoteProcedures`]. pub trait apply_chapter_progression_ledger_entry_and_return { - fn apply_chapter_progression_ledger_entry_and_return( - &self, - input: ChapterProgressionLedgerInput, - ) { - self.apply_chapter_progression_ledger_entry_and_return_then(input, |_, _| {}); + fn apply_chapter_progression_ledger_entry_and_return(&self, input: ChapterProgressionLedgerInput, +) { + self.apply_chapter_progression_ledger_entry_and_return_then(input, |_, _| {}); } fn apply_chapter_progression_ledger_entry_and_return_then( &self, input: ChapterProgressionLedgerInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ); } @@ -46,17 +46,13 @@ impl apply_chapter_progression_ledger_entry_and_return for super::RemoteProcedur &self, input: ChapterProgressionLedgerInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ) { - self.imp - .invoke_procedure_with_callback::<_, ChapterProgressionProcedureResult>( - "apply_chapter_progression_ledger_entry_and_return", - ApplyChapterProgressionLedgerEntryAndReturnArgs { input }, - __callback, - ); + self.imp.invoke_procedure_with_callback::<_, ChapterProgressionProcedureResult>( + "apply_chapter_progression_ledger_entry_and_return", + ApplyChapterProgressionLedgerEntryAndReturnArgs { input, }, + __callback, + ); } } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/apply_chapter_progression_ledger_entry_reducer.rs b/server-rs/crates/spacetime-client/src/module_bindings/apply_chapter_progression_ledger_entry_reducer.rs index 98f821ef..04f54e54 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/apply_chapter_progression_ledger_entry_reducer.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/apply_chapter_progression_ledger_entry_reducer.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::chapter_progression_ledger_input_type::ChapterProgressionLedgerInput; @@ -14,8 +19,10 @@ pub(super) struct ApplyChapterProgressionLedgerEntryArgs { impl From for super::Reducer { fn from(args: ApplyChapterProgressionLedgerEntryArgs) -> Self { - Self::ApplyChapterProgressionLedgerEntry { input: args.input } - } + Self::ApplyChapterProgressionLedgerEntry { + input: args.input, +} +} } impl __sdk::InModule for ApplyChapterProgressionLedgerEntryArgs { @@ -33,11 +40,9 @@ pub trait apply_chapter_progression_ledger_entry { /// The reducer will run asynchronously in the future, /// and this method provides no way to listen for its completion status. /// /// Use [`apply_chapter_progression_ledger_entry:apply_chapter_progression_ledger_entry_then`] to run a callback after the reducer completes. - fn apply_chapter_progression_ledger_entry( - &self, - input: ChapterProgressionLedgerInput, - ) -> __sdk::Result<()> { - self.apply_chapter_progression_ledger_entry_then(input, |_, _| {}) + fn apply_chapter_progression_ledger_entry(&self, input: ChapterProgressionLedgerInput, +) -> __sdk::Result<()> { + self.apply_chapter_progression_ledger_entry_then(input, |_, _| {}) } /// Request that the remote module invoke the reducer `apply_chapter_progression_ledger_entry` to run as soon as possible, @@ -65,9 +70,7 @@ impl apply_chapter_progression_ledger_entry for super::RemoteReducers { + Send + 'static, ) -> __sdk::Result<()> { - self.imp.invoke_reducer_with_callback( - ApplyChapterProgressionLedgerEntryArgs { input }, - callback, - ) + self.imp.invoke_reducer_with_callback(ApplyChapterProgressionLedgerEntryArgs { input, }, callback) } } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/apply_inventory_mutation_reducer.rs b/server-rs/crates/spacetime-client/src/module_bindings/apply_inventory_mutation_reducer.rs index 91f7d2c0..4270f52a 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/apply_inventory_mutation_reducer.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/apply_inventory_mutation_reducer.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::inventory_mutation_input_type::InventoryMutationInput; @@ -14,8 +19,10 @@ pub(super) struct ApplyInventoryMutationArgs { impl From for super::Reducer { fn from(args: ApplyInventoryMutationArgs) -> Self { - Self::ApplyInventoryMutation { input: args.input } - } + Self::ApplyInventoryMutation { + input: args.input, +} +} } impl __sdk::InModule for ApplyInventoryMutationArgs { @@ -33,8 +40,9 @@ pub trait apply_inventory_mutation { /// The reducer will run asynchronously in the future, /// and this method provides no way to listen for its completion status. /// /// Use [`apply_inventory_mutation:apply_inventory_mutation_then`] to run a callback after the reducer completes. - fn apply_inventory_mutation(&self, input: InventoryMutationInput) -> __sdk::Result<()> { - self.apply_inventory_mutation_then(input, |_, _| {}) + fn apply_inventory_mutation(&self, input: InventoryMutationInput, +) -> __sdk::Result<()> { + self.apply_inventory_mutation_then(input, |_, _| {}) } /// Request that the remote module invoke the reducer `apply_inventory_mutation` to run as soon as possible, @@ -62,7 +70,7 @@ impl apply_inventory_mutation for super::RemoteReducers { + Send + 'static, ) -> __sdk::Result<()> { - self.imp - .invoke_reducer_with_callback(ApplyInventoryMutationArgs { input }, callback) + self.imp.invoke_reducer_with_callback(ApplyInventoryMutationArgs { input, }, callback) } } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/apply_quest_signal_reducer.rs b/server-rs/crates/spacetime-client/src/module_bindings/apply_quest_signal_reducer.rs index afb452b5..6976f6aa 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/apply_quest_signal_reducer.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/apply_quest_signal_reducer.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::quest_signal_apply_input_type::QuestSignalApplyInput; @@ -14,8 +19,10 @@ pub(super) struct ApplyQuestSignalArgs { impl From for super::Reducer { fn from(args: ApplyQuestSignalArgs) -> Self { - Self::ApplyQuestSignal { input: args.input } - } + Self::ApplyQuestSignal { + input: args.input, +} +} } impl __sdk::InModule for ApplyQuestSignalArgs { @@ -33,8 +40,9 @@ pub trait apply_quest_signal { /// The reducer will run asynchronously in the future, /// and this method provides no way to listen for its completion status. /// /// Use [`apply_quest_signal:apply_quest_signal_then`] to run a callback after the reducer completes. - fn apply_quest_signal(&self, input: QuestSignalApplyInput) -> __sdk::Result<()> { - self.apply_quest_signal_then(input, |_, _| {}) + fn apply_quest_signal(&self, input: QuestSignalApplyInput, +) -> __sdk::Result<()> { + self.apply_quest_signal_then(input, |_, _| {}) } /// Request that the remote module invoke the reducer `apply_quest_signal` to run as soon as possible, @@ -62,7 +70,7 @@ impl apply_quest_signal for super::RemoteReducers { + Send + 'static, ) -> __sdk::Result<()> { - self.imp - .invoke_reducer_with_callback(ApplyQuestSignalArgs { input }, callback) + self.imp.invoke_reducer_with_callback(ApplyQuestSignalArgs { input, }, callback) } } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/asset_entity_binding_input_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/asset_entity_binding_input_type.rs index 10a39937..10dab835 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/asset_entity_binding_input_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/asset_entity_binding_input_type.rs @@ -2,7 +2,13 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; + #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -13,11 +19,13 @@ pub struct AssetEntityBindingInput { pub entity_id: String, pub slot: String, pub asset_kind: String, - pub owner_user_id: Option, - pub profile_id: Option, + pub owner_user_id: Option::, + pub profile_id: Option::, pub updated_at_micros: i64, } + impl __sdk::InModule for AssetEntityBindingInput { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/asset_entity_binding_procedure_result_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/asset_entity_binding_procedure_result_type.rs index 1c51596f..73eff3a6 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/asset_entity_binding_procedure_result_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/asset_entity_binding_procedure_result_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::asset_entity_binding_snapshot_type::AssetEntityBindingSnapshot; @@ -10,10 +15,12 @@ use super::asset_entity_binding_snapshot_type::AssetEntityBindingSnapshot; #[sats(crate = __lib)] pub struct AssetEntityBindingProcedureResult { pub ok: bool, - pub record: Option, - pub error_message: Option, + pub record: Option::, + pub error_message: Option::, } + impl __sdk::InModule for AssetEntityBindingProcedureResult { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/asset_entity_binding_snapshot_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/asset_entity_binding_snapshot_type.rs index d559ea04..862d1b14 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/asset_entity_binding_snapshot_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/asset_entity_binding_snapshot_type.rs @@ -2,7 +2,13 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; + #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -13,12 +19,14 @@ pub struct AssetEntityBindingSnapshot { pub entity_id: String, pub slot: String, pub asset_kind: String, - pub owner_user_id: Option, - pub profile_id: Option, + pub owner_user_id: Option::, + pub profile_id: Option::, pub created_at_micros: i64, pub updated_at_micros: i64, } + impl __sdk::InModule for AssetEntityBindingSnapshot { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/asset_entity_binding_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/asset_entity_binding_type.rs index f12154e1..8f5423b6 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/asset_entity_binding_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/asset_entity_binding_type.rs @@ -2,7 +2,13 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; + #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -13,16 +19,18 @@ pub struct AssetEntityBinding { pub entity_id: String, pub slot: String, pub asset_kind: String, - pub owner_user_id: Option, - pub profile_id: Option, + pub owner_user_id: Option::, + pub profile_id: Option::, pub created_at: __sdk::Timestamp, pub updated_at: __sdk::Timestamp, } + impl __sdk::InModule for AssetEntityBinding { type Module = super::RemoteModule; } + /// Column accessor struct for the table `AssetEntityBinding`. /// /// Provides typed access to columns for query building. @@ -33,8 +41,8 @@ pub struct AssetEntityBindingCols { pub entity_id: __sdk::__query_builder::Col, pub slot: __sdk::__query_builder::Col, pub asset_kind: __sdk::__query_builder::Col, - pub owner_user_id: __sdk::__query_builder::Col>, - pub profile_id: __sdk::__query_builder::Col>, + pub owner_user_id: __sdk::__query_builder::Col>, + pub profile_id: __sdk::__query_builder::Col>, pub created_at: __sdk::__query_builder::Col, pub updated_at: __sdk::__query_builder::Col, } @@ -53,6 +61,7 @@ impl __sdk::__query_builder::HasCols for AssetEntityBinding { profile_id: __sdk::__query_builder::Col::new(table_name, "profile_id"), created_at: __sdk::__query_builder::Col::new(table_name, "created_at"), updated_at: __sdk::__query_builder::Col::new(table_name, "updated_at"), + } } } @@ -71,8 +80,10 @@ impl __sdk::__query_builder::HasIxCols for AssetEntityBinding { AssetEntityBindingIxCols { asset_object_id: __sdk::__query_builder::IxCol::new(table_name, "asset_object_id"), binding_id: __sdk::__query_builder::IxCol::new(table_name, "binding_id"), + } } } impl __sdk::__query_builder::CanBeLookupTable for AssetEntityBinding {} + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/asset_history_entry_snapshot_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/asset_history_entry_snapshot_type.rs index 0386fd4f..3cc6b38c 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/asset_history_entry_snapshot_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/asset_history_entry_snapshot_type.rs @@ -2,7 +2,13 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; + #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -10,13 +16,15 @@ pub struct AssetHistoryEntrySnapshot { pub asset_object_id: String, pub asset_kind: String, pub image_src: String, - pub owner_user_id: Option, - pub profile_id: Option, - pub entity_id: Option, + pub owner_user_id: Option::, + pub profile_id: Option::, + pub entity_id: Option::, pub created_at_micros: i64, pub updated_at_micros: i64, } + impl __sdk::InModule for AssetHistoryEntrySnapshot { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/asset_history_list_input_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/asset_history_list_input_type.rs index f982a0ab..3cf2b5fd 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/asset_history_list_input_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/asset_history_list_input_type.rs @@ -2,7 +2,13 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; + #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -11,6 +17,8 @@ pub struct AssetHistoryListInput { pub limit: u32, } + impl __sdk::InModule for AssetHistoryListInput { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/asset_history_list_result_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/asset_history_list_result_type.rs index 9dc2dd14..4eb8281d 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/asset_history_list_result_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/asset_history_list_result_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::asset_history_entry_snapshot_type::AssetHistoryEntrySnapshot; @@ -10,10 +15,12 @@ use super::asset_history_entry_snapshot_type::AssetHistoryEntrySnapshot; #[sats(crate = __lib)] pub struct AssetHistoryListResult { pub ok: bool, - pub entries: Vec, - pub error_message: Option, + pub entries: Vec::, + pub error_message: Option::, } + impl __sdk::InModule for AssetHistoryListResult { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/asset_object_access_policy_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/asset_object_access_policy_type.rs index 17d02930..3d6203bb 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/asset_object_access_policy_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/asset_object_access_policy_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -11,8 +16,12 @@ pub enum AssetObjectAccessPolicy { Private, PublicRead, + } + + impl __sdk::InModule for AssetObjectAccessPolicy { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/asset_object_procedure_result_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/asset_object_procedure_result_type.rs index 9ccf22ed..ea327af9 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/asset_object_procedure_result_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/asset_object_procedure_result_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::asset_object_upsert_snapshot_type::AssetObjectUpsertSnapshot; @@ -10,10 +15,12 @@ use super::asset_object_upsert_snapshot_type::AssetObjectUpsertSnapshot; #[sats(crate = __lib)] pub struct AssetObjectProcedureResult { pub ok: bool, - pub record: Option, - pub error_message: Option, + pub record: Option::, + pub error_message: Option::, } + impl __sdk::InModule for AssetObjectProcedureResult { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/asset_object_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/asset_object_type.rs index c9d57ac5..75935935 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/asset_object_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/asset_object_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::asset_object_access_policy_type::AssetObjectAccessPolicy; @@ -13,23 +18,25 @@ pub struct AssetObject { pub bucket: String, pub object_key: String, pub access_policy: AssetObjectAccessPolicy, - pub content_type: Option, + pub content_type: Option::, pub content_length: u64, - pub content_hash: Option, + pub content_hash: Option::, pub version: u32, - pub source_job_id: Option, - pub owner_user_id: Option, - pub profile_id: Option, - pub entity_id: Option, + pub source_job_id: Option::, + pub owner_user_id: Option::, + pub profile_id: Option::, + pub entity_id: Option::, pub asset_kind: String, pub created_at: __sdk::Timestamp, pub updated_at: __sdk::Timestamp, } + impl __sdk::InModule for AssetObject { type Module = super::RemoteModule; } + /// Column accessor struct for the table `AssetObject`. /// /// Provides typed access to columns for query building. @@ -38,14 +45,14 @@ pub struct AssetObjectCols { pub bucket: __sdk::__query_builder::Col, pub object_key: __sdk::__query_builder::Col, pub access_policy: __sdk::__query_builder::Col, - pub content_type: __sdk::__query_builder::Col>, + pub content_type: __sdk::__query_builder::Col>, pub content_length: __sdk::__query_builder::Col, - pub content_hash: __sdk::__query_builder::Col>, + pub content_hash: __sdk::__query_builder::Col>, pub version: __sdk::__query_builder::Col, - pub source_job_id: __sdk::__query_builder::Col>, - pub owner_user_id: __sdk::__query_builder::Col>, - pub profile_id: __sdk::__query_builder::Col>, - pub entity_id: __sdk::__query_builder::Col>, + pub source_job_id: __sdk::__query_builder::Col>, + pub owner_user_id: __sdk::__query_builder::Col>, + pub profile_id: __sdk::__query_builder::Col>, + pub entity_id: __sdk::__query_builder::Col>, pub asset_kind: __sdk::__query_builder::Col, pub created_at: __sdk::__query_builder::Col, pub updated_at: __sdk::__query_builder::Col, @@ -70,6 +77,7 @@ impl __sdk::__query_builder::HasCols for AssetObject { asset_kind: __sdk::__query_builder::Col::new(table_name, "asset_kind"), created_at: __sdk::__query_builder::Col::new(table_name, "created_at"), updated_at: __sdk::__query_builder::Col::new(table_name, "updated_at"), + } } } @@ -88,8 +96,10 @@ impl __sdk::__query_builder::HasIxCols for AssetObject { AssetObjectIxCols { asset_kind: __sdk::__query_builder::IxCol::new(table_name, "asset_kind"), asset_object_id: __sdk::__query_builder::IxCol::new(table_name, "asset_object_id"), + } } } impl __sdk::__query_builder::CanBeLookupTable for AssetObject {} + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/asset_object_upsert_input_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/asset_object_upsert_input_type.rs index 85fcadc6..7e48cffa 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/asset_object_upsert_input_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/asset_object_upsert_input_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::asset_object_access_policy_type::AssetObjectAccessPolicy; @@ -13,18 +18,20 @@ pub struct AssetObjectUpsertInput { pub bucket: String, pub object_key: String, pub access_policy: AssetObjectAccessPolicy, - pub content_type: Option, + pub content_type: Option::, pub content_length: u64, - pub content_hash: Option, + pub content_hash: Option::, pub version: u32, - pub source_job_id: Option, - pub owner_user_id: Option, - pub profile_id: Option, - pub entity_id: Option, + pub source_job_id: Option::, + pub owner_user_id: Option::, + pub profile_id: Option::, + pub entity_id: Option::, pub asset_kind: String, pub updated_at_micros: i64, } + impl __sdk::InModule for AssetObjectUpsertInput { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/asset_object_upsert_snapshot_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/asset_object_upsert_snapshot_type.rs index b349f18d..7b5d3860 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/asset_object_upsert_snapshot_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/asset_object_upsert_snapshot_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::asset_object_access_policy_type::AssetObjectAccessPolicy; @@ -13,19 +18,21 @@ pub struct AssetObjectUpsertSnapshot { pub bucket: String, pub object_key: String, pub access_policy: AssetObjectAccessPolicy, - pub content_type: Option, + pub content_type: Option::, pub content_length: u64, - pub content_hash: Option, + pub content_hash: Option::, pub version: u32, - pub source_job_id: Option, - pub owner_user_id: Option, - pub profile_id: Option, - pub entity_id: Option, + pub source_job_id: Option::, + pub owner_user_id: Option::, + pub profile_id: Option::, + pub entity_id: Option::, pub asset_kind: String, pub created_at_micros: i64, pub updated_at_micros: i64, } + impl __sdk::InModule for AssetObjectUpsertSnapshot { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/attach_ai_result_reference_and_return_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/attach_ai_result_reference_and_return_procedure.rs index 2f3edbe2..c7eb0e22 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/attach_ai_result_reference_and_return_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/attach_ai_result_reference_and_return_procedure.rs @@ -2,17 +2,23 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; -use super::ai_result_reference_input_type::AiResultReferenceInput; use super::ai_task_procedure_result_type::AiTaskProcedureResult; +use super::ai_result_reference_input_type::AiResultReferenceInput; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] -struct AttachAiResultReferenceAndReturnArgs { + struct AttachAiResultReferenceAndReturnArgs { pub input: AiResultReferenceInput, } + impl __sdk::InModule for AttachAiResultReferenceAndReturnArgs { type Module = super::RemoteModule; } @@ -22,19 +28,16 @@ impl __sdk::InModule for AttachAiResultReferenceAndReturnArgs { /// /// Implemented for [`super::RemoteProcedures`]. pub trait attach_ai_result_reference_and_return { - fn attach_ai_result_reference_and_return(&self, input: AiResultReferenceInput) { - self.attach_ai_result_reference_and_return_then(input, |_, _| {}); + fn attach_ai_result_reference_and_return(&self, input: AiResultReferenceInput, +) { + self.attach_ai_result_reference_and_return_then(input, |_, _| {}); } fn attach_ai_result_reference_and_return_then( &self, input: AiResultReferenceInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ); } @@ -43,17 +46,13 @@ impl attach_ai_result_reference_and_return for super::RemoteProcedures { &self, input: AiResultReferenceInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ) { - self.imp - .invoke_procedure_with_callback::<_, AiTaskProcedureResult>( - "attach_ai_result_reference_and_return", - AttachAiResultReferenceAndReturnArgs { input }, - __callback, - ); + self.imp.invoke_procedure_with_callback::<_, AiTaskProcedureResult>( + "attach_ai_result_reference_and_return", + AttachAiResultReferenceAndReturnArgs { input, }, + __callback, + ); } } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/auth_identity_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/auth_identity_type.rs index 324c5681..27a33d40 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/auth_identity_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/auth_identity_type.rs @@ -2,7 +2,13 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; + #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -11,16 +17,18 @@ pub struct AuthIdentity { pub user_id: String, pub provider: String, pub provider_uid: String, - pub provider_union_id: Option, - pub phone_e_164: Option, - pub display_name: Option, - pub avatar_url: Option, + pub provider_union_id: Option::, + pub phone_e_164: Option::, + pub display_name: Option::, + pub avatar_url: Option::, } + impl __sdk::InModule for AuthIdentity { type Module = super::RemoteModule; } + /// Column accessor struct for the table `AuthIdentity`. /// /// Provides typed access to columns for query building. @@ -29,10 +37,10 @@ pub struct AuthIdentityCols { pub user_id: __sdk::__query_builder::Col, pub provider: __sdk::__query_builder::Col, pub provider_uid: __sdk::__query_builder::Col, - pub provider_union_id: __sdk::__query_builder::Col>, - pub phone_e_164: __sdk::__query_builder::Col>, - pub display_name: __sdk::__query_builder::Col>, - pub avatar_url: __sdk::__query_builder::Col>, + pub provider_union_id: __sdk::__query_builder::Col>, + pub phone_e_164: __sdk::__query_builder::Col>, + pub display_name: __sdk::__query_builder::Col>, + pub avatar_url: __sdk::__query_builder::Col>, } impl __sdk::__query_builder::HasCols for AuthIdentity { @@ -47,6 +55,7 @@ impl __sdk::__query_builder::HasCols for AuthIdentity { phone_e_164: __sdk::__query_builder::Col::new(table_name, "phone_e_164"), display_name: __sdk::__query_builder::Col::new(table_name, "display_name"), avatar_url: __sdk::__query_builder::Col::new(table_name, "avatar_url"), + } } } @@ -65,8 +74,10 @@ impl __sdk::__query_builder::HasIxCols for AuthIdentity { AuthIdentityIxCols { identity_id: __sdk::__query_builder::IxCol::new(table_name, "identity_id"), user_id: __sdk::__query_builder::IxCol::new(table_name, "user_id"), + } } } impl __sdk::__query_builder::CanBeLookupTable for AuthIdentity {} + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/auth_store_snapshot_import_procedure_result_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/auth_store_snapshot_import_procedure_result_type.rs index 81d27b2c..5ab89cf2 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/auth_store_snapshot_import_procedure_result_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/auth_store_snapshot_import_procedure_result_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::auth_store_snapshot_import_record_type::AuthStoreSnapshotImportRecord; @@ -10,10 +15,12 @@ use super::auth_store_snapshot_import_record_type::AuthStoreSnapshotImportRecord #[sats(crate = __lib)] pub struct AuthStoreSnapshotImportProcedureResult { pub ok: bool, - pub record: Option, - pub error_message: Option, + pub record: Option::, + pub error_message: Option::, } + impl __sdk::InModule for AuthStoreSnapshotImportProcedureResult { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/auth_store_snapshot_import_record_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/auth_store_snapshot_import_record_type.rs index 43f97584..80effd2b 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/auth_store_snapshot_import_record_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/auth_store_snapshot_import_record_type.rs @@ -2,7 +2,13 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; + #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -12,6 +18,8 @@ pub struct AuthStoreSnapshotImportRecord { pub imported_refresh_session_count: u32, } + impl __sdk::InModule for AuthStoreSnapshotImportRecord { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/auth_store_snapshot_procedure_result_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/auth_store_snapshot_procedure_result_type.rs index af1cdd4d..8c8dbf3e 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/auth_store_snapshot_procedure_result_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/auth_store_snapshot_procedure_result_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::auth_store_snapshot_record_type::AuthStoreSnapshotRecord; @@ -10,10 +15,12 @@ use super::auth_store_snapshot_record_type::AuthStoreSnapshotRecord; #[sats(crate = __lib)] pub struct AuthStoreSnapshotProcedureResult { pub ok: bool, - pub record: Option, - pub error_message: Option, + pub record: Option::, + pub error_message: Option::, } + impl __sdk::InModule for AuthStoreSnapshotProcedureResult { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/auth_store_snapshot_record_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/auth_store_snapshot_record_type.rs index a70ecfd8..ee5e4262 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/auth_store_snapshot_record_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/auth_store_snapshot_record_type.rs @@ -2,15 +2,23 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; + #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] pub struct AuthStoreSnapshotRecord { - pub snapshot_json: Option, - pub updated_at_micros: Option, + pub snapshot_json: Option::, + pub updated_at_micros: Option::, } + impl __sdk::InModule for AuthStoreSnapshotRecord { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/auth_store_snapshot_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/auth_store_snapshot_type.rs index 73beb796..af600737 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/auth_store_snapshot_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/auth_store_snapshot_type.rs @@ -2,7 +2,13 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; + #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -12,10 +18,12 @@ pub struct AuthStoreSnapshot { pub updated_at: __sdk::Timestamp, } + impl __sdk::InModule for AuthStoreSnapshot { type Module = super::RemoteModule; } + /// Column accessor struct for the table `AuthStoreSnapshot`. /// /// Provides typed access to columns for query building. @@ -32,6 +40,7 @@ impl __sdk::__query_builder::HasCols for AuthStoreSnapshot { snapshot_id: __sdk::__query_builder::Col::new(table_name, "snapshot_id"), snapshot_json: __sdk::__query_builder::Col::new(table_name, "snapshot_json"), updated_at: __sdk::__query_builder::Col::new(table_name, "updated_at"), + } } } @@ -48,8 +57,10 @@ impl __sdk::__query_builder::HasIxCols for AuthStoreSnapshot { fn ix_cols(table_name: &'static str) -> Self::IxCols { AuthStoreSnapshotIxCols { snapshot_id: __sdk::__query_builder::IxCol::new(table_name, "snapshot_id"), + } } } impl __sdk::__query_builder::CanBeLookupTable for AuthStoreSnapshot {} + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/auth_store_snapshot_upsert_input_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/auth_store_snapshot_upsert_input_type.rs index f116b6aa..17e2aa35 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/auth_store_snapshot_upsert_input_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/auth_store_snapshot_upsert_input_type.rs @@ -2,7 +2,13 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; + #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -11,6 +17,8 @@ pub struct AuthStoreSnapshotUpsertInput { pub updated_at_micros: i64, } + impl __sdk::InModule for AuthStoreSnapshotUpsertInput { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/authorize_database_migration_operator_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/authorize_database_migration_operator_procedure.rs index b5885022..4c9ded98 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/authorize_database_migration_operator_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/authorize_database_migration_operator_procedure.rs @@ -2,17 +2,23 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::database_migration_authorize_operator_input_type::DatabaseMigrationAuthorizeOperatorInput; use super::database_migration_operator_procedure_result_type::DatabaseMigrationOperatorProcedureResult; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] -struct AuthorizeDatabaseMigrationOperatorArgs { + struct AuthorizeDatabaseMigrationOperatorArgs { pub input: DatabaseMigrationAuthorizeOperatorInput, } + impl __sdk::InModule for AuthorizeDatabaseMigrationOperatorArgs { type Module = super::RemoteModule; } @@ -22,22 +28,16 @@ impl __sdk::InModule for AuthorizeDatabaseMigrationOperatorArgs { /// /// Implemented for [`super::RemoteProcedures`]. pub trait authorize_database_migration_operator { - fn authorize_database_migration_operator( - &self, - input: DatabaseMigrationAuthorizeOperatorInput, - ) { - self.authorize_database_migration_operator_then(input, |_, _| {}); + fn authorize_database_migration_operator(&self, input: DatabaseMigrationAuthorizeOperatorInput, +) { + self.authorize_database_migration_operator_then(input, |_, _| {}); } fn authorize_database_migration_operator_then( &self, input: DatabaseMigrationAuthorizeOperatorInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ); } @@ -46,17 +46,13 @@ impl authorize_database_migration_operator for super::RemoteProcedures { &self, input: DatabaseMigrationAuthorizeOperatorInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ) { - self.imp - .invoke_procedure_with_callback::<_, DatabaseMigrationOperatorProcedureResult>( - "authorize_database_migration_operator", - AuthorizeDatabaseMigrationOperatorArgs { input }, - __callback, - ); + self.imp.invoke_procedure_with_callback::<_, DatabaseMigrationOperatorProcedureResult>( + "authorize_database_migration_operator", + AuthorizeDatabaseMigrationOperatorArgs { input, }, + __callback, + ); } } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/battle_mode_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/battle_mode_type.rs index a88c25f4..1961ac69 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/battle_mode_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/battle_mode_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -11,8 +16,12 @@ pub enum BattleMode { Fight, Spar, + } + + impl __sdk::InModule for BattleMode { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/battle_state_input_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/battle_state_input_type.rs index 6a176e2d..104b96cb 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/battle_state_input_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/battle_state_input_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::battle_mode_type::BattleMode; use super::runtime_item_reward_item_snapshot_type::RuntimeItemRewardItemSnapshot; @@ -14,7 +19,7 @@ pub struct BattleStateInput { pub story_session_id: String, pub runtime_session_id: String, pub actor_user_id: String, - pub chapter_id: Option, + pub chapter_id: Option::, pub target_npc_id: String, pub target_name: String, pub battle_mode: BattleMode, @@ -25,10 +30,12 @@ pub struct BattleStateInput { pub target_hp: i32, pub target_max_hp: i32, pub experience_reward: u32, - pub reward_items: Vec, + pub reward_items: Vec::, pub created_at_micros: i64, } + impl __sdk::InModule for BattleStateInput { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/battle_state_procedure_result_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/battle_state_procedure_result_type.rs index e66352ad..cadc80d8 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/battle_state_procedure_result_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/battle_state_procedure_result_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::battle_state_snapshot_type::BattleStateSnapshot; @@ -10,10 +15,12 @@ use super::battle_state_snapshot_type::BattleStateSnapshot; #[sats(crate = __lib)] pub struct BattleStateProcedureResult { pub ok: bool, - pub snapshot: Option, - pub error_message: Option, + pub snapshot: Option::, + pub error_message: Option::, } + impl __sdk::InModule for BattleStateProcedureResult { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/battle_state_query_input_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/battle_state_query_input_type.rs index 53053cca..6b9298d4 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/battle_state_query_input_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/battle_state_query_input_type.rs @@ -2,7 +2,13 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; + #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -10,6 +16,8 @@ pub struct BattleStateQueryInput { pub battle_state_id: String, } + impl __sdk::InModule for BattleStateQueryInput { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/battle_state_snapshot_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/battle_state_snapshot_type.rs index 925d8468..c20ec25c 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/battle_state_snapshot_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/battle_state_snapshot_type.rs @@ -2,12 +2,17 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::battle_mode_type::BattleMode; use super::battle_status_type::BattleStatus; -use super::combat_outcome_type::CombatOutcome; use super::runtime_item_reward_item_snapshot_type::RuntimeItemRewardItemSnapshot; +use super::combat_outcome_type::CombatOutcome; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -16,7 +21,7 @@ pub struct BattleStateSnapshot { pub story_session_id: String, pub runtime_session_id: String, pub actor_user_id: String, - pub chapter_id: Option, + pub chapter_id: Option::, pub target_npc_id: String, pub target_name: String, pub battle_mode: BattleMode, @@ -28,11 +33,11 @@ pub struct BattleStateSnapshot { pub target_hp: i32, pub target_max_hp: i32, pub experience_reward: u32, - pub reward_items: Vec, + pub reward_items: Vec::, pub turn_index: u32, - pub last_action_function_id: Option, - pub last_action_text: Option, - pub last_result_text: Option, + pub last_action_function_id: Option::, + pub last_action_text: Option::, + pub last_result_text: Option::, pub last_damage_dealt: i32, pub last_damage_taken: i32, pub last_outcome: CombatOutcome, @@ -41,6 +46,8 @@ pub struct BattleStateSnapshot { pub updated_at_micros: i64, } + impl __sdk::InModule for BattleStateSnapshot { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/battle_state_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/battle_state_type.rs index 9d9b852f..1b7e0420 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/battle_state_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/battle_state_type.rs @@ -2,12 +2,17 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::battle_mode_type::BattleMode; use super::battle_status_type::BattleStatus; -use super::combat_outcome_type::CombatOutcome; use super::runtime_item_reward_item_snapshot_type::RuntimeItemRewardItemSnapshot; +use super::combat_outcome_type::CombatOutcome; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -16,7 +21,7 @@ pub struct BattleState { pub story_session_id: String, pub runtime_session_id: String, pub actor_user_id: String, - pub chapter_id: Option, + pub chapter_id: Option::, pub target_npc_id: String, pub target_name: String, pub battle_mode: BattleMode, @@ -28,11 +33,11 @@ pub struct BattleState { pub target_hp: i32, pub target_max_hp: i32, pub experience_reward: u32, - pub reward_items: Vec, + pub reward_items: Vec::, pub turn_index: u32, - pub last_action_function_id: Option, - pub last_action_text: Option, - pub last_result_text: Option, + pub last_action_function_id: Option::, + pub last_action_text: Option::, + pub last_result_text: Option::, pub last_damage_dealt: i32, pub last_damage_taken: i32, pub last_outcome: CombatOutcome, @@ -41,10 +46,12 @@ pub struct BattleState { pub updated_at: __sdk::Timestamp, } + impl __sdk::InModule for BattleState { type Module = super::RemoteModule; } + /// Column accessor struct for the table `BattleState`. /// /// Provides typed access to columns for query building. @@ -53,7 +60,7 @@ pub struct BattleStateCols { pub story_session_id: __sdk::__query_builder::Col, pub runtime_session_id: __sdk::__query_builder::Col, pub actor_user_id: __sdk::__query_builder::Col, - pub chapter_id: __sdk::__query_builder::Col>, + pub chapter_id: __sdk::__query_builder::Col>, pub target_npc_id: __sdk::__query_builder::Col, pub target_name: __sdk::__query_builder::Col, pub battle_mode: __sdk::__query_builder::Col, @@ -65,11 +72,11 @@ pub struct BattleStateCols { pub target_hp: __sdk::__query_builder::Col, pub target_max_hp: __sdk::__query_builder::Col, pub experience_reward: __sdk::__query_builder::Col, - pub reward_items: __sdk::__query_builder::Col>, + pub reward_items: __sdk::__query_builder::Col>, pub turn_index: __sdk::__query_builder::Col, - pub last_action_function_id: __sdk::__query_builder::Col>, - pub last_action_text: __sdk::__query_builder::Col>, - pub last_result_text: __sdk::__query_builder::Col>, + pub last_action_function_id: __sdk::__query_builder::Col>, + pub last_action_text: __sdk::__query_builder::Col>, + pub last_result_text: __sdk::__query_builder::Col>, pub last_damage_dealt: __sdk::__query_builder::Col, pub last_damage_taken: __sdk::__query_builder::Col, pub last_outcome: __sdk::__query_builder::Col, @@ -100,10 +107,7 @@ impl __sdk::__query_builder::HasCols for BattleState { experience_reward: __sdk::__query_builder::Col::new(table_name, "experience_reward"), reward_items: __sdk::__query_builder::Col::new(table_name, "reward_items"), turn_index: __sdk::__query_builder::Col::new(table_name, "turn_index"), - last_action_function_id: __sdk::__query_builder::Col::new( - table_name, - "last_action_function_id", - ), + last_action_function_id: __sdk::__query_builder::Col::new(table_name, "last_action_function_id"), last_action_text: __sdk::__query_builder::Col::new(table_name, "last_action_text"), last_result_text: __sdk::__query_builder::Col::new(table_name, "last_result_text"), last_damage_dealt: __sdk::__query_builder::Col::new(table_name, "last_damage_dealt"), @@ -112,6 +116,7 @@ impl __sdk::__query_builder::HasCols for BattleState { version: __sdk::__query_builder::Col::new(table_name, "version"), created_at: __sdk::__query_builder::Col::new(table_name, "created_at"), updated_at: __sdk::__query_builder::Col::new(table_name, "updated_at"), + } } } @@ -132,13 +137,12 @@ impl __sdk::__query_builder::HasIxCols for BattleState { BattleStateIxCols { actor_user_id: __sdk::__query_builder::IxCol::new(table_name, "actor_user_id"), battle_state_id: __sdk::__query_builder::IxCol::new(table_name, "battle_state_id"), - runtime_session_id: __sdk::__query_builder::IxCol::new( - table_name, - "runtime_session_id", - ), + runtime_session_id: __sdk::__query_builder::IxCol::new(table_name, "runtime_session_id"), story_session_id: __sdk::__query_builder::IxCol::new(table_name, "story_session_id"), + } } } impl __sdk::__query_builder::CanBeLookupTable for BattleState {} + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/battle_status_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/battle_status_type.rs index 0aba4f43..e869befe 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/battle_status_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/battle_status_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -13,8 +18,12 @@ pub enum BattleStatus { Resolved, Aborted, + } + + impl __sdk::InModule for BattleStatus { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/begin_story_session_and_return_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/begin_story_session_and_return_procedure.rs index eef3de0f..75c19e04 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/begin_story_session_and_return_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/begin_story_session_and_return_procedure.rs @@ -2,17 +2,23 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::story_session_input_type::StorySessionInput; use super::story_session_procedure_result_type::StorySessionProcedureResult; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] -struct BeginStorySessionAndReturnArgs { + struct BeginStorySessionAndReturnArgs { pub input: StorySessionInput, } + impl __sdk::InModule for BeginStorySessionAndReturnArgs { type Module = super::RemoteModule; } @@ -22,19 +28,16 @@ impl __sdk::InModule for BeginStorySessionAndReturnArgs { /// /// Implemented for [`super::RemoteProcedures`]. pub trait begin_story_session_and_return { - fn begin_story_session_and_return(&self, input: StorySessionInput) { - self.begin_story_session_and_return_then(input, |_, _| {}); + fn begin_story_session_and_return(&self, input: StorySessionInput, +) { + self.begin_story_session_and_return_then(input, |_, _| {}); } fn begin_story_session_and_return_then( &self, input: StorySessionInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ); } @@ -43,17 +46,13 @@ impl begin_story_session_and_return for super::RemoteProcedures { &self, input: StorySessionInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ) { - self.imp - .invoke_procedure_with_callback::<_, StorySessionProcedureResult>( - "begin_story_session_and_return", - BeginStorySessionAndReturnArgs { input }, - __callback, - ); + self.imp.invoke_procedure_with_callback::<_, StorySessionProcedureResult>( + "begin_story_session_and_return", + BeginStorySessionAndReturnArgs { input, }, + __callback, + ); } } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/begin_story_session_reducer.rs b/server-rs/crates/spacetime-client/src/module_bindings/begin_story_session_reducer.rs index 6a082f41..582bb850 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/begin_story_session_reducer.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/begin_story_session_reducer.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::story_session_input_type::StorySessionInput; @@ -14,8 +19,10 @@ pub(super) struct BeginStorySessionArgs { impl From for super::Reducer { fn from(args: BeginStorySessionArgs) -> Self { - Self::BeginStorySession { input: args.input } - } + Self::BeginStorySession { + input: args.input, +} +} } impl __sdk::InModule for BeginStorySessionArgs { @@ -33,8 +40,9 @@ pub trait begin_story_session { /// The reducer will run asynchronously in the future, /// and this method provides no way to listen for its completion status. /// /// Use [`begin_story_session:begin_story_session_then`] to run a callback after the reducer completes. - fn begin_story_session(&self, input: StorySessionInput) -> __sdk::Result<()> { - self.begin_story_session_then(input, |_, _| {}) + fn begin_story_session(&self, input: StorySessionInput, +) -> __sdk::Result<()> { + self.begin_story_session_then(input, |_, _| {}) } /// Request that the remote module invoke the reducer `begin_story_session` to run as soon as possible, @@ -62,7 +70,7 @@ impl begin_story_session for super::RemoteReducers { + Send + 'static, ) -> __sdk::Result<()> { - self.imp - .invoke_reducer_with_callback(BeginStorySessionArgs { input }, callback) + self.imp.invoke_reducer_with_callback(BeginStorySessionArgs { input, }, callback) } } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/big_fish_agent_message_kind_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/big_fish_agent_message_kind_type.rs index 00f36e4e..174e55f8 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/big_fish_agent_message_kind_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/big_fish_agent_message_kind_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -15,8 +20,12 @@ pub enum BigFishAgentMessageKind { ActionResult, Warning, + } + + impl __sdk::InModule for BigFishAgentMessageKind { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/big_fish_agent_message_role_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/big_fish_agent_message_role_type.rs index d8a1a82c..9f8a0458 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/big_fish_agent_message_role_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/big_fish_agent_message_role_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -13,8 +18,12 @@ pub enum BigFishAgentMessageRole { Assistant, System, + } + + impl __sdk::InModule for BigFishAgentMessageRole { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/big_fish_agent_message_snapshot_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/big_fish_agent_message_snapshot_type.rs index acc31e57..d6b00fc5 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/big_fish_agent_message_snapshot_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/big_fish_agent_message_snapshot_type.rs @@ -2,10 +2,15 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; -use super::big_fish_agent_message_kind_type::BigFishAgentMessageKind; use super::big_fish_agent_message_role_type::BigFishAgentMessageRole; +use super::big_fish_agent_message_kind_type::BigFishAgentMessageKind; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -18,6 +23,8 @@ pub struct BigFishAgentMessageSnapshot { pub created_at_micros: i64, } + impl __sdk::InModule for BigFishAgentMessageSnapshot { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/big_fish_agent_message_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/big_fish_agent_message_type.rs index a5e70272..c55ecc3d 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/big_fish_agent_message_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/big_fish_agent_message_type.rs @@ -2,10 +2,15 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; -use super::big_fish_agent_message_kind_type::BigFishAgentMessageKind; use super::big_fish_agent_message_role_type::BigFishAgentMessageRole; +use super::big_fish_agent_message_kind_type::BigFishAgentMessageKind; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -18,10 +23,12 @@ pub struct BigFishAgentMessage { pub created_at: __sdk::Timestamp, } + impl __sdk::InModule for BigFishAgentMessage { type Module = super::RemoteModule; } + /// Column accessor struct for the table `BigFishAgentMessage`. /// /// Provides typed access to columns for query building. @@ -44,6 +51,7 @@ impl __sdk::__query_builder::HasCols for BigFishAgentMessage { kind: __sdk::__query_builder::Col::new(table_name, "kind"), text: __sdk::__query_builder::Col::new(table_name, "text"), created_at: __sdk::__query_builder::Col::new(table_name, "created_at"), + } } } @@ -62,8 +70,10 @@ impl __sdk::__query_builder::HasIxCols for BigFishAgentMessage { BigFishAgentMessageIxCols { message_id: __sdk::__query_builder::IxCol::new(table_name, "message_id"), session_id: __sdk::__query_builder::IxCol::new(table_name, "session_id"), + } } } impl __sdk::__query_builder::CanBeLookupTable for BigFishAgentMessage {} + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/big_fish_anchor_item_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/big_fish_anchor_item_type.rs index d876588e..80ecc407 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/big_fish_anchor_item_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/big_fish_anchor_item_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::big_fish_anchor_status_type::BigFishAnchorStatus; @@ -15,6 +20,8 @@ pub struct BigFishAnchorItem { pub status: BigFishAnchorStatus, } + impl __sdk::InModule for BigFishAnchorItem { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/big_fish_anchor_pack_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/big_fish_anchor_pack_type.rs index 5d93c291..6f93262e 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/big_fish_anchor_pack_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/big_fish_anchor_pack_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::big_fish_anchor_item_type::BigFishAnchorItem; @@ -15,6 +20,8 @@ pub struct BigFishAnchorPack { pub risk_tempo: BigFishAnchorItem, } + impl __sdk::InModule for BigFishAnchorPack { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/big_fish_anchor_status_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/big_fish_anchor_status_type.rs index bcfe701e..d1ed8bb3 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/big_fish_anchor_status_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/big_fish_anchor_status_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -15,8 +20,12 @@ pub enum BigFishAnchorStatus { Missing, Locked, + } + + impl __sdk::InModule for BigFishAnchorStatus { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/big_fish_asset_coverage_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/big_fish_asset_coverage_type.rs index 607a8c39..c1b3429a 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/big_fish_asset_coverage_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/big_fish_asset_coverage_type.rs @@ -2,7 +2,13 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; + #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -12,9 +18,11 @@ pub struct BigFishAssetCoverage { pub background_ready: bool, pub required_level_count: u32, pub publish_ready: bool, - pub blockers: Vec, + pub blockers: Vec::, } + impl __sdk::InModule for BigFishAssetCoverage { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/big_fish_asset_generate_input_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/big_fish_asset_generate_input_type.rs index e7589076..ba67939a 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/big_fish_asset_generate_input_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/big_fish_asset_generate_input_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::big_fish_asset_kind_type::BigFishAssetKind; @@ -12,12 +17,14 @@ pub struct BigFishAssetGenerateInput { pub session_id: String, pub owner_user_id: String, pub asset_kind: BigFishAssetKind, - pub level: Option, - pub motion_key: Option, - pub asset_url: Option, + pub level: Option::, + pub motion_key: Option::, + pub asset_url: Option::, pub generated_at_micros: i64, } + impl __sdk::InModule for BigFishAssetGenerateInput { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/big_fish_asset_kind_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/big_fish_asset_kind_type.rs index b4c9dcd1..a5d6c3ed 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/big_fish_asset_kind_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/big_fish_asset_kind_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -13,8 +18,12 @@ pub enum BigFishAssetKind { LevelMotion, StageBackground, + } + + impl __sdk::InModule for BigFishAssetKind { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/big_fish_asset_slot_snapshot_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/big_fish_asset_slot_snapshot_type.rs index 200b1a6d..911e51fc 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/big_fish_asset_slot_snapshot_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/big_fish_asset_slot_snapshot_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::big_fish_asset_kind_type::BigFishAssetKind; use super::big_fish_asset_status_type::BigFishAssetStatus; @@ -13,14 +18,16 @@ pub struct BigFishAssetSlotSnapshot { pub slot_id: String, pub session_id: String, pub asset_kind: BigFishAssetKind, - pub level: Option, - pub motion_key: Option, + pub level: Option::, + pub motion_key: Option::, pub status: BigFishAssetStatus, - pub asset_url: Option, + pub asset_url: Option::, pub prompt_snapshot: String, pub updated_at_micros: i64, } + impl __sdk::InModule for BigFishAssetSlotSnapshot { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/big_fish_asset_slot_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/big_fish_asset_slot_type.rs index 406c151d..a6cb0813 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/big_fish_asset_slot_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/big_fish_asset_slot_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::big_fish_asset_kind_type::BigFishAssetKind; use super::big_fish_asset_status_type::BigFishAssetStatus; @@ -13,18 +18,20 @@ pub struct BigFishAssetSlot { pub slot_id: String, pub session_id: String, pub asset_kind: BigFishAssetKind, - pub level: Option, - pub motion_key: Option, + pub level: Option::, + pub motion_key: Option::, pub status: BigFishAssetStatus, - pub asset_url: Option, + pub asset_url: Option::, pub prompt_snapshot: String, pub updated_at: __sdk::Timestamp, } + impl __sdk::InModule for BigFishAssetSlot { type Module = super::RemoteModule; } + /// Column accessor struct for the table `BigFishAssetSlot`. /// /// Provides typed access to columns for query building. @@ -32,10 +39,10 @@ pub struct BigFishAssetSlotCols { pub slot_id: __sdk::__query_builder::Col, pub session_id: __sdk::__query_builder::Col, pub asset_kind: __sdk::__query_builder::Col, - pub level: __sdk::__query_builder::Col>, - pub motion_key: __sdk::__query_builder::Col>, + pub level: __sdk::__query_builder::Col>, + pub motion_key: __sdk::__query_builder::Col>, pub status: __sdk::__query_builder::Col, - pub asset_url: __sdk::__query_builder::Col>, + pub asset_url: __sdk::__query_builder::Col>, pub prompt_snapshot: __sdk::__query_builder::Col, pub updated_at: __sdk::__query_builder::Col, } @@ -53,6 +60,7 @@ impl __sdk::__query_builder::HasCols for BigFishAssetSlot { asset_url: __sdk::__query_builder::Col::new(table_name, "asset_url"), prompt_snapshot: __sdk::__query_builder::Col::new(table_name, "prompt_snapshot"), updated_at: __sdk::__query_builder::Col::new(table_name, "updated_at"), + } } } @@ -71,8 +79,10 @@ impl __sdk::__query_builder::HasIxCols for BigFishAssetSlot { BigFishAssetSlotIxCols { session_id: __sdk::__query_builder::IxCol::new(table_name, "session_id"), slot_id: __sdk::__query_builder::IxCol::new(table_name, "slot_id"), + } } } impl __sdk::__query_builder::CanBeLookupTable for BigFishAssetSlot {} + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/big_fish_asset_status_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/big_fish_asset_status_type.rs index c673f0f3..25ed0f6a 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/big_fish_asset_status_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/big_fish_asset_status_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -11,8 +16,12 @@ pub enum BigFishAssetStatus { Missing, Ready, + } + + impl __sdk::InModule for BigFishAssetStatus { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/big_fish_background_blueprint_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/big_fish_background_blueprint_type.rs index b28e3196..bda8e3be 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/big_fish_background_blueprint_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/big_fish_background_blueprint_type.rs @@ -2,7 +2,13 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; + #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -17,6 +23,8 @@ pub struct BigFishBackgroundBlueprint { pub background_prompt_seed: String, } + impl __sdk::InModule for BigFishBackgroundBlueprint { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/big_fish_creation_session_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/big_fish_creation_session_type.rs index a760fea0..4a736707 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/big_fish_creation_session_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/big_fish_creation_session_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::big_fish_creation_stage_type::BigFishCreationStage; @@ -16,19 +21,21 @@ pub struct BigFishCreationSession { pub progress_percent: u32, pub stage: BigFishCreationStage, pub anchor_pack_json: String, - pub draft_json: Option, + pub draft_json: Option::, pub asset_coverage_json: String, - pub last_assistant_reply: Option, + pub last_assistant_reply: Option::, pub publish_ready: bool, pub play_count: u32, pub created_at: __sdk::Timestamp, pub updated_at: __sdk::Timestamp, } + impl __sdk::InModule for BigFishCreationSession { type Module = super::RemoteModule; } + /// Column accessor struct for the table `BigFishCreationSession`. /// /// Provides typed access to columns for query building. @@ -40,9 +47,9 @@ pub struct BigFishCreationSessionCols { pub progress_percent: __sdk::__query_builder::Col, pub stage: __sdk::__query_builder::Col, pub anchor_pack_json: __sdk::__query_builder::Col, - pub draft_json: __sdk::__query_builder::Col>, + pub draft_json: __sdk::__query_builder::Col>, pub asset_coverage_json: __sdk::__query_builder::Col, - pub last_assistant_reply: __sdk::__query_builder::Col>, + pub last_assistant_reply: __sdk::__query_builder::Col>, pub publish_ready: __sdk::__query_builder::Col, pub play_count: __sdk::__query_builder::Col, pub created_at: __sdk::__query_builder::Col, @@ -61,18 +68,13 @@ impl __sdk::__query_builder::HasCols for BigFishCreationSession { stage: __sdk::__query_builder::Col::new(table_name, "stage"), anchor_pack_json: __sdk::__query_builder::Col::new(table_name, "anchor_pack_json"), draft_json: __sdk::__query_builder::Col::new(table_name, "draft_json"), - asset_coverage_json: __sdk::__query_builder::Col::new( - table_name, - "asset_coverage_json", - ), - last_assistant_reply: __sdk::__query_builder::Col::new( - table_name, - "last_assistant_reply", - ), + asset_coverage_json: __sdk::__query_builder::Col::new(table_name, "asset_coverage_json"), + last_assistant_reply: __sdk::__query_builder::Col::new(table_name, "last_assistant_reply"), publish_ready: __sdk::__query_builder::Col::new(table_name, "publish_ready"), play_count: __sdk::__query_builder::Col::new(table_name, "play_count"), created_at: __sdk::__query_builder::Col::new(table_name, "created_at"), updated_at: __sdk::__query_builder::Col::new(table_name, "updated_at"), + } } } @@ -91,8 +93,10 @@ impl __sdk::__query_builder::HasIxCols for BigFishCreationSession { BigFishCreationSessionIxCols { 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 BigFishCreationSession {} + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/big_fish_creation_stage_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/big_fish_creation_stage_type.rs index c878d467..b96fb5e4 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/big_fish_creation_stage_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/big_fish_creation_stage_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -17,8 +22,12 @@ pub enum BigFishCreationStage { ReadyToPublish, Published, + } + + impl __sdk::InModule for BigFishCreationStage { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/big_fish_draft_compile_input_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/big_fish_draft_compile_input_type.rs index 69076a74..9863d64b 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/big_fish_draft_compile_input_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/big_fish_draft_compile_input_type.rs @@ -2,17 +2,25 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; + #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] pub struct BigFishDraftCompileInput { pub session_id: String, pub owner_user_id: String, - pub draft_json: Option, + pub draft_json: Option::, pub compiled_at_micros: i64, } + impl __sdk::InModule for BigFishDraftCompileInput { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/big_fish_event_kind_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/big_fish_event_kind_type.rs new file mode 100644 index 00000000..ca2ef776 --- /dev/null +++ b/server-rs/crates/spacetime-client/src/module_bindings/big_fish_event_kind_type.rs @@ -0,0 +1,25 @@ +// 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)] +#[derive(Copy, Eq, Hash)] +pub enum BigFishEventKind { + PublishReadinessEvaluated, + +} + + + +impl __sdk::InModule for BigFishEventKind { + type Module = super::RemoteModule; +} + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/big_fish_event_table.rs b/server-rs/crates/spacetime-client/src/module_bindings/big_fish_event_table.rs new file mode 100644 index 00000000..b907f038 --- /dev/null +++ b/server-rs/crates/spacetime-client/src/module_bindings/big_fish_event_table.rs @@ -0,0 +1,103 @@ +// 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::big_fish_event_type::BigFishEvent; +use super::big_fish_event_kind_type::BigFishEventKind; + +/// Table handle for the table `big_fish_event`. +/// +/// Obtain a handle from the [`BigFishEventTableAccess::big_fish_event`] method on [`super::RemoteTables`], +/// like `ctx.db.big_fish_event()`. +/// +/// Users are encouraged not to explicitly reference this type, +/// but to directly chain method calls, +/// like `ctx.db.big_fish_event().on_insert(...)`. +pub struct BigFishEventTableHandle<'ctx> { + imp: __sdk::TableHandle, + ctx: std::marker::PhantomData<&'ctx super::RemoteTables>, +} + +#[allow(non_camel_case_types)] +/// Extension trait for access to the table `big_fish_event`. +/// +/// Implemented for [`super::RemoteTables`]. +pub trait BigFishEventTableAccess { + #[allow(non_snake_case)] + /// Obtain a [`BigFishEventTableHandle`], which mediates access to the table `big_fish_event`. + fn big_fish_event(&self) -> BigFishEventTableHandle<'_>; +} + +impl BigFishEventTableAccess for super::RemoteTables { + fn big_fish_event(&self) -> BigFishEventTableHandle<'_> { + BigFishEventTableHandle { + imp: self.imp.get_table::("big_fish_event"), + ctx: std::marker::PhantomData, + } + } +} + +pub struct BigFishEventInsertCallbackId(__sdk::CallbackId); + +impl<'ctx> __sdk::EventTable for BigFishEventTableHandle<'ctx> { + type Row = BigFishEvent; + type EventContext = super::EventContext; + + fn count(&self) -> u64 { self.imp.count() } + fn iter(&self) -> impl Iterator + '_ { self.imp.iter() } + + type InsertCallbackId = BigFishEventInsertCallbackId; + + fn on_insert( + &self, + callback: impl FnMut(&Self::EventContext, &Self::Row) + Send + 'static, + ) -> BigFishEventInsertCallbackId { + BigFishEventInsertCallbackId(self.imp.on_insert(Box::new(callback))) + } + + fn remove_on_insert(&self, callback: BigFishEventInsertCallbackId) { + self.imp.remove_on_insert(callback.0) + } +} + +#[doc(hidden)] +pub(super) fn register_table(client_cache: &mut __sdk::ClientCache) { + + let _table = client_cache.get_or_make_table::("big_fish_event"); + _table.add_unique_constraint::("event_id", |row| &row.event_id); +} + +#[doc(hidden)] +pub(super) fn parse_table_update( + raw_updates: __ws::v2::TableUpdate, +) -> __sdk::Result<__sdk::TableUpdate> { + __sdk::TableUpdate::parse_table_update(raw_updates).map_err(|e| { + __sdk::InternalError::failed_parse( + "TableUpdate", + "TableUpdate", + ).with_cause(e).into() + }) +} + + #[allow(non_camel_case_types)] + /// Extension trait for query builder access to the table `BigFishEvent`. + /// + /// Implemented for [`__sdk::QueryTableAccessor`]. + pub trait big_fish_eventQueryTableAccess { + #[allow(non_snake_case)] + /// Get a query builder for the table `BigFishEvent`. + fn big_fish_event(&self) -> __sdk::__query_builder::Table; + } + + impl big_fish_eventQueryTableAccess for __sdk::QueryTableAccessor { + fn big_fish_event(&self) -> __sdk::__query_builder::Table { + __sdk::__query_builder::Table::new("big_fish_event") + } + } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/big_fish_event_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/big_fish_event_type.rs new file mode 100644 index 00000000..33cbb41a --- /dev/null +++ b/server-rs/crates/spacetime-client/src/module_bindings/big_fish_event_type.rs @@ -0,0 +1,81 @@ +// 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::big_fish_event_kind_type::BigFishEventKind; + +#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] +#[sats(crate = __lib)] +pub struct BigFishEvent { + pub event_id: String, + pub session_id: String, + pub owner_user_id: String, + pub event_kind: BigFishEventKind, + pub publish_ready: bool, + pub blockers_json: String, + pub occurred_at: __sdk::Timestamp, +} + + +impl __sdk::InModule for BigFishEvent { + type Module = super::RemoteModule; +} + + +/// Column accessor struct for the table `BigFishEvent`. +/// +/// Provides typed access to columns for query building. +pub struct BigFishEventCols { + pub event_id: __sdk::__query_builder::Col, + pub session_id: __sdk::__query_builder::Col, + pub owner_user_id: __sdk::__query_builder::Col, + pub event_kind: __sdk::__query_builder::Col, + pub publish_ready: __sdk::__query_builder::Col, + pub blockers_json: __sdk::__query_builder::Col, + pub occurred_at: __sdk::__query_builder::Col, +} + +impl __sdk::__query_builder::HasCols for BigFishEvent { + type Cols = BigFishEventCols; + fn cols(table_name: &'static str) -> Self::Cols { + BigFishEventCols { + event_id: __sdk::__query_builder::Col::new(table_name, "event_id"), + session_id: __sdk::__query_builder::Col::new(table_name, "session_id"), + owner_user_id: __sdk::__query_builder::Col::new(table_name, "owner_user_id"), + event_kind: __sdk::__query_builder::Col::new(table_name, "event_kind"), + publish_ready: __sdk::__query_builder::Col::new(table_name, "publish_ready"), + blockers_json: __sdk::__query_builder::Col::new(table_name, "blockers_json"), + occurred_at: __sdk::__query_builder::Col::new(table_name, "occurred_at"), + + } + } +} + +/// Indexed column accessor struct for the table `BigFishEvent`. +/// +/// Provides typed access to indexed columns for query building. +pub struct BigFishEventIxCols { + pub event_id: __sdk::__query_builder::IxCol, + pub owner_user_id: __sdk::__query_builder::IxCol, + pub session_id: __sdk::__query_builder::IxCol, +} + +impl __sdk::__query_builder::HasIxCols for BigFishEvent { + type IxCols = BigFishEventIxCols; + fn ix_cols(table_name: &'static str) -> Self::IxCols { + BigFishEventIxCols { + event_id: __sdk::__query_builder::IxCol::new(table_name, "event_id"), + owner_user_id: __sdk::__query_builder::IxCol::new(table_name, "owner_user_id"), + session_id: __sdk::__query_builder::IxCol::new(table_name, "session_id"), + + } + } +} + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/big_fish_game_draft_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/big_fish_game_draft_type.rs index 0c661176..43810d9b 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/big_fish_game_draft_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/big_fish_game_draft_type.rs @@ -2,10 +2,15 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; -use super::big_fish_background_blueprint_type::BigFishBackgroundBlueprint; use super::big_fish_level_blueprint_type::BigFishLevelBlueprint; +use super::big_fish_background_blueprint_type::BigFishBackgroundBlueprint; use super::big_fish_runtime_params_type::BigFishRuntimeParams; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] @@ -15,11 +20,13 @@ pub struct BigFishGameDraft { pub subtitle: String, pub core_fun: String, pub ecology_theme: String, - pub levels: Vec, + pub levels: Vec::, pub background: BigFishBackgroundBlueprint, pub runtime_params: BigFishRuntimeParams, } + impl __sdk::InModule for BigFishGameDraft { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/big_fish_input_submit_input_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/big_fish_input_submit_input_type.rs new file mode 100644 index 00000000..63db54db --- /dev/null +++ b/server-rs/crates/spacetime-client/src/module_bindings/big_fish_input_submit_input_type.rs @@ -0,0 +1,27 @@ +// 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 BigFishInputSubmitInput { + pub run_id: String, + pub owner_user_id: String, + pub x: f32, + pub y: f32, + pub submitted_at_micros: i64, +} + + +impl __sdk::InModule for BigFishInputSubmitInput { + type Module = super::RemoteModule; +} + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/big_fish_level_blueprint_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/big_fish_level_blueprint_type.rs index 4bd70191..8d25b743 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/big_fish_level_blueprint_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/big_fish_level_blueprint_type.rs @@ -2,7 +2,13 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; + #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -18,12 +24,14 @@ pub struct BigFishLevelBlueprint { pub idle_motion_description: String, pub move_motion_description: String, pub motion_prompt_seed: String, - pub merge_source_level: Option, - pub prey_window: Vec, - pub threat_window: Vec, + pub merge_source_level: Option::, + pub prey_window: Vec::, + pub threat_window: Vec::, pub is_final_level: bool, } + impl __sdk::InModule for BigFishLevelBlueprint { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/big_fish_message_finalize_input_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/big_fish_message_finalize_input_type.rs index b54c4b6f..bfe80bda 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/big_fish_message_finalize_input_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/big_fish_message_finalize_input_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::big_fish_creation_stage_type::BigFishCreationStage; @@ -11,15 +16,17 @@ use super::big_fish_creation_stage_type::BigFishCreationStage; pub struct BigFishMessageFinalizeInput { pub session_id: String, pub owner_user_id: String, - pub assistant_message_id: Option, - pub assistant_reply_text: Option, + pub assistant_message_id: Option::, + pub assistant_reply_text: Option::, pub stage: BigFishCreationStage, pub progress_percent: u32, pub anchor_pack_json: String, - pub error_message: Option, + pub error_message: Option::, pub updated_at_micros: i64, } + impl __sdk::InModule for BigFishMessageFinalizeInput { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/big_fish_message_submit_input_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/big_fish_message_submit_input_type.rs index b0e41147..40dfe964 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/big_fish_message_submit_input_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/big_fish_message_submit_input_type.rs @@ -2,7 +2,13 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; + #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -15,6 +21,8 @@ pub struct BigFishMessageSubmitInput { pub submitted_at_micros: i64, } + impl __sdk::InModule for BigFishMessageSubmitInput { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/big_fish_play_record_input_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/big_fish_play_record_input_type.rs index ac5e3715..d6387572 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/big_fish_play_record_input_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/big_fish_play_record_input_type.rs @@ -2,7 +2,13 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; + #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -13,6 +19,8 @@ pub struct BigFishPlayRecordInput { pub played_at_micros: i64, } + impl __sdk::InModule for BigFishPlayRecordInput { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/big_fish_publish_input_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/big_fish_publish_input_type.rs index ec00bf84..a1a0fcca 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/big_fish_publish_input_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/big_fish_publish_input_type.rs @@ -2,7 +2,13 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; + #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -12,6 +18,8 @@ pub struct BigFishPublishInput { pub published_at_micros: i64, } + impl __sdk::InModule for BigFishPublishInput { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/big_fish_run_get_input_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/big_fish_run_get_input_type.rs new file mode 100644 index 00000000..0940b187 --- /dev/null +++ b/server-rs/crates/spacetime-client/src/module_bindings/big_fish_run_get_input_type.rs @@ -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 BigFishRunGetInput { + pub run_id: String, + pub owner_user_id: String, +} + + +impl __sdk::InModule for BigFishRunGetInput { + type Module = super::RemoteModule; +} + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/big_fish_run_procedure_result_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/big_fish_run_procedure_result_type.rs new file mode 100644 index 00000000..2bfcc9b3 --- /dev/null +++ b/server-rs/crates/spacetime-client/src/module_bindings/big_fish_run_procedure_result_type.rs @@ -0,0 +1,25 @@ +// 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 BigFishRunProcedureResult { + pub ok: bool, + pub run_json: Option::, + pub error_message: Option::, +} + + +impl __sdk::InModule for BigFishRunProcedureResult { + type Module = super::RemoteModule; +} + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/big_fish_run_start_input_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/big_fish_run_start_input_type.rs new file mode 100644 index 00000000..4e166892 --- /dev/null +++ b/server-rs/crates/spacetime-client/src/module_bindings/big_fish_run_start_input_type.rs @@ -0,0 +1,26 @@ +// 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 BigFishRunStartInput { + pub run_id: String, + pub session_id: String, + pub owner_user_id: String, + pub started_at_micros: i64, +} + + +impl __sdk::InModule for BigFishRunStartInput { + type Module = super::RemoteModule; +} + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/big_fish_run_status_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/big_fish_run_status_type.rs new file mode 100644 index 00000000..ab1b80cd --- /dev/null +++ b/server-rs/crates/spacetime-client/src/module_bindings/big_fish_run_status_type.rs @@ -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)] +#[derive(Copy, Eq, Hash)] +pub enum BigFishRunStatus { + Running, + + Won, + + Failed, + +} + + + +impl __sdk::InModule for BigFishRunStatus { + type Module = super::RemoteModule; +} + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/big_fish_runtime_params_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/big_fish_runtime_params_type.rs index 78117371..1fece110 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/big_fish_runtime_params_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/big_fish_runtime_params_type.rs @@ -2,7 +2,13 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; + #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -13,11 +19,13 @@ pub struct BigFishRuntimeParams { pub leader_move_speed: f32, pub follower_catch_up_speed: f32, pub offscreen_cull_seconds: f32, - pub prey_spawn_delta_levels: Vec, - pub threat_spawn_delta_levels: Vec, + pub prey_spawn_delta_levels: Vec::, + pub threat_spawn_delta_levels: Vec::, pub win_level: u32, } + impl __sdk::InModule for BigFishRuntimeParams { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/big_fish_runtime_run_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/big_fish_runtime_run_type.rs new file mode 100644 index 00000000..20ac1aa1 --- /dev/null +++ b/server-rs/crates/spacetime-client/src/module_bindings/big_fish_runtime_run_type.rs @@ -0,0 +1,92 @@ +// 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::big_fish_run_status_type::BigFishRunStatus; + +#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] +#[sats(crate = __lib)] +pub struct BigFishRuntimeRun { + pub run_id: String, + pub session_id: String, + pub owner_user_id: String, + pub status: BigFishRunStatus, + pub snapshot_json: String, + pub last_input_x: f32, + pub last_input_y: f32, + pub tick: u64, + pub created_at: __sdk::Timestamp, + pub updated_at: __sdk::Timestamp, +} + + +impl __sdk::InModule for BigFishRuntimeRun { + type Module = super::RemoteModule; +} + + +/// Column accessor struct for the table `BigFishRuntimeRun`. +/// +/// Provides typed access to columns for query building. +pub struct BigFishRuntimeRunCols { + pub run_id: __sdk::__query_builder::Col, + pub session_id: __sdk::__query_builder::Col, + pub owner_user_id: __sdk::__query_builder::Col, + pub status: __sdk::__query_builder::Col, + pub snapshot_json: __sdk::__query_builder::Col, + pub last_input_x: __sdk::__query_builder::Col, + pub last_input_y: __sdk::__query_builder::Col, + pub tick: __sdk::__query_builder::Col, + pub created_at: __sdk::__query_builder::Col, + pub updated_at: __sdk::__query_builder::Col, +} + +impl __sdk::__query_builder::HasCols for BigFishRuntimeRun { + type Cols = BigFishRuntimeRunCols; + fn cols(table_name: &'static str) -> Self::Cols { + BigFishRuntimeRunCols { + run_id: __sdk::__query_builder::Col::new(table_name, "run_id"), + 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"), + snapshot_json: __sdk::__query_builder::Col::new(table_name, "snapshot_json"), + last_input_x: __sdk::__query_builder::Col::new(table_name, "last_input_x"), + last_input_y: __sdk::__query_builder::Col::new(table_name, "last_input_y"), + tick: __sdk::__query_builder::Col::new(table_name, "tick"), + 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 `BigFishRuntimeRun`. +/// +/// Provides typed access to indexed columns for query building. +pub struct BigFishRuntimeRunIxCols { + pub owner_user_id: __sdk::__query_builder::IxCol, + pub run_id: __sdk::__query_builder::IxCol, + pub session_id: __sdk::__query_builder::IxCol, +} + +impl __sdk::__query_builder::HasIxCols for BigFishRuntimeRun { + type IxCols = BigFishRuntimeRunIxCols; + fn ix_cols(table_name: &'static str) -> Self::IxCols { + BigFishRuntimeRunIxCols { + owner_user_id: __sdk::__query_builder::IxCol::new(table_name, "owner_user_id"), + run_id: __sdk::__query_builder::IxCol::new(table_name, "run_id"), + session_id: __sdk::__query_builder::IxCol::new(table_name, "session_id"), + + } + } +} + +impl __sdk::__query_builder::CanBeLookupTable for BigFishRuntimeRun {} + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/big_fish_session_create_input_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/big_fish_session_create_input_type.rs index 13533785..f7ae7eb7 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/big_fish_session_create_input_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/big_fish_session_create_input_type.rs @@ -2,7 +2,13 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; + #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -15,6 +21,8 @@ pub struct BigFishSessionCreateInput { pub created_at_micros: i64, } + impl __sdk::InModule for BigFishSessionCreateInput { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/big_fish_session_get_input_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/big_fish_session_get_input_type.rs index 396808f4..88ef9cfa 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/big_fish_session_get_input_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/big_fish_session_get_input_type.rs @@ -2,7 +2,13 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; + #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -11,6 +17,8 @@ pub struct BigFishSessionGetInput { pub owner_user_id: String, } + impl __sdk::InModule for BigFishSessionGetInput { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/big_fish_session_procedure_result_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/big_fish_session_procedure_result_type.rs index 1f2f7666..db8af7c7 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/big_fish_session_procedure_result_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/big_fish_session_procedure_result_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::big_fish_session_snapshot_type::BigFishSessionSnapshot; @@ -10,10 +15,12 @@ use super::big_fish_session_snapshot_type::BigFishSessionSnapshot; #[sats(crate = __lib)] pub struct BigFishSessionProcedureResult { pub ok: bool, - pub session: Option, - pub error_message: Option, + pub session: Option::, + pub error_message: Option::, } + impl __sdk::InModule for BigFishSessionProcedureResult { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/big_fish_session_snapshot_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/big_fish_session_snapshot_type.rs index 11e6a141..abff56b6 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/big_fish_session_snapshot_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/big_fish_session_snapshot_type.rs @@ -2,14 +2,19 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; -use super::big_fish_agent_message_snapshot_type::BigFishAgentMessageSnapshot; -use super::big_fish_anchor_pack_type::BigFishAnchorPack; -use super::big_fish_asset_coverage_type::BigFishAssetCoverage; -use super::big_fish_asset_slot_snapshot_type::BigFishAssetSlotSnapshot; use super::big_fish_creation_stage_type::BigFishCreationStage; +use super::big_fish_anchor_pack_type::BigFishAnchorPack; use super::big_fish_game_draft_type::BigFishGameDraft; +use super::big_fish_asset_slot_snapshot_type::BigFishAssetSlotSnapshot; +use super::big_fish_asset_coverage_type::BigFishAssetCoverage; +use super::big_fish_agent_message_snapshot_type::BigFishAgentMessageSnapshot; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -21,16 +26,18 @@ pub struct BigFishSessionSnapshot { pub progress_percent: u32, pub stage: BigFishCreationStage, pub anchor_pack: BigFishAnchorPack, - pub draft: Option, - pub asset_slots: Vec, + pub draft: Option::, + pub asset_slots: Vec::, pub asset_coverage: BigFishAssetCoverage, - pub messages: Vec, - pub last_assistant_reply: Option, + pub messages: Vec::, + pub last_assistant_reply: Option::, pub publish_ready: bool, pub created_at_micros: i64, pub updated_at_micros: i64, } + impl __sdk::InModule for BigFishSessionSnapshot { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/big_fish_work_delete_input_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/big_fish_work_delete_input_type.rs index 3589f8d7..fcc357c9 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/big_fish_work_delete_input_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/big_fish_work_delete_input_type.rs @@ -2,7 +2,13 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; + #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -11,6 +17,8 @@ pub struct BigFishWorkDeleteInput { pub owner_user_id: String, } + impl __sdk::InModule for BigFishWorkDeleteInput { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/big_fish_works_list_input_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/big_fish_works_list_input_type.rs index 9b23133a..06a84c29 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/big_fish_works_list_input_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/big_fish_works_list_input_type.rs @@ -2,7 +2,13 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; + #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -11,6 +17,8 @@ pub struct BigFishWorksListInput { pub published_only: bool, } + impl __sdk::InModule for BigFishWorksListInput { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/big_fish_works_procedure_result_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/big_fish_works_procedure_result_type.rs index 37d7c7b6..a8fbe37b 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/big_fish_works_procedure_result_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/big_fish_works_procedure_result_type.rs @@ -2,16 +2,24 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; + #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] pub struct BigFishWorksProcedureResult { pub ok: bool, - pub items_json: Option, - pub error_message: Option, + pub items_json: Option::, + pub error_message: Option::, } + impl __sdk::InModule for BigFishWorksProcedureResult { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/bind_asset_object_to_entity_and_return_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/bind_asset_object_to_entity_and_return_procedure.rs index b709d5c2..aebf2217 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/bind_asset_object_to_entity_and_return_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/bind_asset_object_to_entity_and_return_procedure.rs @@ -2,17 +2,23 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::asset_entity_binding_input_type::AssetEntityBindingInput; use super::asset_entity_binding_procedure_result_type::AssetEntityBindingProcedureResult; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] -struct BindAssetObjectToEntityAndReturnArgs { + struct BindAssetObjectToEntityAndReturnArgs { pub input: AssetEntityBindingInput, } + impl __sdk::InModule for BindAssetObjectToEntityAndReturnArgs { type Module = super::RemoteModule; } @@ -22,19 +28,16 @@ impl __sdk::InModule for BindAssetObjectToEntityAndReturnArgs { /// /// Implemented for [`super::RemoteProcedures`]. pub trait bind_asset_object_to_entity_and_return { - fn bind_asset_object_to_entity_and_return(&self, input: AssetEntityBindingInput) { - self.bind_asset_object_to_entity_and_return_then(input, |_, _| {}); + fn bind_asset_object_to_entity_and_return(&self, input: AssetEntityBindingInput, +) { + self.bind_asset_object_to_entity_and_return_then(input, |_, _| {}); } fn bind_asset_object_to_entity_and_return_then( &self, input: AssetEntityBindingInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ); } @@ -43,17 +46,13 @@ impl bind_asset_object_to_entity_and_return for super::RemoteProcedures { &self, input: AssetEntityBindingInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ) { - self.imp - .invoke_procedure_with_callback::<_, AssetEntityBindingProcedureResult>( - "bind_asset_object_to_entity_and_return", - BindAssetObjectToEntityAndReturnArgs { input }, - __callback, - ); + self.imp.invoke_procedure_with_callback::<_, AssetEntityBindingProcedureResult>( + "bind_asset_object_to_entity_and_return", + BindAssetObjectToEntityAndReturnArgs { input, }, + __callback, + ); } } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/bind_asset_object_to_entity_reducer.rs b/server-rs/crates/spacetime-client/src/module_bindings/bind_asset_object_to_entity_reducer.rs index b20bc5b2..ca2154f7 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/bind_asset_object_to_entity_reducer.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/bind_asset_object_to_entity_reducer.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::asset_entity_binding_input_type::AssetEntityBindingInput; @@ -14,8 +19,10 @@ pub(super) struct BindAssetObjectToEntityArgs { impl From for super::Reducer { fn from(args: BindAssetObjectToEntityArgs) -> Self { - Self::BindAssetObjectToEntity { input: args.input } - } + Self::BindAssetObjectToEntity { + input: args.input, +} +} } impl __sdk::InModule for BindAssetObjectToEntityArgs { @@ -33,8 +40,9 @@ pub trait bind_asset_object_to_entity { /// The reducer will run asynchronously in the future, /// and this method provides no way to listen for its completion status. /// /// Use [`bind_asset_object_to_entity:bind_asset_object_to_entity_then`] to run a callback after the reducer completes. - fn bind_asset_object_to_entity(&self, input: AssetEntityBindingInput) -> __sdk::Result<()> { - self.bind_asset_object_to_entity_then(input, |_, _| {}) + fn bind_asset_object_to_entity(&self, input: AssetEntityBindingInput, +) -> __sdk::Result<()> { + self.bind_asset_object_to_entity_then(input, |_, _| {}) } /// Request that the remote module invoke the reducer `bind_asset_object_to_entity` to run as soon as possible, @@ -62,7 +70,7 @@ impl bind_asset_object_to_entity for super::RemoteReducers { + Send + 'static, ) -> __sdk::Result<()> { - self.imp - .invoke_reducer_with_callback(BindAssetObjectToEntityArgs { input }, callback) + self.imp.invoke_reducer_with_callback(BindAssetObjectToEntityArgs { input, }, callback) } } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/cancel_ai_task_and_return_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/cancel_ai_task_and_return_procedure.rs index b239e060..1a4a6394 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/cancel_ai_task_and_return_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/cancel_ai_task_and_return_procedure.rs @@ -2,17 +2,23 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; -use super::ai_task_cancel_input_type::AiTaskCancelInput; use super::ai_task_procedure_result_type::AiTaskProcedureResult; +use super::ai_task_cancel_input_type::AiTaskCancelInput; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] -struct CancelAiTaskAndReturnArgs { + struct CancelAiTaskAndReturnArgs { pub input: AiTaskCancelInput, } + impl __sdk::InModule for CancelAiTaskAndReturnArgs { type Module = super::RemoteModule; } @@ -22,19 +28,16 @@ impl __sdk::InModule for CancelAiTaskAndReturnArgs { /// /// Implemented for [`super::RemoteProcedures`]. pub trait cancel_ai_task_and_return { - fn cancel_ai_task_and_return(&self, input: AiTaskCancelInput) { - self.cancel_ai_task_and_return_then(input, |_, _| {}); + fn cancel_ai_task_and_return(&self, input: AiTaskCancelInput, +) { + self.cancel_ai_task_and_return_then(input, |_, _| {}); } fn cancel_ai_task_and_return_then( &self, input: AiTaskCancelInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ); } @@ -43,17 +46,13 @@ impl cancel_ai_task_and_return for super::RemoteProcedures { &self, input: AiTaskCancelInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ) { - self.imp - .invoke_procedure_with_callback::<_, AiTaskProcedureResult>( - "cancel_ai_task_and_return", - CancelAiTaskAndReturnArgs { input }, - __callback, - ); + self.imp.invoke_procedure_with_callback::<_, AiTaskProcedureResult>( + "cancel_ai_task_and_return", + CancelAiTaskAndReturnArgs { input, }, + __callback, + ); } } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/chapter_pace_band_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/chapter_pace_band_type.rs index effbeb9d..fe7d5e3a 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/chapter_pace_band_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/chapter_pace_band_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -15,8 +20,12 @@ pub enum ChapterPaceBand { Pressure, FinaleDense, + } + + impl __sdk::InModule for ChapterPaceBand { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/chapter_progression_get_input_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/chapter_progression_get_input_type.rs index 927a4b04..cc88d00b 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/chapter_progression_get_input_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/chapter_progression_get_input_type.rs @@ -2,7 +2,13 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; + #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -11,6 +17,8 @@ pub struct ChapterProgressionGetInput { pub chapter_id: String, } + impl __sdk::InModule for ChapterProgressionGetInput { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/chapter_progression_input_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/chapter_progression_input_type.rs index dae51fa0..ed9aa5ca 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/chapter_progression_input_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/chapter_progression_input_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::chapter_pace_band_type::ChapterPaceBand; @@ -26,6 +31,8 @@ pub struct ChapterProgressionInput { pub updated_at_micros: i64, } + impl __sdk::InModule for ChapterProgressionInput { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/chapter_progression_ledger_input_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/chapter_progression_ledger_input_type.rs index a599ffc9..a9e0a8c3 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/chapter_progression_ledger_input_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/chapter_progression_ledger_input_type.rs @@ -2,7 +2,13 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; + #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -12,10 +18,12 @@ pub struct ChapterProgressionLedgerInput { pub granted_quest_xp: u32, pub granted_hostile_xp: u32, pub hostile_defeat_increment: u32, - pub level_at_exit: Option, + pub level_at_exit: Option::, pub updated_at_micros: i64, } + impl __sdk::InModule for ChapterProgressionLedgerInput { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/chapter_progression_procedure_result_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/chapter_progression_procedure_result_type.rs index 276b3a26..6a7d01af 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/chapter_progression_procedure_result_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/chapter_progression_procedure_result_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::chapter_progression_snapshot_type::ChapterProgressionSnapshot; @@ -10,10 +15,12 @@ use super::chapter_progression_snapshot_type::ChapterProgressionSnapshot; #[sats(crate = __lib)] pub struct ChapterProgressionProcedureResult { pub ok: bool, - pub record: Option, - pub error_message: Option, + pub record: Option::, + pub error_message: Option::, } + impl __sdk::InModule for ChapterProgressionProcedureResult { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/chapter_progression_snapshot_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/chapter_progression_snapshot_type.rs index e1fde7fe..d1eaad92 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/chapter_progression_snapshot_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/chapter_progression_snapshot_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::chapter_pace_band_type::ChapterPaceBand; @@ -25,12 +30,14 @@ pub struct ChapterProgressionSnapshot { pub expected_hostile_defeat_count: u32, pub actual_hostile_defeat_count: u32, pub level_at_entry: u32, - pub level_at_exit: Option, + pub level_at_exit: Option::, pub pace_band: ChapterPaceBand, pub created_at_micros: i64, pub updated_at_micros: i64, } + impl __sdk::InModule for ChapterProgressionSnapshot { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/chapter_progression_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/chapter_progression_type.rs index c94a7196..c5600eb7 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/chapter_progression_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/chapter_progression_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::chapter_pace_band_type::ChapterPaceBand; @@ -26,16 +31,18 @@ pub struct ChapterProgression { pub expected_hostile_defeat_count: u32, pub actual_hostile_defeat_count: u32, pub level_at_entry: u32, - pub level_at_exit: Option, + pub level_at_exit: Option::, pub pace_band: ChapterPaceBand, pub created_at: __sdk::Timestamp, pub updated_at: __sdk::Timestamp, } + impl __sdk::InModule for ChapterProgression { type Module = super::RemoteModule; } + /// Column accessor struct for the table `ChapterProgression`. /// /// Provides typed access to columns for query building. @@ -57,7 +64,7 @@ pub struct ChapterProgressionCols { pub expected_hostile_defeat_count: __sdk::__query_builder::Col, pub actual_hostile_defeat_count: __sdk::__query_builder::Col, pub level_at_entry: __sdk::__query_builder::Col, - pub level_at_exit: __sdk::__query_builder::Col>, + pub level_at_exit: __sdk::__query_builder::Col>, pub pace_band: __sdk::__query_builder::Col, pub created_at: __sdk::__query_builder::Col, pub updated_at: __sdk::__query_builder::Col, @@ -67,22 +74,13 @@ impl __sdk::__query_builder::HasCols for ChapterProgression { type Cols = ChapterProgressionCols; fn cols(table_name: &'static str) -> Self::Cols { ChapterProgressionCols { - chapter_progression_id: __sdk::__query_builder::Col::new( - table_name, - "chapter_progression_id", - ), + chapter_progression_id: __sdk::__query_builder::Col::new(table_name, "chapter_progression_id"), user_id: __sdk::__query_builder::Col::new(table_name, "user_id"), chapter_id: __sdk::__query_builder::Col::new(table_name, "chapter_id"), chapter_index: __sdk::__query_builder::Col::new(table_name, "chapter_index"), total_chapters: __sdk::__query_builder::Col::new(table_name, "total_chapters"), - entry_pseudo_level_millis: __sdk::__query_builder::Col::new( - table_name, - "entry_pseudo_level_millis", - ), - exit_pseudo_level_millis: __sdk::__query_builder::Col::new( - table_name, - "exit_pseudo_level_millis", - ), + entry_pseudo_level_millis: __sdk::__query_builder::Col::new(table_name, "entry_pseudo_level_millis"), + exit_pseudo_level_millis: __sdk::__query_builder::Col::new(table_name, "exit_pseudo_level_millis"), entry_level: __sdk::__query_builder::Col::new(table_name, "entry_level"), exit_level: __sdk::__query_builder::Col::new(table_name, "exit_level"), planned_total_xp: __sdk::__query_builder::Col::new(table_name, "planned_total_xp"), @@ -90,19 +88,14 @@ impl __sdk::__query_builder::HasCols for ChapterProgression { planned_hostile_xp: __sdk::__query_builder::Col::new(table_name, "planned_hostile_xp"), actual_quest_xp: __sdk::__query_builder::Col::new(table_name, "actual_quest_xp"), actual_hostile_xp: __sdk::__query_builder::Col::new(table_name, "actual_hostile_xp"), - expected_hostile_defeat_count: __sdk::__query_builder::Col::new( - table_name, - "expected_hostile_defeat_count", - ), - actual_hostile_defeat_count: __sdk::__query_builder::Col::new( - table_name, - "actual_hostile_defeat_count", - ), + expected_hostile_defeat_count: __sdk::__query_builder::Col::new(table_name, "expected_hostile_defeat_count"), + actual_hostile_defeat_count: __sdk::__query_builder::Col::new(table_name, "actual_hostile_defeat_count"), level_at_entry: __sdk::__query_builder::Col::new(table_name, "level_at_entry"), level_at_exit: __sdk::__query_builder::Col::new(table_name, "level_at_exit"), pace_band: __sdk::__query_builder::Col::new(table_name, "pace_band"), created_at: __sdk::__query_builder::Col::new(table_name, "created_at"), updated_at: __sdk::__query_builder::Col::new(table_name, "updated_at"), + } } } @@ -121,13 +114,12 @@ impl __sdk::__query_builder::HasIxCols for ChapterProgression { fn ix_cols(table_name: &'static str) -> Self::IxCols { ChapterProgressionIxCols { chapter_id: __sdk::__query_builder::IxCol::new(table_name, "chapter_id"), - chapter_progression_id: __sdk::__query_builder::IxCol::new( - table_name, - "chapter_progression_id", - ), + chapter_progression_id: __sdk::__query_builder::IxCol::new(table_name, "chapter_progression_id"), user_id: __sdk::__query_builder::IxCol::new(table_name, "user_id"), + } } } impl __sdk::__query_builder::CanBeLookupTable for ChapterProgression {} + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/clear_platform_browse_history_and_return_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/clear_platform_browse_history_and_return_procedure.rs index 2e623845..5b9a2c9e 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/clear_platform_browse_history_and_return_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/clear_platform_browse_history_and_return_procedure.rs @@ -2,17 +2,23 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::runtime_browse_history_clear_input_type::RuntimeBrowseHistoryClearInput; use super::runtime_browse_history_procedure_result_type::RuntimeBrowseHistoryProcedureResult; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] -struct ClearPlatformBrowseHistoryAndReturnArgs { + struct ClearPlatformBrowseHistoryAndReturnArgs { pub input: RuntimeBrowseHistoryClearInput, } + impl __sdk::InModule for ClearPlatformBrowseHistoryAndReturnArgs { type Module = super::RemoteModule; } @@ -22,19 +28,16 @@ impl __sdk::InModule for ClearPlatformBrowseHistoryAndReturnArgs { /// /// Implemented for [`super::RemoteProcedures`]. pub trait clear_platform_browse_history_and_return { - fn clear_platform_browse_history_and_return(&self, input: RuntimeBrowseHistoryClearInput) { - self.clear_platform_browse_history_and_return_then(input, |_, _| {}); + fn clear_platform_browse_history_and_return(&self, input: RuntimeBrowseHistoryClearInput, +) { + self.clear_platform_browse_history_and_return_then(input, |_, _| {}); } fn clear_platform_browse_history_and_return_then( &self, input: RuntimeBrowseHistoryClearInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ); } @@ -43,17 +46,13 @@ impl clear_platform_browse_history_and_return for super::RemoteProcedures { &self, input: RuntimeBrowseHistoryClearInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ) { - self.imp - .invoke_procedure_with_callback::<_, RuntimeBrowseHistoryProcedureResult>( - "clear_platform_browse_history_and_return", - ClearPlatformBrowseHistoryAndReturnArgs { input }, - __callback, - ); + self.imp.invoke_procedure_with_callback::<_, RuntimeBrowseHistoryProcedureResult>( + "clear_platform_browse_history_and_return", + ClearPlatformBrowseHistoryAndReturnArgs { input, }, + __callback, + ); } } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/combat_outcome_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/combat_outcome_type.rs index 731563dd..5ef77940 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/combat_outcome_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/combat_outcome_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -15,8 +20,12 @@ pub enum CombatOutcome { SparComplete, Escaped, + } + + impl __sdk::InModule for CombatOutcome { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/compile_big_fish_draft_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/compile_big_fish_draft_procedure.rs index 6eea6571..ba45db0a 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/compile_big_fish_draft_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/compile_big_fish_draft_procedure.rs @@ -2,17 +2,23 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::big_fish_draft_compile_input_type::BigFishDraftCompileInput; use super::big_fish_session_procedure_result_type::BigFishSessionProcedureResult; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] -struct CompileBigFishDraftArgs { + struct CompileBigFishDraftArgs { pub input: BigFishDraftCompileInput, } + impl __sdk::InModule for CompileBigFishDraftArgs { type Module = super::RemoteModule; } @@ -22,19 +28,16 @@ impl __sdk::InModule for CompileBigFishDraftArgs { /// /// Implemented for [`super::RemoteProcedures`]. pub trait compile_big_fish_draft { - fn compile_big_fish_draft(&self, input: BigFishDraftCompileInput) { - self.compile_big_fish_draft_then(input, |_, _| {}); + fn compile_big_fish_draft(&self, input: BigFishDraftCompileInput, +) { + self.compile_big_fish_draft_then(input, |_, _| {}); } fn compile_big_fish_draft_then( &self, input: BigFishDraftCompileInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ); } @@ -43,17 +46,13 @@ impl compile_big_fish_draft for super::RemoteProcedures { &self, input: BigFishDraftCompileInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ) { - self.imp - .invoke_procedure_with_callback::<_, BigFishSessionProcedureResult>( - "compile_big_fish_draft", - CompileBigFishDraftArgs { input }, - __callback, - ); + self.imp.invoke_procedure_with_callback::<_, BigFishSessionProcedureResult>( + "compile_big_fish_draft", + CompileBigFishDraftArgs { input, }, + __callback, + ); } } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/compile_custom_world_published_profile_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/compile_custom_world_published_profile_procedure.rs index aefbe602..767a3954 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/compile_custom_world_published_profile_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/compile_custom_world_published_profile_procedure.rs @@ -2,17 +2,23 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::custom_world_published_profile_compile_input_type::CustomWorldPublishedProfileCompileInput; use super::custom_world_published_profile_compile_result_type::CustomWorldPublishedProfileCompileResult; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] -struct CompileCustomWorldPublishedProfileArgs { + struct CompileCustomWorldPublishedProfileArgs { pub input: CustomWorldPublishedProfileCompileInput, } + impl __sdk::InModule for CompileCustomWorldPublishedProfileArgs { type Module = super::RemoteModule; } @@ -22,22 +28,16 @@ impl __sdk::InModule for CompileCustomWorldPublishedProfileArgs { /// /// Implemented for [`super::RemoteProcedures`]. pub trait compile_custom_world_published_profile { - fn compile_custom_world_published_profile( - &self, - input: CustomWorldPublishedProfileCompileInput, - ) { - self.compile_custom_world_published_profile_then(input, |_, _| {}); + fn compile_custom_world_published_profile(&self, input: CustomWorldPublishedProfileCompileInput, +) { + self.compile_custom_world_published_profile_then(input, |_, _| {}); } fn compile_custom_world_published_profile_then( &self, input: CustomWorldPublishedProfileCompileInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ); } @@ -46,17 +46,13 @@ impl compile_custom_world_published_profile for super::RemoteProcedures { &self, input: CustomWorldPublishedProfileCompileInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ) { - self.imp - .invoke_procedure_with_callback::<_, CustomWorldPublishedProfileCompileResult>( - "compile_custom_world_published_profile", - CompileCustomWorldPublishedProfileArgs { input }, - __callback, - ); + self.imp.invoke_procedure_with_callback::<_, CustomWorldPublishedProfileCompileResult>( + "compile_custom_world_published_profile", + CompileCustomWorldPublishedProfileArgs { input, }, + __callback, + ); } } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/compile_puzzle_agent_draft_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/compile_puzzle_agent_draft_procedure.rs index 7badcdae..d90769dc 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/compile_puzzle_agent_draft_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/compile_puzzle_agent_draft_procedure.rs @@ -2,17 +2,23 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; -use super::puzzle_agent_session_procedure_result_type::PuzzleAgentSessionProcedureResult; use super::puzzle_draft_compile_input_type::PuzzleDraftCompileInput; +use super::puzzle_agent_session_procedure_result_type::PuzzleAgentSessionProcedureResult; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] -struct CompilePuzzleAgentDraftArgs { + struct CompilePuzzleAgentDraftArgs { pub input: PuzzleDraftCompileInput, } + impl __sdk::InModule for CompilePuzzleAgentDraftArgs { type Module = super::RemoteModule; } @@ -22,19 +28,16 @@ impl __sdk::InModule for CompilePuzzleAgentDraftArgs { /// /// Implemented for [`super::RemoteProcedures`]. pub trait compile_puzzle_agent_draft { - fn compile_puzzle_agent_draft(&self, input: PuzzleDraftCompileInput) { - self.compile_puzzle_agent_draft_then(input, |_, _| {}); + fn compile_puzzle_agent_draft(&self, input: PuzzleDraftCompileInput, +) { + self.compile_puzzle_agent_draft_then(input, |_, _| {}); } fn compile_puzzle_agent_draft_then( &self, input: PuzzleDraftCompileInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ); } @@ -43,17 +46,13 @@ impl compile_puzzle_agent_draft for super::RemoteProcedures { &self, input: PuzzleDraftCompileInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ) { - self.imp - .invoke_procedure_with_callback::<_, PuzzleAgentSessionProcedureResult>( - "compile_puzzle_agent_draft", - CompilePuzzleAgentDraftArgs { input }, - __callback, - ); + self.imp.invoke_procedure_with_callback::<_, PuzzleAgentSessionProcedureResult>( + "compile_puzzle_agent_draft", + CompilePuzzleAgentDraftArgs { input, }, + __callback, + ); } } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/complete_ai_stage_and_return_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/complete_ai_stage_and_return_procedure.rs index 51375935..f921c224 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/complete_ai_stage_and_return_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/complete_ai_stage_and_return_procedure.rs @@ -2,17 +2,23 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; -use super::ai_stage_completion_input_type::AiStageCompletionInput; use super::ai_task_procedure_result_type::AiTaskProcedureResult; +use super::ai_stage_completion_input_type::AiStageCompletionInput; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] -struct CompleteAiStageAndReturnArgs { + struct CompleteAiStageAndReturnArgs { pub input: AiStageCompletionInput, } + impl __sdk::InModule for CompleteAiStageAndReturnArgs { type Module = super::RemoteModule; } @@ -22,19 +28,16 @@ impl __sdk::InModule for CompleteAiStageAndReturnArgs { /// /// Implemented for [`super::RemoteProcedures`]. pub trait complete_ai_stage_and_return { - fn complete_ai_stage_and_return(&self, input: AiStageCompletionInput) { - self.complete_ai_stage_and_return_then(input, |_, _| {}); + fn complete_ai_stage_and_return(&self, input: AiStageCompletionInput, +) { + self.complete_ai_stage_and_return_then(input, |_, _| {}); } fn complete_ai_stage_and_return_then( &self, input: AiStageCompletionInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ); } @@ -43,17 +46,13 @@ impl complete_ai_stage_and_return for super::RemoteProcedures { &self, input: AiStageCompletionInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ) { - self.imp - .invoke_procedure_with_callback::<_, AiTaskProcedureResult>( - "complete_ai_stage_and_return", - CompleteAiStageAndReturnArgs { input }, - __callback, - ); + self.imp.invoke_procedure_with_callback::<_, AiTaskProcedureResult>( + "complete_ai_stage_and_return", + CompleteAiStageAndReturnArgs { input, }, + __callback, + ); } } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/complete_ai_task_and_return_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/complete_ai_task_and_return_procedure.rs index 040af639..47ae2af1 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/complete_ai_task_and_return_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/complete_ai_task_and_return_procedure.rs @@ -2,17 +2,23 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; -use super::ai_task_finish_input_type::AiTaskFinishInput; use super::ai_task_procedure_result_type::AiTaskProcedureResult; +use super::ai_task_finish_input_type::AiTaskFinishInput; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] -struct CompleteAiTaskAndReturnArgs { + struct CompleteAiTaskAndReturnArgs { pub input: AiTaskFinishInput, } + impl __sdk::InModule for CompleteAiTaskAndReturnArgs { type Module = super::RemoteModule; } @@ -22,19 +28,16 @@ impl __sdk::InModule for CompleteAiTaskAndReturnArgs { /// /// Implemented for [`super::RemoteProcedures`]. pub trait complete_ai_task_and_return { - fn complete_ai_task_and_return(&self, input: AiTaskFinishInput) { - self.complete_ai_task_and_return_then(input, |_, _| {}); + fn complete_ai_task_and_return(&self, input: AiTaskFinishInput, +) { + self.complete_ai_task_and_return_then(input, |_, _| {}); } fn complete_ai_task_and_return_then( &self, input: AiTaskFinishInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ); } @@ -43,17 +46,13 @@ impl complete_ai_task_and_return for super::RemoteProcedures { &self, input: AiTaskFinishInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ) { - self.imp - .invoke_procedure_with_callback::<_, AiTaskProcedureResult>( - "complete_ai_task_and_return", - CompleteAiTaskAndReturnArgs { input }, - __callback, - ); + self.imp.invoke_procedure_with_callback::<_, AiTaskProcedureResult>( + "complete_ai_task_and_return", + CompleteAiTaskAndReturnArgs { input, }, + __callback, + ); } } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/confirm_asset_object_and_return_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/confirm_asset_object_and_return_procedure.rs index 0b4f26b2..11c079e4 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/confirm_asset_object_and_return_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/confirm_asset_object_and_return_procedure.rs @@ -2,17 +2,23 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; -use super::asset_object_procedure_result_type::AssetObjectProcedureResult; use super::asset_object_upsert_input_type::AssetObjectUpsertInput; +use super::asset_object_procedure_result_type::AssetObjectProcedureResult; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] -struct ConfirmAssetObjectAndReturnArgs { + struct ConfirmAssetObjectAndReturnArgs { pub input: AssetObjectUpsertInput, } + impl __sdk::InModule for ConfirmAssetObjectAndReturnArgs { type Module = super::RemoteModule; } @@ -22,19 +28,16 @@ impl __sdk::InModule for ConfirmAssetObjectAndReturnArgs { /// /// Implemented for [`super::RemoteProcedures`]. pub trait confirm_asset_object_and_return { - fn confirm_asset_object_and_return(&self, input: AssetObjectUpsertInput) { - self.confirm_asset_object_and_return_then(input, |_, _| {}); + fn confirm_asset_object_and_return(&self, input: AssetObjectUpsertInput, +) { + self.confirm_asset_object_and_return_then(input, |_, _| {}); } fn confirm_asset_object_and_return_then( &self, input: AssetObjectUpsertInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ); } @@ -43,17 +46,13 @@ impl confirm_asset_object_and_return for super::RemoteProcedures { &self, input: AssetObjectUpsertInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ) { - self.imp - .invoke_procedure_with_callback::<_, AssetObjectProcedureResult>( - "confirm_asset_object_and_return", - ConfirmAssetObjectAndReturnArgs { input }, - __callback, - ); + self.imp.invoke_procedure_with_callback::<_, AssetObjectProcedureResult>( + "confirm_asset_object_and_return", + ConfirmAssetObjectAndReturnArgs { input, }, + __callback, + ); } } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/confirm_asset_object_reducer.rs b/server-rs/crates/spacetime-client/src/module_bindings/confirm_asset_object_reducer.rs index 183c2efa..fb6acde2 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/confirm_asset_object_reducer.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/confirm_asset_object_reducer.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::asset_object_upsert_input_type::AssetObjectUpsertInput; @@ -14,8 +19,10 @@ pub(super) struct ConfirmAssetObjectArgs { impl From for super::Reducer { fn from(args: ConfirmAssetObjectArgs) -> Self { - Self::ConfirmAssetObject { input: args.input } - } + Self::ConfirmAssetObject { + input: args.input, +} +} } impl __sdk::InModule for ConfirmAssetObjectArgs { @@ -33,8 +40,9 @@ pub trait confirm_asset_object { /// The reducer will run asynchronously in the future, /// and this method provides no way to listen for its completion status. /// /// Use [`confirm_asset_object:confirm_asset_object_then`] to run a callback after the reducer completes. - fn confirm_asset_object(&self, input: AssetObjectUpsertInput) -> __sdk::Result<()> { - self.confirm_asset_object_then(input, |_, _| {}) + fn confirm_asset_object(&self, input: AssetObjectUpsertInput, +) -> __sdk::Result<()> { + self.confirm_asset_object_then(input, |_, _| {}) } /// Request that the remote module invoke the reducer `confirm_asset_object` to run as soon as possible, @@ -62,7 +70,7 @@ impl confirm_asset_object for super::RemoteReducers { + Send + 'static, ) -> __sdk::Result<()> { - self.imp - .invoke_reducer_with_callback(ConfirmAssetObjectArgs { input }, callback) + self.imp.invoke_reducer_with_callback(ConfirmAssetObjectArgs { input, }, callback) } } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/consume_inventory_item_input_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/consume_inventory_item_input_type.rs index 0e867e21..3bd69ba8 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/consume_inventory_item_input_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/consume_inventory_item_input_type.rs @@ -2,7 +2,13 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; + #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -11,6 +17,8 @@ pub struct ConsumeInventoryItemInput { pub quantity: u32, } + impl __sdk::InModule for ConsumeInventoryItemInput { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/consume_profile_wallet_points_and_return_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/consume_profile_wallet_points_and_return_procedure.rs index 3d3e47a2..3323358a 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/consume_profile_wallet_points_and_return_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/consume_profile_wallet_points_and_return_procedure.rs @@ -2,17 +2,23 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::runtime_profile_wallet_adjustment_input_type::RuntimeProfileWalletAdjustmentInput; use super::runtime_profile_wallet_adjustment_procedure_result_type::RuntimeProfileWalletAdjustmentProcedureResult; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] -struct ConsumeProfileWalletPointsAndReturnArgs { + struct ConsumeProfileWalletPointsAndReturnArgs { pub input: RuntimeProfileWalletAdjustmentInput, } + impl __sdk::InModule for ConsumeProfileWalletPointsAndReturnArgs { type Module = super::RemoteModule; } @@ -22,19 +28,16 @@ impl __sdk::InModule for ConsumeProfileWalletPointsAndReturnArgs { /// /// Implemented for [`super::RemoteProcedures`]. pub trait consume_profile_wallet_points_and_return { - fn consume_profile_wallet_points_and_return(&self, input: RuntimeProfileWalletAdjustmentInput) { - self.consume_profile_wallet_points_and_return_then(input, |_, _| {}); + fn consume_profile_wallet_points_and_return(&self, input: RuntimeProfileWalletAdjustmentInput, +) { + self.consume_profile_wallet_points_and_return_then(input, |_, _| {}); } fn consume_profile_wallet_points_and_return_then( &self, input: RuntimeProfileWalletAdjustmentInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ); } @@ -43,17 +46,13 @@ impl consume_profile_wallet_points_and_return for super::RemoteProcedures { &self, input: RuntimeProfileWalletAdjustmentInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ) { - self.imp - .invoke_procedure_with_callback::<_, RuntimeProfileWalletAdjustmentProcedureResult>( - "consume_profile_wallet_points_and_return", - ConsumeProfileWalletPointsAndReturnArgs { input }, - __callback, - ); + self.imp.invoke_procedure_with_callback::<_, RuntimeProfileWalletAdjustmentProcedureResult>( + "consume_profile_wallet_points_and_return", + ConsumeProfileWalletPointsAndReturnArgs { input, }, + __callback, + ); } } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/continue_story_and_return_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/continue_story_and_return_procedure.rs index 0d58ec1b..0713f423 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/continue_story_and_return_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/continue_story_and_return_procedure.rs @@ -2,17 +2,23 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; -use super::story_continue_input_type::StoryContinueInput; use super::story_session_procedure_result_type::StorySessionProcedureResult; +use super::story_continue_input_type::StoryContinueInput; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] -struct ContinueStoryAndReturnArgs { + struct ContinueStoryAndReturnArgs { pub input: StoryContinueInput, } + impl __sdk::InModule for ContinueStoryAndReturnArgs { type Module = super::RemoteModule; } @@ -22,19 +28,16 @@ impl __sdk::InModule for ContinueStoryAndReturnArgs { /// /// Implemented for [`super::RemoteProcedures`]. pub trait continue_story_and_return { - fn continue_story_and_return(&self, input: StoryContinueInput) { - self.continue_story_and_return_then(input, |_, _| {}); + fn continue_story_and_return(&self, input: StoryContinueInput, +) { + self.continue_story_and_return_then(input, |_, _| {}); } fn continue_story_and_return_then( &self, input: StoryContinueInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ); } @@ -43,17 +46,13 @@ impl continue_story_and_return for super::RemoteProcedures { &self, input: StoryContinueInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ) { - self.imp - .invoke_procedure_with_callback::<_, StorySessionProcedureResult>( - "continue_story_and_return", - ContinueStoryAndReturnArgs { input }, - __callback, - ); + self.imp.invoke_procedure_with_callback::<_, StorySessionProcedureResult>( + "continue_story_and_return", + ContinueStoryAndReturnArgs { input, }, + __callback, + ); } } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/continue_story_reducer.rs b/server-rs/crates/spacetime-client/src/module_bindings/continue_story_reducer.rs index 4117cfae..9c2b310a 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/continue_story_reducer.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/continue_story_reducer.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::story_continue_input_type::StoryContinueInput; @@ -14,8 +19,10 @@ pub(super) struct ContinueStoryArgs { impl From for super::Reducer { fn from(args: ContinueStoryArgs) -> Self { - Self::ContinueStory { input: args.input } - } + Self::ContinueStory { + input: args.input, +} +} } impl __sdk::InModule for ContinueStoryArgs { @@ -33,8 +40,9 @@ pub trait continue_story { /// The reducer will run asynchronously in the future, /// and this method provides no way to listen for its completion status. /// /// Use [`continue_story:continue_story_then`] to run a callback after the reducer completes. - fn continue_story(&self, input: StoryContinueInput) -> __sdk::Result<()> { - self.continue_story_then(input, |_, _| {}) + fn continue_story(&self, input: StoryContinueInput, +) -> __sdk::Result<()> { + self.continue_story_then(input, |_, _| {}) } /// Request that the remote module invoke the reducer `continue_story` to run as soon as possible, @@ -62,7 +70,7 @@ impl continue_story for super::RemoteReducers { + Send + 'static, ) -> __sdk::Result<()> { - self.imp - .invoke_reducer_with_callback(ContinueStoryArgs { input }, callback) + self.imp.invoke_reducer_with_callback(ContinueStoryArgs { input, }, callback) } } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/create_ai_task_and_return_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/create_ai_task_and_return_procedure.rs index 20d8ceee..cee9ffd8 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/create_ai_task_and_return_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/create_ai_task_and_return_procedure.rs @@ -2,17 +2,23 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; -use super::ai_task_create_input_type::AiTaskCreateInput; use super::ai_task_procedure_result_type::AiTaskProcedureResult; +use super::ai_task_create_input_type::AiTaskCreateInput; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] -struct CreateAiTaskAndReturnArgs { + struct CreateAiTaskAndReturnArgs { pub input: AiTaskCreateInput, } + impl __sdk::InModule for CreateAiTaskAndReturnArgs { type Module = super::RemoteModule; } @@ -22,19 +28,16 @@ impl __sdk::InModule for CreateAiTaskAndReturnArgs { /// /// Implemented for [`super::RemoteProcedures`]. pub trait create_ai_task_and_return { - fn create_ai_task_and_return(&self, input: AiTaskCreateInput) { - self.create_ai_task_and_return_then(input, |_, _| {}); + fn create_ai_task_and_return(&self, input: AiTaskCreateInput, +) { + self.create_ai_task_and_return_then(input, |_, _| {}); } fn create_ai_task_and_return_then( &self, input: AiTaskCreateInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ); } @@ -43,17 +46,13 @@ impl create_ai_task_and_return for super::RemoteProcedures { &self, input: AiTaskCreateInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ) { - self.imp - .invoke_procedure_with_callback::<_, AiTaskProcedureResult>( - "create_ai_task_and_return", - CreateAiTaskAndReturnArgs { input }, - __callback, - ); + self.imp.invoke_procedure_with_callback::<_, AiTaskProcedureResult>( + "create_ai_task_and_return", + CreateAiTaskAndReturnArgs { input, }, + __callback, + ); } } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/create_ai_task_reducer.rs b/server-rs/crates/spacetime-client/src/module_bindings/create_ai_task_reducer.rs index 213f28e5..d57e7291 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/create_ai_task_reducer.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/create_ai_task_reducer.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::ai_task_create_input_type::AiTaskCreateInput; @@ -14,8 +19,10 @@ pub(super) struct CreateAiTaskArgs { impl From for super::Reducer { fn from(args: CreateAiTaskArgs) -> Self { - Self::CreateAiTask { input: args.input } - } + Self::CreateAiTask { + input: args.input, +} +} } impl __sdk::InModule for CreateAiTaskArgs { @@ -33,8 +40,9 @@ pub trait create_ai_task { /// The reducer will run asynchronously in the future, /// and this method provides no way to listen for its completion status. /// /// Use [`create_ai_task:create_ai_task_then`] to run a callback after the reducer completes. - fn create_ai_task(&self, input: AiTaskCreateInput) -> __sdk::Result<()> { - self.create_ai_task_then(input, |_, _| {}) + fn create_ai_task(&self, input: AiTaskCreateInput, +) -> __sdk::Result<()> { + self.create_ai_task_then(input, |_, _| {}) } /// Request that the remote module invoke the reducer `create_ai_task` to run as soon as possible, @@ -62,7 +70,7 @@ impl create_ai_task for super::RemoteReducers { + Send + 'static, ) -> __sdk::Result<()> { - self.imp - .invoke_reducer_with_callback(CreateAiTaskArgs { input }, callback) + self.imp.invoke_reducer_with_callback(CreateAiTaskArgs { input, }, callback) } } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/create_battle_state_and_return_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/create_battle_state_and_return_procedure.rs index ef11107a..bfee133e 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/create_battle_state_and_return_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/create_battle_state_and_return_procedure.rs @@ -2,17 +2,23 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::battle_state_input_type::BattleStateInput; use super::battle_state_procedure_result_type::BattleStateProcedureResult; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] -struct CreateBattleStateAndReturnArgs { + struct CreateBattleStateAndReturnArgs { pub input: BattleStateInput, } + impl __sdk::InModule for CreateBattleStateAndReturnArgs { type Module = super::RemoteModule; } @@ -22,19 +28,16 @@ impl __sdk::InModule for CreateBattleStateAndReturnArgs { /// /// Implemented for [`super::RemoteProcedures`]. pub trait create_battle_state_and_return { - fn create_battle_state_and_return(&self, input: BattleStateInput) { - self.create_battle_state_and_return_then(input, |_, _| {}); + fn create_battle_state_and_return(&self, input: BattleStateInput, +) { + self.create_battle_state_and_return_then(input, |_, _| {}); } fn create_battle_state_and_return_then( &self, input: BattleStateInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ); } @@ -43,17 +46,13 @@ impl create_battle_state_and_return for super::RemoteProcedures { &self, input: BattleStateInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ) { - self.imp - .invoke_procedure_with_callback::<_, BattleStateProcedureResult>( - "create_battle_state_and_return", - CreateBattleStateAndReturnArgs { input }, - __callback, - ); + self.imp.invoke_procedure_with_callback::<_, BattleStateProcedureResult>( + "create_battle_state_and_return", + CreateBattleStateAndReturnArgs { input, }, + __callback, + ); } } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/create_battle_state_reducer.rs b/server-rs/crates/spacetime-client/src/module_bindings/create_battle_state_reducer.rs index 1072f8a5..258d5af2 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/create_battle_state_reducer.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/create_battle_state_reducer.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::battle_state_input_type::BattleStateInput; @@ -14,8 +19,10 @@ pub(super) struct CreateBattleStateArgs { impl From for super::Reducer { fn from(args: CreateBattleStateArgs) -> Self { - Self::CreateBattleState { input: args.input } - } + Self::CreateBattleState { + input: args.input, +} +} } impl __sdk::InModule for CreateBattleStateArgs { @@ -33,8 +40,9 @@ pub trait create_battle_state { /// The reducer will run asynchronously in the future, /// and this method provides no way to listen for its completion status. /// /// Use [`create_battle_state:create_battle_state_then`] to run a callback after the reducer completes. - fn create_battle_state(&self, input: BattleStateInput) -> __sdk::Result<()> { - self.create_battle_state_then(input, |_, _| {}) + fn create_battle_state(&self, input: BattleStateInput, +) -> __sdk::Result<()> { + self.create_battle_state_then(input, |_, _| {}) } /// Request that the remote module invoke the reducer `create_battle_state` to run as soon as possible, @@ -62,7 +70,7 @@ impl create_battle_state for super::RemoteReducers { + Send + 'static, ) -> __sdk::Result<()> { - self.imp - .invoke_reducer_with_callback(CreateBattleStateArgs { input }, callback) + self.imp.invoke_reducer_with_callback(CreateBattleStateArgs { input, }, callback) } } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/create_big_fish_session_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/create_big_fish_session_procedure.rs index bc67436e..cf4398e6 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/create_big_fish_session_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/create_big_fish_session_procedure.rs @@ -2,17 +2,23 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; -use super::big_fish_session_create_input_type::BigFishSessionCreateInput; use super::big_fish_session_procedure_result_type::BigFishSessionProcedureResult; +use super::big_fish_session_create_input_type::BigFishSessionCreateInput; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] -struct CreateBigFishSessionArgs { + struct CreateBigFishSessionArgs { pub input: BigFishSessionCreateInput, } + impl __sdk::InModule for CreateBigFishSessionArgs { type Module = super::RemoteModule; } @@ -22,19 +28,16 @@ impl __sdk::InModule for CreateBigFishSessionArgs { /// /// Implemented for [`super::RemoteProcedures`]. pub trait create_big_fish_session { - fn create_big_fish_session(&self, input: BigFishSessionCreateInput) { - self.create_big_fish_session_then(input, |_, _| {}); + fn create_big_fish_session(&self, input: BigFishSessionCreateInput, +) { + self.create_big_fish_session_then(input, |_, _| {}); } fn create_big_fish_session_then( &self, input: BigFishSessionCreateInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ); } @@ -43,17 +46,13 @@ impl create_big_fish_session for super::RemoteProcedures { &self, input: BigFishSessionCreateInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ) { - self.imp - .invoke_procedure_with_callback::<_, BigFishSessionProcedureResult>( - "create_big_fish_session", - CreateBigFishSessionArgs { input }, - __callback, - ); + self.imp.invoke_procedure_with_callback::<_, BigFishSessionProcedureResult>( + "create_big_fish_session", + CreateBigFishSessionArgs { input, }, + __callback, + ); } } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/create_custom_world_agent_session_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/create_custom_world_agent_session_procedure.rs index 6a08bcc7..18065e71 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/create_custom_world_agent_session_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/create_custom_world_agent_session_procedure.rs @@ -2,17 +2,23 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::custom_world_agent_session_create_input_type::CustomWorldAgentSessionCreateInput; use super::custom_world_agent_session_procedure_result_type::CustomWorldAgentSessionProcedureResult; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] -struct CreateCustomWorldAgentSessionArgs { + struct CreateCustomWorldAgentSessionArgs { pub input: CustomWorldAgentSessionCreateInput, } + impl __sdk::InModule for CreateCustomWorldAgentSessionArgs { type Module = super::RemoteModule; } @@ -22,19 +28,16 @@ impl __sdk::InModule for CreateCustomWorldAgentSessionArgs { /// /// Implemented for [`super::RemoteProcedures`]. pub trait create_custom_world_agent_session { - fn create_custom_world_agent_session(&self, input: CustomWorldAgentSessionCreateInput) { - self.create_custom_world_agent_session_then(input, |_, _| {}); + fn create_custom_world_agent_session(&self, input: CustomWorldAgentSessionCreateInput, +) { + self.create_custom_world_agent_session_then(input, |_, _| {}); } fn create_custom_world_agent_session_then( &self, input: CustomWorldAgentSessionCreateInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ); } @@ -43,17 +46,13 @@ impl create_custom_world_agent_session for super::RemoteProcedures { &self, input: CustomWorldAgentSessionCreateInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ) { - self.imp - .invoke_procedure_with_callback::<_, CustomWorldAgentSessionProcedureResult>( - "create_custom_world_agent_session", - CreateCustomWorldAgentSessionArgs { input }, - __callback, - ); + self.imp.invoke_procedure_with_callback::<_, CustomWorldAgentSessionProcedureResult>( + "create_custom_world_agent_session", + CreateCustomWorldAgentSessionArgs { input, }, + __callback, + ); } } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/create_profile_recharge_order_and_return_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/create_profile_recharge_order_and_return_procedure.rs index 893fbdf6..7c082efd 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/create_profile_recharge_order_and_return_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/create_profile_recharge_order_and_return_procedure.rs @@ -2,17 +2,23 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; -use super::runtime_profile_recharge_center_procedure_result_type::RuntimeProfileRechargeCenterProcedureResult; use super::runtime_profile_recharge_order_create_input_type::RuntimeProfileRechargeOrderCreateInput; +use super::runtime_profile_recharge_center_procedure_result_type::RuntimeProfileRechargeCenterProcedureResult; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] -struct CreateProfileRechargeOrderAndReturnArgs { + struct CreateProfileRechargeOrderAndReturnArgs { pub input: RuntimeProfileRechargeOrderCreateInput, } + impl __sdk::InModule for CreateProfileRechargeOrderAndReturnArgs { type Module = super::RemoteModule; } @@ -22,22 +28,16 @@ impl __sdk::InModule for CreateProfileRechargeOrderAndReturnArgs { /// /// Implemented for [`super::RemoteProcedures`]. pub trait create_profile_recharge_order_and_return { - fn create_profile_recharge_order_and_return( - &self, - input: RuntimeProfileRechargeOrderCreateInput, - ) { - self.create_profile_recharge_order_and_return_then(input, |_, _| {}); + fn create_profile_recharge_order_and_return(&self, input: RuntimeProfileRechargeOrderCreateInput, +) { + self.create_profile_recharge_order_and_return_then(input, |_, _| {}); } fn create_profile_recharge_order_and_return_then( &self, input: RuntimeProfileRechargeOrderCreateInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ); } @@ -46,17 +46,13 @@ impl create_profile_recharge_order_and_return for super::RemoteProcedures { &self, input: RuntimeProfileRechargeOrderCreateInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ) { - self.imp - .invoke_procedure_with_callback::<_, RuntimeProfileRechargeCenterProcedureResult>( - "create_profile_recharge_order_and_return", - CreateProfileRechargeOrderAndReturnArgs { input }, - __callback, - ); + self.imp.invoke_procedure_with_callback::<_, RuntimeProfileRechargeCenterProcedureResult>( + "create_profile_recharge_order_and_return", + CreateProfileRechargeOrderAndReturnArgs { input, }, + __callback, + ); } } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/create_puzzle_agent_session_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/create_puzzle_agent_session_procedure.rs index 9460692b..d5c79fa4 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/create_puzzle_agent_session_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/create_puzzle_agent_session_procedure.rs @@ -2,17 +2,23 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; -use super::puzzle_agent_session_create_input_type::PuzzleAgentSessionCreateInput; use super::puzzle_agent_session_procedure_result_type::PuzzleAgentSessionProcedureResult; +use super::puzzle_agent_session_create_input_type::PuzzleAgentSessionCreateInput; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] -struct CreatePuzzleAgentSessionArgs { + struct CreatePuzzleAgentSessionArgs { pub input: PuzzleAgentSessionCreateInput, } + impl __sdk::InModule for CreatePuzzleAgentSessionArgs { type Module = super::RemoteModule; } @@ -22,19 +28,16 @@ impl __sdk::InModule for CreatePuzzleAgentSessionArgs { /// /// Implemented for [`super::RemoteProcedures`]. pub trait create_puzzle_agent_session { - fn create_puzzle_agent_session(&self, input: PuzzleAgentSessionCreateInput) { - self.create_puzzle_agent_session_then(input, |_, _| {}); + fn create_puzzle_agent_session(&self, input: PuzzleAgentSessionCreateInput, +) { + self.create_puzzle_agent_session_then(input, |_, _| {}); } fn create_puzzle_agent_session_then( &self, input: PuzzleAgentSessionCreateInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ); } @@ -43,17 +46,13 @@ impl create_puzzle_agent_session for super::RemoteProcedures { &self, input: PuzzleAgentSessionCreateInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ) { - self.imp - .invoke_procedure_with_callback::<_, PuzzleAgentSessionProcedureResult>( - "create_puzzle_agent_session", - CreatePuzzleAgentSessionArgs { input }, - __callback, - ); + self.imp.invoke_procedure_with_callback::<_, PuzzleAgentSessionProcedureResult>( + "create_puzzle_agent_session", + CreatePuzzleAgentSessionArgs { input, }, + __callback, + ); } } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/custom_world_agent_action_execute_input_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/custom_world_agent_action_execute_input_type.rs index 9663e12d..3281a80a 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/custom_world_agent_action_execute_input_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/custom_world_agent_action_execute_input_type.rs @@ -2,7 +2,13 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; + #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -11,10 +17,12 @@ pub struct CustomWorldAgentActionExecuteInput { pub owner_user_id: String, pub operation_id: String, pub action: String, - pub payload_json: Option, + pub payload_json: Option::, pub submitted_at_micros: i64, } + impl __sdk::InModule for CustomWorldAgentActionExecuteInput { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/custom_world_agent_action_execute_result_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/custom_world_agent_action_execute_result_type.rs index 39aeb7e0..6729b4db 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/custom_world_agent_action_execute_result_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/custom_world_agent_action_execute_result_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::custom_world_agent_operation_snapshot_type::CustomWorldAgentOperationSnapshot; @@ -10,10 +15,12 @@ use super::custom_world_agent_operation_snapshot_type::CustomWorldAgentOperation #[sats(crate = __lib)] pub struct CustomWorldAgentActionExecuteResult { pub ok: bool, - pub operation: Option, - pub error_message: Option, + pub operation: Option::, + pub error_message: Option::, } + impl __sdk::InModule for CustomWorldAgentActionExecuteResult { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/custom_world_agent_card_detail_get_input_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/custom_world_agent_card_detail_get_input_type.rs index 2b8cde47..c6cdeb3b 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/custom_world_agent_card_detail_get_input_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/custom_world_agent_card_detail_get_input_type.rs @@ -2,7 +2,13 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; + #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -12,6 +18,8 @@ pub struct CustomWorldAgentCardDetailGetInput { pub card_id: String, } + impl __sdk::InModule for CustomWorldAgentCardDetailGetInput { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/custom_world_agent_message_finalize_input_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/custom_world_agent_message_finalize_input_type.rs index 25184d85..d0153b44 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/custom_world_agent_message_finalize_input_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/custom_world_agent_message_finalize_input_type.rs @@ -2,10 +2,15 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; -use super::rpg_agent_operation_status_type::RpgAgentOperationStatus; use super::rpg_agent_stage_type::RpgAgentStage; +use super::rpg_agent_operation_status_type::RpgAgentOperationStatus; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -13,29 +18,31 @@ pub struct CustomWorldAgentMessageFinalizeInput { pub session_id: String, pub owner_user_id: String, pub operation_id: String, - pub assistant_message_id: Option, - pub assistant_reply_text: Option, + pub assistant_message_id: Option::, + pub assistant_reply_text: Option::, pub phase_label: String, pub phase_detail: String, pub operation_status: RpgAgentOperationStatus, pub operation_progress: u32, pub stage: RpgAgentStage, pub progress_percent: u32, - pub focus_card_id: Option, + pub focus_card_id: Option::, pub anchor_content_json: String, - pub creator_intent_json: Option, + pub creator_intent_json: Option::, pub creator_intent_readiness_json: String, - pub anchor_pack_json: Option, - pub draft_profile_json: Option, + pub anchor_pack_json: Option::, + pub draft_profile_json: Option::, pub pending_clarifications_json: String, pub suggested_actions_json: String, pub recommended_replies_json: String, pub quality_findings_json: String, pub asset_coverage_json: String, - pub error_message: Option, + pub error_message: Option::, pub updated_at_micros: i64, } + impl __sdk::InModule for CustomWorldAgentMessageFinalizeInput { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/custom_world_agent_message_snapshot_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/custom_world_agent_message_snapshot_type.rs index 69368214..e57b9839 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/custom_world_agent_message_snapshot_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/custom_world_agent_message_snapshot_type.rs @@ -2,10 +2,15 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; -use super::rpg_agent_message_kind_type::RpgAgentMessageKind; use super::rpg_agent_message_role_type::RpgAgentMessageRole; +use super::rpg_agent_message_kind_type::RpgAgentMessageKind; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -15,10 +20,12 @@ pub struct CustomWorldAgentMessageSnapshot { pub role: RpgAgentMessageRole, pub kind: RpgAgentMessageKind, pub text: String, - pub related_operation_id: Option, + pub related_operation_id: Option::, pub created_at_micros: i64, } + impl __sdk::InModule for CustomWorldAgentMessageSnapshot { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/custom_world_agent_message_submit_input_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/custom_world_agent_message_submit_input_type.rs index cf3a4230..bc2da116 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/custom_world_agent_message_submit_input_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/custom_world_agent_message_submit_input_type.rs @@ -2,7 +2,13 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; + #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -15,6 +21,8 @@ pub struct CustomWorldAgentMessageSubmitInput { pub submitted_at_micros: i64, } + impl __sdk::InModule for CustomWorldAgentMessageSubmitInput { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/custom_world_agent_message_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/custom_world_agent_message_type.rs index 75110860..e33b5c4a 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/custom_world_agent_message_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/custom_world_agent_message_type.rs @@ -2,10 +2,15 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; -use super::rpg_agent_message_kind_type::RpgAgentMessageKind; use super::rpg_agent_message_role_type::RpgAgentMessageRole; +use super::rpg_agent_message_kind_type::RpgAgentMessageKind; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -15,14 +20,16 @@ pub struct CustomWorldAgentMessage { pub role: RpgAgentMessageRole, pub kind: RpgAgentMessageKind, pub text: String, - pub related_operation_id: Option, + pub related_operation_id: Option::, pub created_at: __sdk::Timestamp, } + impl __sdk::InModule for CustomWorldAgentMessage { type Module = super::RemoteModule; } + /// Column accessor struct for the table `CustomWorldAgentMessage`. /// /// Provides typed access to columns for query building. @@ -32,7 +39,7 @@ pub struct CustomWorldAgentMessageCols { pub role: __sdk::__query_builder::Col, pub kind: __sdk::__query_builder::Col, pub text: __sdk::__query_builder::Col, - pub related_operation_id: __sdk::__query_builder::Col>, + pub related_operation_id: __sdk::__query_builder::Col>, pub created_at: __sdk::__query_builder::Col, } @@ -45,11 +52,9 @@ impl __sdk::__query_builder::HasCols for CustomWorldAgentMessage { role: __sdk::__query_builder::Col::new(table_name, "role"), kind: __sdk::__query_builder::Col::new(table_name, "kind"), text: __sdk::__query_builder::Col::new(table_name, "text"), - related_operation_id: __sdk::__query_builder::Col::new( - table_name, - "related_operation_id", - ), + related_operation_id: __sdk::__query_builder::Col::new(table_name, "related_operation_id"), created_at: __sdk::__query_builder::Col::new(table_name, "created_at"), + } } } @@ -68,8 +73,10 @@ impl __sdk::__query_builder::HasIxCols for CustomWorldAgentMessage { CustomWorldAgentMessageIxCols { message_id: __sdk::__query_builder::IxCol::new(table_name, "message_id"), session_id: __sdk::__query_builder::IxCol::new(table_name, "session_id"), + } } } impl __sdk::__query_builder::CanBeLookupTable for CustomWorldAgentMessage {} + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/custom_world_agent_operation_get_input_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/custom_world_agent_operation_get_input_type.rs index 1e249edf..2aa3b05b 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/custom_world_agent_operation_get_input_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/custom_world_agent_operation_get_input_type.rs @@ -2,7 +2,13 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; + #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -12,6 +18,8 @@ pub struct CustomWorldAgentOperationGetInput { pub operation_id: String, } + impl __sdk::InModule for CustomWorldAgentOperationGetInput { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/custom_world_agent_operation_procedure_result_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/custom_world_agent_operation_procedure_result_type.rs index 39d0e493..f9bf13da 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/custom_world_agent_operation_procedure_result_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/custom_world_agent_operation_procedure_result_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::custom_world_agent_operation_snapshot_type::CustomWorldAgentOperationSnapshot; @@ -10,10 +15,12 @@ use super::custom_world_agent_operation_snapshot_type::CustomWorldAgentOperation #[sats(crate = __lib)] pub struct CustomWorldAgentOperationProcedureResult { pub ok: bool, - pub operation: Option, - pub error_message: Option, + pub operation: Option::, + pub error_message: Option::, } + impl __sdk::InModule for CustomWorldAgentOperationProcedureResult { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/custom_world_agent_operation_progress_input_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/custom_world_agent_operation_progress_input_type.rs index b5ecb4a0..d7409cfa 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/custom_world_agent_operation_progress_input_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/custom_world_agent_operation_progress_input_type.rs @@ -2,10 +2,15 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; -use super::rpg_agent_operation_status_type::RpgAgentOperationStatus; use super::rpg_agent_operation_type_type::RpgAgentOperationType; +use super::rpg_agent_operation_status_type::RpgAgentOperationStatus; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -18,10 +23,12 @@ pub struct CustomWorldAgentOperationProgressInput { pub phase_label: String, pub phase_detail: String, pub operation_progress: u32, - pub error_message: Option, + pub error_message: Option::, pub updated_at_micros: i64, } + impl __sdk::InModule for CustomWorldAgentOperationProgressInput { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/custom_world_agent_operation_snapshot_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/custom_world_agent_operation_snapshot_type.rs index 61e86eb3..954a809c 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/custom_world_agent_operation_snapshot_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/custom_world_agent_operation_snapshot_type.rs @@ -2,10 +2,15 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; -use super::rpg_agent_operation_status_type::RpgAgentOperationStatus; use super::rpg_agent_operation_type_type::RpgAgentOperationType; +use super::rpg_agent_operation_status_type::RpgAgentOperationStatus; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -17,11 +22,13 @@ pub struct CustomWorldAgentOperationSnapshot { pub phase_label: String, pub phase_detail: String, pub progress: u32, - pub error_message: Option, + pub error_message: Option::, pub created_at_micros: i64, pub updated_at_micros: i64, } + impl __sdk::InModule for CustomWorldAgentOperationSnapshot { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/custom_world_agent_operation_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/custom_world_agent_operation_type.rs index 41957317..b195855d 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/custom_world_agent_operation_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/custom_world_agent_operation_type.rs @@ -2,10 +2,15 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; -use super::rpg_agent_operation_status_type::RpgAgentOperationStatus; use super::rpg_agent_operation_type_type::RpgAgentOperationType; +use super::rpg_agent_operation_status_type::RpgAgentOperationStatus; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -17,28 +22,29 @@ pub struct CustomWorldAgentOperation { pub phase_label: String, pub phase_detail: String, pub progress: u32, - pub error_message: Option, + pub error_message: Option::, pub created_at: __sdk::Timestamp, pub updated_at: __sdk::Timestamp, } + impl __sdk::InModule for CustomWorldAgentOperation { type Module = super::RemoteModule; } + /// Column accessor struct for the table `CustomWorldAgentOperation`. /// /// Provides typed access to columns for query building. pub struct CustomWorldAgentOperationCols { pub operation_id: __sdk::__query_builder::Col, pub session_id: __sdk::__query_builder::Col, - pub operation_type: - __sdk::__query_builder::Col, + pub operation_type: __sdk::__query_builder::Col, pub status: __sdk::__query_builder::Col, pub phase_label: __sdk::__query_builder::Col, pub phase_detail: __sdk::__query_builder::Col, pub progress: __sdk::__query_builder::Col, - pub error_message: __sdk::__query_builder::Col>, + pub error_message: __sdk::__query_builder::Col>, pub created_at: __sdk::__query_builder::Col, pub updated_at: __sdk::__query_builder::Col, } @@ -57,6 +63,7 @@ impl __sdk::__query_builder::HasCols for CustomWorldAgentOperation { error_message: __sdk::__query_builder::Col::new(table_name, "error_message"), created_at: __sdk::__query_builder::Col::new(table_name, "created_at"), updated_at: __sdk::__query_builder::Col::new(table_name, "updated_at"), + } } } @@ -75,8 +82,10 @@ impl __sdk::__query_builder::HasIxCols for CustomWorldAgentOperation { CustomWorldAgentOperationIxCols { operation_id: __sdk::__query_builder::IxCol::new(table_name, "operation_id"), session_id: __sdk::__query_builder::IxCol::new(table_name, "session_id"), + } } } impl __sdk::__query_builder::CanBeLookupTable for CustomWorldAgentOperation {} + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/custom_world_agent_session_create_input_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/custom_world_agent_session_create_input_type.rs index d2ea6be9..7a36c77b 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/custom_world_agent_session_create_input_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/custom_world_agent_session_create_input_type.rs @@ -2,7 +2,13 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; + #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -13,11 +19,11 @@ pub struct CustomWorldAgentSessionCreateInput { pub welcome_message_id: String, pub welcome_message_text: String, pub anchor_content_json: String, - pub creator_intent_json: Option, + pub creator_intent_json: Option::, pub creator_intent_readiness_json: String, - pub anchor_pack_json: Option, - pub lock_state_json: Option, - pub draft_profile_json: Option, + pub anchor_pack_json: Option::, + pub lock_state_json: Option::, + pub draft_profile_json: Option::, pub pending_clarifications_json: String, pub suggested_actions_json: String, pub recommended_replies_json: String, @@ -27,6 +33,8 @@ pub struct CustomWorldAgentSessionCreateInput { pub created_at_micros: i64, } + impl __sdk::InModule for CustomWorldAgentSessionCreateInput { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/custom_world_agent_session_get_input_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/custom_world_agent_session_get_input_type.rs index 733eaecb..0e4f3319 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/custom_world_agent_session_get_input_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/custom_world_agent_session_get_input_type.rs @@ -2,7 +2,13 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; + #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -11,6 +17,8 @@ pub struct CustomWorldAgentSessionGetInput { pub owner_user_id: String, } + impl __sdk::InModule for CustomWorldAgentSessionGetInput { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/custom_world_agent_session_procedure_result_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/custom_world_agent_session_procedure_result_type.rs index 1ca24f19..4da0e11a 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/custom_world_agent_session_procedure_result_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/custom_world_agent_session_procedure_result_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::custom_world_agent_session_snapshot_type::CustomWorldAgentSessionSnapshot; @@ -10,10 +15,12 @@ use super::custom_world_agent_session_snapshot_type::CustomWorldAgentSessionSnap #[sats(crate = __lib)] pub struct CustomWorldAgentSessionProcedureResult { pub ok: bool, - pub session: Option, - pub error_message: Option, + pub session: Option::, + pub error_message: Option::, } + impl __sdk::InModule for CustomWorldAgentSessionProcedureResult { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/custom_world_agent_session_snapshot_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/custom_world_agent_session_snapshot_type.rs index 6a50455e..e263df53 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/custom_world_agent_session_snapshot_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/custom_world_agent_session_snapshot_type.rs @@ -2,12 +2,17 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; -use super::custom_world_agent_message_snapshot_type::CustomWorldAgentMessageSnapshot; -use super::custom_world_agent_operation_snapshot_type::CustomWorldAgentOperationSnapshot; -use super::custom_world_draft_card_snapshot_type::CustomWorldDraftCardSnapshot; use super::rpg_agent_stage_type::RpgAgentStage; +use super::custom_world_agent_message_snapshot_type::CustomWorldAgentMessageSnapshot; +use super::custom_world_draft_card_snapshot_type::CustomWorldDraftCardSnapshot; +use super::custom_world_agent_operation_snapshot_type::CustomWorldAgentOperationSnapshot; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -18,16 +23,16 @@ pub struct CustomWorldAgentSessionSnapshot { pub current_turn: u32, pub progress_percent: u32, pub stage: RpgAgentStage, - pub focus_card_id: Option, + pub focus_card_id: Option::, pub anchor_content_json: String, - pub creator_intent_json: Option, + pub creator_intent_json: Option::, pub creator_intent_readiness_json: String, - pub anchor_pack_json: Option, - pub lock_state_json: Option, - pub draft_profile_json: Option, - pub last_assistant_reply: Option, - pub publish_gate_json: Option, - pub result_preview_json: Option, + pub anchor_pack_json: Option::, + pub lock_state_json: Option::, + pub draft_profile_json: Option::, + pub last_assistant_reply: Option::, + pub publish_gate_json: Option::, + pub result_preview_json: Option::, pub pending_clarifications_json: String, pub quality_findings_json: String, pub suggested_actions_json: String, @@ -35,13 +40,15 @@ pub struct CustomWorldAgentSessionSnapshot { pub asset_coverage_json: String, pub checkpoints_json: String, pub supported_actions_json: String, - pub messages: Vec, - pub draft_cards: Vec, - pub operations: Vec, + pub messages: Vec::, + pub draft_cards: Vec::, + pub operations: Vec::, pub created_at_micros: i64, pub updated_at_micros: i64, } + impl __sdk::InModule for CustomWorldAgentSessionSnapshot { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/custom_world_agent_session_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/custom_world_agent_session_type.rs index 105bc924..128d93e3 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/custom_world_agent_session_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/custom_world_agent_session_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::rpg_agent_stage_type::RpgAgentStage; @@ -15,16 +20,16 @@ pub struct CustomWorldAgentSession { pub current_turn: u32, pub progress_percent: u32, pub stage: RpgAgentStage, - pub focus_card_id: Option, + pub focus_card_id: Option::, pub anchor_content_json: String, - pub creator_intent_json: Option, + pub creator_intent_json: Option::, pub creator_intent_readiness_json: String, - pub anchor_pack_json: Option, - pub lock_state_json: Option, - pub draft_profile_json: Option, - pub last_assistant_reply: Option, - pub publish_gate_json: Option, - pub result_preview_json: Option, + pub anchor_pack_json: Option::, + pub lock_state_json: Option::, + pub draft_profile_json: Option::, + pub last_assistant_reply: Option::, + pub publish_gate_json: Option::, + pub result_preview_json: Option::, pub pending_clarifications_json: String, pub quality_findings_json: String, pub suggested_actions_json: String, @@ -35,10 +40,12 @@ pub struct CustomWorldAgentSession { pub updated_at: __sdk::Timestamp, } + impl __sdk::InModule for CustomWorldAgentSession { type Module = super::RemoteModule; } + /// Column accessor struct for the table `CustomWorldAgentSession`. /// /// Provides typed access to columns for query building. @@ -49,16 +56,16 @@ pub struct CustomWorldAgentSessionCols { pub current_turn: __sdk::__query_builder::Col, pub progress_percent: __sdk::__query_builder::Col, pub stage: __sdk::__query_builder::Col, - pub focus_card_id: __sdk::__query_builder::Col>, + pub focus_card_id: __sdk::__query_builder::Col>, pub anchor_content_json: __sdk::__query_builder::Col, - pub creator_intent_json: __sdk::__query_builder::Col>, + pub creator_intent_json: __sdk::__query_builder::Col>, pub creator_intent_readiness_json: __sdk::__query_builder::Col, - pub anchor_pack_json: __sdk::__query_builder::Col>, - pub lock_state_json: __sdk::__query_builder::Col>, - pub draft_profile_json: __sdk::__query_builder::Col>, - pub last_assistant_reply: __sdk::__query_builder::Col>, - pub publish_gate_json: __sdk::__query_builder::Col>, - pub result_preview_json: __sdk::__query_builder::Col>, + pub anchor_pack_json: __sdk::__query_builder::Col>, + pub lock_state_json: __sdk::__query_builder::Col>, + pub draft_profile_json: __sdk::__query_builder::Col>, + pub last_assistant_reply: __sdk::__query_builder::Col>, + pub publish_gate_json: __sdk::__query_builder::Col>, + pub result_preview_json: __sdk::__query_builder::Col>, pub pending_clarifications_json: __sdk::__query_builder::Col, pub quality_findings_json: __sdk::__query_builder::Col, pub suggested_actions_json: __sdk::__query_builder::Col, @@ -80,53 +87,24 @@ impl __sdk::__query_builder::HasCols for CustomWorldAgentSession { progress_percent: __sdk::__query_builder::Col::new(table_name, "progress_percent"), stage: __sdk::__query_builder::Col::new(table_name, "stage"), focus_card_id: __sdk::__query_builder::Col::new(table_name, "focus_card_id"), - anchor_content_json: __sdk::__query_builder::Col::new( - table_name, - "anchor_content_json", - ), - creator_intent_json: __sdk::__query_builder::Col::new( - table_name, - "creator_intent_json", - ), - creator_intent_readiness_json: __sdk::__query_builder::Col::new( - table_name, - "creator_intent_readiness_json", - ), + anchor_content_json: __sdk::__query_builder::Col::new(table_name, "anchor_content_json"), + creator_intent_json: __sdk::__query_builder::Col::new(table_name, "creator_intent_json"), + creator_intent_readiness_json: __sdk::__query_builder::Col::new(table_name, "creator_intent_readiness_json"), anchor_pack_json: __sdk::__query_builder::Col::new(table_name, "anchor_pack_json"), lock_state_json: __sdk::__query_builder::Col::new(table_name, "lock_state_json"), draft_profile_json: __sdk::__query_builder::Col::new(table_name, "draft_profile_json"), - last_assistant_reply: __sdk::__query_builder::Col::new( - table_name, - "last_assistant_reply", - ), + last_assistant_reply: __sdk::__query_builder::Col::new(table_name, "last_assistant_reply"), publish_gate_json: __sdk::__query_builder::Col::new(table_name, "publish_gate_json"), - result_preview_json: __sdk::__query_builder::Col::new( - table_name, - "result_preview_json", - ), - pending_clarifications_json: __sdk::__query_builder::Col::new( - table_name, - "pending_clarifications_json", - ), - quality_findings_json: __sdk::__query_builder::Col::new( - table_name, - "quality_findings_json", - ), - suggested_actions_json: __sdk::__query_builder::Col::new( - table_name, - "suggested_actions_json", - ), - recommended_replies_json: __sdk::__query_builder::Col::new( - table_name, - "recommended_replies_json", - ), - asset_coverage_json: __sdk::__query_builder::Col::new( - table_name, - "asset_coverage_json", - ), + result_preview_json: __sdk::__query_builder::Col::new(table_name, "result_preview_json"), + pending_clarifications_json: __sdk::__query_builder::Col::new(table_name, "pending_clarifications_json"), + quality_findings_json: __sdk::__query_builder::Col::new(table_name, "quality_findings_json"), + suggested_actions_json: __sdk::__query_builder::Col::new(table_name, "suggested_actions_json"), + recommended_replies_json: __sdk::__query_builder::Col::new(table_name, "recommended_replies_json"), + asset_coverage_json: __sdk::__query_builder::Col::new(table_name, "asset_coverage_json"), checkpoints_json: __sdk::__query_builder::Col::new(table_name, "checkpoints_json"), created_at: __sdk::__query_builder::Col::new(table_name, "created_at"), updated_at: __sdk::__query_builder::Col::new(table_name, "updated_at"), + } } } @@ -147,8 +125,10 @@ impl __sdk::__query_builder::HasIxCols for CustomWorldAgentSession { owner_user_id: __sdk::__query_builder::IxCol::new(table_name, "owner_user_id"), session_id: __sdk::__query_builder::IxCol::new(table_name, "session_id"), stage: __sdk::__query_builder::IxCol::new(table_name, "stage"), + } } } impl __sdk::__query_builder::CanBeLookupTable for CustomWorldAgentSession {} + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/custom_world_draft_card_detail_result_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/custom_world_draft_card_detail_result_type.rs index ee8f9ba4..384a2c8c 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/custom_world_draft_card_detail_result_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/custom_world_draft_card_detail_result_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::custom_world_draft_card_detail_snapshot_type::CustomWorldDraftCardDetailSnapshot; @@ -10,10 +15,12 @@ use super::custom_world_draft_card_detail_snapshot_type::CustomWorldDraftCardDet #[sats(crate = __lib)] pub struct CustomWorldDraftCardDetailResult { pub ok: bool, - pub card: Option, - pub error_message: Option, + pub card: Option::, + pub error_message: Option::, } + impl __sdk::InModule for CustomWorldDraftCardDetailResult { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/custom_world_draft_card_detail_section_snapshot_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/custom_world_draft_card_detail_section_snapshot_type.rs index b7f30c00..2e256890 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/custom_world_draft_card_detail_section_snapshot_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/custom_world_draft_card_detail_section_snapshot_type.rs @@ -2,7 +2,13 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; + #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -12,6 +18,8 @@ pub struct CustomWorldDraftCardDetailSectionSnapshot { pub value: String, } + impl __sdk::InModule for CustomWorldDraftCardDetailSectionSnapshot { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/custom_world_draft_card_detail_snapshot_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/custom_world_draft_card_detail_snapshot_type.rs index fab895fe..6bb20096 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/custom_world_draft_card_detail_snapshot_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/custom_world_draft_card_detail_snapshot_type.rs @@ -2,11 +2,16 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; -use super::custom_world_draft_card_detail_section_snapshot_type::CustomWorldDraftCardDetailSectionSnapshot; -use super::custom_world_role_asset_status_type::CustomWorldRoleAssetStatus; use super::rpg_agent_draft_card_kind_type::RpgAgentDraftCardKind; +use super::custom_world_role_asset_status_type::CustomWorldRoleAssetStatus; +use super::custom_world_draft_card_detail_section_snapshot_type::CustomWorldDraftCardDetailSectionSnapshot; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -14,16 +19,18 @@ pub struct CustomWorldDraftCardDetailSnapshot { pub card_id: String, pub kind: RpgAgentDraftCardKind, pub title: String, - pub sections: Vec, + pub sections: Vec::, pub linked_ids_json: String, pub locked: bool, pub editable: bool, pub editable_section_ids_json: String, pub warning_messages_json: String, - pub asset_status: Option, - pub asset_status_label: Option, + pub asset_status: Option::, + pub asset_status_label: Option::, } + impl __sdk::InModule for CustomWorldDraftCardDetailSnapshot { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/custom_world_draft_card_snapshot_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/custom_world_draft_card_snapshot_type.rs index 84108f6e..c9497003 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/custom_world_draft_card_snapshot_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/custom_world_draft_card_snapshot_type.rs @@ -2,11 +2,16 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; -use super::custom_world_role_asset_status_type::CustomWorldRoleAssetStatus; use super::rpg_agent_draft_card_kind_type::RpgAgentDraftCardKind; use super::rpg_agent_draft_card_status_type::RpgAgentDraftCardStatus; +use super::custom_world_role_asset_status_type::CustomWorldRoleAssetStatus; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -20,13 +25,15 @@ pub struct CustomWorldDraftCardSnapshot { pub summary: String, pub linked_ids_json: String, pub warning_count: u32, - pub asset_status: Option, - pub asset_status_label: Option, - pub detail_payload_json: Option, + pub asset_status: Option::, + pub asset_status_label: Option::, + pub detail_payload_json: Option::, pub created_at_micros: i64, pub updated_at_micros: i64, } + impl __sdk::InModule for CustomWorldDraftCardSnapshot { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/custom_world_draft_card_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/custom_world_draft_card_type.rs index 600c1f31..64dd162a 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/custom_world_draft_card_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/custom_world_draft_card_type.rs @@ -2,11 +2,16 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; -use super::custom_world_role_asset_status_type::CustomWorldRoleAssetStatus; use super::rpg_agent_draft_card_kind_type::RpgAgentDraftCardKind; use super::rpg_agent_draft_card_status_type::RpgAgentDraftCardStatus; +use super::custom_world_role_asset_status_type::CustomWorldRoleAssetStatus; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -20,17 +25,19 @@ pub struct CustomWorldDraftCard { pub summary: String, pub linked_ids_json: String, pub warning_count: u32, - pub asset_status: Option, - pub asset_status_label: Option, - pub detail_payload_json: Option, + pub asset_status: Option::, + pub asset_status_label: Option::, + pub detail_payload_json: Option::, pub created_at: __sdk::Timestamp, pub updated_at: __sdk::Timestamp, } + impl __sdk::InModule for CustomWorldDraftCard { type Module = super::RemoteModule; } + /// Column accessor struct for the table `CustomWorldDraftCard`. /// /// Provides typed access to columns for query building. @@ -44,10 +51,9 @@ pub struct CustomWorldDraftCardCols { pub summary: __sdk::__query_builder::Col, pub linked_ids_json: __sdk::__query_builder::Col, pub warning_count: __sdk::__query_builder::Col, - pub asset_status: - __sdk::__query_builder::Col>, - pub asset_status_label: __sdk::__query_builder::Col>, - pub detail_payload_json: __sdk::__query_builder::Col>, + pub asset_status: __sdk::__query_builder::Col>, + pub asset_status_label: __sdk::__query_builder::Col>, + pub detail_payload_json: __sdk::__query_builder::Col>, pub created_at: __sdk::__query_builder::Col, pub updated_at: __sdk::__query_builder::Col, } @@ -67,12 +73,10 @@ impl __sdk::__query_builder::HasCols for CustomWorldDraftCard { warning_count: __sdk::__query_builder::Col::new(table_name, "warning_count"), asset_status: __sdk::__query_builder::Col::new(table_name, "asset_status"), asset_status_label: __sdk::__query_builder::Col::new(table_name, "asset_status_label"), - detail_payload_json: __sdk::__query_builder::Col::new( - table_name, - "detail_payload_json", - ), + detail_payload_json: __sdk::__query_builder::Col::new(table_name, "detail_payload_json"), created_at: __sdk::__query_builder::Col::new(table_name, "created_at"), updated_at: __sdk::__query_builder::Col::new(table_name, "updated_at"), + } } } @@ -93,8 +97,10 @@ impl __sdk::__query_builder::HasIxCols for CustomWorldDraftCard { card_id: __sdk::__query_builder::IxCol::new(table_name, "card_id"), kind: __sdk::__query_builder::IxCol::new(table_name, "kind"), session_id: __sdk::__query_builder::IxCol::new(table_name, "session_id"), + } } } impl __sdk::__query_builder::CanBeLookupTable for CustomWorldDraftCard {} + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/custom_world_gallery_detail_by_code_input_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/custom_world_gallery_detail_by_code_input_type.rs index 0dd8127b..acdc32d0 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/custom_world_gallery_detail_by_code_input_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/custom_world_gallery_detail_by_code_input_type.rs @@ -2,7 +2,13 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; + #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -10,6 +16,8 @@ pub struct CustomWorldGalleryDetailByCodeInput { pub public_work_code: String, } + impl __sdk::InModule for CustomWorldGalleryDetailByCodeInput { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/custom_world_gallery_detail_input_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/custom_world_gallery_detail_input_type.rs index 64724446..5df002ea 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/custom_world_gallery_detail_input_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/custom_world_gallery_detail_input_type.rs @@ -2,7 +2,13 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; + #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -11,6 +17,8 @@ pub struct CustomWorldGalleryDetailInput { pub profile_id: String, } + impl __sdk::InModule for CustomWorldGalleryDetailInput { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/custom_world_gallery_entry_snapshot_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/custom_world_gallery_entry_snapshot_type.rs index ac3cf555..04616d88 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/custom_world_gallery_entry_snapshot_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/custom_world_gallery_entry_snapshot_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::custom_world_theme_mode_type::CustomWorldThemeMode; @@ -17,7 +22,7 @@ pub struct CustomWorldGalleryEntrySnapshot { pub world_name: String, pub subtitle: String, pub summary_text: String, - pub cover_image_src: Option, + pub cover_image_src: Option::, pub theme_mode: CustomWorldThemeMode, pub playable_npc_count: u32, pub landmark_count: u32, @@ -25,6 +30,8 @@ pub struct CustomWorldGalleryEntrySnapshot { pub updated_at_micros: i64, } + impl __sdk::InModule for CustomWorldGalleryEntrySnapshot { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/custom_world_gallery_entry_table.rs b/server-rs/crates/spacetime-client/src/module_bindings/custom_world_gallery_entry_table.rs index 425b6a12..f215d0c9 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/custom_world_gallery_entry_table.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/custom_world_gallery_entry_table.rs @@ -2,9 +2,14 @@ // 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::custom_world_gallery_entry_type::CustomWorldGalleryEntry; use super::custom_world_theme_mode_type::CustomWorldThemeMode; -use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws}; /// Table handle for the table `custom_world_gallery_entry`. /// @@ -32,9 +37,7 @@ pub trait CustomWorldGalleryEntryTableAccess { impl CustomWorldGalleryEntryTableAccess for super::RemoteTables { fn custom_world_gallery_entry(&self) -> CustomWorldGalleryEntryTableHandle<'_> { CustomWorldGalleryEntryTableHandle { - imp: self - .imp - .get_table::("custom_world_gallery_entry"), + imp: self.imp.get_table::("custom_world_gallery_entry"), ctx: std::marker::PhantomData, } } @@ -47,12 +50,8 @@ impl<'ctx> __sdk::Table for CustomWorldGalleryEntryTableHandle<'ctx> { type Row = CustomWorldGalleryEntry; type EventContext = super::EventContext; - fn count(&self) -> u64 { - self.imp.count() - } - fn iter(&self) -> impl Iterator + '_ { - self.imp.iter() - } + fn count(&self) -> u64 { self.imp.count() } + fn iter(&self) -> impl Iterator + '_ { self.imp.iter() } type InsertCallbackId = CustomWorldGalleryEntryInsertCallbackId; @@ -98,40 +97,40 @@ impl<'ctx> __sdk::TableWithPrimaryKey for CustomWorldGalleryEntryTableHandle<'ct } } -/// Access to the `profile_id` unique index on the table `custom_world_gallery_entry`, -/// which allows point queries on the field of the same name -/// via the [`CustomWorldGalleryEntryProfileIdUnique::find`] method. -/// -/// Users are encouraged not to explicitly reference this type, -/// but to directly chain method calls, -/// like `ctx.db.custom_world_gallery_entry().profile_id().find(...)`. -pub struct CustomWorldGalleryEntryProfileIdUnique<'ctx> { - imp: __sdk::UniqueConstraintHandle, - phantom: std::marker::PhantomData<&'ctx super::RemoteTables>, -} - -impl<'ctx> CustomWorldGalleryEntryTableHandle<'ctx> { - /// Get a handle on the `profile_id` unique index on the table `custom_world_gallery_entry`. - pub fn profile_id(&self) -> CustomWorldGalleryEntryProfileIdUnique<'ctx> { - CustomWorldGalleryEntryProfileIdUnique { - imp: self.imp.get_unique_constraint::("profile_id"), - phantom: std::marker::PhantomData, + /// Access to the `profile_id` unique index on the table `custom_world_gallery_entry`, + /// which allows point queries on the field of the same name + /// via the [`CustomWorldGalleryEntryProfileIdUnique::find`] method. + /// + /// Users are encouraged not to explicitly reference this type, + /// but to directly chain method calls, + /// like `ctx.db.custom_world_gallery_entry().profile_id().find(...)`. + pub struct CustomWorldGalleryEntryProfileIdUnique<'ctx> { + imp: __sdk::UniqueConstraintHandle, + phantom: std::marker::PhantomData<&'ctx super::RemoteTables>, } - } -} -impl<'ctx> CustomWorldGalleryEntryProfileIdUnique<'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 { - self.imp.find(col_val) - } -} + impl<'ctx> CustomWorldGalleryEntryTableHandle<'ctx> { + /// Get a handle on the `profile_id` unique index on the table `custom_world_gallery_entry`. + pub fn profile_id(&self) -> CustomWorldGalleryEntryProfileIdUnique<'ctx> { + CustomWorldGalleryEntryProfileIdUnique { + imp: self.imp.get_unique_constraint::("profile_id"), + phantom: std::marker::PhantomData, + } + } + } + impl<'ctx> CustomWorldGalleryEntryProfileIdUnique<'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 { + self.imp.find(col_val) + } + } + #[doc(hidden)] pub(super) fn register_table(client_cache: &mut __sdk::ClientCache) { - let _table = - client_cache.get_or_make_table::("custom_world_gallery_entry"); + + let _table = client_cache.get_or_make_table::("custom_world_gallery_entry"); _table.add_unique_constraint::("profile_id", |row| &row.profile_id); } @@ -140,24 +139,26 @@ pub(super) fn parse_table_update( raw_updates: __ws::v2::TableUpdate, ) -> __sdk::Result<__sdk::TableUpdate> { __sdk::TableUpdate::parse_table_update(raw_updates).map_err(|e| { - __sdk::InternalError::failed_parse("TableUpdate", "TableUpdate") - .with_cause(e) - .into() + __sdk::InternalError::failed_parse( + "TableUpdate", + "TableUpdate", + ).with_cause(e).into() }) } -#[allow(non_camel_case_types)] -/// Extension trait for query builder access to the table `CustomWorldGalleryEntry`. -/// -/// Implemented for [`__sdk::QueryTableAccessor`]. -pub trait custom_world_gallery_entryQueryTableAccess { - #[allow(non_snake_case)] - /// Get a query builder for the table `CustomWorldGalleryEntry`. - fn custom_world_gallery_entry(&self) -> __sdk::__query_builder::Table; -} + #[allow(non_camel_case_types)] + /// Extension trait for query builder access to the table `CustomWorldGalleryEntry`. + /// + /// Implemented for [`__sdk::QueryTableAccessor`]. + pub trait custom_world_gallery_entryQueryTableAccess { + #[allow(non_snake_case)] + /// Get a query builder for the table `CustomWorldGalleryEntry`. + fn custom_world_gallery_entry(&self) -> __sdk::__query_builder::Table; + } + + impl custom_world_gallery_entryQueryTableAccess for __sdk::QueryTableAccessor { + fn custom_world_gallery_entry(&self) -> __sdk::__query_builder::Table { + __sdk::__query_builder::Table::new("custom_world_gallery_entry") + } + } -impl custom_world_gallery_entryQueryTableAccess for __sdk::QueryTableAccessor { - fn custom_world_gallery_entry(&self) -> __sdk::__query_builder::Table { - __sdk::__query_builder::Table::new("custom_world_gallery_entry") - } -} diff --git a/server-rs/crates/spacetime-client/src/module_bindings/custom_world_gallery_entry_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/custom_world_gallery_entry_type.rs index a1fc0481..976ccc10 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/custom_world_gallery_entry_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/custom_world_gallery_entry_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::custom_world_theme_mode_type::CustomWorldThemeMode; @@ -17,7 +22,7 @@ pub struct CustomWorldGalleryEntry { pub world_name: String, pub subtitle: String, pub summary_text: String, - pub cover_image_src: Option, + pub cover_image_src: Option::, pub theme_mode: CustomWorldThemeMode, pub playable_npc_count: u32, pub landmark_count: u32, @@ -25,10 +30,12 @@ pub struct CustomWorldGalleryEntry { pub updated_at: __sdk::Timestamp, } + impl __sdk::InModule for CustomWorldGalleryEntry { type Module = super::RemoteModule; } + /// Column accessor struct for the table `CustomWorldGalleryEntry`. /// /// Provides typed access to columns for query building. @@ -41,7 +48,7 @@ pub struct CustomWorldGalleryEntryCols { pub world_name: __sdk::__query_builder::Col, pub subtitle: __sdk::__query_builder::Col, pub summary_text: __sdk::__query_builder::Col, - pub cover_image_src: __sdk::__query_builder::Col>, + pub cover_image_src: __sdk::__query_builder::Col>, pub theme_mode: __sdk::__query_builder::Col, pub playable_npc_count: __sdk::__query_builder::Col, pub landmark_count: __sdk::__query_builder::Col, @@ -56,14 +63,8 @@ impl __sdk::__query_builder::HasCols for CustomWorldGalleryEntry { profile_id: __sdk::__query_builder::Col::new(table_name, "profile_id"), owner_user_id: __sdk::__query_builder::Col::new(table_name, "owner_user_id"), public_work_code: __sdk::__query_builder::Col::new(table_name, "public_work_code"), - author_public_user_code: __sdk::__query_builder::Col::new( - table_name, - "author_public_user_code", - ), - author_display_name: __sdk::__query_builder::Col::new( - table_name, - "author_display_name", - ), + author_public_user_code: __sdk::__query_builder::Col::new(table_name, "author_public_user_code"), + author_display_name: __sdk::__query_builder::Col::new(table_name, "author_display_name"), world_name: __sdk::__query_builder::Col::new(table_name, "world_name"), subtitle: __sdk::__query_builder::Col::new(table_name, "subtitle"), summary_text: __sdk::__query_builder::Col::new(table_name, "summary_text"), @@ -73,6 +74,7 @@ impl __sdk::__query_builder::HasCols for CustomWorldGalleryEntry { landmark_count: __sdk::__query_builder::Col::new(table_name, "landmark_count"), published_at: __sdk::__query_builder::Col::new(table_name, "published_at"), updated_at: __sdk::__query_builder::Col::new(table_name, "updated_at"), + } } } @@ -95,8 +97,10 @@ impl __sdk::__query_builder::HasIxCols for CustomWorldGalleryEntry { profile_id: __sdk::__query_builder::IxCol::new(table_name, "profile_id"), public_work_code: __sdk::__query_builder::IxCol::new(table_name, "public_work_code"), theme_mode: __sdk::__query_builder::IxCol::new(table_name, "theme_mode"), + } } } impl __sdk::__query_builder::CanBeLookupTable for CustomWorldGalleryEntry {} + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/custom_world_gallery_list_result_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/custom_world_gallery_list_result_type.rs index bf101ef1..357c32c5 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/custom_world_gallery_list_result_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/custom_world_gallery_list_result_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::custom_world_gallery_entry_snapshot_type::CustomWorldGalleryEntrySnapshot; @@ -10,10 +15,12 @@ use super::custom_world_gallery_entry_snapshot_type::CustomWorldGalleryEntrySnap #[sats(crate = __lib)] pub struct CustomWorldGalleryListResult { pub ok: bool, - pub entries: Vec, - pub error_message: Option, + pub entries: Vec::, + pub error_message: Option::, } + impl __sdk::InModule for CustomWorldGalleryListResult { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/custom_world_generation_mode_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/custom_world_generation_mode_type.rs index 8b424a9a..375a464c 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/custom_world_generation_mode_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/custom_world_generation_mode_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -11,8 +16,12 @@ pub enum CustomWorldGenerationMode { Fast, Full, + } + + impl __sdk::InModule for CustomWorldGenerationMode { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/custom_world_library_detail_input_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/custom_world_library_detail_input_type.rs index 472c1b80..e29dbaa9 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/custom_world_library_detail_input_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/custom_world_library_detail_input_type.rs @@ -2,7 +2,13 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; + #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -11,6 +17,8 @@ pub struct CustomWorldLibraryDetailInput { pub profile_id: String, } + impl __sdk::InModule for CustomWorldLibraryDetailInput { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/custom_world_library_mutation_result_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/custom_world_library_mutation_result_type.rs index 3c3bb3b3..6fd0d2ff 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/custom_world_library_mutation_result_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/custom_world_library_mutation_result_type.rs @@ -2,20 +2,27 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; -use super::custom_world_gallery_entry_snapshot_type::CustomWorldGalleryEntrySnapshot; use super::custom_world_profile_snapshot_type::CustomWorldProfileSnapshot; +use super::custom_world_gallery_entry_snapshot_type::CustomWorldGalleryEntrySnapshot; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] pub struct CustomWorldLibraryMutationResult { pub ok: bool, - pub entry: Option, - pub gallery_entry: Option, - pub error_message: Option, + pub entry: Option::, + pub gallery_entry: Option::, + pub error_message: Option::, } + impl __sdk::InModule for CustomWorldLibraryMutationResult { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/custom_world_profile_delete_input_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/custom_world_profile_delete_input_type.rs index f9d4a10f..65a7d4ab 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/custom_world_profile_delete_input_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/custom_world_profile_delete_input_type.rs @@ -2,7 +2,13 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; + #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -12,6 +18,8 @@ pub struct CustomWorldProfileDeleteInput { pub deleted_at_micros: i64, } + impl __sdk::InModule for CustomWorldProfileDeleteInput { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/custom_world_profile_list_input_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/custom_world_profile_list_input_type.rs index 53c46830..2d4b431e 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/custom_world_profile_list_input_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/custom_world_profile_list_input_type.rs @@ -2,7 +2,13 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; + #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -10,6 +16,8 @@ pub struct CustomWorldProfileListInput { pub owner_user_id: String, } + impl __sdk::InModule for CustomWorldProfileListInput { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/custom_world_profile_list_result_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/custom_world_profile_list_result_type.rs index 10db7d0e..4e6a5a4a 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/custom_world_profile_list_result_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/custom_world_profile_list_result_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::custom_world_profile_snapshot_type::CustomWorldProfileSnapshot; @@ -10,10 +15,12 @@ use super::custom_world_profile_snapshot_type::CustomWorldProfileSnapshot; #[sats(crate = __lib)] pub struct CustomWorldProfileListResult { pub ok: bool, - pub entries: Vec, - pub error_message: Option, + pub entries: Vec::, + pub error_message: Option::, } + impl __sdk::InModule for CustomWorldProfileListResult { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/custom_world_profile_publish_input_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/custom_world_profile_publish_input_type.rs index 0190e015..3a88edf7 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/custom_world_profile_publish_input_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/custom_world_profile_publish_input_type.rs @@ -2,19 +2,27 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; + #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] pub struct CustomWorldProfilePublishInput { pub profile_id: String, pub owner_user_id: String, - pub public_work_code: Option, + pub public_work_code: Option::, pub author_public_user_code: String, pub author_display_name: String, pub published_at_micros: i64, } + impl __sdk::InModule for CustomWorldProfilePublishInput { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/custom_world_profile_snapshot_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/custom_world_profile_snapshot_type.rs index 3b9f8b5a..0d404e2b 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/custom_world_profile_snapshot_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/custom_world_profile_snapshot_type.rs @@ -2,35 +2,42 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; -use super::custom_world_publication_status_type::CustomWorldPublicationStatus; use super::custom_world_theme_mode_type::CustomWorldThemeMode; +use super::custom_world_publication_status_type::CustomWorldPublicationStatus; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] pub struct CustomWorldProfileSnapshot { pub profile_id: String, pub owner_user_id: String, - pub public_work_code: Option, - pub author_public_user_code: Option, - pub source_agent_session_id: Option, + pub public_work_code: Option::, + pub author_public_user_code: Option::, + pub source_agent_session_id: Option::, pub publication_status: CustomWorldPublicationStatus, pub world_name: String, pub subtitle: String, pub summary_text: String, pub theme_mode: CustomWorldThemeMode, - pub cover_image_src: Option, + pub cover_image_src: Option::, pub profile_payload_json: String, pub playable_npc_count: u32, pub landmark_count: u32, pub author_display_name: String, - pub published_at_micros: Option, - pub deleted_at_micros: Option, + pub published_at_micros: Option::, + pub deleted_at_micros: Option::, pub created_at_micros: i64, pub updated_at_micros: i64, } + impl __sdk::InModule for CustomWorldProfileSnapshot { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/custom_world_profile_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/custom_world_profile_type.rs index 8923286f..ebcb964a 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/custom_world_profile_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/custom_world_profile_type.rs @@ -2,61 +2,67 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; -use super::custom_world_publication_status_type::CustomWorldPublicationStatus; use super::custom_world_theme_mode_type::CustomWorldThemeMode; +use super::custom_world_publication_status_type::CustomWorldPublicationStatus; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] pub struct CustomWorldProfile { pub profile_id: String, pub owner_user_id: String, - pub public_work_code: Option, - pub author_public_user_code: Option, - pub source_agent_session_id: Option, + pub public_work_code: Option::, + pub author_public_user_code: Option::, + pub source_agent_session_id: Option::, pub publication_status: CustomWorldPublicationStatus, pub world_name: String, pub subtitle: String, pub summary_text: String, pub theme_mode: CustomWorldThemeMode, - pub cover_image_src: Option, + pub cover_image_src: Option::, pub profile_payload_json: String, pub playable_npc_count: u32, pub landmark_count: u32, pub author_display_name: String, - pub published_at: Option<__sdk::Timestamp>, - pub deleted_at: Option<__sdk::Timestamp>, + pub published_at: Option::<__sdk::Timestamp>, + pub deleted_at: Option::<__sdk::Timestamp>, pub created_at: __sdk::Timestamp, pub updated_at: __sdk::Timestamp, } + impl __sdk::InModule for CustomWorldProfile { type Module = super::RemoteModule; } + /// Column accessor struct for the table `CustomWorldProfile`. /// /// Provides typed access to columns for query building. pub struct CustomWorldProfileCols { pub profile_id: __sdk::__query_builder::Col, pub owner_user_id: __sdk::__query_builder::Col, - pub public_work_code: __sdk::__query_builder::Col>, - pub author_public_user_code: __sdk::__query_builder::Col>, - pub source_agent_session_id: __sdk::__query_builder::Col>, - pub publication_status: - __sdk::__query_builder::Col, + pub public_work_code: __sdk::__query_builder::Col>, + pub author_public_user_code: __sdk::__query_builder::Col>, + pub source_agent_session_id: __sdk::__query_builder::Col>, + pub publication_status: __sdk::__query_builder::Col, pub world_name: __sdk::__query_builder::Col, pub subtitle: __sdk::__query_builder::Col, pub summary_text: __sdk::__query_builder::Col, pub theme_mode: __sdk::__query_builder::Col, - pub cover_image_src: __sdk::__query_builder::Col>, + pub cover_image_src: __sdk::__query_builder::Col>, pub profile_payload_json: __sdk::__query_builder::Col, pub playable_npc_count: __sdk::__query_builder::Col, pub landmark_count: __sdk::__query_builder::Col, pub author_display_name: __sdk::__query_builder::Col, - pub published_at: __sdk::__query_builder::Col>, - pub deleted_at: __sdk::__query_builder::Col>, + pub published_at: __sdk::__query_builder::Col>, + pub deleted_at: __sdk::__query_builder::Col>, pub created_at: __sdk::__query_builder::Col, pub updated_at: __sdk::__query_builder::Col, } @@ -68,34 +74,23 @@ impl __sdk::__query_builder::HasCols for CustomWorldProfile { profile_id: __sdk::__query_builder::Col::new(table_name, "profile_id"), owner_user_id: __sdk::__query_builder::Col::new(table_name, "owner_user_id"), public_work_code: __sdk::__query_builder::Col::new(table_name, "public_work_code"), - author_public_user_code: __sdk::__query_builder::Col::new( - table_name, - "author_public_user_code", - ), - source_agent_session_id: __sdk::__query_builder::Col::new( - table_name, - "source_agent_session_id", - ), + author_public_user_code: __sdk::__query_builder::Col::new(table_name, "author_public_user_code"), + source_agent_session_id: __sdk::__query_builder::Col::new(table_name, "source_agent_session_id"), publication_status: __sdk::__query_builder::Col::new(table_name, "publication_status"), world_name: __sdk::__query_builder::Col::new(table_name, "world_name"), subtitle: __sdk::__query_builder::Col::new(table_name, "subtitle"), summary_text: __sdk::__query_builder::Col::new(table_name, "summary_text"), theme_mode: __sdk::__query_builder::Col::new(table_name, "theme_mode"), cover_image_src: __sdk::__query_builder::Col::new(table_name, "cover_image_src"), - profile_payload_json: __sdk::__query_builder::Col::new( - table_name, - "profile_payload_json", - ), + profile_payload_json: __sdk::__query_builder::Col::new(table_name, "profile_payload_json"), playable_npc_count: __sdk::__query_builder::Col::new(table_name, "playable_npc_count"), landmark_count: __sdk::__query_builder::Col::new(table_name, "landmark_count"), - author_display_name: __sdk::__query_builder::Col::new( - table_name, - "author_display_name", - ), + author_display_name: __sdk::__query_builder::Col::new(table_name, "author_display_name"), published_at: __sdk::__query_builder::Col::new(table_name, "published_at"), deleted_at: __sdk::__query_builder::Col::new(table_name, "deleted_at"), created_at: __sdk::__query_builder::Col::new(table_name, "created_at"), updated_at: __sdk::__query_builder::Col::new(table_name, "updated_at"), + } } } @@ -106,8 +101,7 @@ impl __sdk::__query_builder::HasCols for CustomWorldProfile { pub struct CustomWorldProfileIxCols { pub owner_user_id: __sdk::__query_builder::IxCol, pub profile_id: __sdk::__query_builder::IxCol, - pub publication_status: - __sdk::__query_builder::IxCol, + pub publication_status: __sdk::__query_builder::IxCol, } impl __sdk::__query_builder::HasIxCols for CustomWorldProfile { @@ -116,12 +110,11 @@ impl __sdk::__query_builder::HasIxCols for CustomWorldProfile { CustomWorldProfileIxCols { 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", - ), + publication_status: __sdk::__query_builder::IxCol::new(table_name, "publication_status"), + } } } impl __sdk::__query_builder::CanBeLookupTable for CustomWorldProfile {} + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/custom_world_profile_unpublish_input_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/custom_world_profile_unpublish_input_type.rs index 7d688d96..79aa9ff6 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/custom_world_profile_unpublish_input_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/custom_world_profile_unpublish_input_type.rs @@ -2,7 +2,13 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; + #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -13,6 +19,8 @@ pub struct CustomWorldProfileUnpublishInput { pub updated_at_micros: i64, } + impl __sdk::InModule for CustomWorldProfileUnpublishInput { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/custom_world_profile_upsert_input_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/custom_world_profile_upsert_input_type.rs index 323a72f3..0ea2ef8c 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/custom_world_profile_upsert_input_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/custom_world_profile_upsert_input_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::custom_world_theme_mode_type::CustomWorldThemeMode; @@ -11,14 +16,14 @@ use super::custom_world_theme_mode_type::CustomWorldThemeMode; pub struct CustomWorldProfileUpsertInput { pub profile_id: String, pub owner_user_id: String, - pub public_work_code: Option, - pub author_public_user_code: Option, - pub source_agent_session_id: Option, + pub public_work_code: Option::, + pub author_public_user_code: Option::, + pub source_agent_session_id: Option::, pub world_name: String, pub subtitle: String, pub summary_text: String, pub theme_mode: CustomWorldThemeMode, - pub cover_image_src: Option, + pub cover_image_src: Option::, pub profile_payload_json: String, pub playable_npc_count: u32, pub landmark_count: u32, @@ -26,6 +31,8 @@ pub struct CustomWorldProfileUpsertInput { pub updated_at_micros: i64, } + impl __sdk::InModule for CustomWorldProfileUpsertInput { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/custom_world_publication_status_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/custom_world_publication_status_type.rs index f21186ff..d81681f4 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/custom_world_publication_status_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/custom_world_publication_status_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -11,8 +16,12 @@ pub enum CustomWorldPublicationStatus { Draft, Published, + } + + impl __sdk::InModule for CustomWorldPublicationStatus { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/custom_world_publish_world_input_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/custom_world_publish_world_input_type.rs index a95c0561..74160cd9 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/custom_world_publish_world_input_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/custom_world_publish_world_input_type.rs @@ -2,7 +2,13 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; + #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -10,15 +16,17 @@ pub struct CustomWorldPublishWorldInput { pub session_id: String, pub profile_id: String, pub owner_user_id: String, - pub public_work_code: Option, + pub public_work_code: Option::, pub author_public_user_code: String, pub draft_profile_json: String, - pub legacy_result_profile_json: Option, + pub legacy_result_profile_json: Option::, pub setting_text: String, pub author_display_name: String, pub published_at_micros: i64, } + impl __sdk::InModule for CustomWorldPublishWorldInput { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/custom_world_publish_world_result_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/custom_world_publish_world_result_type.rs index a44ca2b1..542810a3 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/custom_world_publish_world_result_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/custom_world_publish_world_result_type.rs @@ -2,24 +2,31 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; -use super::custom_world_gallery_entry_snapshot_type::CustomWorldGalleryEntrySnapshot; -use super::custom_world_profile_snapshot_type::CustomWorldProfileSnapshot; use super::custom_world_published_profile_compile_snapshot_type::CustomWorldPublishedProfileCompileSnapshot; use super::rpg_agent_stage_type::RpgAgentStage; +use super::custom_world_profile_snapshot_type::CustomWorldProfileSnapshot; +use super::custom_world_gallery_entry_snapshot_type::CustomWorldGalleryEntrySnapshot; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] pub struct CustomWorldPublishWorldResult { pub ok: bool, - pub compiled_record: Option, - pub entry: Option, - pub gallery_entry: Option, - pub session_stage: Option, - pub error_message: Option, + pub compiled_record: Option::, + pub entry: Option::, + pub gallery_entry: Option::, + pub session_stage: Option::, + pub error_message: Option::, } + impl __sdk::InModule for CustomWorldPublishWorldResult { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/custom_world_published_profile_compile_input_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/custom_world_published_profile_compile_input_type.rs index fda3d08b..c78399a4 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/custom_world_published_profile_compile_input_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/custom_world_published_profile_compile_input_type.rs @@ -2,7 +2,13 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; + #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -11,12 +17,14 @@ pub struct CustomWorldPublishedProfileCompileInput { pub profile_id: String, pub owner_user_id: String, pub draft_profile_json: String, - pub legacy_result_profile_json: Option, + pub legacy_result_profile_json: Option::, pub setting_text: String, pub author_display_name: String, pub updated_at_micros: i64, } + impl __sdk::InModule for CustomWorldPublishedProfileCompileInput { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/custom_world_published_profile_compile_result_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/custom_world_published_profile_compile_result_type.rs index 4783f889..42d770b5 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/custom_world_published_profile_compile_result_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/custom_world_published_profile_compile_result_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::custom_world_published_profile_compile_snapshot_type::CustomWorldPublishedProfileCompileSnapshot; @@ -10,10 +15,12 @@ use super::custom_world_published_profile_compile_snapshot_type::CustomWorldPubl #[sats(crate = __lib)] pub struct CustomWorldPublishedProfileCompileResult { pub ok: bool, - pub record: Option, - pub error_message: Option, + pub record: Option::, + pub error_message: Option::, } + impl __sdk::InModule for CustomWorldPublishedProfileCompileResult { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/custom_world_published_profile_compile_snapshot_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/custom_world_published_profile_compile_snapshot_type.rs index 0af4d59e..9214b492 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/custom_world_published_profile_compile_snapshot_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/custom_world_published_profile_compile_snapshot_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::custom_world_theme_mode_type::CustomWorldThemeMode; @@ -15,7 +20,7 @@ pub struct CustomWorldPublishedProfileCompileSnapshot { pub subtitle: String, pub summary_text: String, pub theme_mode: CustomWorldThemeMode, - pub cover_image_src: Option, + pub cover_image_src: Option::, pub playable_npc_count: u32, pub landmark_count: u32, pub author_display_name: String, @@ -23,6 +28,8 @@ pub struct CustomWorldPublishedProfileCompileSnapshot { pub updated_at_micros: i64, } + impl __sdk::InModule for CustomWorldPublishedProfileCompileSnapshot { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/custom_world_role_asset_status_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/custom_world_role_asset_status_type.rs index 1ab238e9..a969aa55 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/custom_world_role_asset_status_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/custom_world_role_asset_status_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -15,8 +20,12 @@ pub enum CustomWorldRoleAssetStatus { AnimationsReady, Complete, + } + + impl __sdk::InModule for CustomWorldRoleAssetStatus { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/custom_world_session_status_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/custom_world_session_status_type.rs index 9b640e4f..f7fe499d 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/custom_world_session_status_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/custom_world_session_status_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -17,8 +22,12 @@ pub enum CustomWorldSessionStatus { Completed, GenerationError, + } + + impl __sdk::InModule for CustomWorldSessionStatus { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/custom_world_session_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/custom_world_session_type.rs index 58e9f40c..2646073e 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/custom_world_session_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/custom_world_session_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::custom_world_generation_mode_type::CustomWorldGenerationMode; use super::custom_world_session_status_type::CustomWorldSessionStatus; @@ -15,18 +20,20 @@ pub struct CustomWorldSession { pub generation_mode: CustomWorldGenerationMode, pub status: CustomWorldSessionStatus, pub setting_text: String, - pub creator_intent_json: Option, + pub creator_intent_json: Option::, pub question_snapshot_json: String, - pub result_payload_json: Option, - pub last_error_message: Option, + pub result_payload_json: Option::, + pub last_error_message: Option::, pub created_at: __sdk::Timestamp, pub updated_at: __sdk::Timestamp, } + impl __sdk::InModule for CustomWorldSession { type Module = super::RemoteModule; } + /// Column accessor struct for the table `CustomWorldSession`. /// /// Provides typed access to columns for query building. @@ -36,10 +43,10 @@ pub struct CustomWorldSessionCols { pub generation_mode: __sdk::__query_builder::Col, pub status: __sdk::__query_builder::Col, pub setting_text: __sdk::__query_builder::Col, - pub creator_intent_json: __sdk::__query_builder::Col>, + pub creator_intent_json: __sdk::__query_builder::Col>, pub question_snapshot_json: __sdk::__query_builder::Col, - pub result_payload_json: __sdk::__query_builder::Col>, - pub last_error_message: __sdk::__query_builder::Col>, + pub result_payload_json: __sdk::__query_builder::Col>, + pub last_error_message: __sdk::__query_builder::Col>, pub created_at: __sdk::__query_builder::Col, pub updated_at: __sdk::__query_builder::Col, } @@ -53,21 +60,13 @@ impl __sdk::__query_builder::HasCols for CustomWorldSession { generation_mode: __sdk::__query_builder::Col::new(table_name, "generation_mode"), status: __sdk::__query_builder::Col::new(table_name, "status"), setting_text: __sdk::__query_builder::Col::new(table_name, "setting_text"), - creator_intent_json: __sdk::__query_builder::Col::new( - table_name, - "creator_intent_json", - ), - question_snapshot_json: __sdk::__query_builder::Col::new( - table_name, - "question_snapshot_json", - ), - result_payload_json: __sdk::__query_builder::Col::new( - table_name, - "result_payload_json", - ), + creator_intent_json: __sdk::__query_builder::Col::new(table_name, "creator_intent_json"), + question_snapshot_json: __sdk::__query_builder::Col::new(table_name, "question_snapshot_json"), + result_payload_json: __sdk::__query_builder::Col::new(table_name, "result_payload_json"), last_error_message: __sdk::__query_builder::Col::new(table_name, "last_error_message"), created_at: __sdk::__query_builder::Col::new(table_name, "created_at"), updated_at: __sdk::__query_builder::Col::new(table_name, "updated_at"), + } } } @@ -86,8 +85,10 @@ impl __sdk::__query_builder::HasIxCols for CustomWorldSession { CustomWorldSessionIxCols { 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 CustomWorldSession {} + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/custom_world_theme_mode_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/custom_world_theme_mode_type.rs index 2084ea04..766d1d08 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/custom_world_theme_mode_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/custom_world_theme_mode_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -19,8 +24,12 @@ pub enum CustomWorldThemeMode { Rift, Mythic, + } + + impl __sdk::InModule for CustomWorldThemeMode { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/custom_world_work_summary_snapshot_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/custom_world_work_summary_snapshot_type.rs index ed735fc3..e81345b0 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/custom_world_work_summary_snapshot_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/custom_world_work_summary_snapshot_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::rpg_agent_stage_type::RpgAgentStage; @@ -15,26 +20,28 @@ pub struct CustomWorldWorkSummarySnapshot { pub title: String, pub subtitle: String, pub summary: String, - pub cover_image_src: Option, - pub cover_render_mode: Option, + pub cover_image_src: Option::, + pub cover_render_mode: Option::, pub cover_character_image_srcs_json: String, pub updated_at_micros: i64, - pub published_at_micros: Option, - pub stage: Option, - pub stage_label: Option, + pub published_at_micros: Option::, + pub stage: Option::, + pub stage_label: Option::, pub playable_npc_count: u32, pub landmark_count: u32, - pub role_visual_ready_count: Option, - pub role_animation_ready_count: Option, - pub role_asset_summary_label: Option, - pub session_id: Option, - pub profile_id: Option, + pub role_visual_ready_count: Option::, + pub role_animation_ready_count: Option::, + pub role_asset_summary_label: Option::, + pub session_id: Option::, + pub profile_id: Option::, pub can_resume: bool, pub can_enter_world: bool, pub blocker_count: u32, pub publish_ready: bool, } + impl __sdk::InModule for CustomWorldWorkSummarySnapshot { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/custom_world_works_list_input_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/custom_world_works_list_input_type.rs index a007007b..073faecf 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/custom_world_works_list_input_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/custom_world_works_list_input_type.rs @@ -2,7 +2,13 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; + #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -10,6 +16,8 @@ pub struct CustomWorldWorksListInput { pub owner_user_id: String, } + impl __sdk::InModule for CustomWorldWorksListInput { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/custom_world_works_list_result_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/custom_world_works_list_result_type.rs index 9296740e..937b450e 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/custom_world_works_list_result_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/custom_world_works_list_result_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::custom_world_work_summary_snapshot_type::CustomWorldWorkSummarySnapshot; @@ -10,10 +15,12 @@ use super::custom_world_work_summary_snapshot_type::CustomWorldWorkSummarySnapsh #[sats(crate = __lib)] pub struct CustomWorldWorksListResult { pub ok: bool, - pub items: Vec, - pub error_message: Option, + pub items: Vec::, + pub error_message: Option::, } + impl __sdk::InModule for CustomWorldWorksListResult { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/database_migration_authorize_operator_input_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/database_migration_authorize_operator_input_type.rs index 309de81f..f453b44f 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/database_migration_authorize_operator_input_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/database_migration_authorize_operator_input_type.rs @@ -2,7 +2,13 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; + #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -12,6 +18,8 @@ pub struct DatabaseMigrationAuthorizeOperatorInput { pub note: String, } + impl __sdk::InModule for DatabaseMigrationAuthorizeOperatorInput { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/database_migration_export_input_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/database_migration_export_input_type.rs index 39381901..db0e52dd 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/database_migration_export_input_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/database_migration_export_input_type.rs @@ -2,14 +2,22 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; + #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] pub struct DatabaseMigrationExportInput { - pub include_tables: Vec, + pub include_tables: Vec::, } + impl __sdk::InModule for DatabaseMigrationExportInput { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/database_migration_import_input_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/database_migration_import_input_type.rs index 6bdbcb91..c0327e00 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/database_migration_import_input_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/database_migration_import_input_type.rs @@ -2,17 +2,25 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; + #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] pub struct DatabaseMigrationImportInput { pub migration_json: String, - pub include_tables: Vec, + pub include_tables: Vec::, pub replace_existing: bool, pub dry_run: bool, } + impl __sdk::InModule for DatabaseMigrationImportInput { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/database_migration_operator_procedure_result_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/database_migration_operator_procedure_result_type.rs index 0c7e3e65..ba2b1b97 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/database_migration_operator_procedure_result_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/database_migration_operator_procedure_result_type.rs @@ -2,16 +2,24 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; + #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] pub struct DatabaseMigrationOperatorProcedureResult { pub ok: bool, - pub operator_identity_hex: Option, - pub error_message: Option, + pub operator_identity_hex: Option::, + pub error_message: Option::, } + impl __sdk::InModule for DatabaseMigrationOperatorProcedureResult { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/database_migration_operator_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/database_migration_operator_type.rs index 7f8b0b6e..60e13e83 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/database_migration_operator_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/database_migration_operator_type.rs @@ -2,7 +2,13 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; + #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -13,10 +19,12 @@ pub struct DatabaseMigrationOperator { pub note: String, } + impl __sdk::InModule for DatabaseMigrationOperator { type Module = super::RemoteModule; } + /// Column accessor struct for the table `DatabaseMigrationOperator`. /// /// Provides typed access to columns for query building. @@ -35,6 +43,7 @@ impl __sdk::__query_builder::HasCols for DatabaseMigrationOperator { created_at: __sdk::__query_builder::Col::new(table_name, "created_at"), created_by: __sdk::__query_builder::Col::new(table_name, "created_by"), note: __sdk::__query_builder::Col::new(table_name, "note"), + } } } @@ -43,8 +52,7 @@ impl __sdk::__query_builder::HasCols for DatabaseMigrationOperator { /// /// Provides typed access to indexed columns for query building. pub struct DatabaseMigrationOperatorIxCols { - pub operator_identity: - __sdk::__query_builder::IxCol, + pub operator_identity: __sdk::__query_builder::IxCol, } impl __sdk::__query_builder::HasIxCols for DatabaseMigrationOperator { @@ -52,8 +60,10 @@ impl __sdk::__query_builder::HasIxCols for DatabaseMigrationOperator { fn ix_cols(table_name: &'static str) -> Self::IxCols { DatabaseMigrationOperatorIxCols { operator_identity: __sdk::__query_builder::IxCol::new(table_name, "operator_identity"), + } } } impl __sdk::__query_builder::CanBeLookupTable for DatabaseMigrationOperator {} + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/database_migration_procedure_result_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/database_migration_procedure_result_type.rs index a3869c5d..b5fc12b2 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/database_migration_procedure_result_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/database_migration_procedure_result_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::database_migration_table_stat_type::DatabaseMigrationTableStat; @@ -11,11 +16,13 @@ use super::database_migration_table_stat_type::DatabaseMigrationTableStat; pub struct DatabaseMigrationProcedureResult { pub ok: bool, pub schema_version: u32, - pub migration_json: Option, - pub table_stats: Vec, - pub error_message: Option, + pub migration_json: Option::, + pub table_stats: Vec::, + pub error_message: Option::, } + impl __sdk::InModule for DatabaseMigrationProcedureResult { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/database_migration_revoke_operator_input_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/database_migration_revoke_operator_input_type.rs index e550a7a4..cb07f9fc 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/database_migration_revoke_operator_input_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/database_migration_revoke_operator_input_type.rs @@ -2,7 +2,13 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; + #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -10,6 +16,8 @@ pub struct DatabaseMigrationRevokeOperatorInput { pub operator_identity_hex: String, } + impl __sdk::InModule for DatabaseMigrationRevokeOperatorInput { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/database_migration_table_stat_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/database_migration_table_stat_type.rs index 1257661d..07f469dd 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/database_migration_table_stat_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/database_migration_table_stat_type.rs @@ -2,7 +2,13 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; + #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -13,6 +19,8 @@ pub struct DatabaseMigrationTableStat { pub skipped_row_count: u64, } + impl __sdk::InModule for DatabaseMigrationTableStat { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/delete_big_fish_work_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/delete_big_fish_work_procedure.rs index 51cc224f..b8b9c6e6 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/delete_big_fish_work_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/delete_big_fish_work_procedure.rs @@ -2,17 +2,23 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::big_fish_work_delete_input_type::BigFishWorkDeleteInput; use super::big_fish_works_procedure_result_type::BigFishWorksProcedureResult; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] -struct DeleteBigFishWorkArgs { + struct DeleteBigFishWorkArgs { pub input: BigFishWorkDeleteInput, } + impl __sdk::InModule for DeleteBigFishWorkArgs { type Module = super::RemoteModule; } @@ -22,19 +28,16 @@ impl __sdk::InModule for DeleteBigFishWorkArgs { /// /// Implemented for [`super::RemoteProcedures`]. pub trait delete_big_fish_work { - fn delete_big_fish_work(&self, input: BigFishWorkDeleteInput) { - self.delete_big_fish_work_then(input, |_, _| {}); + fn delete_big_fish_work(&self, input: BigFishWorkDeleteInput, +) { + self.delete_big_fish_work_then(input, |_, _| {}); } fn delete_big_fish_work_then( &self, input: BigFishWorkDeleteInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ); } @@ -43,17 +46,13 @@ impl delete_big_fish_work for super::RemoteProcedures { &self, input: BigFishWorkDeleteInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ) { - self.imp - .invoke_procedure_with_callback::<_, BigFishWorksProcedureResult>( - "delete_big_fish_work", - DeleteBigFishWorkArgs { input }, - __callback, - ); + self.imp.invoke_procedure_with_callback::<_, BigFishWorksProcedureResult>( + "delete_big_fish_work", + DeleteBigFishWorkArgs { input, }, + __callback, + ); } } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/delete_custom_world_agent_session_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/delete_custom_world_agent_session_procedure.rs index 27341561..887f7a44 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/delete_custom_world_agent_session_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/delete_custom_world_agent_session_procedure.rs @@ -2,17 +2,23 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::custom_world_agent_session_get_input_type::CustomWorldAgentSessionGetInput; use super::custom_world_works_list_result_type::CustomWorldWorksListResult; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] -struct DeleteCustomWorldAgentSessionArgs { + struct DeleteCustomWorldAgentSessionArgs { pub input: CustomWorldAgentSessionGetInput, } + impl __sdk::InModule for DeleteCustomWorldAgentSessionArgs { type Module = super::RemoteModule; } @@ -22,19 +28,16 @@ impl __sdk::InModule for DeleteCustomWorldAgentSessionArgs { /// /// Implemented for [`super::RemoteProcedures`]. pub trait delete_custom_world_agent_session { - fn delete_custom_world_agent_session(&self, input: CustomWorldAgentSessionGetInput) { - self.delete_custom_world_agent_session_then(input, |_, _| {}); + fn delete_custom_world_agent_session(&self, input: CustomWorldAgentSessionGetInput, +) { + self.delete_custom_world_agent_session_then(input, |_, _| {}); } fn delete_custom_world_agent_session_then( &self, input: CustomWorldAgentSessionGetInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ); } @@ -43,17 +46,13 @@ impl delete_custom_world_agent_session for super::RemoteProcedures { &self, input: CustomWorldAgentSessionGetInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ) { - self.imp - .invoke_procedure_with_callback::<_, CustomWorldWorksListResult>( - "delete_custom_world_agent_session", - DeleteCustomWorldAgentSessionArgs { input }, - __callback, - ); + self.imp.invoke_procedure_with_callback::<_, CustomWorldWorksListResult>( + "delete_custom_world_agent_session", + DeleteCustomWorldAgentSessionArgs { input, }, + __callback, + ); } } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/delete_custom_world_profile_and_return_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/delete_custom_world_profile_and_return_procedure.rs index 246d4d00..4dfc8daf 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/delete_custom_world_profile_and_return_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/delete_custom_world_profile_and_return_procedure.rs @@ -2,17 +2,23 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::custom_world_profile_delete_input_type::CustomWorldProfileDeleteInput; use super::custom_world_profile_list_result_type::CustomWorldProfileListResult; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] -struct DeleteCustomWorldProfileAndReturnArgs { + struct DeleteCustomWorldProfileAndReturnArgs { pub input: CustomWorldProfileDeleteInput, } + impl __sdk::InModule for DeleteCustomWorldProfileAndReturnArgs { type Module = super::RemoteModule; } @@ -22,19 +28,16 @@ impl __sdk::InModule for DeleteCustomWorldProfileAndReturnArgs { /// /// Implemented for [`super::RemoteProcedures`]. pub trait delete_custom_world_profile_and_return { - fn delete_custom_world_profile_and_return(&self, input: CustomWorldProfileDeleteInput) { - self.delete_custom_world_profile_and_return_then(input, |_, _| {}); + fn delete_custom_world_profile_and_return(&self, input: CustomWorldProfileDeleteInput, +) { + self.delete_custom_world_profile_and_return_then(input, |_, _| {}); } fn delete_custom_world_profile_and_return_then( &self, input: CustomWorldProfileDeleteInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ); } @@ -43,17 +46,13 @@ impl delete_custom_world_profile_and_return for super::RemoteProcedures { &self, input: CustomWorldProfileDeleteInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ) { - self.imp - .invoke_procedure_with_callback::<_, CustomWorldProfileListResult>( - "delete_custom_world_profile_and_return", - DeleteCustomWorldProfileAndReturnArgs { input }, - __callback, - ); + self.imp.invoke_procedure_with_callback::<_, CustomWorldProfileListResult>( + "delete_custom_world_profile_and_return", + DeleteCustomWorldProfileAndReturnArgs { input, }, + __callback, + ); } } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/delete_puzzle_work_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/delete_puzzle_work_procedure.rs index 5b7e5375..500eaf84 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/delete_puzzle_work_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/delete_puzzle_work_procedure.rs @@ -2,17 +2,23 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::puzzle_work_delete_input_type::PuzzleWorkDeleteInput; use super::puzzle_works_procedure_result_type::PuzzleWorksProcedureResult; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] -struct DeletePuzzleWorkArgs { + struct DeletePuzzleWorkArgs { pub input: PuzzleWorkDeleteInput, } + impl __sdk::InModule for DeletePuzzleWorkArgs { type Module = super::RemoteModule; } @@ -22,19 +28,16 @@ impl __sdk::InModule for DeletePuzzleWorkArgs { /// /// Implemented for [`super::RemoteProcedures`]. pub trait delete_puzzle_work { - fn delete_puzzle_work(&self, input: PuzzleWorkDeleteInput) { - self.delete_puzzle_work_then(input, |_, _| {}); + fn delete_puzzle_work(&self, input: PuzzleWorkDeleteInput, +) { + self.delete_puzzle_work_then(input, |_, _| {}); } fn delete_puzzle_work_then( &self, input: PuzzleWorkDeleteInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ); } @@ -43,17 +46,13 @@ impl delete_puzzle_work for super::RemoteProcedures { &self, input: PuzzleWorkDeleteInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ) { - self.imp - .invoke_procedure_with_callback::<_, PuzzleWorksProcedureResult>( - "delete_puzzle_work", - DeletePuzzleWorkArgs { input }, - __callback, - ); + self.imp.invoke_procedure_with_callback::<_, PuzzleWorksProcedureResult>( + "delete_puzzle_work", + DeletePuzzleWorkArgs { input, }, + __callback, + ); } } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/delete_runtime_snapshot_and_return_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/delete_runtime_snapshot_and_return_procedure.rs index 6373a19b..6c38b74b 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/delete_runtime_snapshot_and_return_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/delete_runtime_snapshot_and_return_procedure.rs @@ -2,17 +2,23 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::runtime_snapshot_delete_input_type::RuntimeSnapshotDeleteInput; use super::runtime_snapshot_procedure_result_type::RuntimeSnapshotProcedureResult; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] -struct DeleteRuntimeSnapshotAndReturnArgs { + struct DeleteRuntimeSnapshotAndReturnArgs { pub input: RuntimeSnapshotDeleteInput, } + impl __sdk::InModule for DeleteRuntimeSnapshotAndReturnArgs { type Module = super::RemoteModule; } @@ -22,19 +28,16 @@ impl __sdk::InModule for DeleteRuntimeSnapshotAndReturnArgs { /// /// Implemented for [`super::RemoteProcedures`]. pub trait delete_runtime_snapshot_and_return { - fn delete_runtime_snapshot_and_return(&self, input: RuntimeSnapshotDeleteInput) { - self.delete_runtime_snapshot_and_return_then(input, |_, _| {}); + fn delete_runtime_snapshot_and_return(&self, input: RuntimeSnapshotDeleteInput, +) { + self.delete_runtime_snapshot_and_return_then(input, |_, _| {}); } fn delete_runtime_snapshot_and_return_then( &self, input: RuntimeSnapshotDeleteInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ); } @@ -43,17 +46,13 @@ impl delete_runtime_snapshot_and_return for super::RemoteProcedures { &self, input: RuntimeSnapshotDeleteInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ) { - self.imp - .invoke_procedure_with_callback::<_, RuntimeSnapshotProcedureResult>( - "delete_runtime_snapshot_and_return", - DeleteRuntimeSnapshotAndReturnArgs { input }, - __callback, - ); + self.imp.invoke_procedure_with_callback::<_, RuntimeSnapshotProcedureResult>( + "delete_runtime_snapshot_and_return", + DeleteRuntimeSnapshotAndReturnArgs { input, }, + __callback, + ); } } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/drag_puzzle_piece_or_group_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/drag_puzzle_piece_or_group_procedure.rs index daad89bd..49c4ee1c 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/drag_puzzle_piece_or_group_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/drag_puzzle_piece_or_group_procedure.rs @@ -2,17 +2,23 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; -use super::puzzle_run_drag_input_type::PuzzleRunDragInput; use super::puzzle_run_procedure_result_type::PuzzleRunProcedureResult; +use super::puzzle_run_drag_input_type::PuzzleRunDragInput; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] -struct DragPuzzlePieceOrGroupArgs { + struct DragPuzzlePieceOrGroupArgs { pub input: PuzzleRunDragInput, } + impl __sdk::InModule for DragPuzzlePieceOrGroupArgs { type Module = super::RemoteModule; } @@ -22,19 +28,16 @@ impl __sdk::InModule for DragPuzzlePieceOrGroupArgs { /// /// Implemented for [`super::RemoteProcedures`]. pub trait drag_puzzle_piece_or_group { - fn drag_puzzle_piece_or_group(&self, input: PuzzleRunDragInput) { - self.drag_puzzle_piece_or_group_then(input, |_, _| {}); + fn drag_puzzle_piece_or_group(&self, input: PuzzleRunDragInput, +) { + self.drag_puzzle_piece_or_group_then(input, |_, _| {}); } fn drag_puzzle_piece_or_group_then( &self, input: PuzzleRunDragInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ); } @@ -43,17 +46,13 @@ impl drag_puzzle_piece_or_group for super::RemoteProcedures { &self, input: PuzzleRunDragInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ) { - self.imp - .invoke_procedure_with_callback::<_, PuzzleRunProcedureResult>( - "drag_puzzle_piece_or_group", - DragPuzzlePieceOrGroupArgs { input }, - __callback, - ); + self.imp.invoke_procedure_with_callback::<_, PuzzleRunProcedureResult>( + "drag_puzzle_piece_or_group", + DragPuzzlePieceOrGroupArgs { input, }, + __callback, + ); } } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/equip_inventory_item_input_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/equip_inventory_item_input_type.rs index 775ad7fa..19545699 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/equip_inventory_item_input_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/equip_inventory_item_input_type.rs @@ -2,7 +2,13 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; + #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -10,6 +16,8 @@ pub struct EquipInventoryItemInput { pub slot_id: String, } + impl __sdk::InModule for EquipInventoryItemInput { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/execute_custom_world_agent_action_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/execute_custom_world_agent_action_procedure.rs index e778877c..390871f5 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/execute_custom_world_agent_action_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/execute_custom_world_agent_action_procedure.rs @@ -2,17 +2,23 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::custom_world_agent_action_execute_input_type::CustomWorldAgentActionExecuteInput; use super::custom_world_agent_action_execute_result_type::CustomWorldAgentActionExecuteResult; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] -struct ExecuteCustomWorldAgentActionArgs { + struct ExecuteCustomWorldAgentActionArgs { pub input: CustomWorldAgentActionExecuteInput, } + impl __sdk::InModule for ExecuteCustomWorldAgentActionArgs { type Module = super::RemoteModule; } @@ -22,19 +28,16 @@ impl __sdk::InModule for ExecuteCustomWorldAgentActionArgs { /// /// Implemented for [`super::RemoteProcedures`]. pub trait execute_custom_world_agent_action { - fn execute_custom_world_agent_action(&self, input: CustomWorldAgentActionExecuteInput) { - self.execute_custom_world_agent_action_then(input, |_, _| {}); + fn execute_custom_world_agent_action(&self, input: CustomWorldAgentActionExecuteInput, +) { + self.execute_custom_world_agent_action_then(input, |_, _| {}); } fn execute_custom_world_agent_action_then( &self, input: CustomWorldAgentActionExecuteInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ); } @@ -43,17 +46,13 @@ impl execute_custom_world_agent_action for super::RemoteProcedures { &self, input: CustomWorldAgentActionExecuteInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ) { - self.imp - .invoke_procedure_with_callback::<_, CustomWorldAgentActionExecuteResult>( - "execute_custom_world_agent_action", - ExecuteCustomWorldAgentActionArgs { input }, - __callback, - ); + self.imp.invoke_procedure_with_callback::<_, CustomWorldAgentActionExecuteResult>( + "execute_custom_world_agent_action", + ExecuteCustomWorldAgentActionArgs { input, }, + __callback, + ); } } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/export_auth_store_snapshot_from_tables_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/export_auth_store_snapshot_from_tables_procedure.rs index 9f8842ad..2e273a5e 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/export_auth_store_snapshot_from_tables_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/export_auth_store_snapshot_from_tables_procedure.rs @@ -2,13 +2,20 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::auth_store_snapshot_procedure_result_type::AuthStoreSnapshotProcedureResult; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] -struct ExportAuthStoreSnapshotFromTablesArgs {} + struct ExportAuthStoreSnapshotFromTablesArgs { + } + impl __sdk::InModule for ExportAuthStoreSnapshotFromTablesArgs { type Module = super::RemoteModule; @@ -19,36 +26,28 @@ impl __sdk::InModule for ExportAuthStoreSnapshotFromTablesArgs { /// /// Implemented for [`super::RemoteProcedures`]. pub trait export_auth_store_snapshot_from_tables { - fn export_auth_store_snapshot_from_tables(&self) { - self.export_auth_store_snapshot_from_tables_then(|_, _| {}); + fn export_auth_store_snapshot_from_tables(&self, ) { + self.export_auth_store_snapshot_from_tables_then( |_, _| {}); } fn export_auth_store_snapshot_from_tables_then( &self, - - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ); } impl export_auth_store_snapshot_from_tables for super::RemoteProcedures { fn export_auth_store_snapshot_from_tables_then( &self, - - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ) { - self.imp - .invoke_procedure_with_callback::<_, AuthStoreSnapshotProcedureResult>( - "export_auth_store_snapshot_from_tables", - ExportAuthStoreSnapshotFromTablesArgs {}, - __callback, - ); + self.imp.invoke_procedure_with_callback::<_, AuthStoreSnapshotProcedureResult>( + "export_auth_store_snapshot_from_tables", + ExportAuthStoreSnapshotFromTablesArgs { }, + __callback, + ); } } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/export_database_migration_to_file_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/export_database_migration_to_file_procedure.rs index 3dfe18f8..096b48dd 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/export_database_migration_to_file_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/export_database_migration_to_file_procedure.rs @@ -2,17 +2,23 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::database_migration_export_input_type::DatabaseMigrationExportInput; use super::database_migration_procedure_result_type::DatabaseMigrationProcedureResult; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] -struct ExportDatabaseMigrationToFileArgs { + struct ExportDatabaseMigrationToFileArgs { pub input: DatabaseMigrationExportInput, } + impl __sdk::InModule for ExportDatabaseMigrationToFileArgs { type Module = super::RemoteModule; } @@ -22,19 +28,16 @@ impl __sdk::InModule for ExportDatabaseMigrationToFileArgs { /// /// Implemented for [`super::RemoteProcedures`]. pub trait export_database_migration_to_file { - fn export_database_migration_to_file(&self, input: DatabaseMigrationExportInput) { - self.export_database_migration_to_file_then(input, |_, _| {}); + fn export_database_migration_to_file(&self, input: DatabaseMigrationExportInput, +) { + self.export_database_migration_to_file_then(input, |_, _| {}); } fn export_database_migration_to_file_then( &self, input: DatabaseMigrationExportInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ); } @@ -43,17 +46,13 @@ impl export_database_migration_to_file for super::RemoteProcedures { &self, input: DatabaseMigrationExportInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ) { - self.imp - .invoke_procedure_with_callback::<_, DatabaseMigrationProcedureResult>( - "export_database_migration_to_file", - ExportDatabaseMigrationToFileArgs { input }, - __callback, - ); + self.imp.invoke_procedure_with_callback::<_, DatabaseMigrationProcedureResult>( + "export_database_migration_to_file", + ExportDatabaseMigrationToFileArgs { input, }, + __callback, + ); } } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/fail_ai_task_and_return_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/fail_ai_task_and_return_procedure.rs index 46090a01..b92e796d 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/fail_ai_task_and_return_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/fail_ai_task_and_return_procedure.rs @@ -2,17 +2,23 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; -use super::ai_task_failure_input_type::AiTaskFailureInput; use super::ai_task_procedure_result_type::AiTaskProcedureResult; +use super::ai_task_failure_input_type::AiTaskFailureInput; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] -struct FailAiTaskAndReturnArgs { + struct FailAiTaskAndReturnArgs { pub input: AiTaskFailureInput, } + impl __sdk::InModule for FailAiTaskAndReturnArgs { type Module = super::RemoteModule; } @@ -22,19 +28,16 @@ impl __sdk::InModule for FailAiTaskAndReturnArgs { /// /// Implemented for [`super::RemoteProcedures`]. pub trait fail_ai_task_and_return { - fn fail_ai_task_and_return(&self, input: AiTaskFailureInput) { - self.fail_ai_task_and_return_then(input, |_, _| {}); + fn fail_ai_task_and_return(&self, input: AiTaskFailureInput, +) { + self.fail_ai_task_and_return_then(input, |_, _| {}); } fn fail_ai_task_and_return_then( &self, input: AiTaskFailureInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ); } @@ -43,17 +46,13 @@ impl fail_ai_task_and_return for super::RemoteProcedures { &self, input: AiTaskFailureInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ) { - self.imp - .invoke_procedure_with_callback::<_, AiTaskProcedureResult>( - "fail_ai_task_and_return", - FailAiTaskAndReturnArgs { input }, - __callback, - ); + self.imp.invoke_procedure_with_callback::<_, AiTaskProcedureResult>( + "fail_ai_task_and_return", + FailAiTaskAndReturnArgs { input, }, + __callback, + ); } } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/finalize_big_fish_agent_message_turn_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/finalize_big_fish_agent_message_turn_procedure.rs index 9f5c8e7a..29f7ff55 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/finalize_big_fish_agent_message_turn_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/finalize_big_fish_agent_message_turn_procedure.rs @@ -2,17 +2,23 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; -use super::big_fish_message_finalize_input_type::BigFishMessageFinalizeInput; use super::big_fish_session_procedure_result_type::BigFishSessionProcedureResult; +use super::big_fish_message_finalize_input_type::BigFishMessageFinalizeInput; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] -struct FinalizeBigFishAgentMessageTurnArgs { + struct FinalizeBigFishAgentMessageTurnArgs { pub input: BigFishMessageFinalizeInput, } + impl __sdk::InModule for FinalizeBigFishAgentMessageTurnArgs { type Module = super::RemoteModule; } @@ -22,19 +28,16 @@ impl __sdk::InModule for FinalizeBigFishAgentMessageTurnArgs { /// /// Implemented for [`super::RemoteProcedures`]. pub trait finalize_big_fish_agent_message_turn { - fn finalize_big_fish_agent_message_turn(&self, input: BigFishMessageFinalizeInput) { - self.finalize_big_fish_agent_message_turn_then(input, |_, _| {}); + fn finalize_big_fish_agent_message_turn(&self, input: BigFishMessageFinalizeInput, +) { + self.finalize_big_fish_agent_message_turn_then(input, |_, _| {}); } fn finalize_big_fish_agent_message_turn_then( &self, input: BigFishMessageFinalizeInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ); } @@ -43,17 +46,13 @@ impl finalize_big_fish_agent_message_turn for super::RemoteProcedures { &self, input: BigFishMessageFinalizeInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ) { - self.imp - .invoke_procedure_with_callback::<_, BigFishSessionProcedureResult>( - "finalize_big_fish_agent_message_turn", - FinalizeBigFishAgentMessageTurnArgs { input }, - __callback, - ); + self.imp.invoke_procedure_with_callback::<_, BigFishSessionProcedureResult>( + "finalize_big_fish_agent_message_turn", + FinalizeBigFishAgentMessageTurnArgs { input, }, + __callback, + ); } } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/finalize_custom_world_agent_message_turn_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/finalize_custom_world_agent_message_turn_procedure.rs index fad75a7b..e9b3f1d9 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/finalize_custom_world_agent_message_turn_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/finalize_custom_world_agent_message_turn_procedure.rs @@ -2,17 +2,23 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::custom_world_agent_message_finalize_input_type::CustomWorldAgentMessageFinalizeInput; use super::custom_world_agent_operation_procedure_result_type::CustomWorldAgentOperationProcedureResult; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] -struct FinalizeCustomWorldAgentMessageTurnArgs { + struct FinalizeCustomWorldAgentMessageTurnArgs { pub input: CustomWorldAgentMessageFinalizeInput, } + impl __sdk::InModule for FinalizeCustomWorldAgentMessageTurnArgs { type Module = super::RemoteModule; } @@ -22,22 +28,16 @@ impl __sdk::InModule for FinalizeCustomWorldAgentMessageTurnArgs { /// /// Implemented for [`super::RemoteProcedures`]. pub trait finalize_custom_world_agent_message_turn { - fn finalize_custom_world_agent_message_turn( - &self, - input: CustomWorldAgentMessageFinalizeInput, - ) { - self.finalize_custom_world_agent_message_turn_then(input, |_, _| {}); + fn finalize_custom_world_agent_message_turn(&self, input: CustomWorldAgentMessageFinalizeInput, +) { + self.finalize_custom_world_agent_message_turn_then(input, |_, _| {}); } fn finalize_custom_world_agent_message_turn_then( &self, input: CustomWorldAgentMessageFinalizeInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ); } @@ -46,17 +46,13 @@ impl finalize_custom_world_agent_message_turn for super::RemoteProcedures { &self, input: CustomWorldAgentMessageFinalizeInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ) { - self.imp - .invoke_procedure_with_callback::<_, CustomWorldAgentOperationProcedureResult>( - "finalize_custom_world_agent_message_turn", - FinalizeCustomWorldAgentMessageTurnArgs { input }, - __callback, - ); + self.imp.invoke_procedure_with_callback::<_, CustomWorldAgentOperationProcedureResult>( + "finalize_custom_world_agent_message_turn", + FinalizeCustomWorldAgentMessageTurnArgs { input, }, + __callback, + ); } } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/finalize_puzzle_agent_message_turn_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/finalize_puzzle_agent_message_turn_procedure.rs index 0014d394..e2d6566c 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/finalize_puzzle_agent_message_turn_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/finalize_puzzle_agent_message_turn_procedure.rs @@ -2,17 +2,23 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; -use super::puzzle_agent_message_finalize_input_type::PuzzleAgentMessageFinalizeInput; use super::puzzle_agent_session_procedure_result_type::PuzzleAgentSessionProcedureResult; +use super::puzzle_agent_message_finalize_input_type::PuzzleAgentMessageFinalizeInput; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] -struct FinalizePuzzleAgentMessageTurnArgs { + struct FinalizePuzzleAgentMessageTurnArgs { pub input: PuzzleAgentMessageFinalizeInput, } + impl __sdk::InModule for FinalizePuzzleAgentMessageTurnArgs { type Module = super::RemoteModule; } @@ -22,19 +28,16 @@ impl __sdk::InModule for FinalizePuzzleAgentMessageTurnArgs { /// /// Implemented for [`super::RemoteProcedures`]. pub trait finalize_puzzle_agent_message_turn { - fn finalize_puzzle_agent_message_turn(&self, input: PuzzleAgentMessageFinalizeInput) { - self.finalize_puzzle_agent_message_turn_then(input, |_, _| {}); + fn finalize_puzzle_agent_message_turn(&self, input: PuzzleAgentMessageFinalizeInput, +) { + self.finalize_puzzle_agent_message_turn_then(input, |_, _| {}); } fn finalize_puzzle_agent_message_turn_then( &self, input: PuzzleAgentMessageFinalizeInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ); } @@ -43,17 +46,13 @@ impl finalize_puzzle_agent_message_turn for super::RemoteProcedures { &self, input: PuzzleAgentMessageFinalizeInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ) { - self.imp - .invoke_procedure_with_callback::<_, PuzzleAgentSessionProcedureResult>( - "finalize_puzzle_agent_message_turn", - FinalizePuzzleAgentMessageTurnArgs { input }, - __callback, - ); + self.imp.invoke_procedure_with_callback::<_, PuzzleAgentSessionProcedureResult>( + "finalize_puzzle_agent_message_turn", + FinalizePuzzleAgentMessageTurnArgs { input, }, + __callback, + ); } } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/generate_big_fish_asset_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/generate_big_fish_asset_procedure.rs index 1a87c951..6ee820f7 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/generate_big_fish_asset_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/generate_big_fish_asset_procedure.rs @@ -2,17 +2,23 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; -use super::big_fish_asset_generate_input_type::BigFishAssetGenerateInput; use super::big_fish_session_procedure_result_type::BigFishSessionProcedureResult; +use super::big_fish_asset_generate_input_type::BigFishAssetGenerateInput; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] -struct GenerateBigFishAssetArgs { + struct GenerateBigFishAssetArgs { pub input: BigFishAssetGenerateInput, } + impl __sdk::InModule for GenerateBigFishAssetArgs { type Module = super::RemoteModule; } @@ -22,19 +28,16 @@ impl __sdk::InModule for GenerateBigFishAssetArgs { /// /// Implemented for [`super::RemoteProcedures`]. pub trait generate_big_fish_asset { - fn generate_big_fish_asset(&self, input: BigFishAssetGenerateInput) { - self.generate_big_fish_asset_then(input, |_, _| {}); + fn generate_big_fish_asset(&self, input: BigFishAssetGenerateInput, +) { + self.generate_big_fish_asset_then(input, |_, _| {}); } fn generate_big_fish_asset_then( &self, input: BigFishAssetGenerateInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ); } @@ -43,17 +46,13 @@ impl generate_big_fish_asset for super::RemoteProcedures { &self, input: BigFishAssetGenerateInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ) { - self.imp - .invoke_procedure_with_callback::<_, BigFishSessionProcedureResult>( - "generate_big_fish_asset", - GenerateBigFishAssetArgs { input }, - __callback, - ); + self.imp.invoke_procedure_with_callback::<_, BigFishSessionProcedureResult>( + "generate_big_fish_asset", + GenerateBigFishAssetArgs { input, }, + __callback, + ); } } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/get_auth_store_snapshot_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/get_auth_store_snapshot_procedure.rs index 51b3d201..887894ea 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/get_auth_store_snapshot_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/get_auth_store_snapshot_procedure.rs @@ -2,13 +2,20 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::auth_store_snapshot_procedure_result_type::AuthStoreSnapshotProcedureResult; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] -struct GetAuthStoreSnapshotArgs {} + struct GetAuthStoreSnapshotArgs { + } + impl __sdk::InModule for GetAuthStoreSnapshotArgs { type Module = super::RemoteModule; @@ -19,36 +26,28 @@ impl __sdk::InModule for GetAuthStoreSnapshotArgs { /// /// Implemented for [`super::RemoteProcedures`]. pub trait get_auth_store_snapshot { - fn get_auth_store_snapshot(&self) { - self.get_auth_store_snapshot_then(|_, _| {}); + fn get_auth_store_snapshot(&self, ) { + self.get_auth_store_snapshot_then( |_, _| {}); } fn get_auth_store_snapshot_then( &self, - - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ); } impl get_auth_store_snapshot for super::RemoteProcedures { fn get_auth_store_snapshot_then( &self, - - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ) { - self.imp - .invoke_procedure_with_callback::<_, AuthStoreSnapshotProcedureResult>( - "get_auth_store_snapshot", - GetAuthStoreSnapshotArgs {}, - __callback, - ); + self.imp.invoke_procedure_with_callback::<_, AuthStoreSnapshotProcedureResult>( + "get_auth_store_snapshot", + GetAuthStoreSnapshotArgs { }, + __callback, + ); } } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/get_battle_state_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/get_battle_state_procedure.rs index a737fbdf..c6f93416 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/get_battle_state_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/get_battle_state_procedure.rs @@ -2,17 +2,23 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::battle_state_procedure_result_type::BattleStateProcedureResult; use super::battle_state_query_input_type::BattleStateQueryInput; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] -struct GetBattleStateArgs { + struct GetBattleStateArgs { pub input: BattleStateQueryInput, } + impl __sdk::InModule for GetBattleStateArgs { type Module = super::RemoteModule; } @@ -22,19 +28,16 @@ impl __sdk::InModule for GetBattleStateArgs { /// /// Implemented for [`super::RemoteProcedures`]. pub trait get_battle_state { - fn get_battle_state(&self, input: BattleStateQueryInput) { - self.get_battle_state_then(input, |_, _| {}); + fn get_battle_state(&self, input: BattleStateQueryInput, +) { + self.get_battle_state_then(input, |_, _| {}); } fn get_battle_state_then( &self, input: BattleStateQueryInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ); } @@ -43,17 +46,13 @@ impl get_battle_state for super::RemoteProcedures { &self, input: BattleStateQueryInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ) { - self.imp - .invoke_procedure_with_callback::<_, BattleStateProcedureResult>( - "get_battle_state", - GetBattleStateArgs { input }, - __callback, - ); + self.imp.invoke_procedure_with_callback::<_, BattleStateProcedureResult>( + "get_battle_state", + GetBattleStateArgs { input, }, + __callback, + ); } } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/get_big_fish_run_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/get_big_fish_run_procedure.rs new file mode 100644 index 00000000..a16bbc76 --- /dev/null +++ b/server-rs/crates/spacetime-client/src/module_bindings/get_big_fish_run_procedure.rs @@ -0,0 +1,58 @@ +// 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::big_fish_run_get_input_type::BigFishRunGetInput; +use super::big_fish_run_procedure_result_type::BigFishRunProcedureResult; + +#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] +#[sats(crate = __lib)] + struct GetBigFishRunArgs { + pub input: BigFishRunGetInput, +} + + +impl __sdk::InModule for GetBigFishRunArgs { + type Module = super::RemoteModule; +} + +#[allow(non_camel_case_types)] +/// Extension trait for access to the procedure `get_big_fish_run`. +/// +/// Implemented for [`super::RemoteProcedures`]. +pub trait get_big_fish_run { + fn get_big_fish_run(&self, input: BigFishRunGetInput, +) { + self.get_big_fish_run_then(input, |_, _| {}); + } + + fn get_big_fish_run_then( + &self, + input: BigFishRunGetInput, + + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, + ); +} + +impl get_big_fish_run for super::RemoteProcedures { + fn get_big_fish_run_then( + &self, + input: BigFishRunGetInput, + + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, + ) { + self.imp.invoke_procedure_with_callback::<_, BigFishRunProcedureResult>( + "get_big_fish_run", + GetBigFishRunArgs { input, }, + __callback, + ); + } +} + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/get_big_fish_session_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/get_big_fish_session_procedure.rs index 0b0d78f1..f54dc9e4 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/get_big_fish_session_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/get_big_fish_session_procedure.rs @@ -2,17 +2,23 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; -use super::big_fish_session_get_input_type::BigFishSessionGetInput; use super::big_fish_session_procedure_result_type::BigFishSessionProcedureResult; +use super::big_fish_session_get_input_type::BigFishSessionGetInput; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] -struct GetBigFishSessionArgs { + struct GetBigFishSessionArgs { pub input: BigFishSessionGetInput, } + impl __sdk::InModule for GetBigFishSessionArgs { type Module = super::RemoteModule; } @@ -22,19 +28,16 @@ impl __sdk::InModule for GetBigFishSessionArgs { /// /// Implemented for [`super::RemoteProcedures`]. pub trait get_big_fish_session { - fn get_big_fish_session(&self, input: BigFishSessionGetInput) { - self.get_big_fish_session_then(input, |_, _| {}); + fn get_big_fish_session(&self, input: BigFishSessionGetInput, +) { + self.get_big_fish_session_then(input, |_, _| {}); } fn get_big_fish_session_then( &self, input: BigFishSessionGetInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ); } @@ -43,17 +46,13 @@ impl get_big_fish_session for super::RemoteProcedures { &self, input: BigFishSessionGetInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ) { - self.imp - .invoke_procedure_with_callback::<_, BigFishSessionProcedureResult>( - "get_big_fish_session", - GetBigFishSessionArgs { input }, - __callback, - ); + self.imp.invoke_procedure_with_callback::<_, BigFishSessionProcedureResult>( + "get_big_fish_session", + GetBigFishSessionArgs { input, }, + __callback, + ); } } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/get_chapter_progression_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/get_chapter_progression_procedure.rs index eef158dc..dc75dc77 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/get_chapter_progression_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/get_chapter_progression_procedure.rs @@ -2,17 +2,23 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; -use super::chapter_progression_get_input_type::ChapterProgressionGetInput; use super::chapter_progression_procedure_result_type::ChapterProgressionProcedureResult; +use super::chapter_progression_get_input_type::ChapterProgressionGetInput; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] -struct GetChapterProgressionArgs { + struct GetChapterProgressionArgs { pub input: ChapterProgressionGetInput, } + impl __sdk::InModule for GetChapterProgressionArgs { type Module = super::RemoteModule; } @@ -22,19 +28,16 @@ impl __sdk::InModule for GetChapterProgressionArgs { /// /// Implemented for [`super::RemoteProcedures`]. pub trait get_chapter_progression { - fn get_chapter_progression(&self, input: ChapterProgressionGetInput) { - self.get_chapter_progression_then(input, |_, _| {}); + fn get_chapter_progression(&self, input: ChapterProgressionGetInput, +) { + self.get_chapter_progression_then(input, |_, _| {}); } fn get_chapter_progression_then( &self, input: ChapterProgressionGetInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ); } @@ -43,17 +46,13 @@ impl get_chapter_progression for super::RemoteProcedures { &self, input: ChapterProgressionGetInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ) { - self.imp - .invoke_procedure_with_callback::<_, ChapterProgressionProcedureResult>( - "get_chapter_progression", - GetChapterProgressionArgs { input }, - __callback, - ); + self.imp.invoke_procedure_with_callback::<_, ChapterProgressionProcedureResult>( + "get_chapter_progression", + GetChapterProgressionArgs { input, }, + __callback, + ); } } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/get_custom_world_agent_card_detail_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/get_custom_world_agent_card_detail_procedure.rs index 11a90329..f0e38f3b 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/get_custom_world_agent_card_detail_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/get_custom_world_agent_card_detail_procedure.rs @@ -2,17 +2,23 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::custom_world_agent_card_detail_get_input_type::CustomWorldAgentCardDetailGetInput; use super::custom_world_draft_card_detail_result_type::CustomWorldDraftCardDetailResult; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] -struct GetCustomWorldAgentCardDetailArgs { + struct GetCustomWorldAgentCardDetailArgs { pub input: CustomWorldAgentCardDetailGetInput, } + impl __sdk::InModule for GetCustomWorldAgentCardDetailArgs { type Module = super::RemoteModule; } @@ -22,19 +28,16 @@ impl __sdk::InModule for GetCustomWorldAgentCardDetailArgs { /// /// Implemented for [`super::RemoteProcedures`]. pub trait get_custom_world_agent_card_detail { - fn get_custom_world_agent_card_detail(&self, input: CustomWorldAgentCardDetailGetInput) { - self.get_custom_world_agent_card_detail_then(input, |_, _| {}); + fn get_custom_world_agent_card_detail(&self, input: CustomWorldAgentCardDetailGetInput, +) { + self.get_custom_world_agent_card_detail_then(input, |_, _| {}); } fn get_custom_world_agent_card_detail_then( &self, input: CustomWorldAgentCardDetailGetInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ); } @@ -43,17 +46,13 @@ impl get_custom_world_agent_card_detail for super::RemoteProcedures { &self, input: CustomWorldAgentCardDetailGetInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ) { - self.imp - .invoke_procedure_with_callback::<_, CustomWorldDraftCardDetailResult>( - "get_custom_world_agent_card_detail", - GetCustomWorldAgentCardDetailArgs { input }, - __callback, - ); + self.imp.invoke_procedure_with_callback::<_, CustomWorldDraftCardDetailResult>( + "get_custom_world_agent_card_detail", + GetCustomWorldAgentCardDetailArgs { input, }, + __callback, + ); } } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/get_custom_world_agent_operation_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/get_custom_world_agent_operation_procedure.rs index 1c4ffd6a..45ffda03 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/get_custom_world_agent_operation_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/get_custom_world_agent_operation_procedure.rs @@ -2,17 +2,23 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; -use super::custom_world_agent_operation_get_input_type::CustomWorldAgentOperationGetInput; use super::custom_world_agent_operation_procedure_result_type::CustomWorldAgentOperationProcedureResult; +use super::custom_world_agent_operation_get_input_type::CustomWorldAgentOperationGetInput; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] -struct GetCustomWorldAgentOperationArgs { + struct GetCustomWorldAgentOperationArgs { pub input: CustomWorldAgentOperationGetInput, } + impl __sdk::InModule for GetCustomWorldAgentOperationArgs { type Module = super::RemoteModule; } @@ -22,19 +28,16 @@ impl __sdk::InModule for GetCustomWorldAgentOperationArgs { /// /// Implemented for [`super::RemoteProcedures`]. pub trait get_custom_world_agent_operation { - fn get_custom_world_agent_operation(&self, input: CustomWorldAgentOperationGetInput) { - self.get_custom_world_agent_operation_then(input, |_, _| {}); + fn get_custom_world_agent_operation(&self, input: CustomWorldAgentOperationGetInput, +) { + self.get_custom_world_agent_operation_then(input, |_, _| {}); } fn get_custom_world_agent_operation_then( &self, input: CustomWorldAgentOperationGetInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ); } @@ -43,17 +46,13 @@ impl get_custom_world_agent_operation for super::RemoteProcedures { &self, input: CustomWorldAgentOperationGetInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ) { - self.imp - .invoke_procedure_with_callback::<_, CustomWorldAgentOperationProcedureResult>( - "get_custom_world_agent_operation", - GetCustomWorldAgentOperationArgs { input }, - __callback, - ); + self.imp.invoke_procedure_with_callback::<_, CustomWorldAgentOperationProcedureResult>( + "get_custom_world_agent_operation", + GetCustomWorldAgentOperationArgs { input, }, + __callback, + ); } } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/get_custom_world_agent_session_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/get_custom_world_agent_session_procedure.rs index 212987e4..e1660a95 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/get_custom_world_agent_session_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/get_custom_world_agent_session_procedure.rs @@ -2,17 +2,23 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; -use super::custom_world_agent_session_get_input_type::CustomWorldAgentSessionGetInput; use super::custom_world_agent_session_procedure_result_type::CustomWorldAgentSessionProcedureResult; +use super::custom_world_agent_session_get_input_type::CustomWorldAgentSessionGetInput; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] -struct GetCustomWorldAgentSessionArgs { + struct GetCustomWorldAgentSessionArgs { pub input: CustomWorldAgentSessionGetInput, } + impl __sdk::InModule for GetCustomWorldAgentSessionArgs { type Module = super::RemoteModule; } @@ -22,19 +28,16 @@ impl __sdk::InModule for GetCustomWorldAgentSessionArgs { /// /// Implemented for [`super::RemoteProcedures`]. pub trait get_custom_world_agent_session { - fn get_custom_world_agent_session(&self, input: CustomWorldAgentSessionGetInput) { - self.get_custom_world_agent_session_then(input, |_, _| {}); + fn get_custom_world_agent_session(&self, input: CustomWorldAgentSessionGetInput, +) { + self.get_custom_world_agent_session_then(input, |_, _| {}); } fn get_custom_world_agent_session_then( &self, input: CustomWorldAgentSessionGetInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ); } @@ -43,17 +46,13 @@ impl get_custom_world_agent_session for super::RemoteProcedures { &self, input: CustomWorldAgentSessionGetInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ) { - self.imp - .invoke_procedure_with_callback::<_, CustomWorldAgentSessionProcedureResult>( - "get_custom_world_agent_session", - GetCustomWorldAgentSessionArgs { input }, - __callback, - ); + self.imp.invoke_procedure_with_callback::<_, CustomWorldAgentSessionProcedureResult>( + "get_custom_world_agent_session", + GetCustomWorldAgentSessionArgs { input, }, + __callback, + ); } } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/get_custom_world_gallery_detail_by_code_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/get_custom_world_gallery_detail_by_code_procedure.rs index 24768c43..455eb2fc 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/get_custom_world_gallery_detail_by_code_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/get_custom_world_gallery_detail_by_code_procedure.rs @@ -2,17 +2,23 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; -use super::custom_world_gallery_detail_by_code_input_type::CustomWorldGalleryDetailByCodeInput; use super::custom_world_library_mutation_result_type::CustomWorldLibraryMutationResult; +use super::custom_world_gallery_detail_by_code_input_type::CustomWorldGalleryDetailByCodeInput; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] -struct GetCustomWorldGalleryDetailByCodeArgs { + struct GetCustomWorldGalleryDetailByCodeArgs { pub input: CustomWorldGalleryDetailByCodeInput, } + impl __sdk::InModule for GetCustomWorldGalleryDetailByCodeArgs { type Module = super::RemoteModule; } @@ -22,19 +28,16 @@ impl __sdk::InModule for GetCustomWorldGalleryDetailByCodeArgs { /// /// Implemented for [`super::RemoteProcedures`]. pub trait get_custom_world_gallery_detail_by_code { - fn get_custom_world_gallery_detail_by_code(&self, input: CustomWorldGalleryDetailByCodeInput) { - self.get_custom_world_gallery_detail_by_code_then(input, |_, _| {}); + fn get_custom_world_gallery_detail_by_code(&self, input: CustomWorldGalleryDetailByCodeInput, +) { + self.get_custom_world_gallery_detail_by_code_then(input, |_, _| {}); } fn get_custom_world_gallery_detail_by_code_then( &self, input: CustomWorldGalleryDetailByCodeInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ); } @@ -43,17 +46,13 @@ impl get_custom_world_gallery_detail_by_code for super::RemoteProcedures { &self, input: CustomWorldGalleryDetailByCodeInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ) { - self.imp - .invoke_procedure_with_callback::<_, CustomWorldLibraryMutationResult>( - "get_custom_world_gallery_detail_by_code", - GetCustomWorldGalleryDetailByCodeArgs { input }, - __callback, - ); + self.imp.invoke_procedure_with_callback::<_, CustomWorldLibraryMutationResult>( + "get_custom_world_gallery_detail_by_code", + GetCustomWorldGalleryDetailByCodeArgs { input, }, + __callback, + ); } } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/get_custom_world_gallery_detail_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/get_custom_world_gallery_detail_procedure.rs index f5127dcf..79050c11 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/get_custom_world_gallery_detail_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/get_custom_world_gallery_detail_procedure.rs @@ -2,17 +2,23 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::custom_world_gallery_detail_input_type::CustomWorldGalleryDetailInput; use super::custom_world_library_mutation_result_type::CustomWorldLibraryMutationResult; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] -struct GetCustomWorldGalleryDetailArgs { + struct GetCustomWorldGalleryDetailArgs { pub input: CustomWorldGalleryDetailInput, } + impl __sdk::InModule for GetCustomWorldGalleryDetailArgs { type Module = super::RemoteModule; } @@ -22,19 +28,16 @@ impl __sdk::InModule for GetCustomWorldGalleryDetailArgs { /// /// Implemented for [`super::RemoteProcedures`]. pub trait get_custom_world_gallery_detail { - fn get_custom_world_gallery_detail(&self, input: CustomWorldGalleryDetailInput) { - self.get_custom_world_gallery_detail_then(input, |_, _| {}); + fn get_custom_world_gallery_detail(&self, input: CustomWorldGalleryDetailInput, +) { + self.get_custom_world_gallery_detail_then(input, |_, _| {}); } fn get_custom_world_gallery_detail_then( &self, input: CustomWorldGalleryDetailInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ); } @@ -43,17 +46,13 @@ impl get_custom_world_gallery_detail for super::RemoteProcedures { &self, input: CustomWorldGalleryDetailInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ) { - self.imp - .invoke_procedure_with_callback::<_, CustomWorldLibraryMutationResult>( - "get_custom_world_gallery_detail", - GetCustomWorldGalleryDetailArgs { input }, - __callback, - ); + self.imp.invoke_procedure_with_callback::<_, CustomWorldLibraryMutationResult>( + "get_custom_world_gallery_detail", + GetCustomWorldGalleryDetailArgs { input, }, + __callback, + ); } } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/get_custom_world_library_detail_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/get_custom_world_library_detail_procedure.rs index ab99274a..175758c2 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/get_custom_world_library_detail_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/get_custom_world_library_detail_procedure.rs @@ -2,17 +2,23 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; -use super::custom_world_library_detail_input_type::CustomWorldLibraryDetailInput; use super::custom_world_library_mutation_result_type::CustomWorldLibraryMutationResult; +use super::custom_world_library_detail_input_type::CustomWorldLibraryDetailInput; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] -struct GetCustomWorldLibraryDetailArgs { + struct GetCustomWorldLibraryDetailArgs { pub input: CustomWorldLibraryDetailInput, } + impl __sdk::InModule for GetCustomWorldLibraryDetailArgs { type Module = super::RemoteModule; } @@ -22,19 +28,16 @@ impl __sdk::InModule for GetCustomWorldLibraryDetailArgs { /// /// Implemented for [`super::RemoteProcedures`]. pub trait get_custom_world_library_detail { - fn get_custom_world_library_detail(&self, input: CustomWorldLibraryDetailInput) { - self.get_custom_world_library_detail_then(input, |_, _| {}); + fn get_custom_world_library_detail(&self, input: CustomWorldLibraryDetailInput, +) { + self.get_custom_world_library_detail_then(input, |_, _| {}); } fn get_custom_world_library_detail_then( &self, input: CustomWorldLibraryDetailInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ); } @@ -43,17 +46,13 @@ impl get_custom_world_library_detail for super::RemoteProcedures { &self, input: CustomWorldLibraryDetailInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ) { - self.imp - .invoke_procedure_with_callback::<_, CustomWorldLibraryMutationResult>( - "get_custom_world_library_detail", - GetCustomWorldLibraryDetailArgs { input }, - __callback, - ); + self.imp.invoke_procedure_with_callback::<_, CustomWorldLibraryMutationResult>( + "get_custom_world_library_detail", + GetCustomWorldLibraryDetailArgs { input, }, + __callback, + ); } } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/get_player_progression_or_default_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/get_player_progression_or_default_procedure.rs index 97c139fb..73490884 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/get_player_progression_or_default_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/get_player_progression_or_default_procedure.rs @@ -2,17 +2,23 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::player_progression_get_input_type::PlayerProgressionGetInput; use super::player_progression_procedure_result_type::PlayerProgressionProcedureResult; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] -struct GetPlayerProgressionOrDefaultArgs { + struct GetPlayerProgressionOrDefaultArgs { pub input: PlayerProgressionGetInput, } + impl __sdk::InModule for GetPlayerProgressionOrDefaultArgs { type Module = super::RemoteModule; } @@ -22,19 +28,16 @@ impl __sdk::InModule for GetPlayerProgressionOrDefaultArgs { /// /// Implemented for [`super::RemoteProcedures`]. pub trait get_player_progression_or_default { - fn get_player_progression_or_default(&self, input: PlayerProgressionGetInput) { - self.get_player_progression_or_default_then(input, |_, _| {}); + fn get_player_progression_or_default(&self, input: PlayerProgressionGetInput, +) { + self.get_player_progression_or_default_then(input, |_, _| {}); } fn get_player_progression_or_default_then( &self, input: PlayerProgressionGetInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ); } @@ -43,17 +46,13 @@ impl get_player_progression_or_default for super::RemoteProcedures { &self, input: PlayerProgressionGetInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ) { - self.imp - .invoke_procedure_with_callback::<_, PlayerProgressionProcedureResult>( - "get_player_progression_or_default", - GetPlayerProgressionOrDefaultArgs { input }, - __callback, - ); + self.imp.invoke_procedure_with_callback::<_, PlayerProgressionProcedureResult>( + "get_player_progression_or_default", + GetPlayerProgressionOrDefaultArgs { input, }, + __callback, + ); } } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/get_profile_dashboard_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/get_profile_dashboard_procedure.rs index 38200b75..616a11b8 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/get_profile_dashboard_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/get_profile_dashboard_procedure.rs @@ -2,17 +2,23 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::runtime_profile_dashboard_get_input_type::RuntimeProfileDashboardGetInput; use super::runtime_profile_dashboard_procedure_result_type::RuntimeProfileDashboardProcedureResult; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] -struct GetProfileDashboardArgs { + struct GetProfileDashboardArgs { pub input: RuntimeProfileDashboardGetInput, } + impl __sdk::InModule for GetProfileDashboardArgs { type Module = super::RemoteModule; } @@ -22,19 +28,16 @@ impl __sdk::InModule for GetProfileDashboardArgs { /// /// Implemented for [`super::RemoteProcedures`]. pub trait get_profile_dashboard { - fn get_profile_dashboard(&self, input: RuntimeProfileDashboardGetInput) { - self.get_profile_dashboard_then(input, |_, _| {}); + fn get_profile_dashboard(&self, input: RuntimeProfileDashboardGetInput, +) { + self.get_profile_dashboard_then(input, |_, _| {}); } fn get_profile_dashboard_then( &self, input: RuntimeProfileDashboardGetInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ); } @@ -43,17 +46,13 @@ impl get_profile_dashboard for super::RemoteProcedures { &self, input: RuntimeProfileDashboardGetInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ) { - self.imp - .invoke_procedure_with_callback::<_, RuntimeProfileDashboardProcedureResult>( - "get_profile_dashboard", - GetProfileDashboardArgs { input }, - __callback, - ); + self.imp.invoke_procedure_with_callback::<_, RuntimeProfileDashboardProcedureResult>( + "get_profile_dashboard", + GetProfileDashboardArgs { input, }, + __callback, + ); } } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/get_profile_play_stats_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/get_profile_play_stats_procedure.rs index 2ad47ed2..466c6902 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/get_profile_play_stats_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/get_profile_play_stats_procedure.rs @@ -2,17 +2,23 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::runtime_profile_play_stats_get_input_type::RuntimeProfilePlayStatsGetInput; use super::runtime_profile_play_stats_procedure_result_type::RuntimeProfilePlayStatsProcedureResult; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] -struct GetProfilePlayStatsArgs { + struct GetProfilePlayStatsArgs { pub input: RuntimeProfilePlayStatsGetInput, } + impl __sdk::InModule for GetProfilePlayStatsArgs { type Module = super::RemoteModule; } @@ -22,19 +28,16 @@ impl __sdk::InModule for GetProfilePlayStatsArgs { /// /// Implemented for [`super::RemoteProcedures`]. pub trait get_profile_play_stats { - fn get_profile_play_stats(&self, input: RuntimeProfilePlayStatsGetInput) { - self.get_profile_play_stats_then(input, |_, _| {}); + fn get_profile_play_stats(&self, input: RuntimeProfilePlayStatsGetInput, +) { + self.get_profile_play_stats_then(input, |_, _| {}); } fn get_profile_play_stats_then( &self, input: RuntimeProfilePlayStatsGetInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ); } @@ -43,17 +46,13 @@ impl get_profile_play_stats for super::RemoteProcedures { &self, input: RuntimeProfilePlayStatsGetInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ) { - self.imp - .invoke_procedure_with_callback::<_, RuntimeProfilePlayStatsProcedureResult>( - "get_profile_play_stats", - GetProfilePlayStatsArgs { input }, - __callback, - ); + self.imp.invoke_procedure_with_callback::<_, RuntimeProfilePlayStatsProcedureResult>( + "get_profile_play_stats", + GetProfilePlayStatsArgs { input, }, + __callback, + ); } } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/get_profile_recharge_center_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/get_profile_recharge_center_procedure.rs index bf070c9c..a31465f8 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/get_profile_recharge_center_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/get_profile_recharge_center_procedure.rs @@ -2,17 +2,23 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; -use super::runtime_profile_recharge_center_get_input_type::RuntimeProfileRechargeCenterGetInput; use super::runtime_profile_recharge_center_procedure_result_type::RuntimeProfileRechargeCenterProcedureResult; +use super::runtime_profile_recharge_center_get_input_type::RuntimeProfileRechargeCenterGetInput; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] -struct GetProfileRechargeCenterArgs { + struct GetProfileRechargeCenterArgs { pub input: RuntimeProfileRechargeCenterGetInput, } + impl __sdk::InModule for GetProfileRechargeCenterArgs { type Module = super::RemoteModule; } @@ -22,19 +28,16 @@ impl __sdk::InModule for GetProfileRechargeCenterArgs { /// /// Implemented for [`super::RemoteProcedures`]. pub trait get_profile_recharge_center { - fn get_profile_recharge_center(&self, input: RuntimeProfileRechargeCenterGetInput) { - self.get_profile_recharge_center_then(input, |_, _| {}); + fn get_profile_recharge_center(&self, input: RuntimeProfileRechargeCenterGetInput, +) { + self.get_profile_recharge_center_then(input, |_, _| {}); } fn get_profile_recharge_center_then( &self, input: RuntimeProfileRechargeCenterGetInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ); } @@ -43,17 +46,13 @@ impl get_profile_recharge_center for super::RemoteProcedures { &self, input: RuntimeProfileRechargeCenterGetInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ) { - self.imp - .invoke_procedure_with_callback::<_, RuntimeProfileRechargeCenterProcedureResult>( - "get_profile_recharge_center", - GetProfileRechargeCenterArgs { input }, - __callback, - ); + self.imp.invoke_procedure_with_callback::<_, RuntimeProfileRechargeCenterProcedureResult>( + "get_profile_recharge_center", + GetProfileRechargeCenterArgs { input, }, + __callback, + ); } } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/get_profile_referral_invite_center_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/get_profile_referral_invite_center_procedure.rs index 2b3dcdad..1bc6fc86 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/get_profile_referral_invite_center_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/get_profile_referral_invite_center_procedure.rs @@ -2,17 +2,23 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::runtime_referral_invite_center_get_input_type::RuntimeReferralInviteCenterGetInput; use super::runtime_referral_invite_center_procedure_result_type::RuntimeReferralInviteCenterProcedureResult; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] -struct GetProfileReferralInviteCenterArgs { + struct GetProfileReferralInviteCenterArgs { pub input: RuntimeReferralInviteCenterGetInput, } + impl __sdk::InModule for GetProfileReferralInviteCenterArgs { type Module = super::RemoteModule; } @@ -22,19 +28,16 @@ impl __sdk::InModule for GetProfileReferralInviteCenterArgs { /// /// Implemented for [`super::RemoteProcedures`]. pub trait get_profile_referral_invite_center { - fn get_profile_referral_invite_center(&self, input: RuntimeReferralInviteCenterGetInput) { - self.get_profile_referral_invite_center_then(input, |_, _| {}); + fn get_profile_referral_invite_center(&self, input: RuntimeReferralInviteCenterGetInput, +) { + self.get_profile_referral_invite_center_then(input, |_, _| {}); } fn get_profile_referral_invite_center_then( &self, input: RuntimeReferralInviteCenterGetInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ); } @@ -43,17 +46,13 @@ impl get_profile_referral_invite_center for super::RemoteProcedures { &self, input: RuntimeReferralInviteCenterGetInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ) { - self.imp - .invoke_procedure_with_callback::<_, RuntimeReferralInviteCenterProcedureResult>( - "get_profile_referral_invite_center", - GetProfileReferralInviteCenterArgs { input }, - __callback, - ); + self.imp.invoke_procedure_with_callback::<_, RuntimeReferralInviteCenterProcedureResult>( + "get_profile_referral_invite_center", + GetProfileReferralInviteCenterArgs { input, }, + __callback, + ); } } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/get_puzzle_agent_session_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/get_puzzle_agent_session_procedure.rs index 97929382..8946764b 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/get_puzzle_agent_session_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/get_puzzle_agent_session_procedure.rs @@ -2,17 +2,23 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; -use super::puzzle_agent_session_get_input_type::PuzzleAgentSessionGetInput; use super::puzzle_agent_session_procedure_result_type::PuzzleAgentSessionProcedureResult; +use super::puzzle_agent_session_get_input_type::PuzzleAgentSessionGetInput; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] -struct GetPuzzleAgentSessionArgs { + struct GetPuzzleAgentSessionArgs { pub input: PuzzleAgentSessionGetInput, } + impl __sdk::InModule for GetPuzzleAgentSessionArgs { type Module = super::RemoteModule; } @@ -22,19 +28,16 @@ impl __sdk::InModule for GetPuzzleAgentSessionArgs { /// /// Implemented for [`super::RemoteProcedures`]. pub trait get_puzzle_agent_session { - fn get_puzzle_agent_session(&self, input: PuzzleAgentSessionGetInput) { - self.get_puzzle_agent_session_then(input, |_, _| {}); + fn get_puzzle_agent_session(&self, input: PuzzleAgentSessionGetInput, +) { + self.get_puzzle_agent_session_then(input, |_, _| {}); } fn get_puzzle_agent_session_then( &self, input: PuzzleAgentSessionGetInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ); } @@ -43,17 +46,13 @@ impl get_puzzle_agent_session for super::RemoteProcedures { &self, input: PuzzleAgentSessionGetInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ) { - self.imp - .invoke_procedure_with_callback::<_, PuzzleAgentSessionProcedureResult>( - "get_puzzle_agent_session", - GetPuzzleAgentSessionArgs { input }, - __callback, - ); + self.imp.invoke_procedure_with_callback::<_, PuzzleAgentSessionProcedureResult>( + "get_puzzle_agent_session", + GetPuzzleAgentSessionArgs { input, }, + __callback, + ); } } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/get_puzzle_gallery_detail_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/get_puzzle_gallery_detail_procedure.rs index a1471eb3..0c78ac7c 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/get_puzzle_gallery_detail_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/get_puzzle_gallery_detail_procedure.rs @@ -2,17 +2,23 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::puzzle_work_get_input_type::PuzzleWorkGetInput; use super::puzzle_work_procedure_result_type::PuzzleWorkProcedureResult; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] -struct GetPuzzleGalleryDetailArgs { + struct GetPuzzleGalleryDetailArgs { pub input: PuzzleWorkGetInput, } + impl __sdk::InModule for GetPuzzleGalleryDetailArgs { type Module = super::RemoteModule; } @@ -22,19 +28,16 @@ impl __sdk::InModule for GetPuzzleGalleryDetailArgs { /// /// Implemented for [`super::RemoteProcedures`]. pub trait get_puzzle_gallery_detail { - fn get_puzzle_gallery_detail(&self, input: PuzzleWorkGetInput) { - self.get_puzzle_gallery_detail_then(input, |_, _| {}); + fn get_puzzle_gallery_detail(&self, input: PuzzleWorkGetInput, +) { + self.get_puzzle_gallery_detail_then(input, |_, _| {}); } fn get_puzzle_gallery_detail_then( &self, input: PuzzleWorkGetInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ); } @@ -43,17 +46,13 @@ impl get_puzzle_gallery_detail for super::RemoteProcedures { &self, input: PuzzleWorkGetInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ) { - self.imp - .invoke_procedure_with_callback::<_, PuzzleWorkProcedureResult>( - "get_puzzle_gallery_detail", - GetPuzzleGalleryDetailArgs { input }, - __callback, - ); + self.imp.invoke_procedure_with_callback::<_, PuzzleWorkProcedureResult>( + "get_puzzle_gallery_detail", + GetPuzzleGalleryDetailArgs { input, }, + __callback, + ); } } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/get_puzzle_run_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/get_puzzle_run_procedure.rs index 2db5ab66..c409b79d 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/get_puzzle_run_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/get_puzzle_run_procedure.rs @@ -2,17 +2,23 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; -use super::puzzle_run_get_input_type::PuzzleRunGetInput; use super::puzzle_run_procedure_result_type::PuzzleRunProcedureResult; +use super::puzzle_run_get_input_type::PuzzleRunGetInput; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] -struct GetPuzzleRunArgs { + struct GetPuzzleRunArgs { pub input: PuzzleRunGetInput, } + impl __sdk::InModule for GetPuzzleRunArgs { type Module = super::RemoteModule; } @@ -22,19 +28,16 @@ impl __sdk::InModule for GetPuzzleRunArgs { /// /// Implemented for [`super::RemoteProcedures`]. pub trait get_puzzle_run { - fn get_puzzle_run(&self, input: PuzzleRunGetInput) { - self.get_puzzle_run_then(input, |_, _| {}); + fn get_puzzle_run(&self, input: PuzzleRunGetInput, +) { + self.get_puzzle_run_then(input, |_, _| {}); } fn get_puzzle_run_then( &self, input: PuzzleRunGetInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ); } @@ -43,17 +46,13 @@ impl get_puzzle_run for super::RemoteProcedures { &self, input: PuzzleRunGetInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ) { - self.imp - .invoke_procedure_with_callback::<_, PuzzleRunProcedureResult>( - "get_puzzle_run", - GetPuzzleRunArgs { input }, - __callback, - ); + self.imp.invoke_procedure_with_callback::<_, PuzzleRunProcedureResult>( + "get_puzzle_run", + GetPuzzleRunArgs { input, }, + __callback, + ); } } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/get_puzzle_work_detail_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/get_puzzle_work_detail_procedure.rs index d36c7417..c21d1048 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/get_puzzle_work_detail_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/get_puzzle_work_detail_procedure.rs @@ -2,17 +2,23 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::puzzle_work_get_input_type::PuzzleWorkGetInput; use super::puzzle_work_procedure_result_type::PuzzleWorkProcedureResult; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] -struct GetPuzzleWorkDetailArgs { + struct GetPuzzleWorkDetailArgs { pub input: PuzzleWorkGetInput, } + impl __sdk::InModule for GetPuzzleWorkDetailArgs { type Module = super::RemoteModule; } @@ -22,19 +28,16 @@ impl __sdk::InModule for GetPuzzleWorkDetailArgs { /// /// Implemented for [`super::RemoteProcedures`]. pub trait get_puzzle_work_detail { - fn get_puzzle_work_detail(&self, input: PuzzleWorkGetInput) { - self.get_puzzle_work_detail_then(input, |_, _| {}); + fn get_puzzle_work_detail(&self, input: PuzzleWorkGetInput, +) { + self.get_puzzle_work_detail_then(input, |_, _| {}); } fn get_puzzle_work_detail_then( &self, input: PuzzleWorkGetInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ); } @@ -43,17 +46,13 @@ impl get_puzzle_work_detail for super::RemoteProcedures { &self, input: PuzzleWorkGetInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ) { - self.imp - .invoke_procedure_with_callback::<_, PuzzleWorkProcedureResult>( - "get_puzzle_work_detail", - GetPuzzleWorkDetailArgs { input }, - __callback, - ); + self.imp.invoke_procedure_with_callback::<_, PuzzleWorkProcedureResult>( + "get_puzzle_work_detail", + GetPuzzleWorkDetailArgs { input, }, + __callback, + ); } } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/get_runtime_inventory_state_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/get_runtime_inventory_state_procedure.rs index abbf4f20..d5137496 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/get_runtime_inventory_state_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/get_runtime_inventory_state_procedure.rs @@ -2,17 +2,23 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; -use super::runtime_inventory_state_procedure_result_type::RuntimeInventoryStateProcedureResult; use super::runtime_inventory_state_query_input_type::RuntimeInventoryStateQueryInput; +use super::runtime_inventory_state_procedure_result_type::RuntimeInventoryStateProcedureResult; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] -struct GetRuntimeInventoryStateArgs { + struct GetRuntimeInventoryStateArgs { pub input: RuntimeInventoryStateQueryInput, } + impl __sdk::InModule for GetRuntimeInventoryStateArgs { type Module = super::RemoteModule; } @@ -22,19 +28,16 @@ impl __sdk::InModule for GetRuntimeInventoryStateArgs { /// /// Implemented for [`super::RemoteProcedures`]. pub trait get_runtime_inventory_state { - fn get_runtime_inventory_state(&self, input: RuntimeInventoryStateQueryInput) { - self.get_runtime_inventory_state_then(input, |_, _| {}); + fn get_runtime_inventory_state(&self, input: RuntimeInventoryStateQueryInput, +) { + self.get_runtime_inventory_state_then(input, |_, _| {}); } fn get_runtime_inventory_state_then( &self, input: RuntimeInventoryStateQueryInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ); } @@ -43,17 +46,13 @@ impl get_runtime_inventory_state for super::RemoteProcedures { &self, input: RuntimeInventoryStateQueryInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ) { - self.imp - .invoke_procedure_with_callback::<_, RuntimeInventoryStateProcedureResult>( - "get_runtime_inventory_state", - GetRuntimeInventoryStateArgs { input }, - __callback, - ); + self.imp.invoke_procedure_with_callback::<_, RuntimeInventoryStateProcedureResult>( + "get_runtime_inventory_state", + GetRuntimeInventoryStateArgs { input, }, + __callback, + ); } } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/get_runtime_setting_or_default_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/get_runtime_setting_or_default_procedure.rs index 261caed1..a58e515c 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/get_runtime_setting_or_default_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/get_runtime_setting_or_default_procedure.rs @@ -2,17 +2,23 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::runtime_setting_get_input_type::RuntimeSettingGetInput; use super::runtime_setting_procedure_result_type::RuntimeSettingProcedureResult; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] -struct GetRuntimeSettingOrDefaultArgs { + struct GetRuntimeSettingOrDefaultArgs { pub input: RuntimeSettingGetInput, } + impl __sdk::InModule for GetRuntimeSettingOrDefaultArgs { type Module = super::RemoteModule; } @@ -22,19 +28,16 @@ impl __sdk::InModule for GetRuntimeSettingOrDefaultArgs { /// /// Implemented for [`super::RemoteProcedures`]. pub trait get_runtime_setting_or_default { - fn get_runtime_setting_or_default(&self, input: RuntimeSettingGetInput) { - self.get_runtime_setting_or_default_then(input, |_, _| {}); + fn get_runtime_setting_or_default(&self, input: RuntimeSettingGetInput, +) { + self.get_runtime_setting_or_default_then(input, |_, _| {}); } fn get_runtime_setting_or_default_then( &self, input: RuntimeSettingGetInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ); } @@ -43,17 +46,13 @@ impl get_runtime_setting_or_default for super::RemoteProcedures { &self, input: RuntimeSettingGetInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ) { - self.imp - .invoke_procedure_with_callback::<_, RuntimeSettingProcedureResult>( - "get_runtime_setting_or_default", - GetRuntimeSettingOrDefaultArgs { input }, - __callback, - ); + self.imp.invoke_procedure_with_callback::<_, RuntimeSettingProcedureResult>( + "get_runtime_setting_or_default", + GetRuntimeSettingOrDefaultArgs { input, }, + __callback, + ); } } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/get_runtime_snapshot_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/get_runtime_snapshot_procedure.rs index 989fa40e..75117d98 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/get_runtime_snapshot_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/get_runtime_snapshot_procedure.rs @@ -2,17 +2,23 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; -use super::runtime_snapshot_get_input_type::RuntimeSnapshotGetInput; use super::runtime_snapshot_procedure_result_type::RuntimeSnapshotProcedureResult; +use super::runtime_snapshot_get_input_type::RuntimeSnapshotGetInput; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] -struct GetRuntimeSnapshotArgs { + struct GetRuntimeSnapshotArgs { pub input: RuntimeSnapshotGetInput, } + impl __sdk::InModule for GetRuntimeSnapshotArgs { type Module = super::RemoteModule; } @@ -22,19 +28,16 @@ impl __sdk::InModule for GetRuntimeSnapshotArgs { /// /// Implemented for [`super::RemoteProcedures`]. pub trait get_runtime_snapshot { - fn get_runtime_snapshot(&self, input: RuntimeSnapshotGetInput) { - self.get_runtime_snapshot_then(input, |_, _| {}); + fn get_runtime_snapshot(&self, input: RuntimeSnapshotGetInput, +) { + self.get_runtime_snapshot_then(input, |_, _| {}); } fn get_runtime_snapshot_then( &self, input: RuntimeSnapshotGetInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ); } @@ -43,17 +46,13 @@ impl get_runtime_snapshot for super::RemoteProcedures { &self, input: RuntimeSnapshotGetInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ) { - self.imp - .invoke_procedure_with_callback::<_, RuntimeSnapshotProcedureResult>( - "get_runtime_snapshot", - GetRuntimeSnapshotArgs { input }, - __callback, - ); + self.imp.invoke_procedure_with_callback::<_, RuntimeSnapshotProcedureResult>( + "get_runtime_snapshot", + GetRuntimeSnapshotArgs { input, }, + __callback, + ); } } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/get_story_session_state_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/get_story_session_state_procedure.rs index 7e566dc9..b454ab5d 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/get_story_session_state_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/get_story_session_state_procedure.rs @@ -2,17 +2,23 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::story_session_state_input_type::StorySessionStateInput; use super::story_session_state_procedure_result_type::StorySessionStateProcedureResult; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] -struct GetStorySessionStateArgs { + struct GetStorySessionStateArgs { pub input: StorySessionStateInput, } + impl __sdk::InModule for GetStorySessionStateArgs { type Module = super::RemoteModule; } @@ -22,19 +28,16 @@ impl __sdk::InModule for GetStorySessionStateArgs { /// /// Implemented for [`super::RemoteProcedures`]. pub trait get_story_session_state { - fn get_story_session_state(&self, input: StorySessionStateInput) { - self.get_story_session_state_then(input, |_, _| {}); + fn get_story_session_state(&self, input: StorySessionStateInput, +) { + self.get_story_session_state_then(input, |_, _| {}); } fn get_story_session_state_then( &self, input: StorySessionStateInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ); } @@ -43,17 +46,13 @@ impl get_story_session_state for super::RemoteProcedures { &self, input: StorySessionStateInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ) { - self.imp - .invoke_procedure_with_callback::<_, StorySessionStateProcedureResult>( - "get_story_session_state", - GetStorySessionStateArgs { input }, - __callback, - ); + self.imp.invoke_procedure_with_callback::<_, StorySessionStateProcedureResult>( + "get_story_session_state", + GetStorySessionStateArgs { input, }, + __callback, + ); } } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/grant_inventory_item_input_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/grant_inventory_item_input_type.rs index 553aff65..c9389060 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/grant_inventory_item_input_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/grant_inventory_item_input_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::inventory_item_snapshot_type::InventoryItemSnapshot; @@ -13,6 +18,8 @@ pub struct GrantInventoryItemInput { pub item: InventoryItemSnapshot, } + impl __sdk::InModule for GrantInventoryItemInput { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/grant_player_progression_experience_and_return_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/grant_player_progression_experience_and_return_procedure.rs index 4c67da63..04e0ae9a 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/grant_player_progression_experience_and_return_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/grant_player_progression_experience_and_return_procedure.rs @@ -2,17 +2,23 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; -use super::player_progression_grant_input_type::PlayerProgressionGrantInput; use super::player_progression_procedure_result_type::PlayerProgressionProcedureResult; +use super::player_progression_grant_input_type::PlayerProgressionGrantInput; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] -struct GrantPlayerProgressionExperienceAndReturnArgs { + struct GrantPlayerProgressionExperienceAndReturnArgs { pub input: PlayerProgressionGrantInput, } + impl __sdk::InModule for GrantPlayerProgressionExperienceAndReturnArgs { type Module = super::RemoteModule; } @@ -22,19 +28,16 @@ impl __sdk::InModule for GrantPlayerProgressionExperienceAndReturnArgs { /// /// Implemented for [`super::RemoteProcedures`]. pub trait grant_player_progression_experience_and_return { - fn grant_player_progression_experience_and_return(&self, input: PlayerProgressionGrantInput) { - self.grant_player_progression_experience_and_return_then(input, |_, _| {}); + fn grant_player_progression_experience_and_return(&self, input: PlayerProgressionGrantInput, +) { + self.grant_player_progression_experience_and_return_then(input, |_, _| {}); } fn grant_player_progression_experience_and_return_then( &self, input: PlayerProgressionGrantInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ); } @@ -43,17 +46,13 @@ impl grant_player_progression_experience_and_return for super::RemoteProcedures &self, input: PlayerProgressionGrantInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ) { - self.imp - .invoke_procedure_with_callback::<_, PlayerProgressionProcedureResult>( - "grant_player_progression_experience_and_return", - GrantPlayerProgressionExperienceAndReturnArgs { input }, - __callback, - ); + self.imp.invoke_procedure_with_callback::<_, PlayerProgressionProcedureResult>( + "grant_player_progression_experience_and_return", + GrantPlayerProgressionExperienceAndReturnArgs { input, }, + __callback, + ); } } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/grant_player_progression_experience_reducer.rs b/server-rs/crates/spacetime-client/src/module_bindings/grant_player_progression_experience_reducer.rs index 83b48cf7..324bb57f 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/grant_player_progression_experience_reducer.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/grant_player_progression_experience_reducer.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::player_progression_grant_input_type::PlayerProgressionGrantInput; @@ -14,8 +19,10 @@ pub(super) struct GrantPlayerProgressionExperienceArgs { impl From for super::Reducer { fn from(args: GrantPlayerProgressionExperienceArgs) -> Self { - Self::GrantPlayerProgressionExperience { input: args.input } - } + Self::GrantPlayerProgressionExperience { + input: args.input, +} +} } impl __sdk::InModule for GrantPlayerProgressionExperienceArgs { @@ -33,11 +40,9 @@ pub trait grant_player_progression_experience { /// The reducer will run asynchronously in the future, /// and this method provides no way to listen for its completion status. /// /// Use [`grant_player_progression_experience:grant_player_progression_experience_then`] to run a callback after the reducer completes. - fn grant_player_progression_experience( - &self, - input: PlayerProgressionGrantInput, - ) -> __sdk::Result<()> { - self.grant_player_progression_experience_then(input, |_, _| {}) + fn grant_player_progression_experience(&self, input: PlayerProgressionGrantInput, +) -> __sdk::Result<()> { + self.grant_player_progression_experience_then(input, |_, _| {}) } /// Request that the remote module invoke the reducer `grant_player_progression_experience` to run as soon as possible, @@ -65,7 +70,7 @@ impl grant_player_progression_experience for super::RemoteReducers { + Send + 'static, ) -> __sdk::Result<()> { - self.imp - .invoke_reducer_with_callback(GrantPlayerProgressionExperienceArgs { input }, callback) + self.imp.invoke_reducer_with_callback(GrantPlayerProgressionExperienceArgs { input, }, callback) } } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/import_auth_store_snapshot_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/import_auth_store_snapshot_procedure.rs index 2821fddc..656b962b 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/import_auth_store_snapshot_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/import_auth_store_snapshot_procedure.rs @@ -2,13 +2,20 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::auth_store_snapshot_import_procedure_result_type::AuthStoreSnapshotImportProcedureResult; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] -struct ImportAuthStoreSnapshotArgs {} + struct ImportAuthStoreSnapshotArgs { + } + impl __sdk::InModule for ImportAuthStoreSnapshotArgs { type Module = super::RemoteModule; @@ -19,36 +26,28 @@ impl __sdk::InModule for ImportAuthStoreSnapshotArgs { /// /// Implemented for [`super::RemoteProcedures`]. pub trait import_auth_store_snapshot { - fn import_auth_store_snapshot(&self) { - self.import_auth_store_snapshot_then(|_, _| {}); + fn import_auth_store_snapshot(&self, ) { + self.import_auth_store_snapshot_then( |_, _| {}); } fn import_auth_store_snapshot_then( &self, - - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ); } impl import_auth_store_snapshot for super::RemoteProcedures { fn import_auth_store_snapshot_then( &self, - - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ) { - self.imp - .invoke_procedure_with_callback::<_, AuthStoreSnapshotImportProcedureResult>( - "import_auth_store_snapshot", - ImportAuthStoreSnapshotArgs {}, - __callback, - ); + self.imp.invoke_procedure_with_callback::<_, AuthStoreSnapshotImportProcedureResult>( + "import_auth_store_snapshot", + ImportAuthStoreSnapshotArgs { }, + __callback, + ); } } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/import_database_migration_from_file_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/import_database_migration_from_file_procedure.rs index 7b2322ee..926f08ac 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/import_database_migration_from_file_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/import_database_migration_from_file_procedure.rs @@ -2,17 +2,23 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; -use super::database_migration_import_input_type::DatabaseMigrationImportInput; use super::database_migration_procedure_result_type::DatabaseMigrationProcedureResult; +use super::database_migration_import_input_type::DatabaseMigrationImportInput; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] -struct ImportDatabaseMigrationFromFileArgs { + struct ImportDatabaseMigrationFromFileArgs { pub input: DatabaseMigrationImportInput, } + impl __sdk::InModule for ImportDatabaseMigrationFromFileArgs { type Module = super::RemoteModule; } @@ -22,19 +28,16 @@ impl __sdk::InModule for ImportDatabaseMigrationFromFileArgs { /// /// Implemented for [`super::RemoteProcedures`]. pub trait import_database_migration_from_file { - fn import_database_migration_from_file(&self, input: DatabaseMigrationImportInput) { - self.import_database_migration_from_file_then(input, |_, _| {}); + fn import_database_migration_from_file(&self, input: DatabaseMigrationImportInput, +) { + self.import_database_migration_from_file_then(input, |_, _| {}); } fn import_database_migration_from_file_then( &self, input: DatabaseMigrationImportInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ); } @@ -43,17 +46,13 @@ impl import_database_migration_from_file for super::RemoteProcedures { &self, input: DatabaseMigrationImportInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ) { - self.imp - .invoke_procedure_with_callback::<_, DatabaseMigrationProcedureResult>( - "import_database_migration_from_file", - ImportDatabaseMigrationFromFileArgs { input }, - __callback, - ); + self.imp.invoke_procedure_with_callback::<_, DatabaseMigrationProcedureResult>( + "import_database_migration_from_file", + ImportDatabaseMigrationFromFileArgs { input, }, + __callback, + ); } } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/import_database_migration_incremental_from_file_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/import_database_migration_incremental_from_file_procedure.rs index 2fc31804..e522d0cf 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/import_database_migration_incremental_from_file_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/import_database_migration_incremental_from_file_procedure.rs @@ -2,17 +2,23 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; -use super::database_migration_import_input_type::DatabaseMigrationImportInput; use super::database_migration_procedure_result_type::DatabaseMigrationProcedureResult; +use super::database_migration_import_input_type::DatabaseMigrationImportInput; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] -struct ImportDatabaseMigrationIncrementalFromFileArgs { + struct ImportDatabaseMigrationIncrementalFromFileArgs { pub input: DatabaseMigrationImportInput, } + impl __sdk::InModule for ImportDatabaseMigrationIncrementalFromFileArgs { type Module = super::RemoteModule; } @@ -22,19 +28,16 @@ impl __sdk::InModule for ImportDatabaseMigrationIncrementalFromFileArgs { /// /// Implemented for [`super::RemoteProcedures`]. pub trait import_database_migration_incremental_from_file { - fn import_database_migration_incremental_from_file(&self, input: DatabaseMigrationImportInput) { - self.import_database_migration_incremental_from_file_then(input, |_, _| {}); + fn import_database_migration_incremental_from_file(&self, input: DatabaseMigrationImportInput, +) { + self.import_database_migration_incremental_from_file_then(input, |_, _| {}); } fn import_database_migration_incremental_from_file_then( &self, input: DatabaseMigrationImportInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ); } @@ -43,17 +46,13 @@ impl import_database_migration_incremental_from_file for super::RemoteProcedures &self, input: DatabaseMigrationImportInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ) { - self.imp - .invoke_procedure_with_callback::<_, DatabaseMigrationProcedureResult>( - "import_database_migration_incremental_from_file", - ImportDatabaseMigrationIncrementalFromFileArgs { input }, - __callback, - ); + self.imp.invoke_procedure_with_callback::<_, DatabaseMigrationProcedureResult>( + "import_database_migration_incremental_from_file", + ImportDatabaseMigrationIncrementalFromFileArgs { input, }, + __callback, + ); } } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/inventory_container_kind_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/inventory_container_kind_type.rs index 45522ce7..23de3559 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/inventory_container_kind_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/inventory_container_kind_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -11,8 +16,12 @@ pub enum InventoryContainerKind { Backpack, Equipment, + } + + impl __sdk::InModule for InventoryContainerKind { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/inventory_equipment_slot_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/inventory_equipment_slot_type.rs index e2055f6a..81154f9f 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/inventory_equipment_slot_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/inventory_equipment_slot_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -13,8 +18,12 @@ pub enum InventoryEquipmentSlot { Armor, Relic, + } + + impl __sdk::InModule for InventoryEquipmentSlot { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/inventory_item_rarity_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/inventory_item_rarity_type.rs index 85b46090..e277c402 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/inventory_item_rarity_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/inventory_item_rarity_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -17,8 +22,12 @@ pub enum InventoryItemRarity { Epic, Legendary, + } + + impl __sdk::InModule for InventoryItemRarity { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/inventory_item_snapshot_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/inventory_item_snapshot_type.rs index 3769735e..7e962e4d 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/inventory_item_snapshot_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/inventory_item_snapshot_type.rs @@ -2,10 +2,15 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; -use super::inventory_equipment_slot_type::InventoryEquipmentSlot; use super::inventory_item_rarity_type::InventoryItemRarity; +use super::inventory_equipment_slot_type::InventoryEquipmentSlot; use super::inventory_item_source_kind_type::InventoryItemSourceKind; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] @@ -14,17 +19,19 @@ pub struct InventoryItemSnapshot { pub item_id: String, pub category: String, pub name: String, - pub description: Option, + pub description: Option::, pub quantity: u32, pub rarity: InventoryItemRarity, - pub tags: Vec, + pub tags: Vec::, pub stackable: bool, pub stack_key: String, - pub equipment_slot_id: Option, + pub equipment_slot_id: Option::, pub source_kind: InventoryItemSourceKind, - pub source_reference_id: Option, + pub source_reference_id: Option::, } + impl __sdk::InModule for InventoryItemSnapshot { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/inventory_item_source_kind_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/inventory_item_source_kind_type.rs index 1d3961bc..5303c581 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/inventory_item_source_kind_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/inventory_item_source_kind_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -25,8 +30,12 @@ pub enum InventoryItemSourceKind { ForgeReforge, ManualPatch, + } + + impl __sdk::InModule for InventoryItemSourceKind { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/inventory_mutation_input_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/inventory_mutation_input_type.rs index 3540ad3b..c696e383 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/inventory_mutation_input_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/inventory_mutation_input_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::inventory_mutation_type::InventoryMutation; @@ -11,12 +16,14 @@ use super::inventory_mutation_type::InventoryMutation; pub struct InventoryMutationInput { pub mutation_id: String, pub runtime_session_id: String, - pub story_session_id: Option, + pub story_session_id: Option::, pub actor_user_id: String, pub mutation: InventoryMutation, pub updated_at_micros: i64, } + impl __sdk::InModule for InventoryMutationInput { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/inventory_mutation_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/inventory_mutation_type.rs index 3d0d0e4f..d7b64029 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/inventory_mutation_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/inventory_mutation_type.rs @@ -2,11 +2,16 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; +use super::grant_inventory_item_input_type::GrantInventoryItemInput; use super::consume_inventory_item_input_type::ConsumeInventoryItemInput; use super::equip_inventory_item_input_type::EquipInventoryItemInput; -use super::grant_inventory_item_input_type::GrantInventoryItemInput; use super::unequip_inventory_item_input_type::UnequipInventoryItemInput; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] @@ -19,8 +24,12 @@ pub enum InventoryMutation { EquipItem(EquipInventoryItemInput), UnequipItem(UnequipInventoryItemInput), + } + + impl __sdk::InModule for InventoryMutation { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/inventory_slot_snapshot_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/inventory_slot_snapshot_type.rs index f20e5451..4ce26de5 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/inventory_slot_snapshot_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/inventory_slot_snapshot_type.rs @@ -2,38 +2,45 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; -use super::inventory_container_kind_type::InventoryContainerKind; -use super::inventory_equipment_slot_type::InventoryEquipmentSlot; use super::inventory_item_rarity_type::InventoryItemRarity; +use super::inventory_equipment_slot_type::InventoryEquipmentSlot; use super::inventory_item_source_kind_type::InventoryItemSourceKind; +use super::inventory_container_kind_type::InventoryContainerKind; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] pub struct InventorySlotSnapshot { pub slot_id: String, pub runtime_session_id: String, - pub story_session_id: Option, + pub story_session_id: Option::, pub actor_user_id: String, pub container_kind: InventoryContainerKind, pub slot_key: String, pub item_id: String, pub category: String, pub name: String, - pub description: Option, + pub description: Option::, pub quantity: u32, pub rarity: InventoryItemRarity, - pub tags: Vec, + pub tags: Vec::, pub stackable: bool, pub stack_key: String, - pub equipment_slot_id: Option, + pub equipment_slot_id: Option::, pub source_kind: InventoryItemSourceKind, - pub source_reference_id: Option, + pub source_reference_id: Option::, pub created_at_micros: i64, pub updated_at_micros: i64, } + impl __sdk::InModule for InventorySlotSnapshot { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/inventory_slot_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/inventory_slot_type.rs index fcfbab6a..c824c438 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/inventory_slot_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/inventory_slot_type.rs @@ -2,65 +2,71 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; -use super::inventory_container_kind_type::InventoryContainerKind; -use super::inventory_equipment_slot_type::InventoryEquipmentSlot; use super::inventory_item_rarity_type::InventoryItemRarity; +use super::inventory_equipment_slot_type::InventoryEquipmentSlot; use super::inventory_item_source_kind_type::InventoryItemSourceKind; +use super::inventory_container_kind_type::InventoryContainerKind; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] pub struct InventorySlot { pub slot_id: String, pub runtime_session_id: String, - pub story_session_id: Option, + pub story_session_id: Option::, pub actor_user_id: String, pub container_kind: InventoryContainerKind, pub slot_key: String, pub item_id: String, pub category: String, pub name: String, - pub description: Option, + pub description: Option::, pub quantity: u32, pub rarity: InventoryItemRarity, - pub tags: Vec, + pub tags: Vec::, pub stackable: bool, pub stack_key: String, - pub equipment_slot_id: Option, + pub equipment_slot_id: Option::, pub source_kind: InventoryItemSourceKind, - pub source_reference_id: Option, + pub source_reference_id: Option::, pub created_at: __sdk::Timestamp, pub updated_at: __sdk::Timestamp, } + impl __sdk::InModule for InventorySlot { type Module = super::RemoteModule; } + /// Column accessor struct for the table `InventorySlot`. /// /// Provides typed access to columns for query building. pub struct InventorySlotCols { pub slot_id: __sdk::__query_builder::Col, pub runtime_session_id: __sdk::__query_builder::Col, - pub story_session_id: __sdk::__query_builder::Col>, + pub story_session_id: __sdk::__query_builder::Col>, pub actor_user_id: __sdk::__query_builder::Col, pub container_kind: __sdk::__query_builder::Col, pub slot_key: __sdk::__query_builder::Col, pub item_id: __sdk::__query_builder::Col, pub category: __sdk::__query_builder::Col, pub name: __sdk::__query_builder::Col, - pub description: __sdk::__query_builder::Col>, + pub description: __sdk::__query_builder::Col>, pub quantity: __sdk::__query_builder::Col, pub rarity: __sdk::__query_builder::Col, - pub tags: __sdk::__query_builder::Col>, + pub tags: __sdk::__query_builder::Col>, pub stackable: __sdk::__query_builder::Col, pub stack_key: __sdk::__query_builder::Col, - pub equipment_slot_id: - __sdk::__query_builder::Col>, + pub equipment_slot_id: __sdk::__query_builder::Col>, pub source_kind: __sdk::__query_builder::Col, - pub source_reference_id: __sdk::__query_builder::Col>, + pub source_reference_id: __sdk::__query_builder::Col>, pub created_at: __sdk::__query_builder::Col, pub updated_at: __sdk::__query_builder::Col, } @@ -86,12 +92,10 @@ impl __sdk::__query_builder::HasCols for InventorySlot { stack_key: __sdk::__query_builder::Col::new(table_name, "stack_key"), equipment_slot_id: __sdk::__query_builder::Col::new(table_name, "equipment_slot_id"), source_kind: __sdk::__query_builder::Col::new(table_name, "source_kind"), - source_reference_id: __sdk::__query_builder::Col::new( - table_name, - "source_reference_id", - ), + source_reference_id: __sdk::__query_builder::Col::new(table_name, "source_reference_id"), created_at: __sdk::__query_builder::Col::new(table_name, "created_at"), updated_at: __sdk::__query_builder::Col::new(table_name, "updated_at"), + } } } @@ -112,13 +116,12 @@ impl __sdk::__query_builder::HasIxCols for InventorySlot { InventorySlotIxCols { actor_user_id: __sdk::__query_builder::IxCol::new(table_name, "actor_user_id"), item_id: __sdk::__query_builder::IxCol::new(table_name, "item_id"), - runtime_session_id: __sdk::__query_builder::IxCol::new( - table_name, - "runtime_session_id", - ), + runtime_session_id: __sdk::__query_builder::IxCol::new(table_name, "runtime_session_id"), slot_id: __sdk::__query_builder::IxCol::new(table_name, "slot_id"), + } } } impl __sdk::__query_builder::CanBeLookupTable for InventorySlot {} + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/list_asset_history_and_return_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/list_asset_history_and_return_procedure.rs index bcc2a742..28f04f52 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/list_asset_history_and_return_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/list_asset_history_and_return_procedure.rs @@ -2,17 +2,23 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::asset_history_list_input_type::AssetHistoryListInput; use super::asset_history_list_result_type::AssetHistoryListResult; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] -struct ListAssetHistoryAndReturnArgs { + struct ListAssetHistoryAndReturnArgs { pub input: AssetHistoryListInput, } + impl __sdk::InModule for ListAssetHistoryAndReturnArgs { type Module = super::RemoteModule; } @@ -22,19 +28,16 @@ impl __sdk::InModule for ListAssetHistoryAndReturnArgs { /// /// Implemented for [`super::RemoteProcedures`]. pub trait list_asset_history_and_return { - fn list_asset_history_and_return(&self, input: AssetHistoryListInput) { - self.list_asset_history_and_return_then(input, |_, _| {}); + fn list_asset_history_and_return(&self, input: AssetHistoryListInput, +) { + self.list_asset_history_and_return_then(input, |_, _| {}); } fn list_asset_history_and_return_then( &self, input: AssetHistoryListInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ); } @@ -43,17 +46,13 @@ impl list_asset_history_and_return for super::RemoteProcedures { &self, input: AssetHistoryListInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ) { - self.imp - .invoke_procedure_with_callback::<_, AssetHistoryListResult>( - "list_asset_history_and_return", - ListAssetHistoryAndReturnArgs { input }, - __callback, - ); + self.imp.invoke_procedure_with_callback::<_, AssetHistoryListResult>( + "list_asset_history_and_return", + ListAssetHistoryAndReturnArgs { input, }, + __callback, + ); } } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/list_big_fish_works_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/list_big_fish_works_procedure.rs index 8e4c21ba..8a7ae591 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/list_big_fish_works_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/list_big_fish_works_procedure.rs @@ -2,17 +2,23 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; -use super::big_fish_works_list_input_type::BigFishWorksListInput; use super::big_fish_works_procedure_result_type::BigFishWorksProcedureResult; +use super::big_fish_works_list_input_type::BigFishWorksListInput; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] -struct ListBigFishWorksArgs { + struct ListBigFishWorksArgs { pub input: BigFishWorksListInput, } + impl __sdk::InModule for ListBigFishWorksArgs { type Module = super::RemoteModule; } @@ -22,19 +28,16 @@ impl __sdk::InModule for ListBigFishWorksArgs { /// /// Implemented for [`super::RemoteProcedures`]. pub trait list_big_fish_works { - fn list_big_fish_works(&self, input: BigFishWorksListInput) { - self.list_big_fish_works_then(input, |_, _| {}); + fn list_big_fish_works(&self, input: BigFishWorksListInput, +) { + self.list_big_fish_works_then(input, |_, _| {}); } fn list_big_fish_works_then( &self, input: BigFishWorksListInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ); } @@ -43,17 +46,13 @@ impl list_big_fish_works for super::RemoteProcedures { &self, input: BigFishWorksListInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ) { - self.imp - .invoke_procedure_with_callback::<_, BigFishWorksProcedureResult>( - "list_big_fish_works", - ListBigFishWorksArgs { input }, - __callback, - ); + self.imp.invoke_procedure_with_callback::<_, BigFishWorksProcedureResult>( + "list_big_fish_works", + ListBigFishWorksArgs { input, }, + __callback, + ); } } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/list_custom_world_gallery_entries_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/list_custom_world_gallery_entries_procedure.rs index 63ee059f..507757cf 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/list_custom_world_gallery_entries_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/list_custom_world_gallery_entries_procedure.rs @@ -2,13 +2,20 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::custom_world_gallery_list_result_type::CustomWorldGalleryListResult; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] -struct ListCustomWorldGalleryEntriesArgs {} + struct ListCustomWorldGalleryEntriesArgs { + } + impl __sdk::InModule for ListCustomWorldGalleryEntriesArgs { type Module = super::RemoteModule; @@ -19,36 +26,28 @@ impl __sdk::InModule for ListCustomWorldGalleryEntriesArgs { /// /// Implemented for [`super::RemoteProcedures`]. pub trait list_custom_world_gallery_entries { - fn list_custom_world_gallery_entries(&self) { - self.list_custom_world_gallery_entries_then(|_, _| {}); + fn list_custom_world_gallery_entries(&self, ) { + self.list_custom_world_gallery_entries_then( |_, _| {}); } fn list_custom_world_gallery_entries_then( &self, - - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ); } impl list_custom_world_gallery_entries for super::RemoteProcedures { fn list_custom_world_gallery_entries_then( &self, - - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ) { - self.imp - .invoke_procedure_with_callback::<_, CustomWorldGalleryListResult>( - "list_custom_world_gallery_entries", - ListCustomWorldGalleryEntriesArgs {}, - __callback, - ); + self.imp.invoke_procedure_with_callback::<_, CustomWorldGalleryListResult>( + "list_custom_world_gallery_entries", + ListCustomWorldGalleryEntriesArgs { }, + __callback, + ); } } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/list_custom_world_profiles_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/list_custom_world_profiles_procedure.rs index f8834945..2a591015 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/list_custom_world_profiles_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/list_custom_world_profiles_procedure.rs @@ -2,17 +2,23 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; -use super::custom_world_profile_list_input_type::CustomWorldProfileListInput; use super::custom_world_profile_list_result_type::CustomWorldProfileListResult; +use super::custom_world_profile_list_input_type::CustomWorldProfileListInput; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] -struct ListCustomWorldProfilesArgs { + struct ListCustomWorldProfilesArgs { pub input: CustomWorldProfileListInput, } + impl __sdk::InModule for ListCustomWorldProfilesArgs { type Module = super::RemoteModule; } @@ -22,19 +28,16 @@ impl __sdk::InModule for ListCustomWorldProfilesArgs { /// /// Implemented for [`super::RemoteProcedures`]. pub trait list_custom_world_profiles { - fn list_custom_world_profiles(&self, input: CustomWorldProfileListInput) { - self.list_custom_world_profiles_then(input, |_, _| {}); + fn list_custom_world_profiles(&self, input: CustomWorldProfileListInput, +) { + self.list_custom_world_profiles_then(input, |_, _| {}); } fn list_custom_world_profiles_then( &self, input: CustomWorldProfileListInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ); } @@ -43,17 +46,13 @@ impl list_custom_world_profiles for super::RemoteProcedures { &self, input: CustomWorldProfileListInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ) { - self.imp - .invoke_procedure_with_callback::<_, CustomWorldProfileListResult>( - "list_custom_world_profiles", - ListCustomWorldProfilesArgs { input }, - __callback, - ); + self.imp.invoke_procedure_with_callback::<_, CustomWorldProfileListResult>( + "list_custom_world_profiles", + ListCustomWorldProfilesArgs { input, }, + __callback, + ); } } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/list_custom_world_works_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/list_custom_world_works_procedure.rs index d469f660..585a6edf 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/list_custom_world_works_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/list_custom_world_works_procedure.rs @@ -2,17 +2,23 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; -use super::custom_world_works_list_input_type::CustomWorldWorksListInput; use super::custom_world_works_list_result_type::CustomWorldWorksListResult; +use super::custom_world_works_list_input_type::CustomWorldWorksListInput; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] -struct ListCustomWorldWorksArgs { + struct ListCustomWorldWorksArgs { pub input: CustomWorldWorksListInput, } + impl __sdk::InModule for ListCustomWorldWorksArgs { type Module = super::RemoteModule; } @@ -22,19 +28,16 @@ impl __sdk::InModule for ListCustomWorldWorksArgs { /// /// Implemented for [`super::RemoteProcedures`]. pub trait list_custom_world_works { - fn list_custom_world_works(&self, input: CustomWorldWorksListInput) { - self.list_custom_world_works_then(input, |_, _| {}); + fn list_custom_world_works(&self, input: CustomWorldWorksListInput, +) { + self.list_custom_world_works_then(input, |_, _| {}); } fn list_custom_world_works_then( &self, input: CustomWorldWorksListInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ); } @@ -43,17 +46,13 @@ impl list_custom_world_works for super::RemoteProcedures { &self, input: CustomWorldWorksListInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ) { - self.imp - .invoke_procedure_with_callback::<_, CustomWorldWorksListResult>( - "list_custom_world_works", - ListCustomWorldWorksArgs { input }, - __callback, - ); + self.imp.invoke_procedure_with_callback::<_, CustomWorldWorksListResult>( + "list_custom_world_works", + ListCustomWorldWorksArgs { input, }, + __callback, + ); } } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/list_platform_browse_history_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/list_platform_browse_history_procedure.rs index 0d368a99..0c10f6c3 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/list_platform_browse_history_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/list_platform_browse_history_procedure.rs @@ -2,17 +2,23 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; -use super::runtime_browse_history_list_input_type::RuntimeBrowseHistoryListInput; use super::runtime_browse_history_procedure_result_type::RuntimeBrowseHistoryProcedureResult; +use super::runtime_browse_history_list_input_type::RuntimeBrowseHistoryListInput; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] -struct ListPlatformBrowseHistoryArgs { + struct ListPlatformBrowseHistoryArgs { pub input: RuntimeBrowseHistoryListInput, } + impl __sdk::InModule for ListPlatformBrowseHistoryArgs { type Module = super::RemoteModule; } @@ -22,19 +28,16 @@ impl __sdk::InModule for ListPlatformBrowseHistoryArgs { /// /// Implemented for [`super::RemoteProcedures`]. pub trait list_platform_browse_history { - fn list_platform_browse_history(&self, input: RuntimeBrowseHistoryListInput) { - self.list_platform_browse_history_then(input, |_, _| {}); + fn list_platform_browse_history(&self, input: RuntimeBrowseHistoryListInput, +) { + self.list_platform_browse_history_then(input, |_, _| {}); } fn list_platform_browse_history_then( &self, input: RuntimeBrowseHistoryListInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ); } @@ -43,17 +46,13 @@ impl list_platform_browse_history for super::RemoteProcedures { &self, input: RuntimeBrowseHistoryListInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ) { - self.imp - .invoke_procedure_with_callback::<_, RuntimeBrowseHistoryProcedureResult>( - "list_platform_browse_history", - ListPlatformBrowseHistoryArgs { input }, - __callback, - ); + self.imp.invoke_procedure_with_callback::<_, RuntimeBrowseHistoryProcedureResult>( + "list_platform_browse_history", + ListPlatformBrowseHistoryArgs { input, }, + __callback, + ); } } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/list_profile_save_archives_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/list_profile_save_archives_procedure.rs index 31c214bb..6476c3ac 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/list_profile_save_archives_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/list_profile_save_archives_procedure.rs @@ -2,17 +2,23 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::runtime_profile_save_archive_list_input_type::RuntimeProfileSaveArchiveListInput; use super::runtime_profile_save_archive_procedure_result_type::RuntimeProfileSaveArchiveProcedureResult; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] -struct ListProfileSaveArchivesArgs { + struct ListProfileSaveArchivesArgs { pub input: RuntimeProfileSaveArchiveListInput, } + impl __sdk::InModule for ListProfileSaveArchivesArgs { type Module = super::RemoteModule; } @@ -22,19 +28,16 @@ impl __sdk::InModule for ListProfileSaveArchivesArgs { /// /// Implemented for [`super::RemoteProcedures`]. pub trait list_profile_save_archives { - fn list_profile_save_archives(&self, input: RuntimeProfileSaveArchiveListInput) { - self.list_profile_save_archives_then(input, |_, _| {}); + fn list_profile_save_archives(&self, input: RuntimeProfileSaveArchiveListInput, +) { + self.list_profile_save_archives_then(input, |_, _| {}); } fn list_profile_save_archives_then( &self, input: RuntimeProfileSaveArchiveListInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ); } @@ -43,17 +46,13 @@ impl list_profile_save_archives for super::RemoteProcedures { &self, input: RuntimeProfileSaveArchiveListInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ) { - self.imp - .invoke_procedure_with_callback::<_, RuntimeProfileSaveArchiveProcedureResult>( - "list_profile_save_archives", - ListProfileSaveArchivesArgs { input }, - __callback, - ); + self.imp.invoke_procedure_with_callback::<_, RuntimeProfileSaveArchiveProcedureResult>( + "list_profile_save_archives", + ListProfileSaveArchivesArgs { input, }, + __callback, + ); } } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/list_profile_wallet_ledger_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/list_profile_wallet_ledger_procedure.rs index 23496701..4ae618f0 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/list_profile_wallet_ledger_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/list_profile_wallet_ledger_procedure.rs @@ -2,17 +2,23 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::runtime_profile_wallet_ledger_list_input_type::RuntimeProfileWalletLedgerListInput; use super::runtime_profile_wallet_ledger_procedure_result_type::RuntimeProfileWalletLedgerProcedureResult; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] -struct ListProfileWalletLedgerArgs { + struct ListProfileWalletLedgerArgs { pub input: RuntimeProfileWalletLedgerListInput, } + impl __sdk::InModule for ListProfileWalletLedgerArgs { type Module = super::RemoteModule; } @@ -22,19 +28,16 @@ impl __sdk::InModule for ListProfileWalletLedgerArgs { /// /// Implemented for [`super::RemoteProcedures`]. pub trait list_profile_wallet_ledger { - fn list_profile_wallet_ledger(&self, input: RuntimeProfileWalletLedgerListInput) { - self.list_profile_wallet_ledger_then(input, |_, _| {}); + fn list_profile_wallet_ledger(&self, input: RuntimeProfileWalletLedgerListInput, +) { + self.list_profile_wallet_ledger_then(input, |_, _| {}); } fn list_profile_wallet_ledger_then( &self, input: RuntimeProfileWalletLedgerListInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ); } @@ -43,17 +46,13 @@ impl list_profile_wallet_ledger for super::RemoteProcedures { &self, input: RuntimeProfileWalletLedgerListInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ) { - self.imp - .invoke_procedure_with_callback::<_, RuntimeProfileWalletLedgerProcedureResult>( - "list_profile_wallet_ledger", - ListProfileWalletLedgerArgs { input }, - __callback, - ); + self.imp.invoke_procedure_with_callback::<_, RuntimeProfileWalletLedgerProcedureResult>( + "list_profile_wallet_ledger", + ListProfileWalletLedgerArgs { input, }, + __callback, + ); } } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/list_puzzle_gallery_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/list_puzzle_gallery_procedure.rs index 553b8e08..329e53a4 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/list_puzzle_gallery_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/list_puzzle_gallery_procedure.rs @@ -2,13 +2,20 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::puzzle_works_procedure_result_type::PuzzleWorksProcedureResult; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] -struct ListPuzzleGalleryArgs {} + struct ListPuzzleGalleryArgs { + } + impl __sdk::InModule for ListPuzzleGalleryArgs { type Module = super::RemoteModule; @@ -19,36 +26,28 @@ impl __sdk::InModule for ListPuzzleGalleryArgs { /// /// Implemented for [`super::RemoteProcedures`]. pub trait list_puzzle_gallery { - fn list_puzzle_gallery(&self) { - self.list_puzzle_gallery_then(|_, _| {}); + fn list_puzzle_gallery(&self, ) { + self.list_puzzle_gallery_then( |_, _| {}); } fn list_puzzle_gallery_then( &self, - - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ); } impl list_puzzle_gallery for super::RemoteProcedures { fn list_puzzle_gallery_then( &self, - - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ) { - self.imp - .invoke_procedure_with_callback::<_, PuzzleWorksProcedureResult>( - "list_puzzle_gallery", - ListPuzzleGalleryArgs {}, - __callback, - ); + self.imp.invoke_procedure_with_callback::<_, PuzzleWorksProcedureResult>( + "list_puzzle_gallery", + ListPuzzleGalleryArgs { }, + __callback, + ); } } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/list_puzzle_works_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/list_puzzle_works_procedure.rs index 844d16df..980a3698 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/list_puzzle_works_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/list_puzzle_works_procedure.rs @@ -2,17 +2,23 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; -use super::puzzle_works_list_input_type::PuzzleWorksListInput; use super::puzzle_works_procedure_result_type::PuzzleWorksProcedureResult; +use super::puzzle_works_list_input_type::PuzzleWorksListInput; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] -struct ListPuzzleWorksArgs { + struct ListPuzzleWorksArgs { pub input: PuzzleWorksListInput, } + impl __sdk::InModule for ListPuzzleWorksArgs { type Module = super::RemoteModule; } @@ -22,19 +28,16 @@ impl __sdk::InModule for ListPuzzleWorksArgs { /// /// Implemented for [`super::RemoteProcedures`]. pub trait list_puzzle_works { - fn list_puzzle_works(&self, input: PuzzleWorksListInput) { - self.list_puzzle_works_then(input, |_, _| {}); + fn list_puzzle_works(&self, input: PuzzleWorksListInput, +) { + self.list_puzzle_works_then(input, |_, _| {}); } fn list_puzzle_works_then( &self, input: PuzzleWorksListInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ); } @@ -43,17 +46,13 @@ impl list_puzzle_works for super::RemoteProcedures { &self, input: PuzzleWorksListInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ) { - self.imp - .invoke_procedure_with_callback::<_, PuzzleWorksProcedureResult>( - "list_puzzle_works", - ListPuzzleWorksArgs { input }, - __callback, - ); + self.imp.invoke_procedure_with_callback::<_, PuzzleWorksProcedureResult>( + "list_puzzle_works", + ListPuzzleWorksArgs { input, }, + __callback, + ); } } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/mod.rs b/server-rs/crates/spacetime-client/src/module_bindings/mod.rs index 6edd0e7c..557be80a 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/mod.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/mod.rs @@ -4,96 +4,97 @@ // This was generated using spacetimedb cli version 2.1.0 (commit 6981f48b4bc1a71c8dd9bdfe5a2c343f6370243d). #![allow(unused, clippy::all)] -use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws}; +use spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; -pub mod accept_quest_reducer; -pub mod acknowledge_quest_completion_reducer; -pub mod admin_disable_profile_redeem_code_procedure; -pub mod admin_upsert_profile_redeem_code_procedure; -pub mod advance_puzzle_next_level_procedure; +pub mod ai_result_reference_type; pub mod ai_result_reference_input_type; pub mod ai_result_reference_kind_type; pub mod ai_result_reference_snapshot_type; -pub mod ai_result_reference_type; pub mod ai_stage_completion_input_type; +pub mod ai_task_type; pub mod ai_task_cancel_input_type; pub mod ai_task_create_input_type; +pub mod ai_task_event_type; +pub mod ai_task_event_kind_type; pub mod ai_task_failure_input_type; pub mod ai_task_finish_input_type; pub mod ai_task_kind_type; pub mod ai_task_procedure_result_type; pub mod ai_task_snapshot_type; +pub mod ai_task_stage_type; pub mod ai_task_stage_blueprint_type; pub mod ai_task_stage_kind_type; pub mod ai_task_stage_snapshot_type; pub mod ai_task_stage_start_input_type; pub mod ai_task_stage_status_type; -pub mod ai_task_stage_type; pub mod ai_task_start_input_type; pub mod ai_task_status_type; -pub mod ai_task_type; +pub mod ai_text_chunk_type; pub mod ai_text_chunk_append_input_type; pub mod ai_text_chunk_snapshot_type; -pub mod ai_text_chunk_type; -pub mod append_ai_text_chunk_and_return_procedure; -pub mod apply_chapter_progression_ledger_entry_and_return_procedure; -pub mod apply_chapter_progression_ledger_entry_reducer; -pub mod apply_inventory_mutation_reducer; -pub mod apply_quest_signal_reducer; +pub mod asset_entity_binding_type; pub mod asset_entity_binding_input_type; pub mod asset_entity_binding_procedure_result_type; pub mod asset_entity_binding_snapshot_type; -pub mod asset_entity_binding_type; pub mod asset_history_entry_snapshot_type; pub mod asset_history_list_input_type; pub mod asset_history_list_result_type; +pub mod asset_object_type; pub mod asset_object_access_policy_type; pub mod asset_object_procedure_result_type; -pub mod asset_object_type; pub mod asset_object_upsert_input_type; pub mod asset_object_upsert_snapshot_type; -pub mod attach_ai_result_reference_and_return_procedure; pub mod auth_identity_type; +pub mod auth_store_snapshot_type; pub mod auth_store_snapshot_import_procedure_result_type; pub mod auth_store_snapshot_import_record_type; pub mod auth_store_snapshot_procedure_result_type; pub mod auth_store_snapshot_record_type; -pub mod auth_store_snapshot_type; pub mod auth_store_snapshot_upsert_input_type; -pub mod authorize_database_migration_operator_procedure; pub mod battle_mode_type; +pub mod battle_state_type; pub mod battle_state_input_type; pub mod battle_state_procedure_result_type; pub mod battle_state_query_input_type; pub mod battle_state_snapshot_type; -pub mod battle_state_type; pub mod battle_status_type; -pub mod begin_story_session_and_return_procedure; -pub mod begin_story_session_reducer; +pub mod big_fish_agent_message_type; pub mod big_fish_agent_message_kind_type; pub mod big_fish_agent_message_role_type; pub mod big_fish_agent_message_snapshot_type; -pub mod big_fish_agent_message_type; pub mod big_fish_anchor_item_type; pub mod big_fish_anchor_pack_type; pub mod big_fish_anchor_status_type; pub mod big_fish_asset_coverage_type; pub mod big_fish_asset_generate_input_type; pub mod big_fish_asset_kind_type; -pub mod big_fish_asset_slot_snapshot_type; pub mod big_fish_asset_slot_type; +pub mod big_fish_asset_slot_snapshot_type; pub mod big_fish_asset_status_type; pub mod big_fish_background_blueprint_type; pub mod big_fish_creation_session_type; pub mod big_fish_creation_stage_type; pub mod big_fish_draft_compile_input_type; +pub mod big_fish_event_type; +pub mod big_fish_event_kind_type; pub mod big_fish_game_draft_type; +pub mod big_fish_input_submit_input_type; pub mod big_fish_level_blueprint_type; pub mod big_fish_message_finalize_input_type; pub mod big_fish_message_submit_input_type; pub mod big_fish_play_record_input_type; pub mod big_fish_publish_input_type; +pub mod big_fish_run_get_input_type; +pub mod big_fish_run_procedure_result_type; +pub mod big_fish_run_start_input_type; +pub mod big_fish_run_status_type; pub mod big_fish_runtime_params_type; +pub mod big_fish_runtime_run_type; pub mod big_fish_session_create_input_type; pub mod big_fish_session_get_input_type; pub mod big_fish_session_procedure_result_type; @@ -101,74 +102,51 @@ pub mod big_fish_session_snapshot_type; pub mod big_fish_work_delete_input_type; pub mod big_fish_works_list_input_type; pub mod big_fish_works_procedure_result_type; -pub mod bind_asset_object_to_entity_and_return_procedure; -pub mod bind_asset_object_to_entity_reducer; -pub mod cancel_ai_task_and_return_procedure; pub mod chapter_pace_band_type; +pub mod chapter_progression_type; pub mod chapter_progression_get_input_type; pub mod chapter_progression_input_type; pub mod chapter_progression_ledger_input_type; pub mod chapter_progression_procedure_result_type; pub mod chapter_progression_snapshot_type; -pub mod chapter_progression_type; -pub mod clear_platform_browse_history_and_return_procedure; pub mod combat_outcome_type; -pub mod compile_big_fish_draft_procedure; -pub mod compile_custom_world_published_profile_procedure; -pub mod compile_puzzle_agent_draft_procedure; -pub mod complete_ai_stage_and_return_procedure; -pub mod complete_ai_task_and_return_procedure; -pub mod confirm_asset_object_and_return_procedure; -pub mod confirm_asset_object_reducer; pub mod consume_inventory_item_input_type; -pub mod consume_profile_wallet_points_and_return_procedure; -pub mod continue_story_and_return_procedure; -pub mod continue_story_reducer; -pub mod create_ai_task_and_return_procedure; -pub mod create_ai_task_reducer; -pub mod create_battle_state_and_return_procedure; -pub mod create_battle_state_reducer; -pub mod create_big_fish_session_procedure; -pub mod create_custom_world_agent_session_procedure; -pub mod create_profile_recharge_order_and_return_procedure; -pub mod create_puzzle_agent_session_procedure; pub mod custom_world_agent_action_execute_input_type; pub mod custom_world_agent_action_execute_result_type; pub mod custom_world_agent_card_detail_get_input_type; +pub mod custom_world_agent_message_type; pub mod custom_world_agent_message_finalize_input_type; pub mod custom_world_agent_message_snapshot_type; pub mod custom_world_agent_message_submit_input_type; -pub mod custom_world_agent_message_type; +pub mod custom_world_agent_operation_type; pub mod custom_world_agent_operation_get_input_type; pub mod custom_world_agent_operation_procedure_result_type; pub mod custom_world_agent_operation_progress_input_type; pub mod custom_world_agent_operation_snapshot_type; -pub mod custom_world_agent_operation_type; +pub mod custom_world_agent_session_type; pub mod custom_world_agent_session_create_input_type; pub mod custom_world_agent_session_get_input_type; pub mod custom_world_agent_session_procedure_result_type; pub mod custom_world_agent_session_snapshot_type; -pub mod custom_world_agent_session_type; +pub mod custom_world_draft_card_type; pub mod custom_world_draft_card_detail_result_type; pub mod custom_world_draft_card_detail_section_snapshot_type; pub mod custom_world_draft_card_detail_snapshot_type; pub mod custom_world_draft_card_snapshot_type; -pub mod custom_world_draft_card_type; pub mod custom_world_gallery_detail_by_code_input_type; pub mod custom_world_gallery_detail_input_type; -pub mod custom_world_gallery_entry_snapshot_type; -pub mod custom_world_gallery_entry_table; pub mod custom_world_gallery_entry_type; +pub mod custom_world_gallery_entry_snapshot_type; pub mod custom_world_gallery_list_result_type; pub mod custom_world_generation_mode_type; pub mod custom_world_library_detail_input_type; pub mod custom_world_library_mutation_result_type; +pub mod custom_world_profile_type; pub mod custom_world_profile_delete_input_type; pub mod custom_world_profile_list_input_type; pub mod custom_world_profile_list_result_type; pub mod custom_world_profile_publish_input_type; pub mod custom_world_profile_snapshot_type; -pub mod custom_world_profile_type; pub mod custom_world_profile_unpublish_input_type; pub mod custom_world_profile_upsert_input_type; pub mod custom_world_publication_status_type; @@ -178,8 +156,8 @@ pub mod custom_world_published_profile_compile_input_type; pub mod custom_world_published_profile_compile_result_type; pub mod custom_world_published_profile_compile_snapshot_type; pub mod custom_world_role_asset_status_type; -pub mod custom_world_session_status_type; pub mod custom_world_session_type; +pub mod custom_world_session_status_type; pub mod custom_world_theme_mode_type; pub mod custom_world_work_summary_snapshot_type; pub mod custom_world_works_list_input_type; @@ -187,74 +165,22 @@ pub mod custom_world_works_list_result_type; pub mod database_migration_authorize_operator_input_type; pub mod database_migration_export_input_type; pub mod database_migration_import_input_type; -pub mod database_migration_operator_procedure_result_type; pub mod database_migration_operator_type; +pub mod database_migration_operator_procedure_result_type; pub mod database_migration_procedure_result_type; pub mod database_migration_revoke_operator_input_type; pub mod database_migration_table_stat_type; -pub mod delete_big_fish_work_procedure; -pub mod delete_custom_world_agent_session_procedure; -pub mod delete_custom_world_profile_and_return_procedure; -pub mod delete_puzzle_work_procedure; -pub mod delete_runtime_snapshot_and_return_procedure; -pub mod drag_puzzle_piece_or_group_procedure; pub mod equip_inventory_item_input_type; -pub mod execute_custom_world_agent_action_procedure; -pub mod export_auth_store_snapshot_from_tables_procedure; -pub mod export_database_migration_to_file_procedure; -pub mod fail_ai_task_and_return_procedure; -pub mod finalize_big_fish_agent_message_turn_procedure; -pub mod finalize_custom_world_agent_message_turn_procedure; -pub mod finalize_puzzle_agent_message_turn_procedure; -pub mod generate_big_fish_asset_procedure; -pub mod get_auth_store_snapshot_procedure; -pub mod get_battle_state_procedure; -pub mod get_big_fish_session_procedure; -pub mod get_chapter_progression_procedure; -pub mod get_custom_world_agent_card_detail_procedure; -pub mod get_custom_world_agent_operation_procedure; -pub mod get_custom_world_agent_session_procedure; -pub mod get_custom_world_gallery_detail_by_code_procedure; -pub mod get_custom_world_gallery_detail_procedure; -pub mod get_custom_world_library_detail_procedure; -pub mod get_player_progression_or_default_procedure; -pub mod get_profile_dashboard_procedure; -pub mod get_profile_play_stats_procedure; -pub mod get_profile_recharge_center_procedure; -pub mod get_profile_referral_invite_center_procedure; -pub mod get_puzzle_agent_session_procedure; -pub mod get_puzzle_gallery_detail_procedure; -pub mod get_puzzle_run_procedure; -pub mod get_puzzle_work_detail_procedure; -pub mod get_runtime_inventory_state_procedure; -pub mod get_runtime_setting_or_default_procedure; -pub mod get_runtime_snapshot_procedure; -pub mod get_story_session_state_procedure; pub mod grant_inventory_item_input_type; -pub mod grant_player_progression_experience_and_return_procedure; -pub mod grant_player_progression_experience_reducer; -pub mod import_auth_store_snapshot_procedure; -pub mod import_database_migration_from_file_procedure; -pub mod import_database_migration_incremental_from_file_procedure; pub mod inventory_container_kind_type; pub mod inventory_equipment_slot_type; pub mod inventory_item_rarity_type; pub mod inventory_item_snapshot_type; pub mod inventory_item_source_kind_type; -pub mod inventory_mutation_input_type; pub mod inventory_mutation_type; -pub mod inventory_slot_snapshot_type; +pub mod inventory_mutation_input_type; pub mod inventory_slot_type; -pub mod list_asset_history_and_return_procedure; -pub mod list_big_fish_works_procedure; -pub mod list_custom_world_gallery_entries_procedure; -pub mod list_custom_world_profiles_procedure; -pub mod list_custom_world_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_gallery_procedure; -pub mod list_puzzle_works_procedure; +pub mod inventory_slot_snapshot_type; pub mod npc_battle_interaction_procedure_result_type; pub mod npc_battle_interaction_result_type; pub mod npc_interaction_battle_mode_type; @@ -265,16 +191,16 @@ pub mod npc_relation_stance_type; pub mod npc_relation_state_type; pub mod npc_social_action_kind_type; pub mod npc_stance_profile_type; +pub mod npc_state_type; pub mod npc_state_procedure_result_type; pub mod npc_state_snapshot_type; -pub mod npc_state_type; pub mod npc_state_upsert_input_type; +pub mod player_progression_type; pub mod player_progression_get_input_type; pub mod player_progression_grant_input_type; pub mod player_progression_grant_source_type; pub mod player_progression_procedure_result_type; pub mod player_progression_snapshot_type; -pub mod player_progression_type; pub mod profile_dashboard_state_type; pub mod profile_invite_code_type; pub mod profile_membership_type; @@ -285,11 +211,6 @@ pub mod profile_redeem_code_usage_type; pub mod profile_referral_relation_type; pub mod profile_save_archive_type; pub mod profile_wallet_ledger_type; -pub mod publish_big_fish_game_procedure; -pub mod publish_custom_world_profile_and_return_procedure; -pub mod publish_custom_world_profile_reducer; -pub mod publish_custom_world_world_procedure; -pub mod publish_puzzle_work_procedure; pub mod puzzle_agent_message_finalize_input_type; pub mod puzzle_agent_message_kind_type; pub mod puzzle_agent_message_role_type; @@ -301,6 +222,8 @@ pub mod puzzle_agent_session_procedure_result_type; pub mod puzzle_agent_session_row_type; pub mod puzzle_agent_stage_type; pub mod puzzle_draft_compile_input_type; +pub mod puzzle_event_type; +pub mod puzzle_event_kind_type; pub mod puzzle_generated_images_save_input_type; pub mod puzzle_leaderboard_entry_row_type; pub mod puzzle_leaderboard_submit_input_type; @@ -324,8 +247,8 @@ pub mod puzzle_works_procedure_result_type; pub mod quest_completion_ack_input_type; pub mod quest_hostile_npc_defeated_signal_type; pub mod quest_item_delivered_signal_type; -pub mod quest_log_event_kind_type; pub mod quest_log_type; +pub mod quest_log_event_kind_type; pub mod quest_narrative_binding_snapshot_type; pub mod quest_narrative_origin_type; pub mod quest_narrative_type_type; @@ -334,12 +257,12 @@ pub mod quest_npc_talk_completed_signal_type; pub mod quest_objective_kind_type; pub mod quest_objective_snapshot_type; pub mod quest_progress_signal_type; -pub mod quest_record_input_type; pub mod quest_record_type; +pub mod quest_record_input_type; pub mod quest_reward_equipment_slot_type; pub mod quest_reward_intel_type; -pub mod quest_reward_item_rarity_type; pub mod quest_reward_item_type; +pub mod quest_reward_item_rarity_type; pub mod quest_reward_snapshot_type; pub mod quest_scene_reached_signal_type; pub mod quest_signal_apply_input_type; @@ -348,28 +271,13 @@ pub mod quest_status_type; pub mod quest_step_snapshot_type; pub mod quest_treasure_inspected_signal_type; pub mod quest_turn_in_input_type; -pub mod record_big_fish_play_procedure; -pub mod redeem_profile_referral_invite_code_procedure; -pub mod redeem_profile_reward_code_procedure; pub mod refresh_session_type; -pub mod refund_profile_wallet_points_and_return_procedure; -pub mod resolve_combat_action_and_return_procedure; pub mod resolve_combat_action_input_type; pub mod resolve_combat_action_procedure_result_type; -pub mod resolve_combat_action_reducer; pub mod resolve_combat_action_result_type; -pub mod resolve_npc_battle_interaction_and_return_procedure; pub mod resolve_npc_battle_interaction_input_type; -pub mod resolve_npc_interaction_and_return_procedure; pub mod resolve_npc_interaction_input_type; -pub mod resolve_npc_interaction_reducer; -pub mod resolve_npc_social_action_and_return_procedure; pub mod resolve_npc_social_action_input_type; -pub mod resolve_npc_social_action_reducer; -pub mod resolve_treasure_interaction_and_return_procedure; -pub mod resolve_treasure_interaction_reducer; -pub mod resume_profile_save_archive_and_return_procedure; -pub mod revoke_database_migration_operator_procedure; pub mod rpg_agent_draft_card_kind_type; pub mod rpg_agent_draft_card_status_type; pub mod rpg_agent_message_kind_type; @@ -434,150 +342,261 @@ pub mod runtime_referral_invite_center_snapshot_type; pub mod runtime_referral_redeem_input_type; pub mod runtime_referral_redeem_procedure_result_type; pub mod runtime_referral_redeem_snapshot_type; +pub mod runtime_setting_type; pub mod runtime_setting_get_input_type; pub mod runtime_setting_procedure_result_type; pub mod runtime_setting_snapshot_type; -pub mod runtime_setting_type; pub mod runtime_setting_upsert_input_type; +pub mod runtime_snapshot_type; pub mod runtime_snapshot_delete_input_type; pub mod runtime_snapshot_get_input_type; pub mod runtime_snapshot_procedure_result_type; pub mod runtime_snapshot_row_type; -pub mod runtime_snapshot_type; pub mod runtime_snapshot_upsert_input_type; -pub mod save_puzzle_generated_images_procedure; -pub mod select_puzzle_cover_image_procedure; -pub mod start_ai_task_reducer; -pub mod start_ai_task_stage_reducer; -pub mod start_puzzle_run_procedure; pub mod story_continue_input_type; +pub mod story_event_type; pub mod story_event_kind_type; pub mod story_event_snapshot_type; -pub mod story_event_type; +pub mod story_session_type; pub mod story_session_input_type; pub mod story_session_procedure_result_type; pub mod story_session_snapshot_type; pub mod story_session_state_input_type; pub mod story_session_state_procedure_result_type; pub mod story_session_status_type; -pub mod story_session_type; +pub mod treasure_interaction_action_type; +pub mod treasure_record_type; +pub mod treasure_record_procedure_result_type; +pub mod treasure_record_snapshot_type; +pub mod treasure_resolve_input_type; +pub mod unequip_inventory_item_input_type; +pub mod user_account_type; +pub mod user_browse_history_type; +pub mod accept_quest_reducer; +pub mod acknowledge_quest_completion_reducer; +pub mod apply_chapter_progression_ledger_entry_reducer; +pub mod apply_inventory_mutation_reducer; +pub mod apply_quest_signal_reducer; +pub mod begin_story_session_reducer; +pub mod bind_asset_object_to_entity_reducer; +pub mod confirm_asset_object_reducer; +pub mod continue_story_reducer; +pub mod create_ai_task_reducer; +pub mod create_battle_state_reducer; +pub mod grant_player_progression_experience_reducer; +pub mod publish_custom_world_profile_reducer; +pub mod resolve_combat_action_reducer; +pub mod resolve_npc_interaction_reducer; +pub mod resolve_npc_social_action_reducer; +pub mod resolve_treasure_interaction_reducer; +pub mod start_ai_task_reducer; +pub mod start_ai_task_stage_reducer; +pub mod turn_in_quest_reducer; +pub mod unpublish_custom_world_profile_reducer; +pub mod upsert_chapter_progression_reducer; +pub mod upsert_custom_world_profile_reducer; +pub mod upsert_npc_state_reducer; +pub mod ai_task_event_table; +pub mod big_fish_event_table; +pub mod custom_world_gallery_entry_table; +pub mod puzzle_event_table; +pub mod admin_disable_profile_redeem_code_procedure; +pub mod admin_upsert_profile_redeem_code_procedure; +pub mod advance_puzzle_next_level_procedure; +pub mod append_ai_text_chunk_and_return_procedure; +pub mod apply_chapter_progression_ledger_entry_and_return_procedure; +pub mod attach_ai_result_reference_and_return_procedure; +pub mod authorize_database_migration_operator_procedure; +pub mod begin_story_session_and_return_procedure; +pub mod bind_asset_object_to_entity_and_return_procedure; +pub mod cancel_ai_task_and_return_procedure; +pub mod clear_platform_browse_history_and_return_procedure; +pub mod compile_big_fish_draft_procedure; +pub mod compile_custom_world_published_profile_procedure; +pub mod compile_puzzle_agent_draft_procedure; +pub mod complete_ai_stage_and_return_procedure; +pub mod complete_ai_task_and_return_procedure; +pub mod confirm_asset_object_and_return_procedure; +pub mod consume_profile_wallet_points_and_return_procedure; +pub mod continue_story_and_return_procedure; +pub mod create_ai_task_and_return_procedure; +pub mod create_battle_state_and_return_procedure; +pub mod create_big_fish_session_procedure; +pub mod create_custom_world_agent_session_procedure; +pub mod create_profile_recharge_order_and_return_procedure; +pub mod create_puzzle_agent_session_procedure; +pub mod delete_big_fish_work_procedure; +pub mod delete_custom_world_agent_session_procedure; +pub mod delete_custom_world_profile_and_return_procedure; +pub mod delete_puzzle_work_procedure; +pub mod delete_runtime_snapshot_and_return_procedure; +pub mod drag_puzzle_piece_or_group_procedure; +pub mod execute_custom_world_agent_action_procedure; +pub mod export_auth_store_snapshot_from_tables_procedure; +pub mod export_database_migration_to_file_procedure; +pub mod fail_ai_task_and_return_procedure; +pub mod finalize_big_fish_agent_message_turn_procedure; +pub mod finalize_custom_world_agent_message_turn_procedure; +pub mod finalize_puzzle_agent_message_turn_procedure; +pub mod generate_big_fish_asset_procedure; +pub mod get_auth_store_snapshot_procedure; +pub mod get_battle_state_procedure; +pub mod get_big_fish_run_procedure; +pub mod get_big_fish_session_procedure; +pub mod get_chapter_progression_procedure; +pub mod get_custom_world_agent_card_detail_procedure; +pub mod get_custom_world_agent_operation_procedure; +pub mod get_custom_world_agent_session_procedure; +pub mod get_custom_world_gallery_detail_procedure; +pub mod get_custom_world_gallery_detail_by_code_procedure; +pub mod get_custom_world_library_detail_procedure; +pub mod get_player_progression_or_default_procedure; +pub mod get_profile_dashboard_procedure; +pub mod get_profile_play_stats_procedure; +pub mod get_profile_recharge_center_procedure; +pub mod get_profile_referral_invite_center_procedure; +pub mod get_puzzle_agent_session_procedure; +pub mod get_puzzle_gallery_detail_procedure; +pub mod get_puzzle_run_procedure; +pub mod get_puzzle_work_detail_procedure; +pub mod get_runtime_inventory_state_procedure; +pub mod get_runtime_setting_or_default_procedure; +pub mod get_runtime_snapshot_procedure; +pub mod get_story_session_state_procedure; +pub mod grant_player_progression_experience_and_return_procedure; +pub mod import_auth_store_snapshot_procedure; +pub mod import_database_migration_from_file_procedure; +pub mod import_database_migration_incremental_from_file_procedure; +pub mod list_asset_history_and_return_procedure; +pub mod list_big_fish_works_procedure; +pub mod list_custom_world_gallery_entries_procedure; +pub mod list_custom_world_profiles_procedure; +pub mod list_custom_world_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_gallery_procedure; +pub mod list_puzzle_works_procedure; +pub mod publish_big_fish_game_procedure; +pub mod publish_custom_world_profile_and_return_procedure; +pub mod publish_custom_world_world_procedure; +pub mod publish_puzzle_work_procedure; +pub mod record_big_fish_play_procedure; +pub mod redeem_profile_referral_invite_code_procedure; +pub mod redeem_profile_reward_code_procedure; +pub mod refund_profile_wallet_points_and_return_procedure; +pub mod resolve_combat_action_and_return_procedure; +pub mod resolve_npc_battle_interaction_and_return_procedure; +pub mod resolve_npc_interaction_and_return_procedure; +pub mod resolve_npc_social_action_and_return_procedure; +pub mod resolve_treasure_interaction_and_return_procedure; +pub mod resume_profile_save_archive_and_return_procedure; +pub mod revoke_database_migration_operator_procedure; +pub mod save_puzzle_generated_images_procedure; +pub mod select_puzzle_cover_image_procedure; +pub mod start_big_fish_run_procedure; +pub mod start_puzzle_run_procedure; +pub mod submit_big_fish_input_procedure; pub mod submit_big_fish_message_procedure; pub mod submit_custom_world_agent_message_procedure; pub mod submit_puzzle_agent_message_procedure; pub mod submit_puzzle_leaderboard_entry_procedure; pub mod swap_puzzle_pieces_procedure; -pub mod treasure_interaction_action_type; -pub mod treasure_record_procedure_result_type; -pub mod treasure_record_snapshot_type; -pub mod treasure_record_type; -pub mod treasure_resolve_input_type; -pub mod turn_in_quest_reducer; -pub mod unequip_inventory_item_input_type; pub mod unpublish_custom_world_profile_and_return_procedure; -pub mod unpublish_custom_world_profile_reducer; pub mod update_puzzle_work_procedure; pub mod upsert_auth_store_snapshot_procedure; pub mod upsert_chapter_progression_and_return_procedure; -pub mod upsert_chapter_progression_reducer; pub mod upsert_custom_world_agent_operation_progress_procedure; pub mod upsert_custom_world_profile_and_return_procedure; -pub mod upsert_custom_world_profile_reducer; pub mod upsert_npc_state_and_return_procedure; -pub mod upsert_npc_state_reducer; pub mod upsert_platform_browse_history_and_return_procedure; pub mod upsert_runtime_setting_and_return_procedure; pub mod upsert_runtime_snapshot_and_return_procedure; -pub mod user_account_type; -pub mod user_browse_history_type; -pub use accept_quest_reducer::accept_quest; -pub use acknowledge_quest_completion_reducer::acknowledge_quest_completion; -pub use admin_disable_profile_redeem_code_procedure::admin_disable_profile_redeem_code; -pub use admin_upsert_profile_redeem_code_procedure::admin_upsert_profile_redeem_code; -pub use advance_puzzle_next_level_procedure::advance_puzzle_next_level; +pub use ai_result_reference_type::AiResultReference; pub use ai_result_reference_input_type::AiResultReferenceInput; pub use ai_result_reference_kind_type::AiResultReferenceKind; pub use ai_result_reference_snapshot_type::AiResultReferenceSnapshot; -pub use ai_result_reference_type::AiResultReference; pub use ai_stage_completion_input_type::AiStageCompletionInput; +pub use ai_task_type::AiTask; pub use ai_task_cancel_input_type::AiTaskCancelInput; pub use ai_task_create_input_type::AiTaskCreateInput; +pub use ai_task_event_type::AiTaskEvent; +pub use ai_task_event_kind_type::AiTaskEventKind; pub use ai_task_failure_input_type::AiTaskFailureInput; pub use ai_task_finish_input_type::AiTaskFinishInput; pub use ai_task_kind_type::AiTaskKind; pub use ai_task_procedure_result_type::AiTaskProcedureResult; pub use ai_task_snapshot_type::AiTaskSnapshot; +pub use ai_task_stage_type::AiTaskStage; pub use ai_task_stage_blueprint_type::AiTaskStageBlueprint; pub use ai_task_stage_kind_type::AiTaskStageKind; pub use ai_task_stage_snapshot_type::AiTaskStageSnapshot; pub use ai_task_stage_start_input_type::AiTaskStageStartInput; pub use ai_task_stage_status_type::AiTaskStageStatus; -pub use ai_task_stage_type::AiTaskStage; pub use ai_task_start_input_type::AiTaskStartInput; pub use ai_task_status_type::AiTaskStatus; -pub use ai_task_type::AiTask; +pub use ai_text_chunk_type::AiTextChunk; pub use ai_text_chunk_append_input_type::AiTextChunkAppendInput; pub use ai_text_chunk_snapshot_type::AiTextChunkSnapshot; -pub use ai_text_chunk_type::AiTextChunk; -pub use append_ai_text_chunk_and_return_procedure::append_ai_text_chunk_and_return; -pub use apply_chapter_progression_ledger_entry_and_return_procedure::apply_chapter_progression_ledger_entry_and_return; -pub use apply_chapter_progression_ledger_entry_reducer::apply_chapter_progression_ledger_entry; -pub use apply_inventory_mutation_reducer::apply_inventory_mutation; -pub use apply_quest_signal_reducer::apply_quest_signal; +pub use asset_entity_binding_type::AssetEntityBinding; pub use asset_entity_binding_input_type::AssetEntityBindingInput; pub use asset_entity_binding_procedure_result_type::AssetEntityBindingProcedureResult; pub use asset_entity_binding_snapshot_type::AssetEntityBindingSnapshot; -pub use asset_entity_binding_type::AssetEntityBinding; pub use asset_history_entry_snapshot_type::AssetHistoryEntrySnapshot; pub use asset_history_list_input_type::AssetHistoryListInput; pub use asset_history_list_result_type::AssetHistoryListResult; +pub use asset_object_type::AssetObject; pub use asset_object_access_policy_type::AssetObjectAccessPolicy; pub use asset_object_procedure_result_type::AssetObjectProcedureResult; -pub use asset_object_type::AssetObject; pub use asset_object_upsert_input_type::AssetObjectUpsertInput; pub use asset_object_upsert_snapshot_type::AssetObjectUpsertSnapshot; -pub use attach_ai_result_reference_and_return_procedure::attach_ai_result_reference_and_return; pub use auth_identity_type::AuthIdentity; +pub use auth_store_snapshot_type::AuthStoreSnapshot; pub use auth_store_snapshot_import_procedure_result_type::AuthStoreSnapshotImportProcedureResult; pub use auth_store_snapshot_import_record_type::AuthStoreSnapshotImportRecord; pub use auth_store_snapshot_procedure_result_type::AuthStoreSnapshotProcedureResult; pub use auth_store_snapshot_record_type::AuthStoreSnapshotRecord; -pub use auth_store_snapshot_type::AuthStoreSnapshot; pub use auth_store_snapshot_upsert_input_type::AuthStoreSnapshotUpsertInput; -pub use authorize_database_migration_operator_procedure::authorize_database_migration_operator; pub use battle_mode_type::BattleMode; +pub use battle_state_type::BattleState; pub use battle_state_input_type::BattleStateInput; pub use battle_state_procedure_result_type::BattleStateProcedureResult; pub use battle_state_query_input_type::BattleStateQueryInput; pub use battle_state_snapshot_type::BattleStateSnapshot; -pub use battle_state_type::BattleState; pub use battle_status_type::BattleStatus; -pub use begin_story_session_and_return_procedure::begin_story_session_and_return; -pub use begin_story_session_reducer::begin_story_session; +pub use big_fish_agent_message_type::BigFishAgentMessage; pub use big_fish_agent_message_kind_type::BigFishAgentMessageKind; pub use big_fish_agent_message_role_type::BigFishAgentMessageRole; pub use big_fish_agent_message_snapshot_type::BigFishAgentMessageSnapshot; -pub use big_fish_agent_message_type::BigFishAgentMessage; pub use big_fish_anchor_item_type::BigFishAnchorItem; pub use big_fish_anchor_pack_type::BigFishAnchorPack; pub use big_fish_anchor_status_type::BigFishAnchorStatus; pub use big_fish_asset_coverage_type::BigFishAssetCoverage; pub use big_fish_asset_generate_input_type::BigFishAssetGenerateInput; pub use big_fish_asset_kind_type::BigFishAssetKind; -pub use big_fish_asset_slot_snapshot_type::BigFishAssetSlotSnapshot; pub use big_fish_asset_slot_type::BigFishAssetSlot; +pub use big_fish_asset_slot_snapshot_type::BigFishAssetSlotSnapshot; pub use big_fish_asset_status_type::BigFishAssetStatus; pub use big_fish_background_blueprint_type::BigFishBackgroundBlueprint; pub use big_fish_creation_session_type::BigFishCreationSession; pub use big_fish_creation_stage_type::BigFishCreationStage; pub use big_fish_draft_compile_input_type::BigFishDraftCompileInput; +pub use big_fish_event_type::BigFishEvent; +pub use big_fish_event_kind_type::BigFishEventKind; pub use big_fish_game_draft_type::BigFishGameDraft; +pub use big_fish_input_submit_input_type::BigFishInputSubmitInput; pub use big_fish_level_blueprint_type::BigFishLevelBlueprint; pub use big_fish_message_finalize_input_type::BigFishMessageFinalizeInput; pub use big_fish_message_submit_input_type::BigFishMessageSubmitInput; pub use big_fish_play_record_input_type::BigFishPlayRecordInput; pub use big_fish_publish_input_type::BigFishPublishInput; +pub use big_fish_run_get_input_type::BigFishRunGetInput; +pub use big_fish_run_procedure_result_type::BigFishRunProcedureResult; +pub use big_fish_run_start_input_type::BigFishRunStartInput; +pub use big_fish_run_status_type::BigFishRunStatus; pub use big_fish_runtime_params_type::BigFishRuntimeParams; +pub use big_fish_runtime_run_type::BigFishRuntimeRun; pub use big_fish_session_create_input_type::BigFishSessionCreateInput; pub use big_fish_session_get_input_type::BigFishSessionGetInput; pub use big_fish_session_procedure_result_type::BigFishSessionProcedureResult; @@ -585,74 +604,51 @@ pub use big_fish_session_snapshot_type::BigFishSessionSnapshot; pub use big_fish_work_delete_input_type::BigFishWorkDeleteInput; pub use big_fish_works_list_input_type::BigFishWorksListInput; pub use big_fish_works_procedure_result_type::BigFishWorksProcedureResult; -pub use bind_asset_object_to_entity_and_return_procedure::bind_asset_object_to_entity_and_return; -pub use bind_asset_object_to_entity_reducer::bind_asset_object_to_entity; -pub use cancel_ai_task_and_return_procedure::cancel_ai_task_and_return; pub use chapter_pace_band_type::ChapterPaceBand; +pub use chapter_progression_type::ChapterProgression; pub use chapter_progression_get_input_type::ChapterProgressionGetInput; pub use chapter_progression_input_type::ChapterProgressionInput; pub use chapter_progression_ledger_input_type::ChapterProgressionLedgerInput; pub use chapter_progression_procedure_result_type::ChapterProgressionProcedureResult; pub use chapter_progression_snapshot_type::ChapterProgressionSnapshot; -pub use chapter_progression_type::ChapterProgression; -pub use clear_platform_browse_history_and_return_procedure::clear_platform_browse_history_and_return; pub use combat_outcome_type::CombatOutcome; -pub use compile_big_fish_draft_procedure::compile_big_fish_draft; -pub use compile_custom_world_published_profile_procedure::compile_custom_world_published_profile; -pub use compile_puzzle_agent_draft_procedure::compile_puzzle_agent_draft; -pub use complete_ai_stage_and_return_procedure::complete_ai_stage_and_return; -pub use complete_ai_task_and_return_procedure::complete_ai_task_and_return; -pub use confirm_asset_object_and_return_procedure::confirm_asset_object_and_return; -pub use confirm_asset_object_reducer::confirm_asset_object; pub use consume_inventory_item_input_type::ConsumeInventoryItemInput; -pub use consume_profile_wallet_points_and_return_procedure::consume_profile_wallet_points_and_return; -pub use continue_story_and_return_procedure::continue_story_and_return; -pub use continue_story_reducer::continue_story; -pub use create_ai_task_and_return_procedure::create_ai_task_and_return; -pub use create_ai_task_reducer::create_ai_task; -pub use create_battle_state_and_return_procedure::create_battle_state_and_return; -pub use create_battle_state_reducer::create_battle_state; -pub use create_big_fish_session_procedure::create_big_fish_session; -pub use create_custom_world_agent_session_procedure::create_custom_world_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 custom_world_agent_action_execute_input_type::CustomWorldAgentActionExecuteInput; pub use custom_world_agent_action_execute_result_type::CustomWorldAgentActionExecuteResult; pub use custom_world_agent_card_detail_get_input_type::CustomWorldAgentCardDetailGetInput; +pub use custom_world_agent_message_type::CustomWorldAgentMessage; pub use custom_world_agent_message_finalize_input_type::CustomWorldAgentMessageFinalizeInput; pub use custom_world_agent_message_snapshot_type::CustomWorldAgentMessageSnapshot; pub use custom_world_agent_message_submit_input_type::CustomWorldAgentMessageSubmitInput; -pub use custom_world_agent_message_type::CustomWorldAgentMessage; +pub use custom_world_agent_operation_type::CustomWorldAgentOperation; pub use custom_world_agent_operation_get_input_type::CustomWorldAgentOperationGetInput; pub use custom_world_agent_operation_procedure_result_type::CustomWorldAgentOperationProcedureResult; pub use custom_world_agent_operation_progress_input_type::CustomWorldAgentOperationProgressInput; pub use custom_world_agent_operation_snapshot_type::CustomWorldAgentOperationSnapshot; -pub use custom_world_agent_operation_type::CustomWorldAgentOperation; +pub use custom_world_agent_session_type::CustomWorldAgentSession; pub use custom_world_agent_session_create_input_type::CustomWorldAgentSessionCreateInput; pub use custom_world_agent_session_get_input_type::CustomWorldAgentSessionGetInput; pub use custom_world_agent_session_procedure_result_type::CustomWorldAgentSessionProcedureResult; pub use custom_world_agent_session_snapshot_type::CustomWorldAgentSessionSnapshot; -pub use custom_world_agent_session_type::CustomWorldAgentSession; +pub use custom_world_draft_card_type::CustomWorldDraftCard; pub use custom_world_draft_card_detail_result_type::CustomWorldDraftCardDetailResult; pub use custom_world_draft_card_detail_section_snapshot_type::CustomWorldDraftCardDetailSectionSnapshot; pub use custom_world_draft_card_detail_snapshot_type::CustomWorldDraftCardDetailSnapshot; pub use custom_world_draft_card_snapshot_type::CustomWorldDraftCardSnapshot; -pub use custom_world_draft_card_type::CustomWorldDraftCard; pub use custom_world_gallery_detail_by_code_input_type::CustomWorldGalleryDetailByCodeInput; pub use custom_world_gallery_detail_input_type::CustomWorldGalleryDetailInput; -pub use custom_world_gallery_entry_snapshot_type::CustomWorldGalleryEntrySnapshot; -pub use custom_world_gallery_entry_table::*; pub use custom_world_gallery_entry_type::CustomWorldGalleryEntry; +pub use custom_world_gallery_entry_snapshot_type::CustomWorldGalleryEntrySnapshot; pub use custom_world_gallery_list_result_type::CustomWorldGalleryListResult; pub use custom_world_generation_mode_type::CustomWorldGenerationMode; pub use custom_world_library_detail_input_type::CustomWorldLibraryDetailInput; pub use custom_world_library_mutation_result_type::CustomWorldLibraryMutationResult; +pub use custom_world_profile_type::CustomWorldProfile; pub use custom_world_profile_delete_input_type::CustomWorldProfileDeleteInput; pub use custom_world_profile_list_input_type::CustomWorldProfileListInput; pub use custom_world_profile_list_result_type::CustomWorldProfileListResult; pub use custom_world_profile_publish_input_type::CustomWorldProfilePublishInput; pub use custom_world_profile_snapshot_type::CustomWorldProfileSnapshot; -pub use custom_world_profile_type::CustomWorldProfile; pub use custom_world_profile_unpublish_input_type::CustomWorldProfileUnpublishInput; pub use custom_world_profile_upsert_input_type::CustomWorldProfileUpsertInput; pub use custom_world_publication_status_type::CustomWorldPublicationStatus; @@ -662,8 +658,8 @@ pub use custom_world_published_profile_compile_input_type::CustomWorldPublishedP pub use custom_world_published_profile_compile_result_type::CustomWorldPublishedProfileCompileResult; pub use custom_world_published_profile_compile_snapshot_type::CustomWorldPublishedProfileCompileSnapshot; pub use custom_world_role_asset_status_type::CustomWorldRoleAssetStatus; -pub use custom_world_session_status_type::CustomWorldSessionStatus; pub use custom_world_session_type::CustomWorldSession; +pub use custom_world_session_status_type::CustomWorldSessionStatus; pub use custom_world_theme_mode_type::CustomWorldThemeMode; pub use custom_world_work_summary_snapshot_type::CustomWorldWorkSummarySnapshot; pub use custom_world_works_list_input_type::CustomWorldWorksListInput; @@ -671,74 +667,22 @@ pub use custom_world_works_list_result_type::CustomWorldWorksListResult; pub use database_migration_authorize_operator_input_type::DatabaseMigrationAuthorizeOperatorInput; pub use database_migration_export_input_type::DatabaseMigrationExportInput; pub use database_migration_import_input_type::DatabaseMigrationImportInput; -pub use database_migration_operator_procedure_result_type::DatabaseMigrationOperatorProcedureResult; pub use database_migration_operator_type::DatabaseMigrationOperator; +pub use database_migration_operator_procedure_result_type::DatabaseMigrationOperatorProcedureResult; pub use database_migration_procedure_result_type::DatabaseMigrationProcedureResult; pub use database_migration_revoke_operator_input_type::DatabaseMigrationRevokeOperatorInput; pub use database_migration_table_stat_type::DatabaseMigrationTableStat; -pub use delete_big_fish_work_procedure::delete_big_fish_work; -pub use delete_custom_world_agent_session_procedure::delete_custom_world_agent_session; -pub use delete_custom_world_profile_and_return_procedure::delete_custom_world_profile_and_return; -pub use delete_puzzle_work_procedure::delete_puzzle_work; -pub use delete_runtime_snapshot_and_return_procedure::delete_runtime_snapshot_and_return; -pub use drag_puzzle_piece_or_group_procedure::drag_puzzle_piece_or_group; pub use equip_inventory_item_input_type::EquipInventoryItemInput; -pub use execute_custom_world_agent_action_procedure::execute_custom_world_agent_action; -pub use export_auth_store_snapshot_from_tables_procedure::export_auth_store_snapshot_from_tables; -pub use export_database_migration_to_file_procedure::export_database_migration_to_file; -pub use fail_ai_task_and_return_procedure::fail_ai_task_and_return; -pub use finalize_big_fish_agent_message_turn_procedure::finalize_big_fish_agent_message_turn; -pub use finalize_custom_world_agent_message_turn_procedure::finalize_custom_world_agent_message_turn; -pub use finalize_puzzle_agent_message_turn_procedure::finalize_puzzle_agent_message_turn; -pub use generate_big_fish_asset_procedure::generate_big_fish_asset; -pub use get_auth_store_snapshot_procedure::get_auth_store_snapshot; -pub use get_battle_state_procedure::get_battle_state; -pub use get_big_fish_session_procedure::get_big_fish_session; -pub use get_chapter_progression_procedure::get_chapter_progression; -pub use get_custom_world_agent_card_detail_procedure::get_custom_world_agent_card_detail; -pub use get_custom_world_agent_operation_procedure::get_custom_world_agent_operation; -pub use get_custom_world_agent_session_procedure::get_custom_world_agent_session; -pub use get_custom_world_gallery_detail_by_code_procedure::get_custom_world_gallery_detail_by_code; -pub use get_custom_world_gallery_detail_procedure::get_custom_world_gallery_detail; -pub use get_custom_world_library_detail_procedure::get_custom_world_library_detail; -pub use get_player_progression_or_default_procedure::get_player_progression_or_default; -pub use get_profile_dashboard_procedure::get_profile_dashboard; -pub use get_profile_play_stats_procedure::get_profile_play_stats; -pub use get_profile_recharge_center_procedure::get_profile_recharge_center; -pub use get_profile_referral_invite_center_procedure::get_profile_referral_invite_center; -pub use get_puzzle_agent_session_procedure::get_puzzle_agent_session; -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; -pub use get_runtime_inventory_state_procedure::get_runtime_inventory_state; -pub use get_runtime_setting_or_default_procedure::get_runtime_setting_or_default; -pub use get_runtime_snapshot_procedure::get_runtime_snapshot; -pub use get_story_session_state_procedure::get_story_session_state; pub use grant_inventory_item_input_type::GrantInventoryItemInput; -pub use grant_player_progression_experience_and_return_procedure::grant_player_progression_experience_and_return; -pub use grant_player_progression_experience_reducer::grant_player_progression_experience; -pub use import_auth_store_snapshot_procedure::import_auth_store_snapshot; -pub use import_database_migration_from_file_procedure::import_database_migration_from_file; -pub use import_database_migration_incremental_from_file_procedure::import_database_migration_incremental_from_file; pub use inventory_container_kind_type::InventoryContainerKind; pub use inventory_equipment_slot_type::InventoryEquipmentSlot; pub use inventory_item_rarity_type::InventoryItemRarity; pub use inventory_item_snapshot_type::InventoryItemSnapshot; pub use inventory_item_source_kind_type::InventoryItemSourceKind; -pub use inventory_mutation_input_type::InventoryMutationInput; pub use inventory_mutation_type::InventoryMutation; -pub use inventory_slot_snapshot_type::InventorySlotSnapshot; +pub use inventory_mutation_input_type::InventoryMutationInput; pub use inventory_slot_type::InventorySlot; -pub use list_asset_history_and_return_procedure::list_asset_history_and_return; -pub use list_big_fish_works_procedure::list_big_fish_works; -pub use list_custom_world_gallery_entries_procedure::list_custom_world_gallery_entries; -pub use list_custom_world_profiles_procedure::list_custom_world_profiles; -pub use list_custom_world_works_procedure::list_custom_world_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_gallery_procedure::list_puzzle_gallery; -pub use list_puzzle_works_procedure::list_puzzle_works; +pub use inventory_slot_snapshot_type::InventorySlotSnapshot; pub use npc_battle_interaction_procedure_result_type::NpcBattleInteractionProcedureResult; pub use npc_battle_interaction_result_type::NpcBattleInteractionResult; pub use npc_interaction_battle_mode_type::NpcInteractionBattleMode; @@ -749,16 +693,16 @@ pub use npc_relation_stance_type::NpcRelationStance; pub use npc_relation_state_type::NpcRelationState; pub use npc_social_action_kind_type::NpcSocialActionKind; pub use npc_stance_profile_type::NpcStanceProfile; +pub use npc_state_type::NpcState; pub use npc_state_procedure_result_type::NpcStateProcedureResult; pub use npc_state_snapshot_type::NpcStateSnapshot; -pub use npc_state_type::NpcState; pub use npc_state_upsert_input_type::NpcStateUpsertInput; +pub use player_progression_type::PlayerProgression; pub use player_progression_get_input_type::PlayerProgressionGetInput; pub use player_progression_grant_input_type::PlayerProgressionGrantInput; pub use player_progression_grant_source_type::PlayerProgressionGrantSource; pub use player_progression_procedure_result_type::PlayerProgressionProcedureResult; pub use player_progression_snapshot_type::PlayerProgressionSnapshot; -pub use player_progression_type::PlayerProgression; pub use profile_dashboard_state_type::ProfileDashboardState; pub use profile_invite_code_type::ProfileInviteCode; pub use profile_membership_type::ProfileMembership; @@ -769,11 +713,6 @@ pub use profile_redeem_code_usage_type::ProfileRedeemCodeUsage; pub use profile_referral_relation_type::ProfileReferralRelation; pub use profile_save_archive_type::ProfileSaveArchive; pub use profile_wallet_ledger_type::ProfileWalletLedger; -pub use publish_big_fish_game_procedure::publish_big_fish_game; -pub use publish_custom_world_profile_and_return_procedure::publish_custom_world_profile_and_return; -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_puzzle_work_procedure::publish_puzzle_work; pub use puzzle_agent_message_finalize_input_type::PuzzleAgentMessageFinalizeInput; pub use puzzle_agent_message_kind_type::PuzzleAgentMessageKind; pub use puzzle_agent_message_role_type::PuzzleAgentMessageRole; @@ -785,6 +724,8 @@ pub use puzzle_agent_session_procedure_result_type::PuzzleAgentSessionProcedureR pub use puzzle_agent_session_row_type::PuzzleAgentSessionRow; pub use puzzle_agent_stage_type::PuzzleAgentStage; pub use puzzle_draft_compile_input_type::PuzzleDraftCompileInput; +pub use puzzle_event_type::PuzzleEvent; +pub use puzzle_event_kind_type::PuzzleEventKind; pub use puzzle_generated_images_save_input_type::PuzzleGeneratedImagesSaveInput; pub use puzzle_leaderboard_entry_row_type::PuzzleLeaderboardEntryRow; pub use puzzle_leaderboard_submit_input_type::PuzzleLeaderboardSubmitInput; @@ -808,8 +749,8 @@ pub use puzzle_works_procedure_result_type::PuzzleWorksProcedureResult; pub use quest_completion_ack_input_type::QuestCompletionAckInput; pub use quest_hostile_npc_defeated_signal_type::QuestHostileNpcDefeatedSignal; pub use quest_item_delivered_signal_type::QuestItemDeliveredSignal; -pub use quest_log_event_kind_type::QuestLogEventKind; pub use quest_log_type::QuestLog; +pub use quest_log_event_kind_type::QuestLogEventKind; pub use quest_narrative_binding_snapshot_type::QuestNarrativeBindingSnapshot; pub use quest_narrative_origin_type::QuestNarrativeOrigin; pub use quest_narrative_type_type::QuestNarrativeType; @@ -818,12 +759,12 @@ pub use quest_npc_talk_completed_signal_type::QuestNpcTalkCompletedSignal; pub use quest_objective_kind_type::QuestObjectiveKind; pub use quest_objective_snapshot_type::QuestObjectiveSnapshot; pub use quest_progress_signal_type::QuestProgressSignal; -pub use quest_record_input_type::QuestRecordInput; pub use quest_record_type::QuestRecord; +pub use quest_record_input_type::QuestRecordInput; pub use quest_reward_equipment_slot_type::QuestRewardEquipmentSlot; pub use quest_reward_intel_type::QuestRewardIntel; -pub use quest_reward_item_rarity_type::QuestRewardItemRarity; pub use quest_reward_item_type::QuestRewardItem; +pub use quest_reward_item_rarity_type::QuestRewardItemRarity; pub use quest_reward_snapshot_type::QuestRewardSnapshot; pub use quest_scene_reached_signal_type::QuestSceneReachedSignal; pub use quest_signal_apply_input_type::QuestSignalApplyInput; @@ -832,28 +773,13 @@ pub use quest_status_type::QuestStatus; pub use quest_step_snapshot_type::QuestStepSnapshot; pub use quest_treasure_inspected_signal_type::QuestTreasureInspectedSignal; pub use quest_turn_in_input_type::QuestTurnInInput; -pub use record_big_fish_play_procedure::record_big_fish_play; -pub use redeem_profile_referral_invite_code_procedure::redeem_profile_referral_invite_code; -pub use redeem_profile_reward_code_procedure::redeem_profile_reward_code; pub use refresh_session_type::RefreshSession; -pub use refund_profile_wallet_points_and_return_procedure::refund_profile_wallet_points_and_return; -pub use resolve_combat_action_and_return_procedure::resolve_combat_action_and_return; pub use resolve_combat_action_input_type::ResolveCombatActionInput; pub use resolve_combat_action_procedure_result_type::ResolveCombatActionProcedureResult; -pub use resolve_combat_action_reducer::resolve_combat_action; pub use resolve_combat_action_result_type::ResolveCombatActionResult; -pub use resolve_npc_battle_interaction_and_return_procedure::resolve_npc_battle_interaction_and_return; pub use resolve_npc_battle_interaction_input_type::ResolveNpcBattleInteractionInput; -pub use resolve_npc_interaction_and_return_procedure::resolve_npc_interaction_and_return; pub use resolve_npc_interaction_input_type::ResolveNpcInteractionInput; -pub use resolve_npc_interaction_reducer::resolve_npc_interaction; -pub use resolve_npc_social_action_and_return_procedure::resolve_npc_social_action_and_return; pub use resolve_npc_social_action_input_type::ResolveNpcSocialActionInput; -pub use resolve_npc_social_action_reducer::resolve_npc_social_action; -pub use resolve_treasure_interaction_and_return_procedure::resolve_treasure_interaction_and_return; -pub use resolve_treasure_interaction_reducer::resolve_treasure_interaction; -pub use resume_profile_save_archive_and_return_procedure::resume_profile_save_archive_and_return; -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; pub use rpg_agent_message_kind_type::RpgAgentMessageKind; @@ -918,61 +844,176 @@ pub use runtime_referral_invite_center_snapshot_type::RuntimeReferralInviteCente pub use runtime_referral_redeem_input_type::RuntimeReferralRedeemInput; pub use runtime_referral_redeem_procedure_result_type::RuntimeReferralRedeemProcedureResult; pub use runtime_referral_redeem_snapshot_type::RuntimeReferralRedeemSnapshot; +pub use runtime_setting_type::RuntimeSetting; pub use runtime_setting_get_input_type::RuntimeSettingGetInput; pub use runtime_setting_procedure_result_type::RuntimeSettingProcedureResult; pub use runtime_setting_snapshot_type::RuntimeSettingSnapshot; -pub use runtime_setting_type::RuntimeSetting; pub use runtime_setting_upsert_input_type::RuntimeSettingUpsertInput; +pub use runtime_snapshot_type::RuntimeSnapshot; pub use runtime_snapshot_delete_input_type::RuntimeSnapshotDeleteInput; pub use runtime_snapshot_get_input_type::RuntimeSnapshotGetInput; pub use runtime_snapshot_procedure_result_type::RuntimeSnapshotProcedureResult; pub use runtime_snapshot_row_type::RuntimeSnapshotRow; -pub use runtime_snapshot_type::RuntimeSnapshot; pub use runtime_snapshot_upsert_input_type::RuntimeSnapshotUpsertInput; -pub use save_puzzle_generated_images_procedure::save_puzzle_generated_images; -pub use select_puzzle_cover_image_procedure::select_puzzle_cover_image; -pub use start_ai_task_reducer::start_ai_task; -pub use start_ai_task_stage_reducer::start_ai_task_stage; -pub use start_puzzle_run_procedure::start_puzzle_run; pub use story_continue_input_type::StoryContinueInput; +pub use story_event_type::StoryEvent; pub use story_event_kind_type::StoryEventKind; pub use story_event_snapshot_type::StoryEventSnapshot; -pub use story_event_type::StoryEvent; +pub use story_session_type::StorySession; pub use story_session_input_type::StorySessionInput; pub use story_session_procedure_result_type::StorySessionProcedureResult; pub use story_session_snapshot_type::StorySessionSnapshot; pub use story_session_state_input_type::StorySessionStateInput; pub use story_session_state_procedure_result_type::StorySessionStateProcedureResult; pub use story_session_status_type::StorySessionStatus; -pub use story_session_type::StorySession; +pub use treasure_interaction_action_type::TreasureInteractionAction; +pub use treasure_record_type::TreasureRecord; +pub use treasure_record_procedure_result_type::TreasureRecordProcedureResult; +pub use treasure_record_snapshot_type::TreasureRecordSnapshot; +pub use treasure_resolve_input_type::TreasureResolveInput; +pub use unequip_inventory_item_input_type::UnequipInventoryItemInput; +pub use user_account_type::UserAccount; +pub use user_browse_history_type::UserBrowseHistory; +pub use ai_task_event_table::*; +pub use big_fish_event_table::*; +pub use custom_world_gallery_entry_table::*; +pub use puzzle_event_table::*; +pub use accept_quest_reducer::accept_quest; +pub use acknowledge_quest_completion_reducer::acknowledge_quest_completion; +pub use apply_chapter_progression_ledger_entry_reducer::apply_chapter_progression_ledger_entry; +pub use apply_inventory_mutation_reducer::apply_inventory_mutation; +pub use apply_quest_signal_reducer::apply_quest_signal; +pub use begin_story_session_reducer::begin_story_session; +pub use bind_asset_object_to_entity_reducer::bind_asset_object_to_entity; +pub use confirm_asset_object_reducer::confirm_asset_object; +pub use continue_story_reducer::continue_story; +pub use create_ai_task_reducer::create_ai_task; +pub use create_battle_state_reducer::create_battle_state; +pub use grant_player_progression_experience_reducer::grant_player_progression_experience; +pub use publish_custom_world_profile_reducer::publish_custom_world_profile; +pub use resolve_combat_action_reducer::resolve_combat_action; +pub use resolve_npc_interaction_reducer::resolve_npc_interaction; +pub use resolve_npc_social_action_reducer::resolve_npc_social_action; +pub use resolve_treasure_interaction_reducer::resolve_treasure_interaction; +pub use start_ai_task_reducer::start_ai_task; +pub use start_ai_task_stage_reducer::start_ai_task_stage; +pub use turn_in_quest_reducer::turn_in_quest; +pub use unpublish_custom_world_profile_reducer::unpublish_custom_world_profile; +pub use upsert_chapter_progression_reducer::upsert_chapter_progression; +pub use upsert_custom_world_profile_reducer::upsert_custom_world_profile; +pub use upsert_npc_state_reducer::upsert_npc_state; +pub use admin_disable_profile_redeem_code_procedure::admin_disable_profile_redeem_code; +pub use admin_upsert_profile_redeem_code_procedure::admin_upsert_profile_redeem_code; +pub use advance_puzzle_next_level_procedure::advance_puzzle_next_level; +pub use append_ai_text_chunk_and_return_procedure::append_ai_text_chunk_and_return; +pub use apply_chapter_progression_ledger_entry_and_return_procedure::apply_chapter_progression_ledger_entry_and_return; +pub use attach_ai_result_reference_and_return_procedure::attach_ai_result_reference_and_return; +pub use authorize_database_migration_operator_procedure::authorize_database_migration_operator; +pub use begin_story_session_and_return_procedure::begin_story_session_and_return; +pub use bind_asset_object_to_entity_and_return_procedure::bind_asset_object_to_entity_and_return; +pub use cancel_ai_task_and_return_procedure::cancel_ai_task_and_return; +pub use clear_platform_browse_history_and_return_procedure::clear_platform_browse_history_and_return; +pub use compile_big_fish_draft_procedure::compile_big_fish_draft; +pub use compile_custom_world_published_profile_procedure::compile_custom_world_published_profile; +pub use compile_puzzle_agent_draft_procedure::compile_puzzle_agent_draft; +pub use complete_ai_stage_and_return_procedure::complete_ai_stage_and_return; +pub use complete_ai_task_and_return_procedure::complete_ai_task_and_return; +pub use confirm_asset_object_and_return_procedure::confirm_asset_object_and_return; +pub use consume_profile_wallet_points_and_return_procedure::consume_profile_wallet_points_and_return; +pub use continue_story_and_return_procedure::continue_story_and_return; +pub use create_ai_task_and_return_procedure::create_ai_task_and_return; +pub use create_battle_state_and_return_procedure::create_battle_state_and_return; +pub use create_big_fish_session_procedure::create_big_fish_session; +pub use create_custom_world_agent_session_procedure::create_custom_world_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 delete_big_fish_work_procedure::delete_big_fish_work; +pub use delete_custom_world_agent_session_procedure::delete_custom_world_agent_session; +pub use delete_custom_world_profile_and_return_procedure::delete_custom_world_profile_and_return; +pub use delete_puzzle_work_procedure::delete_puzzle_work; +pub use delete_runtime_snapshot_and_return_procedure::delete_runtime_snapshot_and_return; +pub use drag_puzzle_piece_or_group_procedure::drag_puzzle_piece_or_group; +pub use execute_custom_world_agent_action_procedure::execute_custom_world_agent_action; +pub use export_auth_store_snapshot_from_tables_procedure::export_auth_store_snapshot_from_tables; +pub use export_database_migration_to_file_procedure::export_database_migration_to_file; +pub use fail_ai_task_and_return_procedure::fail_ai_task_and_return; +pub use finalize_big_fish_agent_message_turn_procedure::finalize_big_fish_agent_message_turn; +pub use finalize_custom_world_agent_message_turn_procedure::finalize_custom_world_agent_message_turn; +pub use finalize_puzzle_agent_message_turn_procedure::finalize_puzzle_agent_message_turn; +pub use generate_big_fish_asset_procedure::generate_big_fish_asset; +pub use get_auth_store_snapshot_procedure::get_auth_store_snapshot; +pub use get_battle_state_procedure::get_battle_state; +pub use get_big_fish_run_procedure::get_big_fish_run; +pub use get_big_fish_session_procedure::get_big_fish_session; +pub use get_chapter_progression_procedure::get_chapter_progression; +pub use get_custom_world_agent_card_detail_procedure::get_custom_world_agent_card_detail; +pub use get_custom_world_agent_operation_procedure::get_custom_world_agent_operation; +pub use get_custom_world_agent_session_procedure::get_custom_world_agent_session; +pub use get_custom_world_gallery_detail_procedure::get_custom_world_gallery_detail; +pub use get_custom_world_gallery_detail_by_code_procedure::get_custom_world_gallery_detail_by_code; +pub use get_custom_world_library_detail_procedure::get_custom_world_library_detail; +pub use get_player_progression_or_default_procedure::get_player_progression_or_default; +pub use get_profile_dashboard_procedure::get_profile_dashboard; +pub use get_profile_play_stats_procedure::get_profile_play_stats; +pub use get_profile_recharge_center_procedure::get_profile_recharge_center; +pub use get_profile_referral_invite_center_procedure::get_profile_referral_invite_center; +pub use get_puzzle_agent_session_procedure::get_puzzle_agent_session; +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; +pub use get_runtime_inventory_state_procedure::get_runtime_inventory_state; +pub use get_runtime_setting_or_default_procedure::get_runtime_setting_or_default; +pub use get_runtime_snapshot_procedure::get_runtime_snapshot; +pub use get_story_session_state_procedure::get_story_session_state; +pub use grant_player_progression_experience_and_return_procedure::grant_player_progression_experience_and_return; +pub use import_auth_store_snapshot_procedure::import_auth_store_snapshot; +pub use import_database_migration_from_file_procedure::import_database_migration_from_file; +pub use import_database_migration_incremental_from_file_procedure::import_database_migration_incremental_from_file; +pub use list_asset_history_and_return_procedure::list_asset_history_and_return; +pub use list_big_fish_works_procedure::list_big_fish_works; +pub use list_custom_world_gallery_entries_procedure::list_custom_world_gallery_entries; +pub use list_custom_world_profiles_procedure::list_custom_world_profiles; +pub use list_custom_world_works_procedure::list_custom_world_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_gallery_procedure::list_puzzle_gallery; +pub use list_puzzle_works_procedure::list_puzzle_works; +pub use publish_big_fish_game_procedure::publish_big_fish_game; +pub use publish_custom_world_profile_and_return_procedure::publish_custom_world_profile_and_return; +pub use publish_custom_world_world_procedure::publish_custom_world_world; +pub use publish_puzzle_work_procedure::publish_puzzle_work; +pub use record_big_fish_play_procedure::record_big_fish_play; +pub use redeem_profile_referral_invite_code_procedure::redeem_profile_referral_invite_code; +pub use redeem_profile_reward_code_procedure::redeem_profile_reward_code; +pub use refund_profile_wallet_points_and_return_procedure::refund_profile_wallet_points_and_return; +pub use resolve_combat_action_and_return_procedure::resolve_combat_action_and_return; +pub use resolve_npc_battle_interaction_and_return_procedure::resolve_npc_battle_interaction_and_return; +pub use resolve_npc_interaction_and_return_procedure::resolve_npc_interaction_and_return; +pub use resolve_npc_social_action_and_return_procedure::resolve_npc_social_action_and_return; +pub use resolve_treasure_interaction_and_return_procedure::resolve_treasure_interaction_and_return; +pub use resume_profile_save_archive_and_return_procedure::resume_profile_save_archive_and_return; +pub use revoke_database_migration_operator_procedure::revoke_database_migration_operator; +pub use save_puzzle_generated_images_procedure::save_puzzle_generated_images; +pub use select_puzzle_cover_image_procedure::select_puzzle_cover_image; +pub use start_big_fish_run_procedure::start_big_fish_run; +pub use start_puzzle_run_procedure::start_puzzle_run; +pub use submit_big_fish_input_procedure::submit_big_fish_input; pub use submit_big_fish_message_procedure::submit_big_fish_message; pub use submit_custom_world_agent_message_procedure::submit_custom_world_agent_message; pub use submit_puzzle_agent_message_procedure::submit_puzzle_agent_message; pub use submit_puzzle_leaderboard_entry_procedure::submit_puzzle_leaderboard_entry; pub use swap_puzzle_pieces_procedure::swap_puzzle_pieces; -pub use treasure_interaction_action_type::TreasureInteractionAction; -pub use treasure_record_procedure_result_type::TreasureRecordProcedureResult; -pub use treasure_record_snapshot_type::TreasureRecordSnapshot; -pub use treasure_record_type::TreasureRecord; -pub use treasure_resolve_input_type::TreasureResolveInput; -pub use turn_in_quest_reducer::turn_in_quest; -pub use unequip_inventory_item_input_type::UnequipInventoryItemInput; pub use unpublish_custom_world_profile_and_return_procedure::unpublish_custom_world_profile_and_return; -pub use unpublish_custom_world_profile_reducer::unpublish_custom_world_profile; pub use update_puzzle_work_procedure::update_puzzle_work; pub use upsert_auth_store_snapshot_procedure::upsert_auth_store_snapshot; pub use upsert_chapter_progression_and_return_procedure::upsert_chapter_progression_and_return; -pub use upsert_chapter_progression_reducer::upsert_chapter_progression; pub use upsert_custom_world_agent_operation_progress_procedure::upsert_custom_world_agent_operation_progress; pub use upsert_custom_world_profile_and_return_procedure::upsert_custom_world_profile_and_return; -pub use upsert_custom_world_profile_reducer::upsert_custom_world_profile; pub use upsert_npc_state_and_return_procedure::upsert_npc_state_and_return; -pub use upsert_npc_state_reducer::upsert_npc_state; pub use upsert_platform_browse_history_and_return_procedure::upsert_platform_browse_history_and_return; pub use upsert_runtime_setting_and_return_procedure::upsert_runtime_setting_and_return; pub use upsert_runtime_snapshot_and_return_procedure::upsert_runtime_snapshot_and_return; -pub use user_account_type::UserAccount; -pub use user_browse_history_type::UserBrowseHistory; #[derive(Clone, PartialEq, Debug)] @@ -982,80 +1023,81 @@ pub use user_browse_history_type::UserBrowseHistory; /// to indicate which reducer caused the event. pub enum Reducer { - AcceptQuest { + AcceptQuest { input: QuestRecordInput, - }, +} , AcknowledgeQuestCompletion { input: QuestCompletionAckInput, - }, +} , ApplyChapterProgressionLedgerEntry { input: ChapterProgressionLedgerInput, - }, +} , ApplyInventoryMutation { input: InventoryMutationInput, - }, +} , ApplyQuestSignal { input: QuestSignalApplyInput, - }, +} , BeginStorySession { input: StorySessionInput, - }, +} , BindAssetObjectToEntity { input: AssetEntityBindingInput, - }, +} , ConfirmAssetObject { input: AssetObjectUpsertInput, - }, +} , ContinueStory { input: StoryContinueInput, - }, +} , CreateAiTask { input: AiTaskCreateInput, - }, +} , CreateBattleState { input: BattleStateInput, - }, +} , GrantPlayerProgressionExperience { input: PlayerProgressionGrantInput, - }, +} , PublishCustomWorldProfile { input: CustomWorldProfilePublishInput, - }, +} , ResolveCombatAction { input: ResolveCombatActionInput, - }, +} , ResolveNpcInteraction { input: ResolveNpcInteractionInput, - }, +} , ResolveNpcSocialAction { input: ResolveNpcSocialActionInput, - }, +} , ResolveTreasureInteraction { input: TreasureResolveInput, - }, +} , StartAiTask { input: AiTaskStartInput, - }, +} , StartAiTaskStage { input: AiTaskStageStartInput, - }, +} , TurnInQuest { input: QuestTurnInInput, - }, +} , UnpublishCustomWorldProfile { input: CustomWorldProfileUnpublishInput, - }, +} , UpsertChapterProgression { input: ChapterProgressionInput, - }, +} , UpsertCustomWorldProfile { input: CustomWorldProfileUpsertInput, - }, +} , UpsertNpcState { input: NpcStateUpsertInput, - }, +} , } + impl __sdk::InModule for Reducer { type Module = RemoteModule; } @@ -1063,11 +1105,9 @@ impl __sdk::InModule for Reducer { impl __sdk::Reducer for Reducer { fn reducer_name(&self) -> &'static str { match self { - Reducer::AcceptQuest { .. } => "accept_quest", + Reducer::AcceptQuest { .. } => "accept_quest", Reducer::AcknowledgeQuestCompletion { .. } => "acknowledge_quest_completion", - Reducer::ApplyChapterProgressionLedgerEntry { .. } => { - "apply_chapter_progression_ledger_entry" - } + Reducer::ApplyChapterProgressionLedgerEntry { .. } => "apply_chapter_progression_ledger_entry", Reducer::ApplyInventoryMutation { .. } => "apply_inventory_mutation", Reducer::ApplyQuestSignal { .. } => "apply_quest_signal", Reducer::BeginStorySession { .. } => "begin_story_session", @@ -1076,9 +1116,7 @@ impl __sdk::Reducer for Reducer { Reducer::ContinueStory { .. } => "continue_story", Reducer::CreateAiTask { .. } => "create_ai_task", Reducer::CreateBattleState { .. } => "create_battle_state", - Reducer::GrantPlayerProgressionExperience { .. } => { - "grant_player_progression_experience" - } + Reducer::GrantPlayerProgressionExperience { .. } => "grant_player_progression_experience", Reducer::PublishCustomWorldProfile { .. } => "publish_custom_world_profile", Reducer::ResolveCombatAction { .. } => "resolve_combat_action", Reducer::ResolveNpcInteraction { .. } => "resolve_npc_interaction", @@ -1092,10 +1130,10 @@ impl __sdk::Reducer for Reducer { Reducer::UpsertCustomWorldProfile { .. } => "upsert_custom_world_profile", Reducer::UpsertNpcState { .. } => "upsert_npc_state", _ => unreachable!(), - } - } +} +} #[allow(clippy::clone_on_copy)] - fn args_bsatn(&self) -> Result, __sats::bsatn::EncodeError> { +fn args_bsatn(&self) -> Result, __sats::bsatn::EncodeError> { match self { Reducer::AcceptQuest{ input, @@ -1219,33 +1257,38 @@ impl __sdk::Reducer for Reducer { }), _ => unreachable!(), } - } +} } #[derive(Default, Debug)] #[allow(non_snake_case)] #[doc(hidden)] pub struct DbUpdate { + ai_task_event: __sdk::TableUpdate, + big_fish_event: __sdk::TableUpdate, custom_world_gallery_entry: __sdk::TableUpdate, + puzzle_event: __sdk::TableUpdate, } + impl TryFrom<__ws::v2::TransactionUpdate> for DbUpdate { type Error = __sdk::Error; fn try_from(raw: __ws::v2::TransactionUpdate) -> Result { let mut db_update = DbUpdate::default(); for table_update in __sdk::transaction_update_iter_table_updates(raw) { match &table_update.table_name[..] { - "custom_world_gallery_entry" => db_update.custom_world_gallery_entry.append( - custom_world_gallery_entry_table::parse_table_update(table_update)?, - ), + + "ai_task_event" => db_update.ai_task_event.append(ai_task_event_table::parse_table_update(table_update)?), + "big_fish_event" => db_update.big_fish_event.append(big_fish_event_table::parse_table_update(table_update)?), + "custom_world_gallery_entry" => db_update.custom_world_gallery_entry.append(custom_world_gallery_entry_table::parse_table_update(table_update)?), + "puzzle_event" => db_update.puzzle_event.append(puzzle_event_table::parse_table_update(table_update)?), unknown => { return Err(__sdk::InternalError::unknown_name( "table", unknown, "DatabaseUpdate", - ) - .into()); + ).into()); } } } @@ -1258,80 +1301,65 @@ impl __sdk::InModule for DbUpdate { } impl __sdk::DbUpdate for DbUpdate { - fn apply_to_client_cache( - &self, - cache: &mut __sdk::ClientCache, - ) -> AppliedDiff<'_> { - let mut diff = AppliedDiff::default(); + fn apply_to_client_cache(&self, cache: &mut __sdk::ClientCache) -> AppliedDiff<'_> { + let mut diff = AppliedDiff::default(); + + diff.ai_task_event = self.ai_task_event.into_event_diff(); + diff.big_fish_event = self.big_fish_event.into_event_diff(); + diff.custom_world_gallery_entry = cache.apply_diff_to_table::("custom_world_gallery_entry", &self.custom_world_gallery_entry).with_updates_by_pk(|row| &row.profile_id); + diff.puzzle_event = self.puzzle_event.into_event_diff(); - diff.custom_world_gallery_entry = cache - .apply_diff_to_table::( - "custom_world_gallery_entry", - &self.custom_world_gallery_entry, - ) - .with_updates_by_pk(|row| &row.profile_id); - - diff - } - fn parse_initial_rows(raw: __ws::v2::QueryRows) -> __sdk::Result { - let mut db_update = DbUpdate::default(); - for table_rows in raw.tables { - match &table_rows.table[..] { - "custom_world_gallery_entry" => db_update - .custom_world_gallery_entry - .append(__sdk::parse_row_list_as_inserts(table_rows.rows)?), - unknown => { - return Err( - __sdk::InternalError::unknown_name("table", unknown, "QueryRows").into(), - ); + diff } - } - } - Ok(db_update) - } - fn parse_unsubscribe_rows(raw: __ws::v2::QueryRows) -> __sdk::Result { - let mut db_update = DbUpdate::default(); - for table_rows in raw.tables { +fn parse_initial_rows(raw: __ws::v2::QueryRows) -> __sdk::Result { + let mut db_update = DbUpdate::default(); +for table_rows in raw.tables { match &table_rows.table[..] { - "custom_world_gallery_entry" => db_update - .custom_world_gallery_entry - .append(__sdk::parse_row_list_as_deletes(table_rows.rows)?), - unknown => { - return Err( - __sdk::InternalError::unknown_name("table", unknown, "QueryRows").into(), - ); - } - } - } - Ok(db_update) - } + "ai_task_event" => db_update.ai_task_event.append(__sdk::parse_row_list_as_inserts(table_rows.rows)?), + "big_fish_event" => db_update.big_fish_event.append(__sdk::parse_row_list_as_inserts(table_rows.rows)?), + "custom_world_gallery_entry" => db_update.custom_world_gallery_entry.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)?), + unknown => { return Err(__sdk::InternalError::unknown_name("table", unknown, "QueryRows").into()); } +}} Ok(db_update) +} +fn parse_unsubscribe_rows(raw: __ws::v2::QueryRows) -> __sdk::Result { + let mut db_update = DbUpdate::default(); +for table_rows in raw.tables { + match &table_rows.table[..] { + "ai_task_event" => db_update.ai_task_event.append(__sdk::parse_row_list_as_deletes(table_rows.rows)?), + "big_fish_event" => db_update.big_fish_event.append(__sdk::parse_row_list_as_deletes(table_rows.rows)?), + "custom_world_gallery_entry" => db_update.custom_world_gallery_entry.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)?), + unknown => { return Err(__sdk::InternalError::unknown_name("table", unknown, "QueryRows").into()); } +}} Ok(db_update) +} } #[derive(Default)] #[allow(non_snake_case)] #[doc(hidden)] pub struct AppliedDiff<'r> { + ai_task_event: __sdk::TableAppliedDiff<'r, AiTaskEvent>, + big_fish_event: __sdk::TableAppliedDiff<'r, BigFishEvent>, custom_world_gallery_entry: __sdk::TableAppliedDiff<'r, CustomWorldGalleryEntry>, + puzzle_event: __sdk::TableAppliedDiff<'r, PuzzleEvent>, __unused: std::marker::PhantomData<&'r ()>, } + impl __sdk::InModule for AppliedDiff<'_> { type Module = RemoteModule; } impl<'r> __sdk::AppliedDiff<'r> for AppliedDiff<'r> { - fn invoke_row_callbacks( - &self, - event: &EventContext, - callbacks: &mut __sdk::DbCallbacks, - ) { - callbacks.invoke_table_row_callbacks::( - "custom_world_gallery_entry", - &self.custom_world_gallery_entry, - event, - ); - } + fn invoke_row_callbacks(&self, event: &EventContext, callbacks: &mut __sdk::DbCallbacks) { + callbacks.invoke_table_row_callbacks::("ai_task_event", &self.ai_task_event, event); + callbacks.invoke_table_row_callbacks::("big_fish_event", &self.big_fish_event, event); + callbacks.invoke_table_row_callbacks::("custom_world_gallery_entry", &self.custom_world_gallery_entry, event); + callbacks.invoke_table_row_callbacks::("puzzle_event", &self.puzzle_event, event); } +} + #[doc(hidden)] #[derive(Debug)] @@ -1380,16 +1408,10 @@ impl __sdk::InModule for RemoteTables { /// /// - [`DbConnection::frame_tick`]. #[cfg_attr(not(target_arch = "wasm32"), doc = "- [`DbConnection::run_threaded`].")] -#[cfg_attr( - target_arch = "wasm32", - doc = "- [`DbConnection::run_background_task`]." -)] +#[cfg_attr(target_arch = "wasm32", doc = "- [`DbConnection::run_background_task`].")] /// - [`DbConnection::run_async`]. /// - [`DbConnection::advance_one_message`]. -#[cfg_attr( - not(target_arch = "wasm32"), - doc = "- [`DbConnection::advance_one_message_blocking`]." -)] +#[cfg_attr(not(target_arch = "wasm32"), doc = "- [`DbConnection::advance_one_message_blocking`].")] /// - [`DbConnection::advance_one_message_async`]. /// /// Which of these methods you should call depends on the specific needs of your application, @@ -1576,6 +1598,7 @@ impl __sdk::SubscriptionHandle for SubscriptionHandle { fn unsubscribe(self) -> __sdk::Result<()> { self.imp.unsubscribe_then(None) } + } /// Alias trait for a [`__sdk::DbContext`] connected to this module, @@ -1583,23 +1606,17 @@ impl __sdk::SubscriptionHandle for SubscriptionHandle { /// /// Users can use this trait as a boundary on definitions which should accept /// either a [`DbConnection`] or an [`EventContext`] and operate on either. -pub trait RemoteDbContext: - __sdk::DbContext< +pub trait RemoteDbContext: __sdk::DbContext< DbView = RemoteTables, Reducers = RemoteReducers, SubscriptionBuilder = __sdk::SubscriptionBuilder, -> -{ -} -impl< - Ctx: __sdk::DbContext< - DbView = RemoteTables, - Reducers = RemoteReducers, - SubscriptionBuilder = __sdk::SubscriptionBuilder, - >, - > RemoteDbContext for Ctx -{ -} +> {} +impl, +>> RemoteDbContext for Ctx {} + /// An [`__sdk::DbContext`] augmented with a [`__sdk::Event`], /// passed to [`__sdk::Table::on_insert`], [`__sdk::Table::on_delete`] and [`__sdk::TableWithPrimaryKey::on_update`] callbacks. @@ -1974,6 +1991,7 @@ impl __sdk::DbContext for ErrorContext { impl __sdk::ErrorContext for ErrorContext {} impl __sdk::SpacetimeModule for RemoteModule { + type DbConnection = DbConnection; type EventContext = EventContext; type ReducerEventContext = ReducerEventContext; @@ -1989,8 +2007,16 @@ impl __sdk::SpacetimeModule for RemoteModule { type SubscriptionHandle = SubscriptionHandle; type QueryBuilder = __sdk::QueryBuilder; - fn register_tables(client_cache: &mut __sdk::ClientCache) { +fn register_tables(client_cache: &mut __sdk::ClientCache) { + ai_task_event_table::register_table(client_cache); + big_fish_event_table::register_table(client_cache); custom_world_gallery_entry_table::register_table(client_cache); - } - const ALL_TABLE_NAMES: &'static [&'static str] = &["custom_world_gallery_entry"]; + puzzle_event_table::register_table(client_cache); +} +const ALL_TABLE_NAMES: &'static [&'static str] = &[ + "ai_task_event", + "big_fish_event", + "custom_world_gallery_entry", + "puzzle_event", +]; } diff --git a/server-rs/crates/spacetime-client/src/module_bindings/npc_battle_interaction_procedure_result_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/npc_battle_interaction_procedure_result_type.rs index d5f16a3f..1682f3ac 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/npc_battle_interaction_procedure_result_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/npc_battle_interaction_procedure_result_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::npc_battle_interaction_result_type::NpcBattleInteractionResult; @@ -10,10 +15,12 @@ use super::npc_battle_interaction_result_type::NpcBattleInteractionResult; #[sats(crate = __lib)] pub struct NpcBattleInteractionProcedureResult { pub ok: bool, - pub result: Option, - pub error_message: Option, + pub result: Option::, + pub error_message: Option::, } + impl __sdk::InModule for NpcBattleInteractionProcedureResult { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/npc_battle_interaction_result_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/npc_battle_interaction_result_type.rs index c276f969..fc8ca1c9 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/npc_battle_interaction_result_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/npc_battle_interaction_result_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::battle_state_snapshot_type::BattleStateSnapshot; use super::npc_interaction_result_type::NpcInteractionResult; @@ -14,6 +19,8 @@ pub struct NpcBattleInteractionResult { pub battle_state: BattleStateSnapshot, } + impl __sdk::InModule for NpcBattleInteractionResult { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/npc_interaction_battle_mode_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/npc_interaction_battle_mode_type.rs index dc8e8819..e891d6a9 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/npc_interaction_battle_mode_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/npc_interaction_battle_mode_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -11,8 +16,12 @@ pub enum NpcInteractionBattleMode { Fight, Spar, + } + + impl __sdk::InModule for NpcInteractionBattleMode { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/npc_interaction_procedure_result_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/npc_interaction_procedure_result_type.rs index 383dce2a..ef11fa63 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/npc_interaction_procedure_result_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/npc_interaction_procedure_result_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::npc_interaction_result_type::NpcInteractionResult; @@ -10,10 +15,12 @@ use super::npc_interaction_result_type::NpcInteractionResult; #[sats(crate = __lib)] pub struct NpcInteractionProcedureResult { pub ok: bool, - pub result: Option, - pub error_message: Option, + pub result: Option::, + pub error_message: Option::, } + impl __sdk::InModule for NpcInteractionProcedureResult { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/npc_interaction_result_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/npc_interaction_result_type.rs index 83624807..d457b697 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/npc_interaction_result_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/npc_interaction_result_type.rs @@ -2,11 +2,16 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; -use super::npc_interaction_battle_mode_type::NpcInteractionBattleMode; -use super::npc_interaction_status_type::NpcInteractionStatus; use super::npc_state_snapshot_type::NpcStateSnapshot; +use super::npc_interaction_status_type::NpcInteractionStatus; +use super::npc_interaction_battle_mode_type::NpcInteractionBattleMode; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -15,14 +20,16 @@ pub struct NpcInteractionResult { pub interaction_status: NpcInteractionStatus, pub action_text: String, pub result_text: String, - pub story_text: Option, - pub battle_mode: Option, + pub story_text: Option::, + pub battle_mode: Option::, pub encounter_closed: bool, pub affinity_changed: bool, pub previous_affinity: i32, pub next_affinity: i32, } + impl __sdk::InModule for NpcInteractionResult { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/npc_interaction_status_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/npc_interaction_status_type.rs index 8032abf9..cba6b5ac 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/npc_interaction_status_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/npc_interaction_status_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -19,8 +24,12 @@ pub enum NpcInteractionStatus { BattlePending, Left, + } + + impl __sdk::InModule for NpcInteractionStatus { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/npc_relation_stance_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/npc_relation_stance_type.rs index 48e8ac70..2a45f52c 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/npc_relation_stance_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/npc_relation_stance_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -17,8 +22,12 @@ pub enum NpcRelationStance { Cooperative, Bonded, + } + + impl __sdk::InModule for NpcRelationStance { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/npc_relation_state_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/npc_relation_state_type.rs index b6bbc07d..cccb0cc5 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/npc_relation_state_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/npc_relation_state_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::npc_relation_stance_type::NpcRelationStance; @@ -13,6 +18,8 @@ pub struct NpcRelationState { pub stance: NpcRelationStance, } + impl __sdk::InModule for NpcRelationState { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/npc_social_action_kind_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/npc_social_action_kind_type.rs index 9c6d2c1e..cc79b8c5 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/npc_social_action_kind_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/npc_social_action_kind_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -17,8 +22,12 @@ pub enum NpcSocialActionKind { Recruit, QuestAccept, + } + + impl __sdk::InModule for NpcSocialActionKind { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/npc_stance_profile_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/npc_stance_profile_type.rs index 73177673..5a0d7497 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/npc_stance_profile_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/npc_stance_profile_type.rs @@ -2,7 +2,13 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; + #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -12,11 +18,13 @@ pub struct NpcStanceProfile { pub ideological_fit: u8, pub fear_or_guard: u8, pub loyalty: u8, - pub current_conflict_tag: Option, - pub recent_approvals: Vec, - pub recent_disapprovals: Vec, + pub current_conflict_tag: Option::, + pub recent_approvals: Vec::, + pub recent_disapprovals: Vec::, } + impl __sdk::InModule for NpcStanceProfile { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/npc_state_procedure_result_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/npc_state_procedure_result_type.rs index c4e60bef..39a8ed60 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/npc_state_procedure_result_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/npc_state_procedure_result_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::npc_state_snapshot_type::NpcStateSnapshot; @@ -10,10 +15,12 @@ use super::npc_state_snapshot_type::NpcStateSnapshot; #[sats(crate = __lib)] pub struct NpcStateProcedureResult { pub ok: bool, - pub record: Option, - pub error_message: Option, + pub record: Option::, + pub error_message: Option::, } + impl __sdk::InModule for NpcStateProcedureResult { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/npc_state_snapshot_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/npc_state_snapshot_type.rs index 351b45ae..c11e0ef8 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/npc_state_snapshot_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/npc_state_snapshot_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::npc_relation_state_type::NpcRelationState; use super::npc_stance_profile_type::NpcStanceProfile; @@ -20,16 +25,18 @@ pub struct NpcStateSnapshot { pub chatted_count: u32, pub gifts_given: u32, pub recruited: bool, - pub trade_stock_signature: Option, - pub revealed_facts: Vec, - pub known_attribute_rumors: Vec, + pub trade_stock_signature: Option::, + pub revealed_facts: Vec::, + pub known_attribute_rumors: Vec::, pub first_meaningful_contact_resolved: bool, - pub seen_backstory_chapter_ids: Vec, + pub seen_backstory_chapter_ids: Vec::, pub stance_profile: NpcStanceProfile, pub created_at_micros: i64, pub updated_at_micros: i64, } + impl __sdk::InModule for NpcStateSnapshot { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/npc_state_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/npc_state_type.rs index 1ddeec42..9e8f87ae 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/npc_state_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/npc_state_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::npc_relation_state_type::NpcRelationState; use super::npc_stance_profile_type::NpcStanceProfile; @@ -20,20 +25,22 @@ pub struct NpcState { pub chatted_count: u32, pub gifts_given: u32, pub recruited: bool, - pub trade_stock_signature: Option, - pub revealed_facts: Vec, - pub known_attribute_rumors: Vec, + pub trade_stock_signature: Option::, + pub revealed_facts: Vec::, + pub known_attribute_rumors: Vec::, pub first_meaningful_contact_resolved: bool, - pub seen_backstory_chapter_ids: Vec, + pub seen_backstory_chapter_ids: Vec::, pub stance_profile: NpcStanceProfile, pub created_at: __sdk::Timestamp, pub updated_at: __sdk::Timestamp, } + impl __sdk::InModule for NpcState { type Module = super::RemoteModule; } + /// Column accessor struct for the table `NpcState`. /// /// Provides typed access to columns for query building. @@ -48,11 +55,11 @@ pub struct NpcStateCols { pub chatted_count: __sdk::__query_builder::Col, pub gifts_given: __sdk::__query_builder::Col, pub recruited: __sdk::__query_builder::Col, - pub trade_stock_signature: __sdk::__query_builder::Col>, - pub revealed_facts: __sdk::__query_builder::Col>, - pub known_attribute_rumors: __sdk::__query_builder::Col>, + pub trade_stock_signature: __sdk::__query_builder::Col>, + pub revealed_facts: __sdk::__query_builder::Col>, + pub known_attribute_rumors: __sdk::__query_builder::Col>, pub first_meaningful_contact_resolved: __sdk::__query_builder::Col, - pub seen_backstory_chapter_ids: __sdk::__query_builder::Col>, + pub seen_backstory_chapter_ids: __sdk::__query_builder::Col>, pub stance_profile: __sdk::__query_builder::Col, pub created_at: __sdk::__query_builder::Col, pub updated_at: __sdk::__query_builder::Col, @@ -72,26 +79,15 @@ impl __sdk::__query_builder::HasCols for NpcState { chatted_count: __sdk::__query_builder::Col::new(table_name, "chatted_count"), gifts_given: __sdk::__query_builder::Col::new(table_name, "gifts_given"), recruited: __sdk::__query_builder::Col::new(table_name, "recruited"), - trade_stock_signature: __sdk::__query_builder::Col::new( - table_name, - "trade_stock_signature", - ), + trade_stock_signature: __sdk::__query_builder::Col::new(table_name, "trade_stock_signature"), revealed_facts: __sdk::__query_builder::Col::new(table_name, "revealed_facts"), - known_attribute_rumors: __sdk::__query_builder::Col::new( - table_name, - "known_attribute_rumors", - ), - first_meaningful_contact_resolved: __sdk::__query_builder::Col::new( - table_name, - "first_meaningful_contact_resolved", - ), - seen_backstory_chapter_ids: __sdk::__query_builder::Col::new( - table_name, - "seen_backstory_chapter_ids", - ), + known_attribute_rumors: __sdk::__query_builder::Col::new(table_name, "known_attribute_rumors"), + first_meaningful_contact_resolved: __sdk::__query_builder::Col::new(table_name, "first_meaningful_contact_resolved"), + seen_backstory_chapter_ids: __sdk::__query_builder::Col::new(table_name, "seen_backstory_chapter_ids"), stance_profile: __sdk::__query_builder::Col::new(table_name, "stance_profile"), created_at: __sdk::__query_builder::Col::new(table_name, "created_at"), updated_at: __sdk::__query_builder::Col::new(table_name, "updated_at"), + } } } @@ -111,12 +107,11 @@ impl __sdk::__query_builder::HasIxCols for NpcState { NpcStateIxCols { npc_id: __sdk::__query_builder::IxCol::new(table_name, "npc_id"), npc_state_id: __sdk::__query_builder::IxCol::new(table_name, "npc_state_id"), - runtime_session_id: __sdk::__query_builder::IxCol::new( - table_name, - "runtime_session_id", - ), + runtime_session_id: __sdk::__query_builder::IxCol::new(table_name, "runtime_session_id"), + } } } impl __sdk::__query_builder::CanBeLookupTable for NpcState {} + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/npc_state_upsert_input_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/npc_state_upsert_input_type.rs index c31346e7..ff59315a 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/npc_state_upsert_input_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/npc_state_upsert_input_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::npc_stance_profile_type::NpcStanceProfile; @@ -17,15 +22,17 @@ pub struct NpcStateUpsertInput { pub chatted_count: u32, pub gifts_given: u32, pub recruited: bool, - pub trade_stock_signature: Option, - pub revealed_facts: Vec, - pub known_attribute_rumors: Vec, + pub trade_stock_signature: Option::, + pub revealed_facts: Vec::, + pub known_attribute_rumors: Vec::, pub first_meaningful_contact_resolved: bool, - pub seen_backstory_chapter_ids: Vec, - pub stance_profile: Option, + pub seen_backstory_chapter_ids: Vec::, + pub stance_profile: Option::, pub updated_at_micros: i64, } + impl __sdk::InModule for NpcStateUpsertInput { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/player_progression_get_input_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/player_progression_get_input_type.rs index a4dd27f5..eb031dc3 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/player_progression_get_input_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/player_progression_get_input_type.rs @@ -2,7 +2,13 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; + #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -10,6 +16,8 @@ pub struct PlayerProgressionGetInput { pub user_id: String, } + impl __sdk::InModule for PlayerProgressionGetInput { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/player_progression_grant_input_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/player_progression_grant_input_type.rs index b5761753..66a8696c 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/player_progression_grant_input_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/player_progression_grant_input_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::player_progression_grant_source_type::PlayerProgressionGrantSource; @@ -15,6 +20,8 @@ pub struct PlayerProgressionGrantInput { pub updated_at_micros: i64, } + impl __sdk::InModule for PlayerProgressionGrantInput { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/player_progression_grant_source_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/player_progression_grant_source_type.rs index bf3ae257..c45b9dd6 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/player_progression_grant_source_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/player_progression_grant_source_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -11,8 +16,12 @@ pub enum PlayerProgressionGrantSource { Quest, HostileNpc, + } + + impl __sdk::InModule for PlayerProgressionGrantSource { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/player_progression_procedure_result_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/player_progression_procedure_result_type.rs index 413c1208..0f3686ec 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/player_progression_procedure_result_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/player_progression_procedure_result_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::player_progression_snapshot_type::PlayerProgressionSnapshot; @@ -10,10 +15,12 @@ use super::player_progression_snapshot_type::PlayerProgressionSnapshot; #[sats(crate = __lib)] pub struct PlayerProgressionProcedureResult { pub ok: bool, - pub record: Option, - pub error_message: Option, + pub record: Option::, + pub error_message: Option::, } + impl __sdk::InModule for PlayerProgressionProcedureResult { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/player_progression_snapshot_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/player_progression_snapshot_type.rs index 1361178e..116938c5 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/player_progression_snapshot_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/player_progression_snapshot_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::player_progression_grant_source_type::PlayerProgressionGrantSource; @@ -15,11 +20,13 @@ pub struct PlayerProgressionSnapshot { pub total_xp: u32, pub xp_to_next_level: u32, pub pending_level_ups: u32, - pub last_granted_source: Option, + pub last_granted_source: Option::, pub created_at_micros: i64, pub updated_at_micros: i64, } + impl __sdk::InModule for PlayerProgressionSnapshot { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/player_progression_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/player_progression_type.rs index c734d1c2..04af99cc 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/player_progression_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/player_progression_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::player_progression_grant_source_type::PlayerProgressionGrantSource; @@ -15,15 +20,17 @@ pub struct PlayerProgression { pub total_xp: u32, pub xp_to_next_level: u32, pub pending_level_ups: u32, - pub last_granted_source: Option, + pub last_granted_source: Option::, pub created_at: __sdk::Timestamp, pub updated_at: __sdk::Timestamp, } + impl __sdk::InModule for PlayerProgression { type Module = super::RemoteModule; } + /// Column accessor struct for the table `PlayerProgression`. /// /// Provides typed access to columns for query building. @@ -34,8 +41,7 @@ pub struct PlayerProgressionCols { pub total_xp: __sdk::__query_builder::Col, pub xp_to_next_level: __sdk::__query_builder::Col, pub pending_level_ups: __sdk::__query_builder::Col, - pub last_granted_source: - __sdk::__query_builder::Col>, + pub last_granted_source: __sdk::__query_builder::Col>, pub created_at: __sdk::__query_builder::Col, pub updated_at: __sdk::__query_builder::Col, } @@ -50,12 +56,10 @@ impl __sdk::__query_builder::HasCols for PlayerProgression { total_xp: __sdk::__query_builder::Col::new(table_name, "total_xp"), xp_to_next_level: __sdk::__query_builder::Col::new(table_name, "xp_to_next_level"), pending_level_ups: __sdk::__query_builder::Col::new(table_name, "pending_level_ups"), - last_granted_source: __sdk::__query_builder::Col::new( - table_name, - "last_granted_source", - ), + last_granted_source: __sdk::__query_builder::Col::new(table_name, "last_granted_source"), created_at: __sdk::__query_builder::Col::new(table_name, "created_at"), updated_at: __sdk::__query_builder::Col::new(table_name, "updated_at"), + } } } @@ -72,8 +76,10 @@ impl __sdk::__query_builder::HasIxCols for PlayerProgression { fn ix_cols(table_name: &'static str) -> Self::IxCols { PlayerProgressionIxCols { user_id: __sdk::__query_builder::IxCol::new(table_name, "user_id"), + } } } impl __sdk::__query_builder::CanBeLookupTable for PlayerProgression {} + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/profile_dashboard_state_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/profile_dashboard_state_type.rs index e5124048..3abd8b06 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/profile_dashboard_state_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/profile_dashboard_state_type.rs @@ -2,7 +2,13 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; + #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -14,10 +20,12 @@ pub struct ProfileDashboardState { pub updated_at: __sdk::Timestamp, } + impl __sdk::InModule for ProfileDashboardState { type Module = super::RemoteModule; } + /// Column accessor struct for the table `ProfileDashboardState`. /// /// Provides typed access to columns for query building. @@ -38,6 +46,7 @@ impl __sdk::__query_builder::HasCols for ProfileDashboardState { total_play_time_ms: __sdk::__query_builder::Col::new(table_name, "total_play_time_ms"), created_at: __sdk::__query_builder::Col::new(table_name, "created_at"), updated_at: __sdk::__query_builder::Col::new(table_name, "updated_at"), + } } } @@ -54,8 +63,10 @@ impl __sdk::__query_builder::HasIxCols for ProfileDashboardState { fn ix_cols(table_name: &'static str) -> Self::IxCols { ProfileDashboardStateIxCols { user_id: __sdk::__query_builder::IxCol::new(table_name, "user_id"), + } } } impl __sdk::__query_builder::CanBeLookupTable for ProfileDashboardState {} + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/profile_invite_code_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/profile_invite_code_type.rs index e556d2f7..1a3ba152 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/profile_invite_code_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/profile_invite_code_type.rs @@ -2,7 +2,13 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; + #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -13,10 +19,12 @@ pub struct ProfileInviteCode { pub updated_at: __sdk::Timestamp, } + impl __sdk::InModule for ProfileInviteCode { type Module = super::RemoteModule; } + /// Column accessor struct for the table `ProfileInviteCode`. /// /// Provides typed access to columns for query building. @@ -35,6 +43,7 @@ impl __sdk::__query_builder::HasCols for ProfileInviteCode { invite_code: __sdk::__query_builder::Col::new(table_name, "invite_code"), created_at: __sdk::__query_builder::Col::new(table_name, "created_at"), updated_at: __sdk::__query_builder::Col::new(table_name, "updated_at"), + } } } @@ -53,8 +62,10 @@ impl __sdk::__query_builder::HasIxCols for ProfileInviteCode { ProfileInviteCodeIxCols { invite_code: __sdk::__query_builder::IxCol::new(table_name, "invite_code"), user_id: __sdk::__query_builder::IxCol::new(table_name, "user_id"), + } } } impl __sdk::__query_builder::CanBeLookupTable for ProfileInviteCode {} + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/profile_membership_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/profile_membership_type.rs index e0889718..e09b6ebe 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/profile_membership_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/profile_membership_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::runtime_profile_membership_status_type::RuntimeProfileMembershipStatus; use super::runtime_profile_membership_tier_type::RuntimeProfileMembershipTier; @@ -18,10 +23,12 @@ pub struct ProfileMembership { pub updated_at: __sdk::Timestamp, } + impl __sdk::InModule for ProfileMembership { type Module = super::RemoteModule; } + /// Column accessor struct for the table `ProfileMembership`. /// /// Provides typed access to columns for query building. @@ -44,6 +51,7 @@ impl __sdk::__query_builder::HasCols for ProfileMembership { started_at: __sdk::__query_builder::Col::new(table_name, "started_at"), expires_at: __sdk::__query_builder::Col::new(table_name, "expires_at"), updated_at: __sdk::__query_builder::Col::new(table_name, "updated_at"), + } } } @@ -60,8 +68,10 @@ impl __sdk::__query_builder::HasIxCols for ProfileMembership { fn ix_cols(table_name: &'static str) -> Self::IxCols { ProfileMembershipIxCols { user_id: __sdk::__query_builder::IxCol::new(table_name, "user_id"), + } } } impl __sdk::__query_builder::CanBeLookupTable for ProfileMembership {} + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/profile_played_world_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/profile_played_world_type.rs index 3b94036c..b58b66c1 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/profile_played_world_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/profile_played_world_type.rs @@ -2,7 +2,13 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; + #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -10,9 +16,9 @@ pub struct ProfilePlayedWorld { pub played_world_id: String, pub user_id: String, pub world_key: String, - pub owner_user_id: Option, - pub profile_id: Option, - pub world_type: Option, + pub owner_user_id: Option::, + pub profile_id: Option::, + pub world_type: Option::, pub world_title: String, pub world_subtitle: String, pub first_played_at: __sdk::Timestamp, @@ -20,10 +26,12 @@ pub struct ProfilePlayedWorld { pub last_observed_play_time_ms: u64, } + impl __sdk::InModule for ProfilePlayedWorld { type Module = super::RemoteModule; } + /// Column accessor struct for the table `ProfilePlayedWorld`. /// /// Provides typed access to columns for query building. @@ -31,9 +39,9 @@ pub struct ProfilePlayedWorldCols { pub played_world_id: __sdk::__query_builder::Col, pub user_id: __sdk::__query_builder::Col, pub world_key: __sdk::__query_builder::Col, - pub owner_user_id: __sdk::__query_builder::Col>, - pub profile_id: __sdk::__query_builder::Col>, - pub world_type: __sdk::__query_builder::Col>, + pub owner_user_id: __sdk::__query_builder::Col>, + pub profile_id: __sdk::__query_builder::Col>, + pub world_type: __sdk::__query_builder::Col>, pub world_title: __sdk::__query_builder::Col, pub world_subtitle: __sdk::__query_builder::Col, pub first_played_at: __sdk::__query_builder::Col, @@ -55,10 +63,8 @@ impl __sdk::__query_builder::HasCols for ProfilePlayedWorld { world_subtitle: __sdk::__query_builder::Col::new(table_name, "world_subtitle"), first_played_at: __sdk::__query_builder::Col::new(table_name, "first_played_at"), last_played_at: __sdk::__query_builder::Col::new(table_name, "last_played_at"), - last_observed_play_time_ms: __sdk::__query_builder::Col::new( - table_name, - "last_observed_play_time_ms", - ), + last_observed_play_time_ms: __sdk::__query_builder::Col::new(table_name, "last_observed_play_time_ms"), + } } } @@ -77,8 +83,10 @@ impl __sdk::__query_builder::HasIxCols for ProfilePlayedWorld { ProfilePlayedWorldIxCols { played_world_id: __sdk::__query_builder::IxCol::new(table_name, "played_world_id"), user_id: __sdk::__query_builder::IxCol::new(table_name, "user_id"), + } } } impl __sdk::__query_builder::CanBeLookupTable for ProfilePlayedWorld {} + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/profile_recharge_order_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/profile_recharge_order_type.rs index fbca5da3..8140f320 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/profile_recharge_order_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/profile_recharge_order_type.rs @@ -2,10 +2,15 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; -use super::runtime_profile_recharge_order_status_type::RuntimeProfileRechargeOrderStatus; use super::runtime_profile_recharge_product_kind_type::RuntimeProfileRechargeProductKind; +use super::runtime_profile_recharge_order_status_type::RuntimeProfileRechargeOrderStatus; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -21,13 +26,15 @@ pub struct ProfileRechargeOrder { pub paid_at: __sdk::Timestamp, pub created_at: __sdk::Timestamp, pub points_delta: i64, - pub membership_expires_at: Option<__sdk::Timestamp>, + pub membership_expires_at: Option::<__sdk::Timestamp>, } + impl __sdk::InModule for ProfileRechargeOrder { type Module = super::RemoteModule; } + /// Column accessor struct for the table `ProfileRechargeOrder`. /// /// Provides typed access to columns for query building. @@ -38,14 +45,12 @@ pub struct ProfileRechargeOrderCols { pub product_title: __sdk::__query_builder::Col, pub kind: __sdk::__query_builder::Col, pub amount_cents: __sdk::__query_builder::Col, - pub status: - __sdk::__query_builder::Col, + pub status: __sdk::__query_builder::Col, pub payment_channel: __sdk::__query_builder::Col, pub paid_at: __sdk::__query_builder::Col, pub created_at: __sdk::__query_builder::Col, pub points_delta: __sdk::__query_builder::Col, - pub membership_expires_at: - __sdk::__query_builder::Col>, + pub membership_expires_at: __sdk::__query_builder::Col>, } impl __sdk::__query_builder::HasCols for ProfileRechargeOrder { @@ -63,10 +68,8 @@ impl __sdk::__query_builder::HasCols for ProfileRechargeOrder { paid_at: __sdk::__query_builder::Col::new(table_name, "paid_at"), created_at: __sdk::__query_builder::Col::new(table_name, "created_at"), points_delta: __sdk::__query_builder::Col::new(table_name, "points_delta"), - membership_expires_at: __sdk::__query_builder::Col::new( - table_name, - "membership_expires_at", - ), + membership_expires_at: __sdk::__query_builder::Col::new(table_name, "membership_expires_at"), + } } } @@ -85,8 +88,10 @@ impl __sdk::__query_builder::HasIxCols for ProfileRechargeOrder { ProfileRechargeOrderIxCols { order_id: __sdk::__query_builder::IxCol::new(table_name, "order_id"), user_id: __sdk::__query_builder::IxCol::new(table_name, "user_id"), + } } } impl __sdk::__query_builder::CanBeLookupTable for ProfileRechargeOrder {} + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/profile_redeem_code_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/profile_redeem_code_type.rs index 9be48e6d..27d8736c 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/profile_redeem_code_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/profile_redeem_code_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::runtime_profile_redeem_code_mode_type::RuntimeProfileRedeemCodeMode; @@ -15,16 +20,18 @@ pub struct ProfileRedeemCode { pub max_uses: u32, pub global_used_count: u32, pub enabled: bool, - pub allowed_user_ids: Vec, + pub allowed_user_ids: Vec::, pub created_by: String, pub created_at: __sdk::Timestamp, pub updated_at: __sdk::Timestamp, } + impl __sdk::InModule for ProfileRedeemCode { type Module = super::RemoteModule; } + /// Column accessor struct for the table `ProfileRedeemCode`. /// /// Provides typed access to columns for query building. @@ -35,7 +42,7 @@ pub struct ProfileRedeemCodeCols { pub max_uses: __sdk::__query_builder::Col, pub global_used_count: __sdk::__query_builder::Col, pub enabled: __sdk::__query_builder::Col, - pub allowed_user_ids: __sdk::__query_builder::Col>, + pub allowed_user_ids: __sdk::__query_builder::Col>, pub created_by: __sdk::__query_builder::Col, pub created_at: __sdk::__query_builder::Col, pub updated_at: __sdk::__query_builder::Col, @@ -55,6 +62,7 @@ impl __sdk::__query_builder::HasCols for ProfileRedeemCode { created_by: __sdk::__query_builder::Col::new(table_name, "created_by"), created_at: __sdk::__query_builder::Col::new(table_name, "created_at"), updated_at: __sdk::__query_builder::Col::new(table_name, "updated_at"), + } } } @@ -71,8 +79,10 @@ impl __sdk::__query_builder::HasIxCols for ProfileRedeemCode { fn ix_cols(table_name: &'static str) -> Self::IxCols { ProfileRedeemCodeIxCols { code: __sdk::__query_builder::IxCol::new(table_name, "code"), + } } } impl __sdk::__query_builder::CanBeLookupTable for ProfileRedeemCode {} + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/profile_redeem_code_usage_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/profile_redeem_code_usage_type.rs index cbbd93ae..bdb639d2 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/profile_redeem_code_usage_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/profile_redeem_code_usage_type.rs @@ -2,7 +2,13 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; + #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -14,10 +20,12 @@ pub struct ProfileRedeemCodeUsage { pub created_at: __sdk::Timestamp, } + impl __sdk::InModule for ProfileRedeemCodeUsage { type Module = super::RemoteModule; } + /// Column accessor struct for the table `ProfileRedeemCodeUsage`. /// /// Provides typed access to columns for query building. @@ -38,6 +46,7 @@ impl __sdk::__query_builder::HasCols for ProfileRedeemCodeUsage { user_id: __sdk::__query_builder::Col::new(table_name, "user_id"), amount_granted: __sdk::__query_builder::Col::new(table_name, "amount_granted"), created_at: __sdk::__query_builder::Col::new(table_name, "created_at"), + } } } @@ -58,8 +67,10 @@ impl __sdk::__query_builder::HasIxCols for ProfileRedeemCodeUsage { code: __sdk::__query_builder::IxCol::new(table_name, "code"), usage_id: __sdk::__query_builder::IxCol::new(table_name, "usage_id"), user_id: __sdk::__query_builder::IxCol::new(table_name, "user_id"), + } } } impl __sdk::__query_builder::CanBeLookupTable for ProfileRedeemCodeUsage {} + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/profile_referral_relation_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/profile_referral_relation_type.rs index 12c9b460..6da25f89 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/profile_referral_relation_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/profile_referral_relation_type.rs @@ -2,7 +2,13 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; + #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -15,10 +21,12 @@ pub struct ProfileReferralRelation { pub bound_at: __sdk::Timestamp, } + impl __sdk::InModule for ProfileReferralRelation { type Module = super::RemoteModule; } + /// Column accessor struct for the table `ProfileReferralRelation`. /// /// Provides typed access to columns for query building. @@ -38,15 +46,10 @@ impl __sdk::__query_builder::HasCols for ProfileReferralRelation { invitee_user_id: __sdk::__query_builder::Col::new(table_name, "invitee_user_id"), inviter_user_id: __sdk::__query_builder::Col::new(table_name, "inviter_user_id"), invite_code: __sdk::__query_builder::Col::new(table_name, "invite_code"), - inviter_reward_granted: __sdk::__query_builder::Col::new( - table_name, - "inviter_reward_granted", - ), - invitee_reward_granted: __sdk::__query_builder::Col::new( - table_name, - "invitee_reward_granted", - ), + inviter_reward_granted: __sdk::__query_builder::Col::new(table_name, "inviter_reward_granted"), + invitee_reward_granted: __sdk::__query_builder::Col::new(table_name, "invitee_reward_granted"), bound_at: __sdk::__query_builder::Col::new(table_name, "bound_at"), + } } } @@ -65,8 +68,10 @@ impl __sdk::__query_builder::HasIxCols for ProfileReferralRelation { ProfileReferralRelationIxCols { invitee_user_id: __sdk::__query_builder::IxCol::new(table_name, "invitee_user_id"), inviter_user_id: __sdk::__query_builder::IxCol::new(table_name, "inviter_user_id"), + } } } impl __sdk::__query_builder::CanBeLookupTable for ProfileReferralRelation {} + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/profile_save_archive_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/profile_save_archive_type.rs index 5244fe4e..f11c6024 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/profile_save_archive_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/profile_save_archive_type.rs @@ -2,7 +2,13 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; + #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -10,25 +16,27 @@ pub struct ProfileSaveArchive { pub archive_id: String, pub user_id: String, pub world_key: String, - pub owner_user_id: Option, - pub profile_id: Option, - pub world_type: Option, + pub owner_user_id: Option::, + pub profile_id: Option::, + pub world_type: Option::, pub world_name: String, pub subtitle: String, pub summary_text: String, - pub cover_image_src: Option, + pub cover_image_src: Option::, pub saved_at: __sdk::Timestamp, pub bottom_tab: String, pub game_state_json: String, - pub current_story_json: Option, + pub current_story_json: Option::, pub created_at: __sdk::Timestamp, pub updated_at: __sdk::Timestamp, } + impl __sdk::InModule for ProfileSaveArchive { type Module = super::RemoteModule; } + /// Column accessor struct for the table `ProfileSaveArchive`. /// /// Provides typed access to columns for query building. @@ -36,17 +44,17 @@ pub struct ProfileSaveArchiveCols { pub archive_id: __sdk::__query_builder::Col, pub user_id: __sdk::__query_builder::Col, pub world_key: __sdk::__query_builder::Col, - pub owner_user_id: __sdk::__query_builder::Col>, - pub profile_id: __sdk::__query_builder::Col>, - pub world_type: __sdk::__query_builder::Col>, + pub owner_user_id: __sdk::__query_builder::Col>, + pub profile_id: __sdk::__query_builder::Col>, + pub world_type: __sdk::__query_builder::Col>, pub world_name: __sdk::__query_builder::Col, pub subtitle: __sdk::__query_builder::Col, pub summary_text: __sdk::__query_builder::Col, - pub cover_image_src: __sdk::__query_builder::Col>, + pub cover_image_src: __sdk::__query_builder::Col>, pub saved_at: __sdk::__query_builder::Col, pub bottom_tab: __sdk::__query_builder::Col, pub game_state_json: __sdk::__query_builder::Col, - pub current_story_json: __sdk::__query_builder::Col>, + pub current_story_json: __sdk::__query_builder::Col>, pub created_at: __sdk::__query_builder::Col, pub updated_at: __sdk::__query_builder::Col, } @@ -71,6 +79,7 @@ impl __sdk::__query_builder::HasCols for ProfileSaveArchive { current_story_json: __sdk::__query_builder::Col::new(table_name, "current_story_json"), created_at: __sdk::__query_builder::Col::new(table_name, "created_at"), updated_at: __sdk::__query_builder::Col::new(table_name, "updated_at"), + } } } @@ -89,8 +98,10 @@ impl __sdk::__query_builder::HasIxCols for ProfileSaveArchive { ProfileSaveArchiveIxCols { archive_id: __sdk::__query_builder::IxCol::new(table_name, "archive_id"), user_id: __sdk::__query_builder::IxCol::new(table_name, "user_id"), + } } } impl __sdk::__query_builder::CanBeLookupTable for ProfileSaveArchive {} + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/profile_wallet_ledger_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/profile_wallet_ledger_type.rs index 78364d5a..b5535645 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/profile_wallet_ledger_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/profile_wallet_ledger_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::runtime_profile_wallet_ledger_source_type_type::RuntimeProfileWalletLedgerSourceType; @@ -17,10 +22,12 @@ pub struct ProfileWalletLedger { pub created_at: __sdk::Timestamp, } + impl __sdk::InModule for ProfileWalletLedger { type Module = super::RemoteModule; } + /// Column accessor struct for the table `ProfileWalletLedger`. /// /// Provides typed access to columns for query building. @@ -29,8 +36,7 @@ pub struct ProfileWalletLedgerCols { pub user_id: __sdk::__query_builder::Col, pub amount_delta: __sdk::__query_builder::Col, pub balance_after: __sdk::__query_builder::Col, - pub source_type: - __sdk::__query_builder::Col, + pub source_type: __sdk::__query_builder::Col, pub created_at: __sdk::__query_builder::Col, } @@ -44,6 +50,7 @@ impl __sdk::__query_builder::HasCols for ProfileWalletLedger { balance_after: __sdk::__query_builder::Col::new(table_name, "balance_after"), source_type: __sdk::__query_builder::Col::new(table_name, "source_type"), created_at: __sdk::__query_builder::Col::new(table_name, "created_at"), + } } } @@ -62,8 +69,10 @@ impl __sdk::__query_builder::HasIxCols for ProfileWalletLedger { ProfileWalletLedgerIxCols { user_id: __sdk::__query_builder::IxCol::new(table_name, "user_id"), wallet_ledger_id: __sdk::__query_builder::IxCol::new(table_name, "wallet_ledger_id"), + } } } impl __sdk::__query_builder::CanBeLookupTable for ProfileWalletLedger {} + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/publish_big_fish_game_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/publish_big_fish_game_procedure.rs index e8007288..6a2bfdd8 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/publish_big_fish_game_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/publish_big_fish_game_procedure.rs @@ -2,17 +2,23 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; -use super::big_fish_publish_input_type::BigFishPublishInput; use super::big_fish_session_procedure_result_type::BigFishSessionProcedureResult; +use super::big_fish_publish_input_type::BigFishPublishInput; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] -struct PublishBigFishGameArgs { + struct PublishBigFishGameArgs { pub input: BigFishPublishInput, } + impl __sdk::InModule for PublishBigFishGameArgs { type Module = super::RemoteModule; } @@ -22,19 +28,16 @@ impl __sdk::InModule for PublishBigFishGameArgs { /// /// Implemented for [`super::RemoteProcedures`]. pub trait publish_big_fish_game { - fn publish_big_fish_game(&self, input: BigFishPublishInput) { - self.publish_big_fish_game_then(input, |_, _| {}); + fn publish_big_fish_game(&self, input: BigFishPublishInput, +) { + self.publish_big_fish_game_then(input, |_, _| {}); } fn publish_big_fish_game_then( &self, input: BigFishPublishInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ); } @@ -43,17 +46,13 @@ impl publish_big_fish_game for super::RemoteProcedures { &self, input: BigFishPublishInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ) { - self.imp - .invoke_procedure_with_callback::<_, BigFishSessionProcedureResult>( - "publish_big_fish_game", - PublishBigFishGameArgs { input }, - __callback, - ); + self.imp.invoke_procedure_with_callback::<_, BigFishSessionProcedureResult>( + "publish_big_fish_game", + PublishBigFishGameArgs { input, }, + __callback, + ); } } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/publish_custom_world_profile_and_return_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/publish_custom_world_profile_and_return_procedure.rs index e5434aca..2dcaf010 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/publish_custom_world_profile_and_return_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/publish_custom_world_profile_and_return_procedure.rs @@ -2,17 +2,23 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::custom_world_library_mutation_result_type::CustomWorldLibraryMutationResult; use super::custom_world_profile_publish_input_type::CustomWorldProfilePublishInput; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] -struct PublishCustomWorldProfileAndReturnArgs { + struct PublishCustomWorldProfileAndReturnArgs { pub input: CustomWorldProfilePublishInput, } + impl __sdk::InModule for PublishCustomWorldProfileAndReturnArgs { type Module = super::RemoteModule; } @@ -22,19 +28,16 @@ impl __sdk::InModule for PublishCustomWorldProfileAndReturnArgs { /// /// Implemented for [`super::RemoteProcedures`]. pub trait publish_custom_world_profile_and_return { - fn publish_custom_world_profile_and_return(&self, input: CustomWorldProfilePublishInput) { - self.publish_custom_world_profile_and_return_then(input, |_, _| {}); + fn publish_custom_world_profile_and_return(&self, input: CustomWorldProfilePublishInput, +) { + self.publish_custom_world_profile_and_return_then(input, |_, _| {}); } fn publish_custom_world_profile_and_return_then( &self, input: CustomWorldProfilePublishInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ); } @@ -43,17 +46,13 @@ impl publish_custom_world_profile_and_return for super::RemoteProcedures { &self, input: CustomWorldProfilePublishInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ) { - self.imp - .invoke_procedure_with_callback::<_, CustomWorldLibraryMutationResult>( - "publish_custom_world_profile_and_return", - PublishCustomWorldProfileAndReturnArgs { input }, - __callback, - ); + self.imp.invoke_procedure_with_callback::<_, CustomWorldLibraryMutationResult>( + "publish_custom_world_profile_and_return", + PublishCustomWorldProfileAndReturnArgs { input, }, + __callback, + ); } } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/publish_custom_world_profile_reducer.rs b/server-rs/crates/spacetime-client/src/module_bindings/publish_custom_world_profile_reducer.rs index 84e6b339..f6d34d5c 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/publish_custom_world_profile_reducer.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/publish_custom_world_profile_reducer.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::custom_world_profile_publish_input_type::CustomWorldProfilePublishInput; @@ -14,8 +19,10 @@ pub(super) struct PublishCustomWorldProfileArgs { impl From for super::Reducer { fn from(args: PublishCustomWorldProfileArgs) -> Self { - Self::PublishCustomWorldProfile { input: args.input } - } + Self::PublishCustomWorldProfile { + input: args.input, +} +} } impl __sdk::InModule for PublishCustomWorldProfileArgs { @@ -33,11 +40,9 @@ pub trait publish_custom_world_profile { /// The reducer will run asynchronously in the future, /// and this method provides no way to listen for its completion status. /// /// Use [`publish_custom_world_profile:publish_custom_world_profile_then`] to run a callback after the reducer completes. - fn publish_custom_world_profile( - &self, - input: CustomWorldProfilePublishInput, - ) -> __sdk::Result<()> { - self.publish_custom_world_profile_then(input, |_, _| {}) + fn publish_custom_world_profile(&self, input: CustomWorldProfilePublishInput, +) -> __sdk::Result<()> { + self.publish_custom_world_profile_then(input, |_, _| {}) } /// Request that the remote module invoke the reducer `publish_custom_world_profile` to run as soon as possible, @@ -65,7 +70,7 @@ impl publish_custom_world_profile for super::RemoteReducers { + Send + 'static, ) -> __sdk::Result<()> { - self.imp - .invoke_reducer_with_callback(PublishCustomWorldProfileArgs { input }, callback) + self.imp.invoke_reducer_with_callback(PublishCustomWorldProfileArgs { input, }, callback) } } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/publish_custom_world_world_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/publish_custom_world_world_procedure.rs index 1eb935a2..083034b9 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/publish_custom_world_world_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/publish_custom_world_world_procedure.rs @@ -2,17 +2,23 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::custom_world_publish_world_input_type::CustomWorldPublishWorldInput; use super::custom_world_publish_world_result_type::CustomWorldPublishWorldResult; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] -struct PublishCustomWorldWorldArgs { + struct PublishCustomWorldWorldArgs { pub input: CustomWorldPublishWorldInput, } + impl __sdk::InModule for PublishCustomWorldWorldArgs { type Module = super::RemoteModule; } @@ -22,19 +28,16 @@ impl __sdk::InModule for PublishCustomWorldWorldArgs { /// /// Implemented for [`super::RemoteProcedures`]. pub trait publish_custom_world_world { - fn publish_custom_world_world(&self, input: CustomWorldPublishWorldInput) { - self.publish_custom_world_world_then(input, |_, _| {}); + fn publish_custom_world_world(&self, input: CustomWorldPublishWorldInput, +) { + self.publish_custom_world_world_then(input, |_, _| {}); } fn publish_custom_world_world_then( &self, input: CustomWorldPublishWorldInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ); } @@ -43,17 +46,13 @@ impl publish_custom_world_world for super::RemoteProcedures { &self, input: CustomWorldPublishWorldInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ) { - self.imp - .invoke_procedure_with_callback::<_, CustomWorldPublishWorldResult>( - "publish_custom_world_world", - PublishCustomWorldWorldArgs { input }, - __callback, - ); + self.imp.invoke_procedure_with_callback::<_, CustomWorldPublishWorldResult>( + "publish_custom_world_world", + PublishCustomWorldWorldArgs { input, }, + __callback, + ); } } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/publish_puzzle_work_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/publish_puzzle_work_procedure.rs index 288b44a5..4592d939 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/publish_puzzle_work_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/publish_puzzle_work_procedure.rs @@ -2,17 +2,23 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; -use super::puzzle_publish_input_type::PuzzlePublishInput; use super::puzzle_work_procedure_result_type::PuzzleWorkProcedureResult; +use super::puzzle_publish_input_type::PuzzlePublishInput; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] -struct PublishPuzzleWorkArgs { + struct PublishPuzzleWorkArgs { pub input: PuzzlePublishInput, } + impl __sdk::InModule for PublishPuzzleWorkArgs { type Module = super::RemoteModule; } @@ -22,19 +28,16 @@ impl __sdk::InModule for PublishPuzzleWorkArgs { /// /// Implemented for [`super::RemoteProcedures`]. pub trait publish_puzzle_work { - fn publish_puzzle_work(&self, input: PuzzlePublishInput) { - self.publish_puzzle_work_then(input, |_, _| {}); + fn publish_puzzle_work(&self, input: PuzzlePublishInput, +) { + self.publish_puzzle_work_then(input, |_, _| {}); } fn publish_puzzle_work_then( &self, input: PuzzlePublishInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ); } @@ -43,17 +46,13 @@ impl publish_puzzle_work for super::RemoteProcedures { &self, input: PuzzlePublishInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ) { - self.imp - .invoke_procedure_with_callback::<_, PuzzleWorkProcedureResult>( - "publish_puzzle_work", - PublishPuzzleWorkArgs { input }, - __callback, - ); + self.imp.invoke_procedure_with_callback::<_, PuzzleWorkProcedureResult>( + "publish_puzzle_work", + PublishPuzzleWorkArgs { input, }, + __callback, + ); } } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/puzzle_agent_message_finalize_input_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/puzzle_agent_message_finalize_input_type.rs index 486919f2..22dfb32e 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/puzzle_agent_message_finalize_input_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/puzzle_agent_message_finalize_input_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::puzzle_agent_stage_type::PuzzleAgentStage; @@ -11,15 +16,17 @@ use super::puzzle_agent_stage_type::PuzzleAgentStage; pub struct PuzzleAgentMessageFinalizeInput { pub session_id: String, pub owner_user_id: String, - pub assistant_message_id: Option, - pub assistant_reply_text: Option, + pub assistant_message_id: Option::, + pub assistant_reply_text: Option::, pub stage: PuzzleAgentStage, pub progress_percent: u32, pub anchor_pack_json: String, - pub error_message: Option, + pub error_message: Option::, pub updated_at_micros: i64, } + impl __sdk::InModule for PuzzleAgentMessageFinalizeInput { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/puzzle_agent_message_kind_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/puzzle_agent_message_kind_type.rs index ca46edeb..56f8df2d 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/puzzle_agent_message_kind_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/puzzle_agent_message_kind_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -15,8 +20,12 @@ pub enum PuzzleAgentMessageKind { ActionResult, Warning, + } + + impl __sdk::InModule for PuzzleAgentMessageKind { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/puzzle_agent_message_role_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/puzzle_agent_message_role_type.rs index 5dd7eb51..b34f41c4 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/puzzle_agent_message_role_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/puzzle_agent_message_role_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -13,8 +18,12 @@ pub enum PuzzleAgentMessageRole { Assistant, System, + } + + impl __sdk::InModule for PuzzleAgentMessageRole { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/puzzle_agent_message_row_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/puzzle_agent_message_row_type.rs index b0bb85f2..ceb74765 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/puzzle_agent_message_row_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/puzzle_agent_message_row_type.rs @@ -2,10 +2,15 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; -use super::puzzle_agent_message_kind_type::PuzzleAgentMessageKind; use super::puzzle_agent_message_role_type::PuzzleAgentMessageRole; +use super::puzzle_agent_message_kind_type::PuzzleAgentMessageKind; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -18,10 +23,12 @@ pub struct PuzzleAgentMessageRow { pub created_at: __sdk::Timestamp, } + impl __sdk::InModule for PuzzleAgentMessageRow { type Module = super::RemoteModule; } + /// Column accessor struct for the table `PuzzleAgentMessageRow`. /// /// Provides typed access to columns for query building. @@ -44,6 +51,7 @@ impl __sdk::__query_builder::HasCols for PuzzleAgentMessageRow { kind: __sdk::__query_builder::Col::new(table_name, "kind"), text: __sdk::__query_builder::Col::new(table_name, "text"), created_at: __sdk::__query_builder::Col::new(table_name, "created_at"), + } } } @@ -62,8 +70,10 @@ impl __sdk::__query_builder::HasIxCols for PuzzleAgentMessageRow { PuzzleAgentMessageRowIxCols { message_id: __sdk::__query_builder::IxCol::new(table_name, "message_id"), session_id: __sdk::__query_builder::IxCol::new(table_name, "session_id"), + } } } impl __sdk::__query_builder::CanBeLookupTable for PuzzleAgentMessageRow {} + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/puzzle_agent_message_submit_input_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/puzzle_agent_message_submit_input_type.rs index 93086643..3a9fa169 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/puzzle_agent_message_submit_input_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/puzzle_agent_message_submit_input_type.rs @@ -2,7 +2,13 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; + #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -14,6 +20,8 @@ pub struct PuzzleAgentMessageSubmitInput { pub submitted_at_micros: i64, } + impl __sdk::InModule for PuzzleAgentMessageSubmitInput { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/puzzle_agent_session_create_input_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/puzzle_agent_session_create_input_type.rs index c3ccc7d2..f07fcd6b 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/puzzle_agent_session_create_input_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/puzzle_agent_session_create_input_type.rs @@ -2,7 +2,13 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; + #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -15,6 +21,8 @@ pub struct PuzzleAgentSessionCreateInput { pub created_at_micros: i64, } + impl __sdk::InModule for PuzzleAgentSessionCreateInput { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/puzzle_agent_session_get_input_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/puzzle_agent_session_get_input_type.rs index af4159c6..e411bead 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/puzzle_agent_session_get_input_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/puzzle_agent_session_get_input_type.rs @@ -2,7 +2,13 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; + #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -11,6 +17,8 @@ pub struct PuzzleAgentSessionGetInput { pub owner_user_id: String, } + impl __sdk::InModule for PuzzleAgentSessionGetInput { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/puzzle_agent_session_procedure_result_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/puzzle_agent_session_procedure_result_type.rs index 39506659..f19f290d 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/puzzle_agent_session_procedure_result_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/puzzle_agent_session_procedure_result_type.rs @@ -2,16 +2,24 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; + #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] pub struct PuzzleAgentSessionProcedureResult { pub ok: bool, - pub session_json: Option, - pub error_message: Option, + pub session_json: Option::, + pub error_message: Option::, } + impl __sdk::InModule for PuzzleAgentSessionProcedureResult { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/puzzle_agent_session_row_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/puzzle_agent_session_row_type.rs index e056ada1..96e98e56 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/puzzle_agent_session_row_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/puzzle_agent_session_row_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::puzzle_agent_stage_type::PuzzleAgentStage; @@ -16,17 +21,19 @@ pub struct PuzzleAgentSessionRow { pub progress_percent: u32, pub stage: PuzzleAgentStage, pub anchor_pack_json: String, - pub draft_json: Option, - pub last_assistant_reply: Option, - pub published_profile_id: Option, + pub draft_json: Option::, + pub last_assistant_reply: Option::, + pub published_profile_id: Option::, pub created_at: __sdk::Timestamp, pub updated_at: __sdk::Timestamp, } + impl __sdk::InModule for PuzzleAgentSessionRow { type Module = super::RemoteModule; } + /// Column accessor struct for the table `PuzzleAgentSessionRow`. /// /// Provides typed access to columns for query building. @@ -38,9 +45,9 @@ pub struct PuzzleAgentSessionRowCols { pub progress_percent: __sdk::__query_builder::Col, pub stage: __sdk::__query_builder::Col, pub anchor_pack_json: __sdk::__query_builder::Col, - pub draft_json: __sdk::__query_builder::Col>, - pub last_assistant_reply: __sdk::__query_builder::Col>, - pub published_profile_id: __sdk::__query_builder::Col>, + pub draft_json: __sdk::__query_builder::Col>, + pub last_assistant_reply: __sdk::__query_builder::Col>, + pub published_profile_id: __sdk::__query_builder::Col>, pub created_at: __sdk::__query_builder::Col, pub updated_at: __sdk::__query_builder::Col, } @@ -57,16 +64,11 @@ impl __sdk::__query_builder::HasCols for PuzzleAgentSessionRow { stage: __sdk::__query_builder::Col::new(table_name, "stage"), anchor_pack_json: __sdk::__query_builder::Col::new(table_name, "anchor_pack_json"), draft_json: __sdk::__query_builder::Col::new(table_name, "draft_json"), - last_assistant_reply: __sdk::__query_builder::Col::new( - table_name, - "last_assistant_reply", - ), - published_profile_id: __sdk::__query_builder::Col::new( - table_name, - "published_profile_id", - ), + last_assistant_reply: __sdk::__query_builder::Col::new(table_name, "last_assistant_reply"), + 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"), + } } } @@ -85,8 +87,10 @@ impl __sdk::__query_builder::HasIxCols for PuzzleAgentSessionRow { PuzzleAgentSessionRowIxCols { 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 PuzzleAgentSessionRow {} + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/puzzle_agent_stage_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/puzzle_agent_stage_type.rs index 9d41ea54..00cbc81b 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/puzzle_agent_stage_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/puzzle_agent_stage_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -17,8 +22,12 @@ pub enum PuzzleAgentStage { ReadyToPublish, Published, + } + + impl __sdk::InModule for PuzzleAgentStage { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/puzzle_draft_compile_input_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/puzzle_draft_compile_input_type.rs index 3b5f565f..703538e4 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/puzzle_draft_compile_input_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/puzzle_draft_compile_input_type.rs @@ -2,7 +2,13 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; + #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -12,6 +18,8 @@ pub struct PuzzleDraftCompileInput { pub compiled_at_micros: i64, } + impl __sdk::InModule for PuzzleDraftCompileInput { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/puzzle_event_kind_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/puzzle_event_kind_type.rs new file mode 100644 index 00000000..7f0fd5af --- /dev/null +++ b/server-rs/crates/spacetime-client/src/module_bindings/puzzle_event_kind_type.rs @@ -0,0 +1,25 @@ +// 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)] +#[derive(Copy, Eq, Hash)] +pub enum PuzzleEventKind { + WorkPublished, + +} + + + +impl __sdk::InModule for PuzzleEventKind { + type Module = super::RemoteModule; +} + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/puzzle_event_table.rs b/server-rs/crates/spacetime-client/src/module_bindings/puzzle_event_table.rs new file mode 100644 index 00000000..f44181cb --- /dev/null +++ b/server-rs/crates/spacetime-client/src/module_bindings/puzzle_event_table.rs @@ -0,0 +1,103 @@ +// 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_event_type::PuzzleEvent; +use super::puzzle_event_kind_type::PuzzleEventKind; + +/// Table handle for the table `puzzle_event`. +/// +/// Obtain a handle from the [`PuzzleEventTableAccess::puzzle_event`] method on [`super::RemoteTables`], +/// like `ctx.db.puzzle_event()`. +/// +/// Users are encouraged not to explicitly reference this type, +/// but to directly chain method calls, +/// like `ctx.db.puzzle_event().on_insert(...)`. +pub struct PuzzleEventTableHandle<'ctx> { + imp: __sdk::TableHandle, + ctx: std::marker::PhantomData<&'ctx super::RemoteTables>, +} + +#[allow(non_camel_case_types)] +/// Extension trait for access to the table `puzzle_event`. +/// +/// Implemented for [`super::RemoteTables`]. +pub trait PuzzleEventTableAccess { + #[allow(non_snake_case)] + /// Obtain a [`PuzzleEventTableHandle`], which mediates access to the table `puzzle_event`. + fn puzzle_event(&self) -> PuzzleEventTableHandle<'_>; +} + +impl PuzzleEventTableAccess for super::RemoteTables { + fn puzzle_event(&self) -> PuzzleEventTableHandle<'_> { + PuzzleEventTableHandle { + imp: self.imp.get_table::("puzzle_event"), + ctx: std::marker::PhantomData, + } + } +} + +pub struct PuzzleEventInsertCallbackId(__sdk::CallbackId); + +impl<'ctx> __sdk::EventTable for PuzzleEventTableHandle<'ctx> { + type Row = PuzzleEvent; + type EventContext = super::EventContext; + + fn count(&self) -> u64 { self.imp.count() } + fn iter(&self) -> impl Iterator + '_ { self.imp.iter() } + + type InsertCallbackId = PuzzleEventInsertCallbackId; + + fn on_insert( + &self, + callback: impl FnMut(&Self::EventContext, &Self::Row) + Send + 'static, + ) -> PuzzleEventInsertCallbackId { + PuzzleEventInsertCallbackId(self.imp.on_insert(Box::new(callback))) + } + + fn remove_on_insert(&self, callback: PuzzleEventInsertCallbackId) { + self.imp.remove_on_insert(callback.0) + } +} + +#[doc(hidden)] +pub(super) fn register_table(client_cache: &mut __sdk::ClientCache) { + + let _table = client_cache.get_or_make_table::("puzzle_event"); + _table.add_unique_constraint::("event_id", |row| &row.event_id); +} + +#[doc(hidden)] +pub(super) fn parse_table_update( + raw_updates: __ws::v2::TableUpdate, +) -> __sdk::Result<__sdk::TableUpdate> { + __sdk::TableUpdate::parse_table_update(raw_updates).map_err(|e| { + __sdk::InternalError::failed_parse( + "TableUpdate", + "TableUpdate", + ).with_cause(e).into() + }) +} + + #[allow(non_camel_case_types)] + /// Extension trait for query builder access to the table `PuzzleEvent`. + /// + /// Implemented for [`__sdk::QueryTableAccessor`]. + pub trait puzzle_eventQueryTableAccess { + #[allow(non_snake_case)] + /// Get a query builder for the table `PuzzleEvent`. + fn puzzle_event(&self) -> __sdk::__query_builder::Table; + } + + impl puzzle_eventQueryTableAccess for __sdk::QueryTableAccessor { + fn puzzle_event(&self) -> __sdk::__query_builder::Table { + __sdk::__query_builder::Table::new("puzzle_event") + } + } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/puzzle_event_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/puzzle_event_type.rs new file mode 100644 index 00000000..27179c96 --- /dev/null +++ b/server-rs/crates/spacetime-client/src/module_bindings/puzzle_event_type.rs @@ -0,0 +1,81 @@ +// 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_event_kind_type::PuzzleEventKind; + +#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] +#[sats(crate = __lib)] +pub struct PuzzleEvent { + pub event_id: String, + pub profile_id: String, + pub work_id: String, + pub session_id: Option::, + pub owner_user_id: String, + pub event_kind: PuzzleEventKind, + pub occurred_at: __sdk::Timestamp, +} + + +impl __sdk::InModule for PuzzleEvent { + type Module = super::RemoteModule; +} + + +/// Column accessor struct for the table `PuzzleEvent`. +/// +/// Provides typed access to columns for query building. +pub struct PuzzleEventCols { + pub event_id: __sdk::__query_builder::Col, + pub profile_id: __sdk::__query_builder::Col, + pub work_id: __sdk::__query_builder::Col, + pub session_id: __sdk::__query_builder::Col>, + pub owner_user_id: __sdk::__query_builder::Col, + pub event_kind: __sdk::__query_builder::Col, + pub occurred_at: __sdk::__query_builder::Col, +} + +impl __sdk::__query_builder::HasCols for PuzzleEvent { + type Cols = PuzzleEventCols; + fn cols(table_name: &'static str) -> Self::Cols { + PuzzleEventCols { + event_id: __sdk::__query_builder::Col::new(table_name, "event_id"), + profile_id: __sdk::__query_builder::Col::new(table_name, "profile_id"), + work_id: __sdk::__query_builder::Col::new(table_name, "work_id"), + session_id: __sdk::__query_builder::Col::new(table_name, "session_id"), + owner_user_id: __sdk::__query_builder::Col::new(table_name, "owner_user_id"), + event_kind: __sdk::__query_builder::Col::new(table_name, "event_kind"), + occurred_at: __sdk::__query_builder::Col::new(table_name, "occurred_at"), + + } + } +} + +/// Indexed column accessor struct for the table `PuzzleEvent`. +/// +/// Provides typed access to indexed columns for query building. +pub struct PuzzleEventIxCols { + pub event_id: __sdk::__query_builder::IxCol, + pub owner_user_id: __sdk::__query_builder::IxCol, + pub profile_id: __sdk::__query_builder::IxCol, +} + +impl __sdk::__query_builder::HasIxCols for PuzzleEvent { + type IxCols = PuzzleEventIxCols; + fn ix_cols(table_name: &'static str) -> Self::IxCols { + PuzzleEventIxCols { + event_id: __sdk::__query_builder::IxCol::new(table_name, "event_id"), + owner_user_id: __sdk::__query_builder::IxCol::new(table_name, "owner_user_id"), + profile_id: __sdk::__query_builder::IxCol::new(table_name, "profile_id"), + + } + } +} + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/puzzle_generated_images_save_input_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/puzzle_generated_images_save_input_type.rs index 8c409aed..0c2196b4 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/puzzle_generated_images_save_input_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/puzzle_generated_images_save_input_type.rs @@ -2,7 +2,13 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; + #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -13,6 +19,8 @@ pub struct PuzzleGeneratedImagesSaveInput { pub saved_at_micros: i64, } + impl __sdk::InModule for PuzzleGeneratedImagesSaveInput { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/puzzle_leaderboard_entry_row_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/puzzle_leaderboard_entry_row_type.rs index a9b1a782..64694a9d 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/puzzle_leaderboard_entry_row_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/puzzle_leaderboard_entry_row_type.rs @@ -2,7 +2,13 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; + #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -17,10 +23,12 @@ pub struct PuzzleLeaderboardEntryRow { pub updated_at: __sdk::Timestamp, } + impl __sdk::InModule for PuzzleLeaderboardEntryRow { type Module = super::RemoteModule; } + /// Column accessor struct for the table `PuzzleLeaderboardEntryRow`. /// /// Provides typed access to columns for query building. @@ -47,6 +55,7 @@ impl __sdk::__query_builder::HasCols for PuzzleLeaderboardEntryRow { best_elapsed_ms: __sdk::__query_builder::Col::new(table_name, "best_elapsed_ms"), last_run_id: __sdk::__query_builder::Col::new(table_name, "last_run_id"), updated_at: __sdk::__query_builder::Col::new(table_name, "updated_at"), + } } } @@ -63,8 +72,10 @@ impl __sdk::__query_builder::HasIxCols for PuzzleLeaderboardEntryRow { fn ix_cols(table_name: &'static str) -> Self::IxCols { PuzzleLeaderboardEntryRowIxCols { entry_id: __sdk::__query_builder::IxCol::new(table_name, "entry_id"), + } } } impl __sdk::__query_builder::CanBeLookupTable for PuzzleLeaderboardEntryRow {} + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/puzzle_leaderboard_submit_input_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/puzzle_leaderboard_submit_input_type.rs index daad92b8..a3bc46c0 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/puzzle_leaderboard_submit_input_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/puzzle_leaderboard_submit_input_type.rs @@ -2,7 +2,13 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; + #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -16,6 +22,8 @@ pub struct PuzzleLeaderboardSubmitInput { pub submitted_at_micros: i64, } + impl __sdk::InModule for PuzzleLeaderboardSubmitInput { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/puzzle_publication_status_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/puzzle_publication_status_type.rs index 38ece3cf..7ff8b5c6 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/puzzle_publication_status_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/puzzle_publication_status_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -11,8 +16,12 @@ pub enum PuzzlePublicationStatus { Draft, Published, + } + + impl __sdk::InModule for PuzzlePublicationStatus { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/puzzle_publish_input_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/puzzle_publish_input_type.rs index 83cbc37c..d3a5d26d 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/puzzle_publish_input_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/puzzle_publish_input_type.rs @@ -2,7 +2,13 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; + #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -12,12 +18,14 @@ pub struct PuzzlePublishInput { pub work_id: String, pub profile_id: String, pub author_display_name: String, - pub level_name: Option, - pub summary: Option, - pub theme_tags: Option>, + pub level_name: Option::, + pub summary: Option::, + pub theme_tags: Option::>, pub published_at_micros: i64, } + impl __sdk::InModule for PuzzlePublishInput { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/puzzle_run_drag_input_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/puzzle_run_drag_input_type.rs index b5036117..753e2932 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/puzzle_run_drag_input_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/puzzle_run_drag_input_type.rs @@ -2,7 +2,13 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; + #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -15,6 +21,8 @@ pub struct PuzzleRunDragInput { pub dragged_at_micros: i64, } + impl __sdk::InModule for PuzzleRunDragInput { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/puzzle_run_get_input_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/puzzle_run_get_input_type.rs index b3961e57..cf15f8b3 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/puzzle_run_get_input_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/puzzle_run_get_input_type.rs @@ -2,7 +2,13 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; + #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -11,6 +17,8 @@ pub struct PuzzleRunGetInput { pub owner_user_id: String, } + impl __sdk::InModule for PuzzleRunGetInput { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/puzzle_run_next_level_input_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/puzzle_run_next_level_input_type.rs index 18258482..d6a9a285 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/puzzle_run_next_level_input_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/puzzle_run_next_level_input_type.rs @@ -2,7 +2,13 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; + #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -12,6 +18,8 @@ pub struct PuzzleRunNextLevelInput { pub advanced_at_micros: i64, } + impl __sdk::InModule for PuzzleRunNextLevelInput { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/puzzle_run_procedure_result_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/puzzle_run_procedure_result_type.rs index 54f6349b..215490dc 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/puzzle_run_procedure_result_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/puzzle_run_procedure_result_type.rs @@ -2,16 +2,24 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; + #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] pub struct PuzzleRunProcedureResult { pub ok: bool, - pub run_json: Option, - pub error_message: Option, + pub run_json: Option::, + pub error_message: Option::, } + impl __sdk::InModule for PuzzleRunProcedureResult { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/puzzle_run_start_input_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/puzzle_run_start_input_type.rs index 7b4bed29..55e0a93e 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/puzzle_run_start_input_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/puzzle_run_start_input_type.rs @@ -2,7 +2,13 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; + #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -13,6 +19,8 @@ pub struct PuzzleRunStartInput { pub started_at_micros: i64, } + impl __sdk::InModule for PuzzleRunStartInput { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/puzzle_run_swap_input_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/puzzle_run_swap_input_type.rs index e92ca711..3b5baa18 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/puzzle_run_swap_input_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/puzzle_run_swap_input_type.rs @@ -2,7 +2,13 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; + #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -14,6 +20,8 @@ pub struct PuzzleRunSwapInput { pub swapped_at_micros: i64, } + impl __sdk::InModule for PuzzleRunSwapInput { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/puzzle_runtime_run_row_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/puzzle_runtime_run_row_type.rs index e610dfbf..daf67f5b 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/puzzle_runtime_run_row_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/puzzle_runtime_run_row_type.rs @@ -2,7 +2,13 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; + #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -21,10 +27,12 @@ pub struct PuzzleRuntimeRunRow { pub updated_at: __sdk::Timestamp, } + impl __sdk::InModule for PuzzleRuntimeRunRow { type Module = super::RemoteModule; } + /// Column accessor struct for the table `PuzzleRuntimeRunRow`. /// /// Provides typed access to columns for query building. @@ -51,26 +59,15 @@ impl __sdk::__query_builder::HasCols for PuzzleRuntimeRunRow { owner_user_id: __sdk::__query_builder::Col::new(table_name, "owner_user_id"), entry_profile_id: __sdk::__query_builder::Col::new(table_name, "entry_profile_id"), current_profile_id: __sdk::__query_builder::Col::new(table_name, "current_profile_id"), - cleared_level_count: __sdk::__query_builder::Col::new( - table_name, - "cleared_level_count", - ), - current_level_index: __sdk::__query_builder::Col::new( - table_name, - "current_level_index", - ), + cleared_level_count: __sdk::__query_builder::Col::new(table_name, "cleared_level_count"), + current_level_index: __sdk::__query_builder::Col::new(table_name, "current_level_index"), current_grid_size: __sdk::__query_builder::Col::new(table_name, "current_grid_size"), - played_profile_ids_json: __sdk::__query_builder::Col::new( - table_name, - "played_profile_ids_json", - ), - previous_level_tags_json: __sdk::__query_builder::Col::new( - table_name, - "previous_level_tags_json", - ), + played_profile_ids_json: __sdk::__query_builder::Col::new(table_name, "played_profile_ids_json"), + previous_level_tags_json: __sdk::__query_builder::Col::new(table_name, "previous_level_tags_json"), snapshot_json: __sdk::__query_builder::Col::new(table_name, "snapshot_json"), created_at: __sdk::__query_builder::Col::new(table_name, "created_at"), updated_at: __sdk::__query_builder::Col::new(table_name, "updated_at"), + } } } @@ -89,8 +86,10 @@ impl __sdk::__query_builder::HasIxCols for PuzzleRuntimeRunRow { PuzzleRuntimeRunRowIxCols { owner_user_id: __sdk::__query_builder::IxCol::new(table_name, "owner_user_id"), run_id: __sdk::__query_builder::IxCol::new(table_name, "run_id"), + } } } impl __sdk::__query_builder::CanBeLookupTable for PuzzleRuntimeRunRow {} + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/puzzle_select_cover_image_input_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/puzzle_select_cover_image_input_type.rs index 516d16b9..847ac3a4 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/puzzle_select_cover_image_input_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/puzzle_select_cover_image_input_type.rs @@ -2,7 +2,13 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; + #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -13,6 +19,8 @@ pub struct PuzzleSelectCoverImageInput { pub selected_at_micros: i64, } + impl __sdk::InModule for PuzzleSelectCoverImageInput { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/puzzle_work_delete_input_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/puzzle_work_delete_input_type.rs index e71d6d3a..73044da6 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/puzzle_work_delete_input_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/puzzle_work_delete_input_type.rs @@ -2,7 +2,13 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; + #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -11,6 +17,8 @@ pub struct PuzzleWorkDeleteInput { pub owner_user_id: String, } + impl __sdk::InModule for PuzzleWorkDeleteInput { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/puzzle_work_get_input_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/puzzle_work_get_input_type.rs index 5f37e13f..74cf57a1 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/puzzle_work_get_input_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/puzzle_work_get_input_type.rs @@ -2,7 +2,13 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; + #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -10,6 +16,8 @@ pub struct PuzzleWorkGetInput { pub profile_id: String, } + impl __sdk::InModule for PuzzleWorkGetInput { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/puzzle_work_procedure_result_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/puzzle_work_procedure_result_type.rs index d59a56cc..0da3b49c 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/puzzle_work_procedure_result_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/puzzle_work_procedure_result_type.rs @@ -2,16 +2,24 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; + #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] pub struct PuzzleWorkProcedureResult { pub ok: bool, - pub item_json: Option, - pub error_message: Option, + pub item_json: Option::, + pub error_message: Option::, } + impl __sdk::InModule for PuzzleWorkProcedureResult { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/puzzle_work_profile_row_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/puzzle_work_profile_row_type.rs index be53fc17..e5482282 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/puzzle_work_profile_row_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/puzzle_work_profile_row_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::puzzle_publication_status_type::PuzzlePublicationStatus; @@ -12,26 +17,28 @@ pub struct PuzzleWorkProfileRow { pub profile_id: String, pub work_id: String, pub owner_user_id: String, - pub source_session_id: Option, + pub source_session_id: Option::, pub author_display_name: String, pub level_name: String, pub summary: String, pub theme_tags_json: String, - pub cover_image_src: Option, - pub cover_asset_id: Option, + pub cover_image_src: Option::, + pub cover_asset_id: Option::, pub publication_status: PuzzlePublicationStatus, pub play_count: u32, pub anchor_pack_json: String, pub publish_ready: bool, pub created_at: __sdk::Timestamp, pub updated_at: __sdk::Timestamp, - pub published_at: Option<__sdk::Timestamp>, + pub published_at: Option::<__sdk::Timestamp>, } + impl __sdk::InModule for PuzzleWorkProfileRow { type Module = super::RemoteModule; } + /// Column accessor struct for the table `PuzzleWorkProfileRow`. /// /// Provides typed access to columns for query building. @@ -39,21 +46,20 @@ pub struct PuzzleWorkProfileRowCols { pub profile_id: __sdk::__query_builder::Col, pub work_id: __sdk::__query_builder::Col, pub owner_user_id: __sdk::__query_builder::Col, - pub source_session_id: __sdk::__query_builder::Col>, + pub source_session_id: __sdk::__query_builder::Col>, pub author_display_name: __sdk::__query_builder::Col, pub level_name: __sdk::__query_builder::Col, pub summary: __sdk::__query_builder::Col, pub theme_tags_json: __sdk::__query_builder::Col, - pub cover_image_src: __sdk::__query_builder::Col>, - pub cover_asset_id: __sdk::__query_builder::Col>, - pub publication_status: - __sdk::__query_builder::Col, + pub cover_image_src: __sdk::__query_builder::Col>, + pub cover_asset_id: __sdk::__query_builder::Col>, + pub publication_status: __sdk::__query_builder::Col, pub play_count: __sdk::__query_builder::Col, pub anchor_pack_json: __sdk::__query_builder::Col, pub publish_ready: __sdk::__query_builder::Col, pub created_at: __sdk::__query_builder::Col, pub updated_at: __sdk::__query_builder::Col, - pub published_at: __sdk::__query_builder::Col>, + pub published_at: __sdk::__query_builder::Col>, } impl __sdk::__query_builder::HasCols for PuzzleWorkProfileRow { @@ -64,10 +70,7 @@ impl __sdk::__query_builder::HasCols for PuzzleWorkProfileRow { 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", - ), + author_display_name: __sdk::__query_builder::Col::new(table_name, "author_display_name"), level_name: __sdk::__query_builder::Col::new(table_name, "level_name"), summary: __sdk::__query_builder::Col::new(table_name, "summary"), theme_tags_json: __sdk::__query_builder::Col::new(table_name, "theme_tags_json"), @@ -80,6 +83,7 @@ impl __sdk::__query_builder::HasCols for PuzzleWorkProfileRow { created_at: __sdk::__query_builder::Col::new(table_name, "created_at"), updated_at: __sdk::__query_builder::Col::new(table_name, "updated_at"), published_at: __sdk::__query_builder::Col::new(table_name, "published_at"), + } } } @@ -90,8 +94,7 @@ impl __sdk::__query_builder::HasCols for PuzzleWorkProfileRow { pub struct PuzzleWorkProfileRowIxCols { pub owner_user_id: __sdk::__query_builder::IxCol, pub profile_id: __sdk::__query_builder::IxCol, - pub publication_status: - __sdk::__query_builder::IxCol, + pub publication_status: __sdk::__query_builder::IxCol, } impl __sdk::__query_builder::HasIxCols for PuzzleWorkProfileRow { @@ -100,12 +103,11 @@ impl __sdk::__query_builder::HasIxCols for PuzzleWorkProfileRow { PuzzleWorkProfileRowIxCols { 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", - ), + publication_status: __sdk::__query_builder::IxCol::new(table_name, "publication_status"), + } } } impl __sdk::__query_builder::CanBeLookupTable for PuzzleWorkProfileRow {} + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/puzzle_work_upsert_input_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/puzzle_work_upsert_input_type.rs index 809ce64d..ae412d68 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/puzzle_work_upsert_input_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/puzzle_work_upsert_input_type.rs @@ -2,7 +2,13 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; + #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -11,12 +17,14 @@ pub struct PuzzleWorkUpsertInput { pub owner_user_id: String, pub level_name: String, pub summary: String, - pub theme_tags: Vec, - pub cover_image_src: Option, - pub cover_asset_id: Option, + pub theme_tags: Vec::, + pub cover_image_src: Option::, + pub cover_asset_id: Option::, pub updated_at_micros: i64, } + impl __sdk::InModule for PuzzleWorkUpsertInput { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/puzzle_works_list_input_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/puzzle_works_list_input_type.rs index 4ad5ef5b..e1d55a5f 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/puzzle_works_list_input_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/puzzle_works_list_input_type.rs @@ -2,7 +2,13 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; + #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -10,6 +16,8 @@ pub struct PuzzleWorksListInput { pub owner_user_id: String, } + impl __sdk::InModule for PuzzleWorksListInput { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/puzzle_works_procedure_result_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/puzzle_works_procedure_result_type.rs index 6a34c60f..80a1c401 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/puzzle_works_procedure_result_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/puzzle_works_procedure_result_type.rs @@ -2,16 +2,24 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; + #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] pub struct PuzzleWorksProcedureResult { pub ok: bool, - pub items_json: Option, - pub error_message: Option, + pub items_json: Option::, + pub error_message: Option::, } + impl __sdk::InModule for PuzzleWorksProcedureResult { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/quest_completion_ack_input_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/quest_completion_ack_input_type.rs index 678ec032..dbe06a17 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/quest_completion_ack_input_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/quest_completion_ack_input_type.rs @@ -2,7 +2,13 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; + #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -11,6 +17,8 @@ pub struct QuestCompletionAckInput { pub updated_at_micros: i64, } + impl __sdk::InModule for QuestCompletionAckInput { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/quest_hostile_npc_defeated_signal_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/quest_hostile_npc_defeated_signal_type.rs index d12363ff..08debc1e 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/quest_hostile_npc_defeated_signal_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/quest_hostile_npc_defeated_signal_type.rs @@ -2,15 +2,23 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; + #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] pub struct QuestHostileNpcDefeatedSignal { - pub scene_id: Option, + pub scene_id: Option::, pub hostile_npc_id: String, } + impl __sdk::InModule for QuestHostileNpcDefeatedSignal { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/quest_item_delivered_signal_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/quest_item_delivered_signal_type.rs index 4e1d0a5d..9b69c1f7 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/quest_item_delivered_signal_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/quest_item_delivered_signal_type.rs @@ -2,7 +2,13 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; + #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -12,6 +18,8 @@ pub struct QuestItemDeliveredSignal { pub quantity: u32, } + impl __sdk::InModule for QuestItemDeliveredSignal { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/quest_log_event_kind_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/quest_log_event_kind_type.rs index c3176590..2e742860 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/quest_log_event_kind_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/quest_log_event_kind_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -17,8 +22,12 @@ pub enum QuestLogEventKind { CompletionAcknowledged, TurnedIn, + } + + impl __sdk::InModule for QuestLogEventKind { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/quest_log_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/quest_log_type.rs index d893857a..d4e28a66 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/quest_log_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/quest_log_type.rs @@ -2,12 +2,17 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; -use super::quest_log_event_kind_type::QuestLogEventKind; -use super::quest_progress_signal_type::QuestProgressSignal; -use super::quest_signal_kind_type::QuestSignalKind; use super::quest_status_type::QuestStatus; +use super::quest_progress_signal_type::QuestProgressSignal; +use super::quest_log_event_kind_type::QuestLogEventKind; +use super::quest_signal_kind_type::QuestSignalKind; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -18,17 +23,19 @@ pub struct QuestLog { pub actor_user_id: String, pub event_kind: QuestLogEventKind, pub status_after: QuestStatus, - pub signal_kind: Option, - pub signal: Option, - pub step_id: Option, - pub step_progress: Option, + pub signal_kind: Option::, + pub signal: Option::, + pub step_id: Option::, + pub step_progress: Option::, pub created_at: __sdk::Timestamp, } + impl __sdk::InModule for QuestLog { type Module = super::RemoteModule; } + /// Column accessor struct for the table `QuestLog`. /// /// Provides typed access to columns for query building. @@ -39,10 +46,10 @@ pub struct QuestLogCols { pub actor_user_id: __sdk::__query_builder::Col, pub event_kind: __sdk::__query_builder::Col, pub status_after: __sdk::__query_builder::Col, - pub signal_kind: __sdk::__query_builder::Col>, - pub signal: __sdk::__query_builder::Col>, - pub step_id: __sdk::__query_builder::Col>, - pub step_progress: __sdk::__query_builder::Col>, + pub signal_kind: __sdk::__query_builder::Col>, + pub signal: __sdk::__query_builder::Col>, + pub step_id: __sdk::__query_builder::Col>, + pub step_progress: __sdk::__query_builder::Col>, pub created_at: __sdk::__query_builder::Col, } @@ -61,6 +68,7 @@ impl __sdk::__query_builder::HasCols for QuestLog { step_id: __sdk::__query_builder::Col::new(table_name, "step_id"), step_progress: __sdk::__query_builder::Col::new(table_name, "step_progress"), created_at: __sdk::__query_builder::Col::new(table_name, "created_at"), + } } } @@ -82,12 +90,11 @@ impl __sdk::__query_builder::HasIxCols for QuestLog { actor_user_id: __sdk::__query_builder::IxCol::new(table_name, "actor_user_id"), log_id: __sdk::__query_builder::IxCol::new(table_name, "log_id"), quest_id: __sdk::__query_builder::IxCol::new(table_name, "quest_id"), - runtime_session_id: __sdk::__query_builder::IxCol::new( - table_name, - "runtime_session_id", - ), + runtime_session_id: __sdk::__query_builder::IxCol::new(table_name, "runtime_session_id"), + } } } impl __sdk::__query_builder::CanBeLookupTable for QuestLog {} + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/quest_narrative_binding_snapshot_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/quest_narrative_binding_snapshot_type.rs index 730e7565..45c26c18 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/quest_narrative_binding_snapshot_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/quest_narrative_binding_snapshot_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::quest_narrative_origin_type::QuestNarrativeOrigin; use super::quest_narrative_type_type::QuestNarrativeType; @@ -16,9 +21,11 @@ pub struct QuestNarrativeBindingSnapshot { pub issuer_goal: String, pub player_hook: String, pub world_reason: String, - pub followup_hooks: Vec, + pub followup_hooks: Vec::, } + impl __sdk::InModule for QuestNarrativeBindingSnapshot { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/quest_narrative_origin_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/quest_narrative_origin_type.rs index 34ce63f3..53f324af 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/quest_narrative_origin_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/quest_narrative_origin_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -11,8 +16,12 @@ pub enum QuestNarrativeOrigin { AiCompiled, FallbackBuilder, + } + + impl __sdk::InModule for QuestNarrativeOrigin { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/quest_narrative_type_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/quest_narrative_type_type.rs index ac5319cd..e390707e 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/quest_narrative_type_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/quest_narrative_type_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -19,8 +24,12 @@ pub enum QuestNarrativeType { Relationship, Trial, + } + + impl __sdk::InModule for QuestNarrativeType { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/quest_npc_spar_completed_signal_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/quest_npc_spar_completed_signal_type.rs index a0f1f8bb..356ae48e 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/quest_npc_spar_completed_signal_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/quest_npc_spar_completed_signal_type.rs @@ -2,7 +2,13 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; + #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -10,6 +16,8 @@ pub struct QuestNpcSparCompletedSignal { pub npc_id: String, } + impl __sdk::InModule for QuestNpcSparCompletedSignal { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/quest_npc_talk_completed_signal_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/quest_npc_talk_completed_signal_type.rs index 39042f37..a62032a7 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/quest_npc_talk_completed_signal_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/quest_npc_talk_completed_signal_type.rs @@ -2,7 +2,13 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; + #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -10,6 +16,8 @@ pub struct QuestNpcTalkCompletedSignal { pub npc_id: String, } + impl __sdk::InModule for QuestNpcTalkCompletedSignal { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/quest_objective_kind_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/quest_objective_kind_type.rs index 665faa30..4139db37 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/quest_objective_kind_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/quest_objective_kind_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -19,8 +24,12 @@ pub enum QuestObjectiveKind { ReachScene, DeliverItem, + } + + impl __sdk::InModule for QuestObjectiveKind { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/quest_objective_snapshot_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/quest_objective_snapshot_type.rs index 38682e68..e617912b 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/quest_objective_snapshot_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/quest_objective_snapshot_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::quest_objective_kind_type::QuestObjectiveKind; @@ -10,13 +15,15 @@ use super::quest_objective_kind_type::QuestObjectiveKind; #[sats(crate = __lib)] pub struct QuestObjectiveSnapshot { pub kind: QuestObjectiveKind, - pub target_hostile_npc_id: Option, - pub target_npc_id: Option, - pub target_scene_id: Option, - pub target_item_id: Option, + pub target_hostile_npc_id: Option::, + pub target_npc_id: Option::, + pub target_scene_id: Option::, + pub target_item_id: Option::, pub required_count: u32, } + impl __sdk::InModule for QuestObjectiveSnapshot { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/quest_progress_signal_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/quest_progress_signal_type.rs index 5764d273..b957a0a7 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/quest_progress_signal_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/quest_progress_signal_type.rs @@ -2,14 +2,19 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::quest_hostile_npc_defeated_signal_type::QuestHostileNpcDefeatedSignal; -use super::quest_item_delivered_signal_type::QuestItemDeliveredSignal; +use super::quest_treasure_inspected_signal_type::QuestTreasureInspectedSignal; use super::quest_npc_spar_completed_signal_type::QuestNpcSparCompletedSignal; use super::quest_npc_talk_completed_signal_type::QuestNpcTalkCompletedSignal; use super::quest_scene_reached_signal_type::QuestSceneReachedSignal; -use super::quest_treasure_inspected_signal_type::QuestTreasureInspectedSignal; +use super::quest_item_delivered_signal_type::QuestItemDeliveredSignal; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -25,8 +30,12 @@ pub enum QuestProgressSignal { SceneReached(QuestSceneReachedSignal), ItemDelivered(QuestItemDeliveredSignal), + } + + impl __sdk::InModule for QuestProgressSignal { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/quest_record_input_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/quest_record_input_type.rs index d07da423..6a82982d 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/quest_record_input_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/quest_record_input_type.rs @@ -2,11 +2,16 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; -use super::quest_narrative_binding_snapshot_type::QuestNarrativeBindingSnapshot; -use super::quest_reward_snapshot_type::QuestRewardSnapshot; use super::quest_status_type::QuestStatus; +use super::quest_reward_snapshot_type::QuestRewardSnapshot; +use super::quest_narrative_binding_snapshot_type::QuestNarrativeBindingSnapshot; use super::quest_step_snapshot_type::QuestStepSnapshot; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] @@ -14,15 +19,15 @@ use super::quest_step_snapshot_type::QuestStepSnapshot; pub struct QuestRecordInput { pub quest_id: String, pub runtime_session_id: String, - pub story_session_id: Option, + pub story_session_id: Option::, pub actor_user_id: String, pub issuer_npc_id: String, pub issuer_npc_name: String, - pub scene_id: Option, - pub chapter_id: Option, - pub act_id: Option, - pub thread_id: Option, - pub contract_id: Option, + pub scene_id: Option::, + pub chapter_id: Option::, + pub act_id: Option::, + pub thread_id: Option::, + pub contract_id: Option::, pub title: String, pub description: String, pub summary: String, @@ -31,16 +36,18 @@ pub struct QuestRecordInput { pub reward: QuestRewardSnapshot, pub reward_text: String, pub narrative_binding: QuestNarrativeBindingSnapshot, - pub steps: Vec, - pub active_step_id: Option, + pub steps: Vec::, + pub active_step_id: Option::, pub visible_stage: u32, - pub hidden_flags: Vec, - pub discovered_fact_ids: Vec, - pub related_carrier_ids: Vec, - pub consequence_ids: Vec, + pub hidden_flags: Vec::, + pub discovered_fact_ids: Vec::, + pub related_carrier_ids: Vec::, + pub consequence_ids: Vec::, pub created_at_micros: i64, } + impl __sdk::InModule for QuestRecordInput { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/quest_record_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/quest_record_type.rs index 4243e91c..10f98806 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/quest_record_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/quest_record_type.rs @@ -2,28 +2,33 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; -use super::quest_narrative_binding_snapshot_type::QuestNarrativeBindingSnapshot; -use super::quest_objective_snapshot_type::QuestObjectiveSnapshot; -use super::quest_reward_snapshot_type::QuestRewardSnapshot; use super::quest_status_type::QuestStatus; +use super::quest_reward_snapshot_type::QuestRewardSnapshot; +use super::quest_narrative_binding_snapshot_type::QuestNarrativeBindingSnapshot; use super::quest_step_snapshot_type::QuestStepSnapshot; +use super::quest_objective_snapshot_type::QuestObjectiveSnapshot; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] pub struct QuestRecord { pub quest_id: String, pub runtime_session_id: String, - pub story_session_id: Option, + pub story_session_id: Option::, pub actor_user_id: String, pub issuer_npc_id: String, pub issuer_npc_name: String, - pub scene_id: Option, - pub chapter_id: Option, - pub act_id: Option, - pub thread_id: Option, - pub contract_id: Option, + pub scene_id: Option::, + pub chapter_id: Option::, + pub act_id: Option::, + pub thread_id: Option::, + pub contract_id: Option::, pub title: String, pub description: String, pub summary: String, @@ -34,38 +39,40 @@ pub struct QuestRecord { pub reward: QuestRewardSnapshot, pub reward_text: String, pub narrative_binding: QuestNarrativeBindingSnapshot, - pub steps: Vec, - pub active_step_id: Option, + pub steps: Vec::, + pub active_step_id: Option::, pub visible_stage: u32, - pub hidden_flags: Vec, - pub discovered_fact_ids: Vec, - pub related_carrier_ids: Vec, - pub consequence_ids: Vec, + pub hidden_flags: Vec::, + pub discovered_fact_ids: Vec::, + pub related_carrier_ids: Vec::, + pub consequence_ids: Vec::, pub created_at: __sdk::Timestamp, pub updated_at: __sdk::Timestamp, - pub completed_at: Option<__sdk::Timestamp>, - pub turned_in_at: Option<__sdk::Timestamp>, + pub completed_at: Option::<__sdk::Timestamp>, + pub turned_in_at: Option::<__sdk::Timestamp>, } + impl __sdk::InModule for QuestRecord { type Module = super::RemoteModule; } + /// Column accessor struct for the table `QuestRecord`. /// /// Provides typed access to columns for query building. pub struct QuestRecordCols { pub quest_id: __sdk::__query_builder::Col, pub runtime_session_id: __sdk::__query_builder::Col, - pub story_session_id: __sdk::__query_builder::Col>, + pub story_session_id: __sdk::__query_builder::Col>, pub actor_user_id: __sdk::__query_builder::Col, pub issuer_npc_id: __sdk::__query_builder::Col, pub issuer_npc_name: __sdk::__query_builder::Col, - pub scene_id: __sdk::__query_builder::Col>, - pub chapter_id: __sdk::__query_builder::Col>, - pub act_id: __sdk::__query_builder::Col>, - pub thread_id: __sdk::__query_builder::Col>, - pub contract_id: __sdk::__query_builder::Col>, + pub scene_id: __sdk::__query_builder::Col>, + pub chapter_id: __sdk::__query_builder::Col>, + pub act_id: __sdk::__query_builder::Col>, + pub thread_id: __sdk::__query_builder::Col>, + pub contract_id: __sdk::__query_builder::Col>, pub title: __sdk::__query_builder::Col, pub description: __sdk::__query_builder::Col, pub summary: __sdk::__query_builder::Col, @@ -76,17 +83,17 @@ pub struct QuestRecordCols { pub reward: __sdk::__query_builder::Col, pub reward_text: __sdk::__query_builder::Col, pub narrative_binding: __sdk::__query_builder::Col, - pub steps: __sdk::__query_builder::Col>, - pub active_step_id: __sdk::__query_builder::Col>, + pub steps: __sdk::__query_builder::Col>, + pub active_step_id: __sdk::__query_builder::Col>, pub visible_stage: __sdk::__query_builder::Col, - pub hidden_flags: __sdk::__query_builder::Col>, - pub discovered_fact_ids: __sdk::__query_builder::Col>, - pub related_carrier_ids: __sdk::__query_builder::Col>, - pub consequence_ids: __sdk::__query_builder::Col>, + pub hidden_flags: __sdk::__query_builder::Col>, + pub discovered_fact_ids: __sdk::__query_builder::Col>, + pub related_carrier_ids: __sdk::__query_builder::Col>, + pub consequence_ids: __sdk::__query_builder::Col>, pub created_at: __sdk::__query_builder::Col, pub updated_at: __sdk::__query_builder::Col, - pub completed_at: __sdk::__query_builder::Col>, - pub turned_in_at: __sdk::__query_builder::Col>, + pub completed_at: __sdk::__query_builder::Col>, + pub turned_in_at: __sdk::__query_builder::Col>, } impl __sdk::__query_builder::HasCols for QuestRecord { @@ -110,10 +117,7 @@ impl __sdk::__query_builder::HasCols for QuestRecord { objective: __sdk::__query_builder::Col::new(table_name, "objective"), progress: __sdk::__query_builder::Col::new(table_name, "progress"), status: __sdk::__query_builder::Col::new(table_name, "status"), - completion_notified: __sdk::__query_builder::Col::new( - table_name, - "completion_notified", - ), + completion_notified: __sdk::__query_builder::Col::new(table_name, "completion_notified"), reward: __sdk::__query_builder::Col::new(table_name, "reward"), reward_text: __sdk::__query_builder::Col::new(table_name, "reward_text"), narrative_binding: __sdk::__query_builder::Col::new(table_name, "narrative_binding"), @@ -121,19 +125,14 @@ impl __sdk::__query_builder::HasCols for QuestRecord { active_step_id: __sdk::__query_builder::Col::new(table_name, "active_step_id"), visible_stage: __sdk::__query_builder::Col::new(table_name, "visible_stage"), hidden_flags: __sdk::__query_builder::Col::new(table_name, "hidden_flags"), - discovered_fact_ids: __sdk::__query_builder::Col::new( - table_name, - "discovered_fact_ids", - ), - related_carrier_ids: __sdk::__query_builder::Col::new( - table_name, - "related_carrier_ids", - ), + discovered_fact_ids: __sdk::__query_builder::Col::new(table_name, "discovered_fact_ids"), + related_carrier_ids: __sdk::__query_builder::Col::new(table_name, "related_carrier_ids"), consequence_ids: __sdk::__query_builder::Col::new(table_name, "consequence_ids"), created_at: __sdk::__query_builder::Col::new(table_name, "created_at"), updated_at: __sdk::__query_builder::Col::new(table_name, "updated_at"), completed_at: __sdk::__query_builder::Col::new(table_name, "completed_at"), turned_in_at: __sdk::__query_builder::Col::new(table_name, "turned_in_at"), + } } } @@ -155,12 +154,11 @@ impl __sdk::__query_builder::HasIxCols for QuestRecord { actor_user_id: __sdk::__query_builder::IxCol::new(table_name, "actor_user_id"), issuer_npc_id: __sdk::__query_builder::IxCol::new(table_name, "issuer_npc_id"), quest_id: __sdk::__query_builder::IxCol::new(table_name, "quest_id"), - runtime_session_id: __sdk::__query_builder::IxCol::new( - table_name, - "runtime_session_id", - ), + runtime_session_id: __sdk::__query_builder::IxCol::new(table_name, "runtime_session_id"), + } } } impl __sdk::__query_builder::CanBeLookupTable for QuestRecord {} + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/quest_reward_equipment_slot_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/quest_reward_equipment_slot_type.rs index 43a942c4..333521e0 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/quest_reward_equipment_slot_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/quest_reward_equipment_slot_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -13,8 +18,12 @@ pub enum QuestRewardEquipmentSlot { Armor, Relic, + } + + impl __sdk::InModule for QuestRewardEquipmentSlot { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/quest_reward_intel_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/quest_reward_intel_type.rs index 9938686a..560847bd 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/quest_reward_intel_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/quest_reward_intel_type.rs @@ -2,15 +2,23 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; + #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] pub struct QuestRewardIntel { pub rumor_text: String, - pub unlocked_scene_id: Option, + pub unlocked_scene_id: Option::, } + impl __sdk::InModule for QuestRewardIntel { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/quest_reward_item_rarity_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/quest_reward_item_rarity_type.rs index 7fc4f7ec..632c46aa 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/quest_reward_item_rarity_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/quest_reward_item_rarity_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -17,8 +22,12 @@ pub enum QuestRewardItemRarity { Epic, Legendary, + } + + impl __sdk::InModule for QuestRewardItemRarity { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/quest_reward_item_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/quest_reward_item_type.rs index a62f9d93..dc0af611 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/quest_reward_item_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/quest_reward_item_type.rs @@ -2,10 +2,15 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; -use super::quest_reward_equipment_slot_type::QuestRewardEquipmentSlot; use super::quest_reward_item_rarity_type::QuestRewardItemRarity; +use super::quest_reward_equipment_slot_type::QuestRewardEquipmentSlot; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -13,15 +18,17 @@ pub struct QuestRewardItem { pub item_id: String, pub category: String, pub name: String, - pub description: Option, + pub description: Option::, pub quantity: u32, pub rarity: QuestRewardItemRarity, - pub tags: Vec, + pub tags: Vec::, pub stackable: bool, pub stack_key: String, - pub equipment_slot_id: Option, + pub equipment_slot_id: Option::, } + impl __sdk::InModule for QuestRewardItem { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/quest_reward_snapshot_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/quest_reward_snapshot_type.rs index 995e84ad..55790858 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/quest_reward_snapshot_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/quest_reward_snapshot_type.rs @@ -2,22 +2,29 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; -use super::quest_reward_intel_type::QuestRewardIntel; use super::quest_reward_item_type::QuestRewardItem; +use super::quest_reward_intel_type::QuestRewardIntel; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] pub struct QuestRewardSnapshot { pub affinity_bonus: i32, pub currency: i64, - pub experience: Option, - pub items: Vec, - pub intel: Option, - pub story_hint: Option, + pub experience: Option::, + pub items: Vec::, + pub intel: Option::, + pub story_hint: Option::, } + impl __sdk::InModule for QuestRewardSnapshot { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/quest_scene_reached_signal_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/quest_scene_reached_signal_type.rs index 723d9325..d188fab1 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/quest_scene_reached_signal_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/quest_scene_reached_signal_type.rs @@ -2,7 +2,13 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; + #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -10,6 +16,8 @@ pub struct QuestSceneReachedSignal { pub scene_id: String, } + impl __sdk::InModule for QuestSceneReachedSignal { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/quest_signal_apply_input_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/quest_signal_apply_input_type.rs index d93ff928..89cc2c6f 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/quest_signal_apply_input_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/quest_signal_apply_input_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::quest_progress_signal_type::QuestProgressSignal; @@ -14,6 +19,8 @@ pub struct QuestSignalApplyInput { pub updated_at_micros: i64, } + impl __sdk::InModule for QuestSignalApplyInput { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/quest_signal_kind_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/quest_signal_kind_type.rs index 04ea6661..0d36f2fa 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/quest_signal_kind_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/quest_signal_kind_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -19,8 +24,12 @@ pub enum QuestSignalKind { SceneReached, ItemDelivered, + } + + impl __sdk::InModule for QuestSignalKind { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/quest_status_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/quest_status_type.rs index f5defbb7..3ea098d8 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/quest_status_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/quest_status_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -19,8 +24,12 @@ pub enum QuestStatus { Failed, Expired, + } + + impl __sdk::InModule for QuestStatus { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/quest_step_snapshot_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/quest_step_snapshot_type.rs index 0f27cb9e..c1e3bc1a 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/quest_step_snapshot_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/quest_step_snapshot_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::quest_objective_kind_type::QuestObjectiveKind; @@ -11,10 +16,10 @@ use super::quest_objective_kind_type::QuestObjectiveKind; pub struct QuestStepSnapshot { pub step_id: String, pub kind: QuestObjectiveKind, - pub target_hostile_npc_id: Option, - pub target_npc_id: Option, - pub target_scene_id: Option, - pub target_item_id: Option, + pub target_hostile_npc_id: Option::, + pub target_npc_id: Option::, + pub target_scene_id: Option::, + pub target_item_id: Option::, pub required_count: u32, pub progress: u32, pub title: String, @@ -22,6 +27,8 @@ pub struct QuestStepSnapshot { pub complete_text: String, } + impl __sdk::InModule for QuestStepSnapshot { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/quest_treasure_inspected_signal_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/quest_treasure_inspected_signal_type.rs index 51caed77..8003e616 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/quest_treasure_inspected_signal_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/quest_treasure_inspected_signal_type.rs @@ -2,14 +2,22 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; + #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] pub struct QuestTreasureInspectedSignal { - pub scene_id: Option, + pub scene_id: Option::, } + impl __sdk::InModule for QuestTreasureInspectedSignal { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/quest_turn_in_input_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/quest_turn_in_input_type.rs index 4f48a44a..9d87ae49 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/quest_turn_in_input_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/quest_turn_in_input_type.rs @@ -2,7 +2,13 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; + #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -11,6 +17,8 @@ pub struct QuestTurnInInput { pub turned_in_at_micros: i64, } + impl __sdk::InModule for QuestTurnInInput { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/record_big_fish_play_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/record_big_fish_play_procedure.rs index f4cfaa6b..8d648a8c 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/record_big_fish_play_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/record_big_fish_play_procedure.rs @@ -2,17 +2,23 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; -use super::big_fish_play_record_input_type::BigFishPlayRecordInput; use super::big_fish_works_procedure_result_type::BigFishWorksProcedureResult; +use super::big_fish_play_record_input_type::BigFishPlayRecordInput; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] -struct RecordBigFishPlayArgs { + struct RecordBigFishPlayArgs { pub input: BigFishPlayRecordInput, } + impl __sdk::InModule for RecordBigFishPlayArgs { type Module = super::RemoteModule; } @@ -22,19 +28,16 @@ impl __sdk::InModule for RecordBigFishPlayArgs { /// /// Implemented for [`super::RemoteProcedures`]. pub trait record_big_fish_play { - fn record_big_fish_play(&self, input: BigFishPlayRecordInput) { - self.record_big_fish_play_then(input, |_, _| {}); + fn record_big_fish_play(&self, input: BigFishPlayRecordInput, +) { + self.record_big_fish_play_then(input, |_, _| {}); } fn record_big_fish_play_then( &self, input: BigFishPlayRecordInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ); } @@ -43,17 +46,13 @@ impl record_big_fish_play for super::RemoteProcedures { &self, input: BigFishPlayRecordInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ) { - self.imp - .invoke_procedure_with_callback::<_, BigFishWorksProcedureResult>( - "record_big_fish_play", - RecordBigFishPlayArgs { input }, - __callback, - ); + self.imp.invoke_procedure_with_callback::<_, BigFishWorksProcedureResult>( + "record_big_fish_play", + RecordBigFishPlayArgs { input, }, + __callback, + ); } } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/redeem_profile_referral_invite_code_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/redeem_profile_referral_invite_code_procedure.rs index 44354acd..b1f125b6 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/redeem_profile_referral_invite_code_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/redeem_profile_referral_invite_code_procedure.rs @@ -2,17 +2,23 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::runtime_referral_redeem_input_type::RuntimeReferralRedeemInput; use super::runtime_referral_redeem_procedure_result_type::RuntimeReferralRedeemProcedureResult; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] -struct RedeemProfileReferralInviteCodeArgs { + struct RedeemProfileReferralInviteCodeArgs { pub input: RuntimeReferralRedeemInput, } + impl __sdk::InModule for RedeemProfileReferralInviteCodeArgs { type Module = super::RemoteModule; } @@ -22,19 +28,16 @@ impl __sdk::InModule for RedeemProfileReferralInviteCodeArgs { /// /// Implemented for [`super::RemoteProcedures`]. pub trait redeem_profile_referral_invite_code { - fn redeem_profile_referral_invite_code(&self, input: RuntimeReferralRedeemInput) { - self.redeem_profile_referral_invite_code_then(input, |_, _| {}); + fn redeem_profile_referral_invite_code(&self, input: RuntimeReferralRedeemInput, +) { + self.redeem_profile_referral_invite_code_then(input, |_, _| {}); } fn redeem_profile_referral_invite_code_then( &self, input: RuntimeReferralRedeemInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ); } @@ -43,17 +46,13 @@ impl redeem_profile_referral_invite_code for super::RemoteProcedures { &self, input: RuntimeReferralRedeemInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ) { - self.imp - .invoke_procedure_with_callback::<_, RuntimeReferralRedeemProcedureResult>( - "redeem_profile_referral_invite_code", - RedeemProfileReferralInviteCodeArgs { input }, - __callback, - ); + self.imp.invoke_procedure_with_callback::<_, RuntimeReferralRedeemProcedureResult>( + "redeem_profile_referral_invite_code", + RedeemProfileReferralInviteCodeArgs { input, }, + __callback, + ); } } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/redeem_profile_reward_code_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/redeem_profile_reward_code_procedure.rs index 4d048a49..858cf489 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/redeem_profile_reward_code_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/redeem_profile_reward_code_procedure.rs @@ -2,17 +2,23 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::runtime_profile_reward_code_redeem_input_type::RuntimeProfileRewardCodeRedeemInput; use super::runtime_profile_reward_code_redeem_procedure_result_type::RuntimeProfileRewardCodeRedeemProcedureResult; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] -struct RedeemProfileRewardCodeArgs { + struct RedeemProfileRewardCodeArgs { pub input: RuntimeProfileRewardCodeRedeemInput, } + impl __sdk::InModule for RedeemProfileRewardCodeArgs { type Module = super::RemoteModule; } @@ -22,19 +28,16 @@ impl __sdk::InModule for RedeemProfileRewardCodeArgs { /// /// Implemented for [`super::RemoteProcedures`]. pub trait redeem_profile_reward_code { - fn redeem_profile_reward_code(&self, input: RuntimeProfileRewardCodeRedeemInput) { - self.redeem_profile_reward_code_then(input, |_, _| {}); + fn redeem_profile_reward_code(&self, input: RuntimeProfileRewardCodeRedeemInput, +) { + self.redeem_profile_reward_code_then(input, |_, _| {}); } fn redeem_profile_reward_code_then( &self, input: RuntimeProfileRewardCodeRedeemInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ); } @@ -43,17 +46,13 @@ impl redeem_profile_reward_code for super::RemoteProcedures { &self, input: RuntimeProfileRewardCodeRedeemInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ) { - self.imp - .invoke_procedure_with_callback::<_, RuntimeProfileRewardCodeRedeemProcedureResult>( - "redeem_profile_reward_code", - RedeemProfileRewardCodeArgs { input }, - __callback, - ); + self.imp.invoke_procedure_with_callback::<_, RuntimeProfileRewardCodeRedeemProcedureResult>( + "redeem_profile_reward_code", + RedeemProfileRewardCodeArgs { input, }, + __callback, + ); } } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/refresh_session_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/refresh_session_type.rs index 931fe680..5e7369e3 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/refresh_session_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/refresh_session_type.rs @@ -2,7 +2,13 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; + #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -13,16 +19,18 @@ pub struct RefreshSession { pub issued_by_provider: String, pub client_info_json: String, pub expires_at: String, - pub revoked_at: Option, + pub revoked_at: Option::, pub created_at: String, pub updated_at: String, pub last_seen_at: String, } + impl __sdk::InModule for RefreshSession { type Module = super::RemoteModule; } + /// Column accessor struct for the table `RefreshSession`. /// /// Provides typed access to columns for query building. @@ -33,7 +41,7 @@ pub struct RefreshSessionCols { pub issued_by_provider: __sdk::__query_builder::Col, pub client_info_json: __sdk::__query_builder::Col, pub expires_at: __sdk::__query_builder::Col, - pub revoked_at: __sdk::__query_builder::Col>, + pub revoked_at: __sdk::__query_builder::Col>, pub created_at: __sdk::__query_builder::Col, pub updated_at: __sdk::__query_builder::Col, pub last_seen_at: __sdk::__query_builder::Col, @@ -53,6 +61,7 @@ impl __sdk::__query_builder::HasCols for RefreshSession { created_at: __sdk::__query_builder::Col::new(table_name, "created_at"), updated_at: __sdk::__query_builder::Col::new(table_name, "updated_at"), last_seen_at: __sdk::__query_builder::Col::new(table_name, "last_seen_at"), + } } } @@ -70,14 +79,13 @@ impl __sdk::__query_builder::HasIxCols for RefreshSession { type IxCols = RefreshSessionIxCols; fn ix_cols(table_name: &'static str) -> Self::IxCols { RefreshSessionIxCols { - refresh_token_hash: __sdk::__query_builder::IxCol::new( - table_name, - "refresh_token_hash", - ), + refresh_token_hash: __sdk::__query_builder::IxCol::new(table_name, "refresh_token_hash"), session_id: __sdk::__query_builder::IxCol::new(table_name, "session_id"), user_id: __sdk::__query_builder::IxCol::new(table_name, "user_id"), + } } } impl __sdk::__query_builder::CanBeLookupTable for RefreshSession {} + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/refund_profile_wallet_points_and_return_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/refund_profile_wallet_points_and_return_procedure.rs index fb86172c..4cd698b2 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/refund_profile_wallet_points_and_return_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/refund_profile_wallet_points_and_return_procedure.rs @@ -2,17 +2,23 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::runtime_profile_wallet_adjustment_input_type::RuntimeProfileWalletAdjustmentInput; use super::runtime_profile_wallet_adjustment_procedure_result_type::RuntimeProfileWalletAdjustmentProcedureResult; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] -struct RefundProfileWalletPointsAndReturnArgs { + struct RefundProfileWalletPointsAndReturnArgs { pub input: RuntimeProfileWalletAdjustmentInput, } + impl __sdk::InModule for RefundProfileWalletPointsAndReturnArgs { type Module = super::RemoteModule; } @@ -22,19 +28,16 @@ impl __sdk::InModule for RefundProfileWalletPointsAndReturnArgs { /// /// Implemented for [`super::RemoteProcedures`]. pub trait refund_profile_wallet_points_and_return { - fn refund_profile_wallet_points_and_return(&self, input: RuntimeProfileWalletAdjustmentInput) { - self.refund_profile_wallet_points_and_return_then(input, |_, _| {}); + fn refund_profile_wallet_points_and_return(&self, input: RuntimeProfileWalletAdjustmentInput, +) { + self.refund_profile_wallet_points_and_return_then(input, |_, _| {}); } fn refund_profile_wallet_points_and_return_then( &self, input: RuntimeProfileWalletAdjustmentInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ); } @@ -43,17 +46,13 @@ impl refund_profile_wallet_points_and_return for super::RemoteProcedures { &self, input: RuntimeProfileWalletAdjustmentInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ) { - self.imp - .invoke_procedure_with_callback::<_, RuntimeProfileWalletAdjustmentProcedureResult>( - "refund_profile_wallet_points_and_return", - RefundProfileWalletPointsAndReturnArgs { input }, - __callback, - ); + self.imp.invoke_procedure_with_callback::<_, RuntimeProfileWalletAdjustmentProcedureResult>( + "refund_profile_wallet_points_and_return", + RefundProfileWalletPointsAndReturnArgs { input, }, + __callback, + ); } } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/resolve_combat_action_and_return_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/resolve_combat_action_and_return_procedure.rs index ac8aa07d..9b886a0d 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/resolve_combat_action_and_return_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/resolve_combat_action_and_return_procedure.rs @@ -2,17 +2,23 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::resolve_combat_action_input_type::ResolveCombatActionInput; use super::resolve_combat_action_procedure_result_type::ResolveCombatActionProcedureResult; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] -struct ResolveCombatActionAndReturnArgs { + struct ResolveCombatActionAndReturnArgs { pub input: ResolveCombatActionInput, } + impl __sdk::InModule for ResolveCombatActionAndReturnArgs { type Module = super::RemoteModule; } @@ -22,19 +28,16 @@ impl __sdk::InModule for ResolveCombatActionAndReturnArgs { /// /// Implemented for [`super::RemoteProcedures`]. pub trait resolve_combat_action_and_return { - fn resolve_combat_action_and_return(&self, input: ResolveCombatActionInput) { - self.resolve_combat_action_and_return_then(input, |_, _| {}); + fn resolve_combat_action_and_return(&self, input: ResolveCombatActionInput, +) { + self.resolve_combat_action_and_return_then(input, |_, _| {}); } fn resolve_combat_action_and_return_then( &self, input: ResolveCombatActionInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ); } @@ -43,17 +46,13 @@ impl resolve_combat_action_and_return for super::RemoteProcedures { &self, input: ResolveCombatActionInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ) { - self.imp - .invoke_procedure_with_callback::<_, ResolveCombatActionProcedureResult>( - "resolve_combat_action_and_return", - ResolveCombatActionAndReturnArgs { input }, - __callback, - ); + self.imp.invoke_procedure_with_callback::<_, ResolveCombatActionProcedureResult>( + "resolve_combat_action_and_return", + ResolveCombatActionAndReturnArgs { input, }, + __callback, + ); } } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/resolve_combat_action_input_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/resolve_combat_action_input_type.rs index 7dea1c21..72c6524f 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/resolve_combat_action_input_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/resolve_combat_action_input_type.rs @@ -2,7 +2,13 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; + #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -18,6 +24,8 @@ pub struct ResolveCombatActionInput { pub updated_at_micros: i64, } + impl __sdk::InModule for ResolveCombatActionInput { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/resolve_combat_action_procedure_result_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/resolve_combat_action_procedure_result_type.rs index c7a13f85..f21fc0ee 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/resolve_combat_action_procedure_result_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/resolve_combat_action_procedure_result_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::resolve_combat_action_result_type::ResolveCombatActionResult; @@ -10,10 +15,12 @@ use super::resolve_combat_action_result_type::ResolveCombatActionResult; #[sats(crate = __lib)] pub struct ResolveCombatActionProcedureResult { pub ok: bool, - pub result: Option, - pub error_message: Option, + pub result: Option::, + pub error_message: Option::, } + impl __sdk::InModule for ResolveCombatActionProcedureResult { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/resolve_combat_action_reducer.rs b/server-rs/crates/spacetime-client/src/module_bindings/resolve_combat_action_reducer.rs index 41340e2f..1c3f29c9 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/resolve_combat_action_reducer.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/resolve_combat_action_reducer.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::resolve_combat_action_input_type::ResolveCombatActionInput; @@ -14,8 +19,10 @@ pub(super) struct ResolveCombatActionArgs { impl From for super::Reducer { fn from(args: ResolveCombatActionArgs) -> Self { - Self::ResolveCombatAction { input: args.input } - } + Self::ResolveCombatAction { + input: args.input, +} +} } impl __sdk::InModule for ResolveCombatActionArgs { @@ -33,8 +40,9 @@ pub trait resolve_combat_action { /// The reducer will run asynchronously in the future, /// and this method provides no way to listen for its completion status. /// /// Use [`resolve_combat_action:resolve_combat_action_then`] to run a callback after the reducer completes. - fn resolve_combat_action(&self, input: ResolveCombatActionInput) -> __sdk::Result<()> { - self.resolve_combat_action_then(input, |_, _| {}) + fn resolve_combat_action(&self, input: ResolveCombatActionInput, +) -> __sdk::Result<()> { + self.resolve_combat_action_then(input, |_, _| {}) } /// Request that the remote module invoke the reducer `resolve_combat_action` to run as soon as possible, @@ -62,7 +70,7 @@ impl resolve_combat_action for super::RemoteReducers { + Send + 'static, ) -> __sdk::Result<()> { - self.imp - .invoke_reducer_with_callback(ResolveCombatActionArgs { input }, callback) + self.imp.invoke_reducer_with_callback(ResolveCombatActionArgs { input, }, callback) } } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/resolve_combat_action_result_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/resolve_combat_action_result_type.rs index b47e088c..8057358f 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/resolve_combat_action_result_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/resolve_combat_action_result_type.rs @@ -2,10 +2,15 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; -use super::battle_state_snapshot_type::BattleStateSnapshot; use super::combat_outcome_type::CombatOutcome; +use super::battle_state_snapshot_type::BattleStateSnapshot; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -16,6 +21,8 @@ pub struct ResolveCombatActionResult { pub outcome: CombatOutcome, } + impl __sdk::InModule for ResolveCombatActionResult { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/resolve_npc_battle_interaction_and_return_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/resolve_npc_battle_interaction_and_return_procedure.rs index ff4cca29..150fa443 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/resolve_npc_battle_interaction_and_return_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/resolve_npc_battle_interaction_and_return_procedure.rs @@ -2,17 +2,23 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; -use super::npc_battle_interaction_procedure_result_type::NpcBattleInteractionProcedureResult; use super::resolve_npc_battle_interaction_input_type::ResolveNpcBattleInteractionInput; +use super::npc_battle_interaction_procedure_result_type::NpcBattleInteractionProcedureResult; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] -struct ResolveNpcBattleInteractionAndReturnArgs { + struct ResolveNpcBattleInteractionAndReturnArgs { pub input: ResolveNpcBattleInteractionInput, } + impl __sdk::InModule for ResolveNpcBattleInteractionAndReturnArgs { type Module = super::RemoteModule; } @@ -22,19 +28,16 @@ impl __sdk::InModule for ResolveNpcBattleInteractionAndReturnArgs { /// /// Implemented for [`super::RemoteProcedures`]. pub trait resolve_npc_battle_interaction_and_return { - fn resolve_npc_battle_interaction_and_return(&self, input: ResolveNpcBattleInteractionInput) { - self.resolve_npc_battle_interaction_and_return_then(input, |_, _| {}); + fn resolve_npc_battle_interaction_and_return(&self, input: ResolveNpcBattleInteractionInput, +) { + self.resolve_npc_battle_interaction_and_return_then(input, |_, _| {}); } fn resolve_npc_battle_interaction_and_return_then( &self, input: ResolveNpcBattleInteractionInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ); } @@ -43,17 +46,13 @@ impl resolve_npc_battle_interaction_and_return for super::RemoteProcedures { &self, input: ResolveNpcBattleInteractionInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ) { - self.imp - .invoke_procedure_with_callback::<_, NpcBattleInteractionProcedureResult>( - "resolve_npc_battle_interaction_and_return", - ResolveNpcBattleInteractionAndReturnArgs { input }, - __callback, - ); + self.imp.invoke_procedure_with_callback::<_, NpcBattleInteractionProcedureResult>( + "resolve_npc_battle_interaction_and_return", + ResolveNpcBattleInteractionAndReturnArgs { input, }, + __callback, + ); } } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/resolve_npc_battle_interaction_input_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/resolve_npc_battle_interaction_input_type.rs index e8eba7a6..907cc7e2 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/resolve_npc_battle_interaction_input_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/resolve_npc_battle_interaction_input_type.rs @@ -2,10 +2,15 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; -use super::resolve_npc_interaction_input_type::ResolveNpcInteractionInput; use super::runtime_item_reward_item_snapshot_type::RuntimeItemRewardItemSnapshot; +use super::resolve_npc_interaction_input_type::ResolveNpcInteractionInput; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -13,7 +18,7 @@ pub struct ResolveNpcBattleInteractionInput { pub npc_interaction: ResolveNpcInteractionInput, pub story_session_id: String, pub actor_user_id: String, - pub battle_state_id: Option, + pub battle_state_id: Option::, pub player_hp: i32, pub player_max_hp: i32, pub player_mana: i32, @@ -21,9 +26,11 @@ pub struct ResolveNpcBattleInteractionInput { pub target_hp: i32, pub target_max_hp: i32, pub experience_reward: u32, - pub reward_items: Vec, + pub reward_items: Vec::, } + impl __sdk::InModule for ResolveNpcBattleInteractionInput { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/resolve_npc_interaction_and_return_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/resolve_npc_interaction_and_return_procedure.rs index aaba3d9c..4952a32a 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/resolve_npc_interaction_and_return_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/resolve_npc_interaction_and_return_procedure.rs @@ -2,17 +2,23 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; -use super::npc_interaction_procedure_result_type::NpcInteractionProcedureResult; use super::resolve_npc_interaction_input_type::ResolveNpcInteractionInput; +use super::npc_interaction_procedure_result_type::NpcInteractionProcedureResult; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] -struct ResolveNpcInteractionAndReturnArgs { + struct ResolveNpcInteractionAndReturnArgs { pub input: ResolveNpcInteractionInput, } + impl __sdk::InModule for ResolveNpcInteractionAndReturnArgs { type Module = super::RemoteModule; } @@ -22,19 +28,16 @@ impl __sdk::InModule for ResolveNpcInteractionAndReturnArgs { /// /// Implemented for [`super::RemoteProcedures`]. pub trait resolve_npc_interaction_and_return { - fn resolve_npc_interaction_and_return(&self, input: ResolveNpcInteractionInput) { - self.resolve_npc_interaction_and_return_then(input, |_, _| {}); + fn resolve_npc_interaction_and_return(&self, input: ResolveNpcInteractionInput, +) { + self.resolve_npc_interaction_and_return_then(input, |_, _| {}); } fn resolve_npc_interaction_and_return_then( &self, input: ResolveNpcInteractionInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ); } @@ -43,17 +46,13 @@ impl resolve_npc_interaction_and_return for super::RemoteProcedures { &self, input: ResolveNpcInteractionInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ) { - self.imp - .invoke_procedure_with_callback::<_, NpcInteractionProcedureResult>( - "resolve_npc_interaction_and_return", - ResolveNpcInteractionAndReturnArgs { input }, - __callback, - ); + self.imp.invoke_procedure_with_callback::<_, NpcInteractionProcedureResult>( + "resolve_npc_interaction_and_return", + ResolveNpcInteractionAndReturnArgs { input, }, + __callback, + ); } } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/resolve_npc_interaction_input_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/resolve_npc_interaction_input_type.rs index 9a4439ce..d553ea4e 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/resolve_npc_interaction_input_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/resolve_npc_interaction_input_type.rs @@ -2,7 +2,13 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; + #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -11,10 +17,12 @@ pub struct ResolveNpcInteractionInput { pub npc_id: String, pub npc_name: String, pub interaction_function_id: String, - pub release_npc_id: Option, + pub release_npc_id: Option::, pub updated_at_micros: i64, } + impl __sdk::InModule for ResolveNpcInteractionInput { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/resolve_npc_interaction_reducer.rs b/server-rs/crates/spacetime-client/src/module_bindings/resolve_npc_interaction_reducer.rs index 52d213b5..37563788 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/resolve_npc_interaction_reducer.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/resolve_npc_interaction_reducer.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::resolve_npc_interaction_input_type::ResolveNpcInteractionInput; @@ -14,8 +19,10 @@ pub(super) struct ResolveNpcInteractionArgs { impl From for super::Reducer { fn from(args: ResolveNpcInteractionArgs) -> Self { - Self::ResolveNpcInteraction { input: args.input } - } + Self::ResolveNpcInteraction { + input: args.input, +} +} } impl __sdk::InModule for ResolveNpcInteractionArgs { @@ -33,8 +40,9 @@ pub trait resolve_npc_interaction { /// The reducer will run asynchronously in the future, /// and this method provides no way to listen for its completion status. /// /// Use [`resolve_npc_interaction:resolve_npc_interaction_then`] to run a callback after the reducer completes. - fn resolve_npc_interaction(&self, input: ResolveNpcInteractionInput) -> __sdk::Result<()> { - self.resolve_npc_interaction_then(input, |_, _| {}) + fn resolve_npc_interaction(&self, input: ResolveNpcInteractionInput, +) -> __sdk::Result<()> { + self.resolve_npc_interaction_then(input, |_, _| {}) } /// Request that the remote module invoke the reducer `resolve_npc_interaction` to run as soon as possible, @@ -62,7 +70,7 @@ impl resolve_npc_interaction for super::RemoteReducers { + Send + 'static, ) -> __sdk::Result<()> { - self.imp - .invoke_reducer_with_callback(ResolveNpcInteractionArgs { input }, callback) + self.imp.invoke_reducer_with_callback(ResolveNpcInteractionArgs { input, }, callback) } } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/resolve_npc_social_action_and_return_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/resolve_npc_social_action_and_return_procedure.rs index c1425649..2411e620 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/resolve_npc_social_action_and_return_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/resolve_npc_social_action_and_return_procedure.rs @@ -2,17 +2,23 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; -use super::npc_state_procedure_result_type::NpcStateProcedureResult; use super::resolve_npc_social_action_input_type::ResolveNpcSocialActionInput; +use super::npc_state_procedure_result_type::NpcStateProcedureResult; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] -struct ResolveNpcSocialActionAndReturnArgs { + struct ResolveNpcSocialActionAndReturnArgs { pub input: ResolveNpcSocialActionInput, } + impl __sdk::InModule for ResolveNpcSocialActionAndReturnArgs { type Module = super::RemoteModule; } @@ -22,19 +28,16 @@ impl __sdk::InModule for ResolveNpcSocialActionAndReturnArgs { /// /// Implemented for [`super::RemoteProcedures`]. pub trait resolve_npc_social_action_and_return { - fn resolve_npc_social_action_and_return(&self, input: ResolveNpcSocialActionInput) { - self.resolve_npc_social_action_and_return_then(input, |_, _| {}); + fn resolve_npc_social_action_and_return(&self, input: ResolveNpcSocialActionInput, +) { + self.resolve_npc_social_action_and_return_then(input, |_, _| {}); } fn resolve_npc_social_action_and_return_then( &self, input: ResolveNpcSocialActionInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ); } @@ -43,17 +46,13 @@ impl resolve_npc_social_action_and_return for super::RemoteProcedures { &self, input: ResolveNpcSocialActionInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ) { - self.imp - .invoke_procedure_with_callback::<_, NpcStateProcedureResult>( - "resolve_npc_social_action_and_return", - ResolveNpcSocialActionAndReturnArgs { input }, - __callback, - ); + self.imp.invoke_procedure_with_callback::<_, NpcStateProcedureResult>( + "resolve_npc_social_action_and_return", + ResolveNpcSocialActionAndReturnArgs { input, }, + __callback, + ); } } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/resolve_npc_social_action_input_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/resolve_npc_social_action_input_type.rs index 29487297..10df08b1 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/resolve_npc_social_action_input_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/resolve_npc_social_action_input_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::npc_social_action_kind_type::NpcSocialActionKind; @@ -13,11 +18,13 @@ pub struct ResolveNpcSocialActionInput { pub npc_id: String, pub npc_name: String, pub action_kind: NpcSocialActionKind, - pub affinity_gain_override: Option, - pub note: Option, + pub affinity_gain_override: Option::, + pub note: Option::, pub updated_at_micros: i64, } + impl __sdk::InModule for ResolveNpcSocialActionInput { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/resolve_npc_social_action_reducer.rs b/server-rs/crates/spacetime-client/src/module_bindings/resolve_npc_social_action_reducer.rs index 28e5ce36..e8717b6f 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/resolve_npc_social_action_reducer.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/resolve_npc_social_action_reducer.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::resolve_npc_social_action_input_type::ResolveNpcSocialActionInput; @@ -14,8 +19,10 @@ pub(super) struct ResolveNpcSocialActionArgs { impl From for super::Reducer { fn from(args: ResolveNpcSocialActionArgs) -> Self { - Self::ResolveNpcSocialAction { input: args.input } - } + Self::ResolveNpcSocialAction { + input: args.input, +} +} } impl __sdk::InModule for ResolveNpcSocialActionArgs { @@ -33,8 +40,9 @@ pub trait resolve_npc_social_action { /// The reducer will run asynchronously in the future, /// and this method provides no way to listen for its completion status. /// /// Use [`resolve_npc_social_action:resolve_npc_social_action_then`] to run a callback after the reducer completes. - fn resolve_npc_social_action(&self, input: ResolveNpcSocialActionInput) -> __sdk::Result<()> { - self.resolve_npc_social_action_then(input, |_, _| {}) + fn resolve_npc_social_action(&self, input: ResolveNpcSocialActionInput, +) -> __sdk::Result<()> { + self.resolve_npc_social_action_then(input, |_, _| {}) } /// Request that the remote module invoke the reducer `resolve_npc_social_action` to run as soon as possible, @@ -62,7 +70,7 @@ impl resolve_npc_social_action for super::RemoteReducers { + Send + 'static, ) -> __sdk::Result<()> { - self.imp - .invoke_reducer_with_callback(ResolveNpcSocialActionArgs { input }, callback) + self.imp.invoke_reducer_with_callback(ResolveNpcSocialActionArgs { input, }, callback) } } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/resolve_treasure_interaction_and_return_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/resolve_treasure_interaction_and_return_procedure.rs index a224c122..cc50e10f 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/resolve_treasure_interaction_and_return_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/resolve_treasure_interaction_and_return_procedure.rs @@ -2,17 +2,23 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; -use super::treasure_record_procedure_result_type::TreasureRecordProcedureResult; use super::treasure_resolve_input_type::TreasureResolveInput; +use super::treasure_record_procedure_result_type::TreasureRecordProcedureResult; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] -struct ResolveTreasureInteractionAndReturnArgs { + struct ResolveTreasureInteractionAndReturnArgs { pub input: TreasureResolveInput, } + impl __sdk::InModule for ResolveTreasureInteractionAndReturnArgs { type Module = super::RemoteModule; } @@ -22,19 +28,16 @@ impl __sdk::InModule for ResolveTreasureInteractionAndReturnArgs { /// /// Implemented for [`super::RemoteProcedures`]. pub trait resolve_treasure_interaction_and_return { - fn resolve_treasure_interaction_and_return(&self, input: TreasureResolveInput) { - self.resolve_treasure_interaction_and_return_then(input, |_, _| {}); + fn resolve_treasure_interaction_and_return(&self, input: TreasureResolveInput, +) { + self.resolve_treasure_interaction_and_return_then(input, |_, _| {}); } fn resolve_treasure_interaction_and_return_then( &self, input: TreasureResolveInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ); } @@ -43,17 +46,13 @@ impl resolve_treasure_interaction_and_return for super::RemoteProcedures { &self, input: TreasureResolveInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ) { - self.imp - .invoke_procedure_with_callback::<_, TreasureRecordProcedureResult>( - "resolve_treasure_interaction_and_return", - ResolveTreasureInteractionAndReturnArgs { input }, - __callback, - ); + self.imp.invoke_procedure_with_callback::<_, TreasureRecordProcedureResult>( + "resolve_treasure_interaction_and_return", + ResolveTreasureInteractionAndReturnArgs { input, }, + __callback, + ); } } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/resolve_treasure_interaction_reducer.rs b/server-rs/crates/spacetime-client/src/module_bindings/resolve_treasure_interaction_reducer.rs index 942b377a..6a830425 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/resolve_treasure_interaction_reducer.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/resolve_treasure_interaction_reducer.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::treasure_resolve_input_type::TreasureResolveInput; @@ -14,8 +19,10 @@ pub(super) struct ResolveTreasureInteractionArgs { impl From for super::Reducer { fn from(args: ResolveTreasureInteractionArgs) -> Self { - Self::ResolveTreasureInteraction { input: args.input } - } + Self::ResolveTreasureInteraction { + input: args.input, +} +} } impl __sdk::InModule for ResolveTreasureInteractionArgs { @@ -33,8 +40,9 @@ pub trait resolve_treasure_interaction { /// The reducer will run asynchronously in the future, /// and this method provides no way to listen for its completion status. /// /// Use [`resolve_treasure_interaction:resolve_treasure_interaction_then`] to run a callback after the reducer completes. - fn resolve_treasure_interaction(&self, input: TreasureResolveInput) -> __sdk::Result<()> { - self.resolve_treasure_interaction_then(input, |_, _| {}) + fn resolve_treasure_interaction(&self, input: TreasureResolveInput, +) -> __sdk::Result<()> { + self.resolve_treasure_interaction_then(input, |_, _| {}) } /// Request that the remote module invoke the reducer `resolve_treasure_interaction` to run as soon as possible, @@ -62,7 +70,7 @@ impl resolve_treasure_interaction for super::RemoteReducers { + Send + 'static, ) -> __sdk::Result<()> { - self.imp - .invoke_reducer_with_callback(ResolveTreasureInteractionArgs { input }, callback) + self.imp.invoke_reducer_with_callback(ResolveTreasureInteractionArgs { input, }, callback) } } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/resume_profile_save_archive_and_return_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/resume_profile_save_archive_and_return_procedure.rs index 957c105f..8505e52a 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/resume_profile_save_archive_and_return_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/resume_profile_save_archive_and_return_procedure.rs @@ -2,17 +2,23 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::runtime_profile_save_archive_procedure_result_type::RuntimeProfileSaveArchiveProcedureResult; use super::runtime_profile_save_archive_resume_input_type::RuntimeProfileSaveArchiveResumeInput; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] -struct ResumeProfileSaveArchiveAndReturnArgs { + struct ResumeProfileSaveArchiveAndReturnArgs { pub input: RuntimeProfileSaveArchiveResumeInput, } + impl __sdk::InModule for ResumeProfileSaveArchiveAndReturnArgs { type Module = super::RemoteModule; } @@ -22,19 +28,16 @@ impl __sdk::InModule for ResumeProfileSaveArchiveAndReturnArgs { /// /// Implemented for [`super::RemoteProcedures`]. pub trait resume_profile_save_archive_and_return { - fn resume_profile_save_archive_and_return(&self, input: RuntimeProfileSaveArchiveResumeInput) { - self.resume_profile_save_archive_and_return_then(input, |_, _| {}); + fn resume_profile_save_archive_and_return(&self, input: RuntimeProfileSaveArchiveResumeInput, +) { + self.resume_profile_save_archive_and_return_then(input, |_, _| {}); } fn resume_profile_save_archive_and_return_then( &self, input: RuntimeProfileSaveArchiveResumeInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ); } @@ -43,17 +46,13 @@ impl resume_profile_save_archive_and_return for super::RemoteProcedures { &self, input: RuntimeProfileSaveArchiveResumeInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ) { - self.imp - .invoke_procedure_with_callback::<_, RuntimeProfileSaveArchiveProcedureResult>( - "resume_profile_save_archive_and_return", - ResumeProfileSaveArchiveAndReturnArgs { input }, - __callback, - ); + self.imp.invoke_procedure_with_callback::<_, RuntimeProfileSaveArchiveProcedureResult>( + "resume_profile_save_archive_and_return", + ResumeProfileSaveArchiveAndReturnArgs { input, }, + __callback, + ); } } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/revoke_database_migration_operator_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/revoke_database_migration_operator_procedure.rs index feb5086e..c420de60 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/revoke_database_migration_operator_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/revoke_database_migration_operator_procedure.rs @@ -2,17 +2,23 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::database_migration_operator_procedure_result_type::DatabaseMigrationOperatorProcedureResult; use super::database_migration_revoke_operator_input_type::DatabaseMigrationRevokeOperatorInput; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] -struct RevokeDatabaseMigrationOperatorArgs { + struct RevokeDatabaseMigrationOperatorArgs { pub input: DatabaseMigrationRevokeOperatorInput, } + impl __sdk::InModule for RevokeDatabaseMigrationOperatorArgs { type Module = super::RemoteModule; } @@ -22,19 +28,16 @@ impl __sdk::InModule for RevokeDatabaseMigrationOperatorArgs { /// /// Implemented for [`super::RemoteProcedures`]. pub trait revoke_database_migration_operator { - fn revoke_database_migration_operator(&self, input: DatabaseMigrationRevokeOperatorInput) { - self.revoke_database_migration_operator_then(input, |_, _| {}); + fn revoke_database_migration_operator(&self, input: DatabaseMigrationRevokeOperatorInput, +) { + self.revoke_database_migration_operator_then(input, |_, _| {}); } fn revoke_database_migration_operator_then( &self, input: DatabaseMigrationRevokeOperatorInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ); } @@ -43,17 +46,13 @@ impl revoke_database_migration_operator for super::RemoteProcedures { &self, input: DatabaseMigrationRevokeOperatorInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ) { - self.imp - .invoke_procedure_with_callback::<_, DatabaseMigrationOperatorProcedureResult>( - "revoke_database_migration_operator", - RevokeDatabaseMigrationOperatorArgs { input }, - __callback, - ); + self.imp.invoke_procedure_with_callback::<_, DatabaseMigrationOperatorProcedureResult>( + "revoke_database_migration_operator", + RevokeDatabaseMigrationOperatorArgs { input, }, + __callback, + ); } } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/rpg_agent_draft_card_kind_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/rpg_agent_draft_card_kind_type.rs index e082dc36..3888a866 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/rpg_agent_draft_card_kind_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/rpg_agent_draft_card_kind_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -27,8 +32,12 @@ pub enum RpgAgentDraftCardKind { Carrier, SidequestSeed, + } + + impl __sdk::InModule for RpgAgentDraftCardKind { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/rpg_agent_draft_card_status_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/rpg_agent_draft_card_status_type.rs index 391625f5..f890218d 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/rpg_agent_draft_card_status_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/rpg_agent_draft_card_status_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -15,8 +20,12 @@ pub enum RpgAgentDraftCardStatus { Locked, Warning, + } + + impl __sdk::InModule for RpgAgentDraftCardStatus { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/rpg_agent_message_kind_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/rpg_agent_message_kind_type.rs index fc54a7b1..180466b0 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/rpg_agent_message_kind_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/rpg_agent_message_kind_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -19,8 +24,12 @@ pub enum RpgAgentMessageKind { Warning, ActionResult, + } + + impl __sdk::InModule for RpgAgentMessageKind { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/rpg_agent_message_role_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/rpg_agent_message_role_type.rs index 67e383f2..ad7e445b 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/rpg_agent_message_role_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/rpg_agent_message_role_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -13,8 +18,12 @@ pub enum RpgAgentMessageRole { Assistant, System, + } + + impl __sdk::InModule for RpgAgentMessageRole { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/rpg_agent_operation_status_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/rpg_agent_operation_status_type.rs index 555c0439..42cd9fc2 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/rpg_agent_operation_status_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/rpg_agent_operation_status_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -15,8 +20,12 @@ pub enum RpgAgentOperationStatus { Completed, Failed, + } + + impl __sdk::InModule for RpgAgentOperationStatus { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/rpg_agent_operation_type_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/rpg_agent_operation_type_type.rs index 8ae1d00e..da820cbc 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/rpg_agent_operation_type_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/rpg_agent_operation_type_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -37,8 +42,12 @@ pub enum RpgAgentOperationType { PublishWorld, RevertCheckpoint, + } + + impl __sdk::InModule for RpgAgentOperationType { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/rpg_agent_stage_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/rpg_agent_stage_type.rs index 1a94e20f..b9d0cf0e 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/rpg_agent_stage_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/rpg_agent_stage_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -25,8 +30,12 @@ pub enum RpgAgentStage { Published, Error, + } + + impl __sdk::InModule for RpgAgentStage { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/runtime_browse_history_clear_input_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/runtime_browse_history_clear_input_type.rs index e5bc78f0..41fc6954 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/runtime_browse_history_clear_input_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/runtime_browse_history_clear_input_type.rs @@ -2,7 +2,13 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; + #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -10,6 +16,8 @@ pub struct RuntimeBrowseHistoryClearInput { pub user_id: String, } + impl __sdk::InModule for RuntimeBrowseHistoryClearInput { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/runtime_browse_history_list_input_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/runtime_browse_history_list_input_type.rs index cdc31641..5795bb3a 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/runtime_browse_history_list_input_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/runtime_browse_history_list_input_type.rs @@ -2,7 +2,13 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; + #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -10,6 +16,8 @@ pub struct RuntimeBrowseHistoryListInput { pub user_id: String, } + impl __sdk::InModule for RuntimeBrowseHistoryListInput { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/runtime_browse_history_procedure_result_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/runtime_browse_history_procedure_result_type.rs index 3721358f..9c02dd59 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/runtime_browse_history_procedure_result_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/runtime_browse_history_procedure_result_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::runtime_browse_history_snapshot_type::RuntimeBrowseHistorySnapshot; @@ -10,10 +15,12 @@ use super::runtime_browse_history_snapshot_type::RuntimeBrowseHistorySnapshot; #[sats(crate = __lib)] pub struct RuntimeBrowseHistoryProcedureResult { pub ok: bool, - pub entries: Vec, - pub error_message: Option, + pub entries: Vec::, + pub error_message: Option::, } + impl __sdk::InModule for RuntimeBrowseHistoryProcedureResult { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/runtime_browse_history_snapshot_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/runtime_browse_history_snapshot_type.rs index 9222eaed..05c404c1 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/runtime_browse_history_snapshot_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/runtime_browse_history_snapshot_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::runtime_browse_history_theme_mode_type::RuntimeBrowseHistoryThemeMode; @@ -16,7 +21,7 @@ pub struct RuntimeBrowseHistorySnapshot { pub world_name: String, pub subtitle: String, pub summary_text: String, - pub cover_image_src: Option, + pub cover_image_src: Option::, pub theme_mode: RuntimeBrowseHistoryThemeMode, pub author_display_name: String, pub visited_at_micros: i64, @@ -24,6 +29,8 @@ pub struct RuntimeBrowseHistorySnapshot { pub updated_at_micros: i64, } + impl __sdk::InModule for RuntimeBrowseHistorySnapshot { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/runtime_browse_history_sync_input_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/runtime_browse_history_sync_input_type.rs index 0996e66e..09c9fb18 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/runtime_browse_history_sync_input_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/runtime_browse_history_sync_input_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::runtime_browse_history_write_input_type::RuntimeBrowseHistoryWriteInput; @@ -10,10 +15,12 @@ use super::runtime_browse_history_write_input_type::RuntimeBrowseHistoryWriteInp #[sats(crate = __lib)] pub struct RuntimeBrowseHistorySyncInput { pub user_id: String, - pub entries: Vec, + pub entries: Vec::, pub updated_at_micros: i64, } + impl __sdk::InModule for RuntimeBrowseHistorySyncInput { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/runtime_browse_history_theme_mode_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/runtime_browse_history_theme_mode_type.rs index fc8a1b11..de120fac 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/runtime_browse_history_theme_mode_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/runtime_browse_history_theme_mode_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -19,8 +24,12 @@ pub enum RuntimeBrowseHistoryThemeMode { Rift, Mythic, + } + + impl __sdk::InModule for RuntimeBrowseHistoryThemeMode { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/runtime_browse_history_write_input_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/runtime_browse_history_write_input_type.rs index 9a54bd81..fb087aac 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/runtime_browse_history_write_input_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/runtime_browse_history_write_input_type.rs @@ -2,7 +2,13 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; + #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -10,14 +16,16 @@ pub struct RuntimeBrowseHistoryWriteInput { pub owner_user_id: String, pub profile_id: String, pub world_name: String, - pub subtitle: Option, - pub summary_text: Option, - pub cover_image_src: Option, - pub theme_mode: Option, - pub author_display_name: Option, - pub visited_at: Option, + pub subtitle: Option::, + pub summary_text: Option::, + pub cover_image_src: Option::, + pub theme_mode: Option::, + pub author_display_name: Option::, + pub visited_at: Option::, } + impl __sdk::InModule for RuntimeBrowseHistoryWriteInput { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/runtime_inventory_state_procedure_result_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/runtime_inventory_state_procedure_result_type.rs index bf0e3103..09374d4c 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/runtime_inventory_state_procedure_result_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/runtime_inventory_state_procedure_result_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::runtime_inventory_state_snapshot_type::RuntimeInventoryStateSnapshot; @@ -10,10 +15,12 @@ use super::runtime_inventory_state_snapshot_type::RuntimeInventoryStateSnapshot; #[sats(crate = __lib)] pub struct RuntimeInventoryStateProcedureResult { pub ok: bool, - pub snapshot: Option, - pub error_message: Option, + pub snapshot: Option::, + pub error_message: Option::, } + impl __sdk::InModule for RuntimeInventoryStateProcedureResult { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/runtime_inventory_state_query_input_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/runtime_inventory_state_query_input_type.rs index e6b02ed3..75c93962 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/runtime_inventory_state_query_input_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/runtime_inventory_state_query_input_type.rs @@ -2,7 +2,13 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; + #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -11,6 +17,8 @@ pub struct RuntimeInventoryStateQueryInput { pub actor_user_id: String, } + impl __sdk::InModule for RuntimeInventoryStateQueryInput { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/runtime_inventory_state_snapshot_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/runtime_inventory_state_snapshot_type.rs index 624e66c6..7ab3ebea 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/runtime_inventory_state_snapshot_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/runtime_inventory_state_snapshot_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::inventory_slot_snapshot_type::InventorySlotSnapshot; @@ -11,10 +16,12 @@ use super::inventory_slot_snapshot_type::InventorySlotSnapshot; pub struct RuntimeInventoryStateSnapshot { pub runtime_session_id: String, pub actor_user_id: String, - pub backpack_items: Vec, - pub equipment_items: Vec, + pub backpack_items: Vec::, + pub equipment_items: Vec::, } + impl __sdk::InModule for RuntimeInventoryStateSnapshot { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/runtime_item_equipment_slot_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/runtime_item_equipment_slot_type.rs index b539d4f3..01cd4888 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/runtime_item_equipment_slot_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/runtime_item_equipment_slot_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -13,8 +18,12 @@ pub enum RuntimeItemEquipmentSlot { Armor, Relic, + } + + impl __sdk::InModule for RuntimeItemEquipmentSlot { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/runtime_item_reward_item_rarity_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/runtime_item_reward_item_rarity_type.rs index dc4250ab..a3316ea0 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/runtime_item_reward_item_rarity_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/runtime_item_reward_item_rarity_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -17,8 +22,12 @@ pub enum RuntimeItemRewardItemRarity { Epic, Legendary, + } + + impl __sdk::InModule for RuntimeItemRewardItemRarity { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/runtime_item_reward_item_snapshot_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/runtime_item_reward_item_snapshot_type.rs index 4df1f248..65bcb55c 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/runtime_item_reward_item_snapshot_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/runtime_item_reward_item_snapshot_type.rs @@ -2,10 +2,15 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; -use super::runtime_item_equipment_slot_type::RuntimeItemEquipmentSlot; use super::runtime_item_reward_item_rarity_type::RuntimeItemRewardItemRarity; +use super::runtime_item_equipment_slot_type::RuntimeItemEquipmentSlot; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -13,15 +18,17 @@ pub struct RuntimeItemRewardItemSnapshot { pub item_id: String, pub category: String, pub item_name: String, - pub description: Option, + pub description: Option::, pub quantity: u32, pub rarity: RuntimeItemRewardItemRarity, - pub tags: Vec, + pub tags: Vec::, pub stackable: bool, pub stack_key: String, - pub equipment_slot_id: Option, + pub equipment_slot_id: Option::, } + impl __sdk::InModule for RuntimeItemRewardItemSnapshot { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/runtime_platform_theme_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/runtime_platform_theme_type.rs index cbb36c5d..7eef5e97 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/runtime_platform_theme_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/runtime_platform_theme_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -11,8 +16,12 @@ pub enum RuntimePlatformTheme { Light, Dark, + } + + impl __sdk::InModule for RuntimePlatformTheme { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_dashboard_get_input_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_dashboard_get_input_type.rs index 47af88ec..9be0a158 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_dashboard_get_input_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_dashboard_get_input_type.rs @@ -2,7 +2,13 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; + #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -10,6 +16,8 @@ pub struct RuntimeProfileDashboardGetInput { pub user_id: String, } + impl __sdk::InModule for RuntimeProfileDashboardGetInput { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_dashboard_procedure_result_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_dashboard_procedure_result_type.rs index fa80a719..a04facbb 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_dashboard_procedure_result_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_dashboard_procedure_result_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::runtime_profile_dashboard_snapshot_type::RuntimeProfileDashboardSnapshot; @@ -10,10 +15,12 @@ use super::runtime_profile_dashboard_snapshot_type::RuntimeProfileDashboardSnaps #[sats(crate = __lib)] pub struct RuntimeProfileDashboardProcedureResult { pub ok: bool, - pub record: Option, - pub error_message: Option, + pub record: Option::, + pub error_message: Option::, } + impl __sdk::InModule for RuntimeProfileDashboardProcedureResult { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_dashboard_snapshot_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_dashboard_snapshot_type.rs index a3e97b05..6a54e5ff 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_dashboard_snapshot_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_dashboard_snapshot_type.rs @@ -2,7 +2,13 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; + #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -11,9 +17,11 @@ pub struct RuntimeProfileDashboardSnapshot { pub wallet_balance: u64, pub total_play_time_ms: u64, pub played_world_count: u32, - pub updated_at_micros: Option, + pub updated_at_micros: Option::, } + impl __sdk::InModule for RuntimeProfileDashboardSnapshot { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_membership_benefit_snapshot_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_membership_benefit_snapshot_type.rs index 780977ed..adbf5c21 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_membership_benefit_snapshot_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_membership_benefit_snapshot_type.rs @@ -2,7 +2,13 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; + #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -14,6 +20,8 @@ pub struct RuntimeProfileMembershipBenefitSnapshot { pub year_value: String, } + impl __sdk::InModule for RuntimeProfileMembershipBenefitSnapshot { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_membership_snapshot_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_membership_snapshot_type.rs index 03026d21..1dc13cd3 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_membership_snapshot_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_membership_snapshot_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::runtime_profile_membership_status_type::RuntimeProfileMembershipStatus; use super::runtime_profile_membership_tier_type::RuntimeProfileMembershipTier; @@ -13,11 +18,13 @@ pub struct RuntimeProfileMembershipSnapshot { pub user_id: String, pub status: RuntimeProfileMembershipStatus, pub tier: RuntimeProfileMembershipTier, - pub started_at_micros: Option, - pub expires_at_micros: Option, - pub updated_at_micros: Option, + pub started_at_micros: Option::, + pub expires_at_micros: Option::, + pub updated_at_micros: Option::, } + impl __sdk::InModule for RuntimeProfileMembershipSnapshot { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_membership_status_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_membership_status_type.rs index 392a1576..5090c29c 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_membership_status_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_membership_status_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -11,8 +16,12 @@ pub enum RuntimeProfileMembershipStatus { Normal, Active, + } + + impl __sdk::InModule for RuntimeProfileMembershipStatus { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_membership_tier_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_membership_tier_type.rs index 1799b3f2..1d8993ce 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_membership_tier_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_membership_tier_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -15,8 +20,12 @@ pub enum RuntimeProfileMembershipTier { Season, Year, + } + + impl __sdk::InModule for RuntimeProfileMembershipTier { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_play_stats_get_input_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_play_stats_get_input_type.rs index 825a6707..99665ea0 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_play_stats_get_input_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_play_stats_get_input_type.rs @@ -2,7 +2,13 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; + #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -10,6 +16,8 @@ pub struct RuntimeProfilePlayStatsGetInput { pub user_id: String, } + impl __sdk::InModule for RuntimeProfilePlayStatsGetInput { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_play_stats_procedure_result_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_play_stats_procedure_result_type.rs index 5572f438..24044b8b 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_play_stats_procedure_result_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_play_stats_procedure_result_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::runtime_profile_play_stats_snapshot_type::RuntimeProfilePlayStatsSnapshot; @@ -10,10 +15,12 @@ use super::runtime_profile_play_stats_snapshot_type::RuntimeProfilePlayStatsSnap #[sats(crate = __lib)] pub struct RuntimeProfilePlayStatsProcedureResult { pub ok: bool, - pub record: Option, - pub error_message: Option, + pub record: Option::, + pub error_message: Option::, } + impl __sdk::InModule for RuntimeProfilePlayStatsProcedureResult { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_play_stats_snapshot_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_play_stats_snapshot_type.rs index 472a99c0..e99d2e72 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_play_stats_snapshot_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_play_stats_snapshot_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::runtime_profile_played_world_snapshot_type::RuntimeProfilePlayedWorldSnapshot; @@ -11,10 +16,12 @@ use super::runtime_profile_played_world_snapshot_type::RuntimeProfilePlayedWorld pub struct RuntimeProfilePlayStatsSnapshot { pub user_id: String, pub total_play_time_ms: u64, - pub played_works: Vec, - pub updated_at_micros: Option, + pub played_works: Vec::, + pub updated_at_micros: Option::, } + impl __sdk::InModule for RuntimeProfilePlayStatsSnapshot { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_played_world_snapshot_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_played_world_snapshot_type.rs index fa6c1d2c..f642a84d 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_played_world_snapshot_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_played_world_snapshot_type.rs @@ -2,7 +2,13 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; + #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -10,9 +16,9 @@ pub struct RuntimeProfilePlayedWorldSnapshot { pub played_world_id: String, pub user_id: String, pub world_key: String, - pub owner_user_id: Option, - pub profile_id: Option, - pub world_type: Option, + pub owner_user_id: Option::, + pub profile_id: Option::, + pub world_type: Option::, pub world_title: String, pub world_subtitle: String, pub first_played_at_micros: i64, @@ -20,6 +26,8 @@ pub struct RuntimeProfilePlayedWorldSnapshot { pub last_observed_play_time_ms: u64, } + impl __sdk::InModule for RuntimeProfilePlayedWorldSnapshot { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_recharge_center_get_input_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_recharge_center_get_input_type.rs index 51702dc6..e4f5905c 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_recharge_center_get_input_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_recharge_center_get_input_type.rs @@ -2,7 +2,13 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; + #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -10,6 +16,8 @@ pub struct RuntimeProfileRechargeCenterGetInput { pub user_id: String, } + impl __sdk::InModule for RuntimeProfileRechargeCenterGetInput { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_recharge_center_procedure_result_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_recharge_center_procedure_result_type.rs index e9e055e0..7f12d30f 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_recharge_center_procedure_result_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_recharge_center_procedure_result_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::runtime_profile_recharge_center_snapshot_type::RuntimeProfileRechargeCenterSnapshot; use super::runtime_profile_recharge_order_snapshot_type::RuntimeProfileRechargeOrderSnapshot; @@ -11,11 +16,13 @@ use super::runtime_profile_recharge_order_snapshot_type::RuntimeProfileRechargeO #[sats(crate = __lib)] pub struct RuntimeProfileRechargeCenterProcedureResult { pub ok: bool, - pub record: Option, - pub order: Option, - pub error_message: Option, + pub record: Option::, + pub order: Option::, + pub error_message: Option::, } + impl __sdk::InModule for RuntimeProfileRechargeCenterProcedureResult { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_recharge_center_snapshot_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_recharge_center_snapshot_type.rs index a9f7f4ad..2833f415 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_recharge_center_snapshot_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_recharge_center_snapshot_type.rs @@ -2,12 +2,17 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; -use super::runtime_profile_membership_benefit_snapshot_type::RuntimeProfileMembershipBenefitSnapshot; use super::runtime_profile_membership_snapshot_type::RuntimeProfileMembershipSnapshot; -use super::runtime_profile_recharge_order_snapshot_type::RuntimeProfileRechargeOrderSnapshot; use super::runtime_profile_recharge_product_snapshot_type::RuntimeProfileRechargeProductSnapshot; +use super::runtime_profile_membership_benefit_snapshot_type::RuntimeProfileMembershipBenefitSnapshot; +use super::runtime_profile_recharge_order_snapshot_type::RuntimeProfileRechargeOrderSnapshot; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -15,13 +20,15 @@ pub struct RuntimeProfileRechargeCenterSnapshot { pub user_id: String, pub wallet_balance: u64, pub membership: RuntimeProfileMembershipSnapshot, - pub point_products: Vec, - pub membership_products: Vec, - pub benefits: Vec, - pub latest_order: Option, + pub point_products: Vec::, + pub membership_products: Vec::, + pub benefits: Vec::, + pub latest_order: Option::, pub has_points_recharged: bool, } + impl __sdk::InModule for RuntimeProfileRechargeCenterSnapshot { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_recharge_order_create_input_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_recharge_order_create_input_type.rs index 7a4c7ee4..de908fc5 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_recharge_order_create_input_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_recharge_order_create_input_type.rs @@ -2,7 +2,13 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; + #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -13,6 +19,8 @@ pub struct RuntimeProfileRechargeOrderCreateInput { pub created_at_micros: i64, } + impl __sdk::InModule for RuntimeProfileRechargeOrderCreateInput { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_recharge_order_snapshot_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_recharge_order_snapshot_type.rs index d2beea3b..9fc22b1b 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_recharge_order_snapshot_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_recharge_order_snapshot_type.rs @@ -2,10 +2,15 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; -use super::runtime_profile_recharge_order_status_type::RuntimeProfileRechargeOrderStatus; use super::runtime_profile_recharge_product_kind_type::RuntimeProfileRechargeProductKind; +use super::runtime_profile_recharge_order_status_type::RuntimeProfileRechargeOrderStatus; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -21,9 +26,11 @@ pub struct RuntimeProfileRechargeOrderSnapshot { pub paid_at_micros: i64, pub created_at_micros: i64, pub points_delta: i64, - pub membership_expires_at_micros: Option, + pub membership_expires_at_micros: Option::, } + impl __sdk::InModule for RuntimeProfileRechargeOrderSnapshot { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_recharge_order_status_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_recharge_order_status_type.rs index 3a1f01a8..619f6b6c 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_recharge_order_status_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_recharge_order_status_type.rs @@ -2,15 +2,24 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] #[derive(Copy, Eq, Hash)] pub enum RuntimeProfileRechargeOrderStatus { Paid, + } + + impl __sdk::InModule for RuntimeProfileRechargeOrderStatus { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_recharge_product_kind_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_recharge_product_kind_type.rs index 362af979..a26d3762 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_recharge_product_kind_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_recharge_product_kind_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -11,8 +16,12 @@ pub enum RuntimeProfileRechargeProductKind { Points, Membership, + } + + impl __sdk::InModule for RuntimeProfileRechargeProductKind { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_recharge_product_snapshot_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_recharge_product_snapshot_type.rs index 0aa2e5c1..0ca44536 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_recharge_product_snapshot_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_recharge_product_snapshot_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::runtime_profile_membership_tier_type::RuntimeProfileMembershipTier; use super::runtime_profile_recharge_product_kind_type::RuntimeProfileRechargeProductKind; @@ -22,6 +27,8 @@ pub struct RuntimeProfileRechargeProductSnapshot { pub tier: RuntimeProfileMembershipTier, } + impl __sdk::InModule for RuntimeProfileRechargeProductSnapshot { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_redeem_code_admin_disable_input_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_redeem_code_admin_disable_input_type.rs index 5a7ed897..73b2431b 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_redeem_code_admin_disable_input_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_redeem_code_admin_disable_input_type.rs @@ -2,7 +2,13 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; + #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -12,6 +18,8 @@ pub struct RuntimeProfileRedeemCodeAdminDisableInput { pub updated_at_micros: i64, } + impl __sdk::InModule for RuntimeProfileRedeemCodeAdminDisableInput { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_redeem_code_admin_procedure_result_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_redeem_code_admin_procedure_result_type.rs index 62254ff9..f825779d 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_redeem_code_admin_procedure_result_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_redeem_code_admin_procedure_result_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::runtime_profile_redeem_code_snapshot_type::RuntimeProfileRedeemCodeSnapshot; @@ -10,10 +15,12 @@ use super::runtime_profile_redeem_code_snapshot_type::RuntimeProfileRedeemCodeSn #[sats(crate = __lib)] pub struct RuntimeProfileRedeemCodeAdminProcedureResult { pub ok: bool, - pub record: Option, - pub error_message: Option, + pub record: Option::, + pub error_message: Option::, } + impl __sdk::InModule for RuntimeProfileRedeemCodeAdminProcedureResult { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_redeem_code_admin_upsert_input_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_redeem_code_admin_upsert_input_type.rs index 5f18a875..27a2e33c 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_redeem_code_admin_upsert_input_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_redeem_code_admin_upsert_input_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::runtime_profile_redeem_code_mode_type::RuntimeProfileRedeemCodeMode; @@ -15,11 +20,13 @@ pub struct RuntimeProfileRedeemCodeAdminUpsertInput { pub reward_points: u64, pub max_uses: u32, pub enabled: bool, - pub allowed_user_ids: Vec, - pub allowed_public_user_codes: Vec, + pub allowed_user_ids: Vec::, + pub allowed_public_user_codes: Vec::, pub updated_at_micros: i64, } + impl __sdk::InModule for RuntimeProfileRedeemCodeAdminUpsertInput { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_redeem_code_mode_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_redeem_code_mode_type.rs index 4bea6d79..da0de06c 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_redeem_code_mode_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_redeem_code_mode_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -13,8 +18,12 @@ pub enum RuntimeProfileRedeemCodeMode { Unique, Private, + } + + impl __sdk::InModule for RuntimeProfileRedeemCodeMode { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_redeem_code_snapshot_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_redeem_code_snapshot_type.rs index aea09f25..e88a2a68 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_redeem_code_snapshot_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_redeem_code_snapshot_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::runtime_profile_redeem_code_mode_type::RuntimeProfileRedeemCodeMode; @@ -15,12 +20,14 @@ pub struct RuntimeProfileRedeemCodeSnapshot { pub max_uses: u32, pub global_used_count: u32, pub enabled: bool, - pub allowed_user_ids: Vec, + pub allowed_user_ids: Vec::, pub created_by: String, pub created_at_micros: i64, pub updated_at_micros: i64, } + impl __sdk::InModule for RuntimeProfileRedeemCodeSnapshot { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_reward_code_redeem_input_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_reward_code_redeem_input_type.rs index e99bc781..93c5faf5 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_reward_code_redeem_input_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_reward_code_redeem_input_type.rs @@ -2,7 +2,13 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; + #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -12,6 +18,8 @@ pub struct RuntimeProfileRewardCodeRedeemInput { pub redeemed_at_micros: i64, } + impl __sdk::InModule for RuntimeProfileRewardCodeRedeemInput { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_reward_code_redeem_procedure_result_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_reward_code_redeem_procedure_result_type.rs index dd8936d7..f7c55fff 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_reward_code_redeem_procedure_result_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_reward_code_redeem_procedure_result_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::runtime_profile_reward_code_redeem_snapshot_type::RuntimeProfileRewardCodeRedeemSnapshot; @@ -10,10 +15,12 @@ use super::runtime_profile_reward_code_redeem_snapshot_type::RuntimeProfileRewar #[sats(crate = __lib)] pub struct RuntimeProfileRewardCodeRedeemProcedureResult { pub ok: bool, - pub record: Option, - pub error_message: Option, + pub record: Option::, + pub error_message: Option::, } + impl __sdk::InModule for RuntimeProfileRewardCodeRedeemProcedureResult { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_reward_code_redeem_snapshot_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_reward_code_redeem_snapshot_type.rs index 614e5d78..632e2312 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_reward_code_redeem_snapshot_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_reward_code_redeem_snapshot_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::runtime_profile_wallet_ledger_entry_snapshot_type::RuntimeProfileWalletLedgerEntrySnapshot; @@ -14,6 +19,8 @@ pub struct RuntimeProfileRewardCodeRedeemSnapshot { pub ledger_entry: RuntimeProfileWalletLedgerEntrySnapshot, } + impl __sdk::InModule for RuntimeProfileRewardCodeRedeemSnapshot { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_save_archive_list_input_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_save_archive_list_input_type.rs index 29eea52e..89524eb1 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_save_archive_list_input_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_save_archive_list_input_type.rs @@ -2,7 +2,13 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; + #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -10,6 +16,8 @@ pub struct RuntimeProfileSaveArchiveListInput { pub user_id: String, } + impl __sdk::InModule for RuntimeProfileSaveArchiveListInput { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_save_archive_procedure_result_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_save_archive_procedure_result_type.rs index 4f4d5c0c..7c8e7b07 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_save_archive_procedure_result_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_save_archive_procedure_result_type.rs @@ -2,21 +2,28 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; -use super::runtime_profile_save_archive_snapshot_type::RuntimeProfileSaveArchiveSnapshot; use super::runtime_snapshot_type::RuntimeSnapshot; +use super::runtime_profile_save_archive_snapshot_type::RuntimeProfileSaveArchiveSnapshot; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] pub struct RuntimeProfileSaveArchiveProcedureResult { pub ok: bool, - pub entries: Vec, - pub record: Option, - pub current_snapshot: Option, - pub error_message: Option, + pub entries: Vec::, + pub record: Option::, + pub current_snapshot: Option::, + pub error_message: Option::, } + impl __sdk::InModule for RuntimeProfileSaveArchiveProcedureResult { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_save_archive_resume_input_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_save_archive_resume_input_type.rs index 186de3e0..a290bf1f 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_save_archive_resume_input_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_save_archive_resume_input_type.rs @@ -2,7 +2,13 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; + #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -11,6 +17,8 @@ pub struct RuntimeProfileSaveArchiveResumeInput { pub world_key: String, } + impl __sdk::InModule for RuntimeProfileSaveArchiveResumeInput { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_save_archive_snapshot_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_save_archive_snapshot_type.rs index 9843ec8c..d88a1fab 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_save_archive_snapshot_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_save_archive_snapshot_type.rs @@ -2,7 +2,13 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; + #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -10,21 +16,23 @@ pub struct RuntimeProfileSaveArchiveSnapshot { pub archive_id: String, pub user_id: String, pub world_key: String, - pub owner_user_id: Option, - pub profile_id: Option, - pub world_type: Option, + pub owner_user_id: Option::, + pub profile_id: Option::, + pub world_type: Option::, pub world_name: String, pub subtitle: String, pub summary_text: String, - pub cover_image_src: Option, + pub cover_image_src: Option::, pub saved_at_micros: i64, pub bottom_tab: String, pub game_state_json: String, - pub current_story_json: Option, + pub current_story_json: Option::, pub created_at_micros: i64, pub updated_at_micros: i64, } + impl __sdk::InModule for RuntimeProfileSaveArchiveSnapshot { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_wallet_adjustment_input_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_wallet_adjustment_input_type.rs index 65315693..5b0d3307 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_wallet_adjustment_input_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_wallet_adjustment_input_type.rs @@ -2,7 +2,13 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; + #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -13,6 +19,8 @@ pub struct RuntimeProfileWalletAdjustmentInput { pub created_at_micros: i64, } + impl __sdk::InModule for RuntimeProfileWalletAdjustmentInput { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_wallet_adjustment_procedure_result_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_wallet_adjustment_procedure_result_type.rs index 6633088e..e9785ff0 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_wallet_adjustment_procedure_result_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_wallet_adjustment_procedure_result_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::runtime_profile_dashboard_snapshot_type::RuntimeProfileDashboardSnapshot; @@ -10,10 +15,12 @@ use super::runtime_profile_dashboard_snapshot_type::RuntimeProfileDashboardSnaps #[sats(crate = __lib)] pub struct RuntimeProfileWalletAdjustmentProcedureResult { pub ok: bool, - pub record: Option, - pub error_message: Option, + pub record: Option::, + pub error_message: Option::, } + impl __sdk::InModule for RuntimeProfileWalletAdjustmentProcedureResult { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_wallet_ledger_entry_snapshot_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_wallet_ledger_entry_snapshot_type.rs index 8513884e..e4aa66b2 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_wallet_ledger_entry_snapshot_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_wallet_ledger_entry_snapshot_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::runtime_profile_wallet_ledger_source_type_type::RuntimeProfileWalletLedgerSourceType; @@ -17,6 +22,8 @@ pub struct RuntimeProfileWalletLedgerEntrySnapshot { pub created_at_micros: i64, } + impl __sdk::InModule for RuntimeProfileWalletLedgerEntrySnapshot { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_wallet_ledger_list_input_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_wallet_ledger_list_input_type.rs index e8a1c111..10d9d0ff 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_wallet_ledger_list_input_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_wallet_ledger_list_input_type.rs @@ -2,7 +2,13 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; + #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -10,6 +16,8 @@ pub struct RuntimeProfileWalletLedgerListInput { pub user_id: String, } + impl __sdk::InModule for RuntimeProfileWalletLedgerListInput { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_wallet_ledger_procedure_result_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_wallet_ledger_procedure_result_type.rs index 1c0a2b05..809092ae 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_wallet_ledger_procedure_result_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_wallet_ledger_procedure_result_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::runtime_profile_wallet_ledger_entry_snapshot_type::RuntimeProfileWalletLedgerEntrySnapshot; @@ -10,10 +15,12 @@ use super::runtime_profile_wallet_ledger_entry_snapshot_type::RuntimeProfileWall #[sats(crate = __lib)] pub struct RuntimeProfileWalletLedgerProcedureResult { pub ok: bool, - pub entries: Vec, - pub error_message: Option, + pub entries: Vec::, + pub error_message: Option::, } + impl __sdk::InModule for RuntimeProfileWalletLedgerProcedureResult { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_wallet_ledger_source_type_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_wallet_ledger_source_type_type.rs index 21635ff9..8e091481 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_wallet_ledger_source_type_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_wallet_ledger_source_type_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -21,8 +26,12 @@ pub enum RuntimeProfileWalletLedgerSourceType { AssetOperationRefund, RedeemCodeReward, + } + + impl __sdk::InModule for RuntimeProfileWalletLedgerSourceType { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/runtime_referral_invite_center_get_input_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/runtime_referral_invite_center_get_input_type.rs index df7f21d9..386a9cf9 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/runtime_referral_invite_center_get_input_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/runtime_referral_invite_center_get_input_type.rs @@ -2,7 +2,13 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; + #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -10,6 +16,8 @@ pub struct RuntimeReferralInviteCenterGetInput { pub user_id: String, } + impl __sdk::InModule for RuntimeReferralInviteCenterGetInput { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/runtime_referral_invite_center_procedure_result_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/runtime_referral_invite_center_procedure_result_type.rs index bee7cfc2..397a3ebe 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/runtime_referral_invite_center_procedure_result_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/runtime_referral_invite_center_procedure_result_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::runtime_referral_invite_center_snapshot_type::RuntimeReferralInviteCenterSnapshot; @@ -10,10 +15,12 @@ use super::runtime_referral_invite_center_snapshot_type::RuntimeReferralInviteCe #[sats(crate = __lib)] pub struct RuntimeReferralInviteCenterProcedureResult { pub ok: bool, - pub record: Option, - pub error_message: Option, + pub record: Option::, + pub error_message: Option::, } + impl __sdk::InModule for RuntimeReferralInviteCenterProcedureResult { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/runtime_referral_invite_center_snapshot_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/runtime_referral_invite_center_snapshot_type.rs index ca50d69f..498f3881 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/runtime_referral_invite_center_snapshot_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/runtime_referral_invite_center_snapshot_type.rs @@ -2,7 +2,13 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; + #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -16,11 +22,13 @@ pub struct RuntimeReferralInviteCenterSnapshot { pub today_inviter_reward_remaining: u32, pub reward_points: u64, pub has_redeemed_code: bool, - pub bound_inviter_user_id: Option, - pub bound_at_micros: Option, + pub bound_inviter_user_id: Option::, + pub bound_at_micros: Option::, pub updated_at_micros: i64, } + impl __sdk::InModule for RuntimeReferralInviteCenterSnapshot { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/runtime_referral_redeem_input_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/runtime_referral_redeem_input_type.rs index 0a667782..12bc8e73 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/runtime_referral_redeem_input_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/runtime_referral_redeem_input_type.rs @@ -2,7 +2,13 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; + #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -12,6 +18,8 @@ pub struct RuntimeReferralRedeemInput { pub updated_at_micros: i64, } + impl __sdk::InModule for RuntimeReferralRedeemInput { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/runtime_referral_redeem_procedure_result_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/runtime_referral_redeem_procedure_result_type.rs index c642c4b2..aaf63756 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/runtime_referral_redeem_procedure_result_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/runtime_referral_redeem_procedure_result_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::runtime_referral_redeem_snapshot_type::RuntimeReferralRedeemSnapshot; @@ -10,10 +15,12 @@ use super::runtime_referral_redeem_snapshot_type::RuntimeReferralRedeemSnapshot; #[sats(crate = __lib)] pub struct RuntimeReferralRedeemProcedureResult { pub ok: bool, - pub record: Option, - pub error_message: Option, + pub record: Option::, + pub error_message: Option::, } + impl __sdk::InModule for RuntimeReferralRedeemProcedureResult { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/runtime_referral_redeem_snapshot_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/runtime_referral_redeem_snapshot_type.rs index 5bbf2da3..04c68e48 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/runtime_referral_redeem_snapshot_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/runtime_referral_redeem_snapshot_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::runtime_referral_invite_center_snapshot_type::RuntimeReferralInviteCenterSnapshot; @@ -16,6 +21,8 @@ pub struct RuntimeReferralRedeemSnapshot { pub inviter_balance_after: u64, } + impl __sdk::InModule for RuntimeReferralRedeemSnapshot { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/runtime_setting_get_input_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/runtime_setting_get_input_type.rs index 1b83318a..555c7863 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/runtime_setting_get_input_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/runtime_setting_get_input_type.rs @@ -2,7 +2,13 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; + #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -10,6 +16,8 @@ pub struct RuntimeSettingGetInput { pub user_id: String, } + impl __sdk::InModule for RuntimeSettingGetInput { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/runtime_setting_procedure_result_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/runtime_setting_procedure_result_type.rs index 792e33d4..e823af97 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/runtime_setting_procedure_result_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/runtime_setting_procedure_result_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::runtime_setting_snapshot_type::RuntimeSettingSnapshot; @@ -10,10 +15,12 @@ use super::runtime_setting_snapshot_type::RuntimeSettingSnapshot; #[sats(crate = __lib)] pub struct RuntimeSettingProcedureResult { pub ok: bool, - pub record: Option, - pub error_message: Option, + pub record: Option::, + pub error_message: Option::, } + impl __sdk::InModule for RuntimeSettingProcedureResult { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/runtime_setting_snapshot_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/runtime_setting_snapshot_type.rs index 5d92990f..6d6ebae1 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/runtime_setting_snapshot_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/runtime_setting_snapshot_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::runtime_platform_theme_type::RuntimePlatformTheme; @@ -16,6 +21,8 @@ pub struct RuntimeSettingSnapshot { pub updated_at_micros: i64, } + impl __sdk::InModule for RuntimeSettingSnapshot { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/runtime_setting_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/runtime_setting_type.rs index 5a7b61b0..f3c72f73 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/runtime_setting_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/runtime_setting_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::runtime_platform_theme_type::RuntimePlatformTheme; @@ -16,10 +21,12 @@ pub struct RuntimeSetting { pub updated_at: __sdk::Timestamp, } + impl __sdk::InModule for RuntimeSetting { type Module = super::RemoteModule; } + /// Column accessor struct for the table `RuntimeSetting`. /// /// Provides typed access to columns for query building. @@ -40,6 +47,7 @@ impl __sdk::__query_builder::HasCols for RuntimeSetting { platform_theme: __sdk::__query_builder::Col::new(table_name, "platform_theme"), created_at: __sdk::__query_builder::Col::new(table_name, "created_at"), updated_at: __sdk::__query_builder::Col::new(table_name, "updated_at"), + } } } @@ -56,8 +64,10 @@ impl __sdk::__query_builder::HasIxCols for RuntimeSetting { fn ix_cols(table_name: &'static str) -> Self::IxCols { RuntimeSettingIxCols { user_id: __sdk::__query_builder::IxCol::new(table_name, "user_id"), + } } } impl __sdk::__query_builder::CanBeLookupTable for RuntimeSetting {} + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/runtime_setting_upsert_input_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/runtime_setting_upsert_input_type.rs index 7091bc8d..347e1675 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/runtime_setting_upsert_input_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/runtime_setting_upsert_input_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::runtime_platform_theme_type::RuntimePlatformTheme; @@ -15,6 +20,8 @@ pub struct RuntimeSettingUpsertInput { pub updated_at_micros: i64, } + impl __sdk::InModule for RuntimeSettingUpsertInput { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/runtime_snapshot_delete_input_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/runtime_snapshot_delete_input_type.rs index 53bb194c..251417e4 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/runtime_snapshot_delete_input_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/runtime_snapshot_delete_input_type.rs @@ -2,7 +2,13 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; + #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -10,6 +16,8 @@ pub struct RuntimeSnapshotDeleteInput { pub user_id: String, } + impl __sdk::InModule for RuntimeSnapshotDeleteInput { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/runtime_snapshot_get_input_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/runtime_snapshot_get_input_type.rs index c19034c6..7340dbf6 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/runtime_snapshot_get_input_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/runtime_snapshot_get_input_type.rs @@ -2,7 +2,13 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; + #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -10,6 +16,8 @@ pub struct RuntimeSnapshotGetInput { pub user_id: String, } + impl __sdk::InModule for RuntimeSnapshotGetInput { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/runtime_snapshot_procedure_result_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/runtime_snapshot_procedure_result_type.rs index 4066d915..466a3e41 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/runtime_snapshot_procedure_result_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/runtime_snapshot_procedure_result_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::runtime_snapshot_type::RuntimeSnapshot; @@ -10,10 +15,12 @@ use super::runtime_snapshot_type::RuntimeSnapshot; #[sats(crate = __lib)] pub struct RuntimeSnapshotProcedureResult { pub ok: bool, - pub record: Option, - pub error_message: Option, + pub record: Option::, + pub error_message: Option::, } + impl __sdk::InModule for RuntimeSnapshotProcedureResult { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/runtime_snapshot_row_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/runtime_snapshot_row_type.rs index da66941e..8eec233a 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/runtime_snapshot_row_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/runtime_snapshot_row_type.rs @@ -2,7 +2,13 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; + #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -12,15 +18,17 @@ pub struct RuntimeSnapshotRow { pub saved_at: __sdk::Timestamp, pub bottom_tab: String, pub game_state_json: String, - pub current_story_json: Option, + pub current_story_json: Option::, pub created_at: __sdk::Timestamp, pub updated_at: __sdk::Timestamp, } + impl __sdk::InModule for RuntimeSnapshotRow { type Module = super::RemoteModule; } + /// Column accessor struct for the table `RuntimeSnapshotRow`. /// /// Provides typed access to columns for query building. @@ -30,7 +38,7 @@ pub struct RuntimeSnapshotRowCols { pub saved_at: __sdk::__query_builder::Col, pub bottom_tab: __sdk::__query_builder::Col, pub game_state_json: __sdk::__query_builder::Col, - pub current_story_json: __sdk::__query_builder::Col>, + pub current_story_json: __sdk::__query_builder::Col>, pub created_at: __sdk::__query_builder::Col, pub updated_at: __sdk::__query_builder::Col, } @@ -47,6 +55,7 @@ impl __sdk::__query_builder::HasCols for RuntimeSnapshotRow { current_story_json: __sdk::__query_builder::Col::new(table_name, "current_story_json"), created_at: __sdk::__query_builder::Col::new(table_name, "created_at"), updated_at: __sdk::__query_builder::Col::new(table_name, "updated_at"), + } } } @@ -63,8 +72,10 @@ impl __sdk::__query_builder::HasIxCols for RuntimeSnapshotRow { fn ix_cols(table_name: &'static str) -> Self::IxCols { RuntimeSnapshotRowIxCols { user_id: __sdk::__query_builder::IxCol::new(table_name, "user_id"), + } } } impl __sdk::__query_builder::CanBeLookupTable for RuntimeSnapshotRow {} + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/runtime_snapshot_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/runtime_snapshot_type.rs index 1e9e14ad..e8ede33b 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/runtime_snapshot_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/runtime_snapshot_type.rs @@ -2,7 +2,13 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; + #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -12,11 +18,13 @@ pub struct RuntimeSnapshot { pub saved_at_micros: i64, pub bottom_tab: String, pub game_state_json: String, - pub current_story_json: Option, + pub current_story_json: Option::, pub created_at_micros: i64, pub updated_at_micros: i64, } + impl __sdk::InModule for RuntimeSnapshot { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/runtime_snapshot_upsert_input_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/runtime_snapshot_upsert_input_type.rs index 9b3c75df..2379473f 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/runtime_snapshot_upsert_input_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/runtime_snapshot_upsert_input_type.rs @@ -2,7 +2,13 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; + #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -11,10 +17,12 @@ pub struct RuntimeSnapshotUpsertInput { pub saved_at_micros: i64, pub bottom_tab: String, pub game_state_json: String, - pub current_story_json: Option, + pub current_story_json: Option::, pub updated_at_micros: i64, } + impl __sdk::InModule for RuntimeSnapshotUpsertInput { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/save_puzzle_generated_images_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/save_puzzle_generated_images_procedure.rs index 85d17456..15ccc81f 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/save_puzzle_generated_images_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/save_puzzle_generated_images_procedure.rs @@ -2,17 +2,23 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::puzzle_agent_session_procedure_result_type::PuzzleAgentSessionProcedureResult; use super::puzzle_generated_images_save_input_type::PuzzleGeneratedImagesSaveInput; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] -struct SavePuzzleGeneratedImagesArgs { + struct SavePuzzleGeneratedImagesArgs { pub input: PuzzleGeneratedImagesSaveInput, } + impl __sdk::InModule for SavePuzzleGeneratedImagesArgs { type Module = super::RemoteModule; } @@ -22,19 +28,16 @@ impl __sdk::InModule for SavePuzzleGeneratedImagesArgs { /// /// Implemented for [`super::RemoteProcedures`]. pub trait save_puzzle_generated_images { - fn save_puzzle_generated_images(&self, input: PuzzleGeneratedImagesSaveInput) { - self.save_puzzle_generated_images_then(input, |_, _| {}); + fn save_puzzle_generated_images(&self, input: PuzzleGeneratedImagesSaveInput, +) { + self.save_puzzle_generated_images_then(input, |_, _| {}); } fn save_puzzle_generated_images_then( &self, input: PuzzleGeneratedImagesSaveInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ); } @@ -43,17 +46,13 @@ impl save_puzzle_generated_images for super::RemoteProcedures { &self, input: PuzzleGeneratedImagesSaveInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ) { - self.imp - .invoke_procedure_with_callback::<_, PuzzleAgentSessionProcedureResult>( - "save_puzzle_generated_images", - SavePuzzleGeneratedImagesArgs { input }, - __callback, - ); + self.imp.invoke_procedure_with_callback::<_, PuzzleAgentSessionProcedureResult>( + "save_puzzle_generated_images", + SavePuzzleGeneratedImagesArgs { input, }, + __callback, + ); } } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/select_puzzle_cover_image_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/select_puzzle_cover_image_procedure.rs index 9dde8aaa..b12bb89a 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/select_puzzle_cover_image_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/select_puzzle_cover_image_procedure.rs @@ -2,17 +2,23 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::puzzle_agent_session_procedure_result_type::PuzzleAgentSessionProcedureResult; use super::puzzle_select_cover_image_input_type::PuzzleSelectCoverImageInput; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] -struct SelectPuzzleCoverImageArgs { + struct SelectPuzzleCoverImageArgs { pub input: PuzzleSelectCoverImageInput, } + impl __sdk::InModule for SelectPuzzleCoverImageArgs { type Module = super::RemoteModule; } @@ -22,19 +28,16 @@ impl __sdk::InModule for SelectPuzzleCoverImageArgs { /// /// Implemented for [`super::RemoteProcedures`]. pub trait select_puzzle_cover_image { - fn select_puzzle_cover_image(&self, input: PuzzleSelectCoverImageInput) { - self.select_puzzle_cover_image_then(input, |_, _| {}); + fn select_puzzle_cover_image(&self, input: PuzzleSelectCoverImageInput, +) { + self.select_puzzle_cover_image_then(input, |_, _| {}); } fn select_puzzle_cover_image_then( &self, input: PuzzleSelectCoverImageInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ); } @@ -43,17 +46,13 @@ impl select_puzzle_cover_image for super::RemoteProcedures { &self, input: PuzzleSelectCoverImageInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ) { - self.imp - .invoke_procedure_with_callback::<_, PuzzleAgentSessionProcedureResult>( - "select_puzzle_cover_image", - SelectPuzzleCoverImageArgs { input }, - __callback, - ); + self.imp.invoke_procedure_with_callback::<_, PuzzleAgentSessionProcedureResult>( + "select_puzzle_cover_image", + SelectPuzzleCoverImageArgs { input, }, + __callback, + ); } } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/start_ai_task_reducer.rs b/server-rs/crates/spacetime-client/src/module_bindings/start_ai_task_reducer.rs index 5809736b..44e89238 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/start_ai_task_reducer.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/start_ai_task_reducer.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::ai_task_start_input_type::AiTaskStartInput; @@ -14,8 +19,10 @@ pub(super) struct StartAiTaskArgs { impl From for super::Reducer { fn from(args: StartAiTaskArgs) -> Self { - Self::StartAiTask { input: args.input } - } + Self::StartAiTask { + input: args.input, +} +} } impl __sdk::InModule for StartAiTaskArgs { @@ -33,8 +40,9 @@ pub trait start_ai_task { /// The reducer will run asynchronously in the future, /// and this method provides no way to listen for its completion status. /// /// Use [`start_ai_task:start_ai_task_then`] to run a callback after the reducer completes. - fn start_ai_task(&self, input: AiTaskStartInput) -> __sdk::Result<()> { - self.start_ai_task_then(input, |_, _| {}) + fn start_ai_task(&self, input: AiTaskStartInput, +) -> __sdk::Result<()> { + self.start_ai_task_then(input, |_, _| {}) } /// Request that the remote module invoke the reducer `start_ai_task` to run as soon as possible, @@ -62,7 +70,7 @@ impl start_ai_task for super::RemoteReducers { + Send + 'static, ) -> __sdk::Result<()> { - self.imp - .invoke_reducer_with_callback(StartAiTaskArgs { input }, callback) + self.imp.invoke_reducer_with_callback(StartAiTaskArgs { input, }, callback) } } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/start_ai_task_stage_reducer.rs b/server-rs/crates/spacetime-client/src/module_bindings/start_ai_task_stage_reducer.rs index 1d7b7582..74b85d62 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/start_ai_task_stage_reducer.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/start_ai_task_stage_reducer.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::ai_task_stage_start_input_type::AiTaskStageStartInput; @@ -14,8 +19,10 @@ pub(super) struct StartAiTaskStageArgs { impl From for super::Reducer { fn from(args: StartAiTaskStageArgs) -> Self { - Self::StartAiTaskStage { input: args.input } - } + Self::StartAiTaskStage { + input: args.input, +} +} } impl __sdk::InModule for StartAiTaskStageArgs { @@ -33,8 +40,9 @@ pub trait start_ai_task_stage { /// The reducer will run asynchronously in the future, /// and this method provides no way to listen for its completion status. /// /// Use [`start_ai_task_stage:start_ai_task_stage_then`] to run a callback after the reducer completes. - fn start_ai_task_stage(&self, input: AiTaskStageStartInput) -> __sdk::Result<()> { - self.start_ai_task_stage_then(input, |_, _| {}) + fn start_ai_task_stage(&self, input: AiTaskStageStartInput, +) -> __sdk::Result<()> { + self.start_ai_task_stage_then(input, |_, _| {}) } /// Request that the remote module invoke the reducer `start_ai_task_stage` to run as soon as possible, @@ -62,7 +70,7 @@ impl start_ai_task_stage for super::RemoteReducers { + Send + 'static, ) -> __sdk::Result<()> { - self.imp - .invoke_reducer_with_callback(StartAiTaskStageArgs { input }, callback) + self.imp.invoke_reducer_with_callback(StartAiTaskStageArgs { input, }, callback) } } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/start_big_fish_run_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/start_big_fish_run_procedure.rs new file mode 100644 index 00000000..b9b3b28c --- /dev/null +++ b/server-rs/crates/spacetime-client/src/module_bindings/start_big_fish_run_procedure.rs @@ -0,0 +1,58 @@ +// 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::big_fish_run_procedure_result_type::BigFishRunProcedureResult; +use super::big_fish_run_start_input_type::BigFishRunStartInput; + +#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] +#[sats(crate = __lib)] + struct StartBigFishRunArgs { + pub input: BigFishRunStartInput, +} + + +impl __sdk::InModule for StartBigFishRunArgs { + type Module = super::RemoteModule; +} + +#[allow(non_camel_case_types)] +/// Extension trait for access to the procedure `start_big_fish_run`. +/// +/// Implemented for [`super::RemoteProcedures`]. +pub trait start_big_fish_run { + fn start_big_fish_run(&self, input: BigFishRunStartInput, +) { + self.start_big_fish_run_then(input, |_, _| {}); + } + + fn start_big_fish_run_then( + &self, + input: BigFishRunStartInput, + + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, + ); +} + +impl start_big_fish_run for super::RemoteProcedures { + fn start_big_fish_run_then( + &self, + input: BigFishRunStartInput, + + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, + ) { + self.imp.invoke_procedure_with_callback::<_, BigFishRunProcedureResult>( + "start_big_fish_run", + StartBigFishRunArgs { input, }, + __callback, + ); + } +} + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/start_puzzle_run_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/start_puzzle_run_procedure.rs index b6baeb83..849856c8 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/start_puzzle_run_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/start_puzzle_run_procedure.rs @@ -2,17 +2,23 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::puzzle_run_procedure_result_type::PuzzleRunProcedureResult; use super::puzzle_run_start_input_type::PuzzleRunStartInput; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] -struct StartPuzzleRunArgs { + struct StartPuzzleRunArgs { pub input: PuzzleRunStartInput, } + impl __sdk::InModule for StartPuzzleRunArgs { type Module = super::RemoteModule; } @@ -22,19 +28,16 @@ impl __sdk::InModule for StartPuzzleRunArgs { /// /// Implemented for [`super::RemoteProcedures`]. pub trait start_puzzle_run { - fn start_puzzle_run(&self, input: PuzzleRunStartInput) { - self.start_puzzle_run_then(input, |_, _| {}); + fn start_puzzle_run(&self, input: PuzzleRunStartInput, +) { + self.start_puzzle_run_then(input, |_, _| {}); } fn start_puzzle_run_then( &self, input: PuzzleRunStartInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ); } @@ -43,17 +46,13 @@ impl start_puzzle_run for super::RemoteProcedures { &self, input: PuzzleRunStartInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ) { - self.imp - .invoke_procedure_with_callback::<_, PuzzleRunProcedureResult>( - "start_puzzle_run", - StartPuzzleRunArgs { input }, - __callback, - ); + self.imp.invoke_procedure_with_callback::<_, PuzzleRunProcedureResult>( + "start_puzzle_run", + StartPuzzleRunArgs { input, }, + __callback, + ); } } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/story_continue_input_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/story_continue_input_type.rs index e0b3f730..d84dde42 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/story_continue_input_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/story_continue_input_type.rs @@ -2,7 +2,13 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; + #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -10,10 +16,12 @@ pub struct StoryContinueInput { pub story_session_id: String, pub event_id: String, pub narrative_text: String, - pub choice_function_id: Option, + pub choice_function_id: Option::, pub updated_at_micros: i64, } + impl __sdk::InModule for StoryContinueInput { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/story_event_kind_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/story_event_kind_type.rs index 29836b4a..1072867e 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/story_event_kind_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/story_event_kind_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -11,8 +16,12 @@ pub enum StoryEventKind { SessionStarted, StoryContinued, + } + + impl __sdk::InModule for StoryEventKind { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/story_event_snapshot_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/story_event_snapshot_type.rs index 881799d5..4afacc4a 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/story_event_snapshot_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/story_event_snapshot_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::story_event_kind_type::StoryEventKind; @@ -13,10 +18,12 @@ pub struct StoryEventSnapshot { pub story_session_id: String, pub event_kind: StoryEventKind, pub narrative_text: String, - pub choice_function_id: Option, + pub choice_function_id: Option::, pub created_at_micros: i64, } + impl __sdk::InModule for StoryEventSnapshot { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/story_event_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/story_event_type.rs index 4d3bdd9f..3c9c102d 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/story_event_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/story_event_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::story_event_kind_type::StoryEventKind; @@ -13,14 +18,16 @@ pub struct StoryEvent { pub story_session_id: String, pub event_kind: StoryEventKind, pub narrative_text: String, - pub choice_function_id: Option, + pub choice_function_id: Option::, pub created_at: __sdk::Timestamp, } + impl __sdk::InModule for StoryEvent { type Module = super::RemoteModule; } + /// Column accessor struct for the table `StoryEvent`. /// /// Provides typed access to columns for query building. @@ -29,7 +36,7 @@ pub struct StoryEventCols { pub story_session_id: __sdk::__query_builder::Col, pub event_kind: __sdk::__query_builder::Col, pub narrative_text: __sdk::__query_builder::Col, - pub choice_function_id: __sdk::__query_builder::Col>, + pub choice_function_id: __sdk::__query_builder::Col>, pub created_at: __sdk::__query_builder::Col, } @@ -43,6 +50,7 @@ impl __sdk::__query_builder::HasCols for StoryEvent { narrative_text: __sdk::__query_builder::Col::new(table_name, "narrative_text"), choice_function_id: __sdk::__query_builder::Col::new(table_name, "choice_function_id"), created_at: __sdk::__query_builder::Col::new(table_name, "created_at"), + } } } @@ -61,8 +69,10 @@ impl __sdk::__query_builder::HasIxCols for StoryEvent { StoryEventIxCols { event_id: __sdk::__query_builder::IxCol::new(table_name, "event_id"), story_session_id: __sdk::__query_builder::IxCol::new(table_name, "story_session_id"), + } } } impl __sdk::__query_builder::CanBeLookupTable for StoryEvent {} + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/story_session_input_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/story_session_input_type.rs index bebf3267..260fc1e4 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/story_session_input_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/story_session_input_type.rs @@ -2,7 +2,13 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; + #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -12,10 +18,12 @@ pub struct StorySessionInput { pub actor_user_id: String, pub world_profile_id: String, pub initial_prompt: String, - pub opening_summary: Option, + pub opening_summary: Option::, pub created_at_micros: i64, } + impl __sdk::InModule for StorySessionInput { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/story_session_procedure_result_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/story_session_procedure_result_type.rs index 4548d9cc..3e1cfa4f 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/story_session_procedure_result_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/story_session_procedure_result_type.rs @@ -2,20 +2,27 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; -use super::story_event_snapshot_type::StoryEventSnapshot; use super::story_session_snapshot_type::StorySessionSnapshot; +use super::story_event_snapshot_type::StoryEventSnapshot; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] pub struct StorySessionProcedureResult { pub ok: bool, - pub session: Option, - pub event: Option, - pub error_message: Option, + pub session: Option::, + pub event: Option::, + pub error_message: Option::, } + impl __sdk::InModule for StorySessionProcedureResult { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/story_session_snapshot_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/story_session_snapshot_type.rs index b8d2daf6..46f1072f 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/story_session_snapshot_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/story_session_snapshot_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::story_session_status_type::StorySessionStatus; @@ -14,15 +19,17 @@ pub struct StorySessionSnapshot { pub actor_user_id: String, pub world_profile_id: String, pub initial_prompt: String, - pub opening_summary: Option, + pub opening_summary: Option::, pub latest_narrative_text: String, - pub latest_choice_function_id: Option, + pub latest_choice_function_id: Option::, pub status: StorySessionStatus, pub version: u32, pub created_at_micros: i64, pub updated_at_micros: i64, } + impl __sdk::InModule for StorySessionSnapshot { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/story_session_state_input_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/story_session_state_input_type.rs index d860b7db..5e4e1476 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/story_session_state_input_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/story_session_state_input_type.rs @@ -2,7 +2,13 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; + #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -10,6 +16,8 @@ pub struct StorySessionStateInput { pub story_session_id: String, } + impl __sdk::InModule for StorySessionStateInput { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/story_session_state_procedure_result_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/story_session_state_procedure_result_type.rs index cf2148d6..c33c4c77 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/story_session_state_procedure_result_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/story_session_state_procedure_result_type.rs @@ -2,20 +2,27 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; -use super::story_event_snapshot_type::StoryEventSnapshot; use super::story_session_snapshot_type::StorySessionSnapshot; +use super::story_event_snapshot_type::StoryEventSnapshot; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] pub struct StorySessionStateProcedureResult { pub ok: bool, - pub session: Option, - pub events: Vec, - pub error_message: Option, + pub session: Option::, + pub events: Vec::, + pub error_message: Option::, } + impl __sdk::InModule for StorySessionStateProcedureResult { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/story_session_status_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/story_session_status_type.rs index f04aae19..334433fa 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/story_session_status_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/story_session_status_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -13,8 +18,12 @@ pub enum StorySessionStatus { Completed, Archived, + } + + impl __sdk::InModule for StorySessionStatus { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/story_session_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/story_session_type.rs index f9534179..7fd0c7e9 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/story_session_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/story_session_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::story_session_status_type::StorySessionStatus; @@ -14,19 +19,21 @@ pub struct StorySession { pub actor_user_id: String, pub world_profile_id: String, pub initial_prompt: String, - pub opening_summary: Option, + pub opening_summary: Option::, pub latest_narrative_text: String, - pub latest_choice_function_id: Option, + pub latest_choice_function_id: Option::, pub status: StorySessionStatus, pub version: u32, pub created_at: __sdk::Timestamp, pub updated_at: __sdk::Timestamp, } + impl __sdk::InModule for StorySession { type Module = super::RemoteModule; } + /// Column accessor struct for the table `StorySession`. /// /// Provides typed access to columns for query building. @@ -36,9 +43,9 @@ pub struct StorySessionCols { pub actor_user_id: __sdk::__query_builder::Col, pub world_profile_id: __sdk::__query_builder::Col, pub initial_prompt: __sdk::__query_builder::Col, - pub opening_summary: __sdk::__query_builder::Col>, + pub opening_summary: __sdk::__query_builder::Col>, pub latest_narrative_text: __sdk::__query_builder::Col, - pub latest_choice_function_id: __sdk::__query_builder::Col>, + pub latest_choice_function_id: __sdk::__query_builder::Col>, pub status: __sdk::__query_builder::Col, pub version: __sdk::__query_builder::Col, pub created_at: __sdk::__query_builder::Col, @@ -55,18 +62,13 @@ impl __sdk::__query_builder::HasCols for StorySession { world_profile_id: __sdk::__query_builder::Col::new(table_name, "world_profile_id"), initial_prompt: __sdk::__query_builder::Col::new(table_name, "initial_prompt"), opening_summary: __sdk::__query_builder::Col::new(table_name, "opening_summary"), - latest_narrative_text: __sdk::__query_builder::Col::new( - table_name, - "latest_narrative_text", - ), - latest_choice_function_id: __sdk::__query_builder::Col::new( - table_name, - "latest_choice_function_id", - ), + latest_narrative_text: __sdk::__query_builder::Col::new(table_name, "latest_narrative_text"), + latest_choice_function_id: __sdk::__query_builder::Col::new(table_name, "latest_choice_function_id"), status: __sdk::__query_builder::Col::new(table_name, "status"), version: __sdk::__query_builder::Col::new(table_name, "version"), created_at: __sdk::__query_builder::Col::new(table_name, "created_at"), updated_at: __sdk::__query_builder::Col::new(table_name, "updated_at"), + } } } @@ -85,13 +87,12 @@ impl __sdk::__query_builder::HasIxCols for StorySession { fn ix_cols(table_name: &'static str) -> Self::IxCols { StorySessionIxCols { actor_user_id: __sdk::__query_builder::IxCol::new(table_name, "actor_user_id"), - runtime_session_id: __sdk::__query_builder::IxCol::new( - table_name, - "runtime_session_id", - ), + runtime_session_id: __sdk::__query_builder::IxCol::new(table_name, "runtime_session_id"), story_session_id: __sdk::__query_builder::IxCol::new(table_name, "story_session_id"), + } } } impl __sdk::__query_builder::CanBeLookupTable for StorySession {} + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/submit_big_fish_input_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/submit_big_fish_input_procedure.rs new file mode 100644 index 00000000..c5e01121 --- /dev/null +++ b/server-rs/crates/spacetime-client/src/module_bindings/submit_big_fish_input_procedure.rs @@ -0,0 +1,58 @@ +// 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::big_fish_run_procedure_result_type::BigFishRunProcedureResult; +use super::big_fish_input_submit_input_type::BigFishInputSubmitInput; + +#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] +#[sats(crate = __lib)] + struct SubmitBigFishInputArgs { + pub input: BigFishInputSubmitInput, +} + + +impl __sdk::InModule for SubmitBigFishInputArgs { + type Module = super::RemoteModule; +} + +#[allow(non_camel_case_types)] +/// Extension trait for access to the procedure `submit_big_fish_input`. +/// +/// Implemented for [`super::RemoteProcedures`]. +pub trait submit_big_fish_input { + fn submit_big_fish_input(&self, input: BigFishInputSubmitInput, +) { + self.submit_big_fish_input_then(input, |_, _| {}); + } + + fn submit_big_fish_input_then( + &self, + input: BigFishInputSubmitInput, + + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, + ); +} + +impl submit_big_fish_input for super::RemoteProcedures { + fn submit_big_fish_input_then( + &self, + input: BigFishInputSubmitInput, + + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, + ) { + self.imp.invoke_procedure_with_callback::<_, BigFishRunProcedureResult>( + "submit_big_fish_input", + SubmitBigFishInputArgs { input, }, + __callback, + ); + } +} + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/submit_big_fish_message_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/submit_big_fish_message_procedure.rs index 0dbc0118..0fd8dc0d 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/submit_big_fish_message_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/submit_big_fish_message_procedure.rs @@ -2,17 +2,23 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; -use super::big_fish_message_submit_input_type::BigFishMessageSubmitInput; use super::big_fish_session_procedure_result_type::BigFishSessionProcedureResult; +use super::big_fish_message_submit_input_type::BigFishMessageSubmitInput; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] -struct SubmitBigFishMessageArgs { + struct SubmitBigFishMessageArgs { pub input: BigFishMessageSubmitInput, } + impl __sdk::InModule for SubmitBigFishMessageArgs { type Module = super::RemoteModule; } @@ -22,19 +28,16 @@ impl __sdk::InModule for SubmitBigFishMessageArgs { /// /// Implemented for [`super::RemoteProcedures`]. pub trait submit_big_fish_message { - fn submit_big_fish_message(&self, input: BigFishMessageSubmitInput) { - self.submit_big_fish_message_then(input, |_, _| {}); + fn submit_big_fish_message(&self, input: BigFishMessageSubmitInput, +) { + self.submit_big_fish_message_then(input, |_, _| {}); } fn submit_big_fish_message_then( &self, input: BigFishMessageSubmitInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ); } @@ -43,17 +46,13 @@ impl submit_big_fish_message for super::RemoteProcedures { &self, input: BigFishMessageSubmitInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ) { - self.imp - .invoke_procedure_with_callback::<_, BigFishSessionProcedureResult>( - "submit_big_fish_message", - SubmitBigFishMessageArgs { input }, - __callback, - ); + self.imp.invoke_procedure_with_callback::<_, BigFishSessionProcedureResult>( + "submit_big_fish_message", + SubmitBigFishMessageArgs { input, }, + __callback, + ); } } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/submit_custom_world_agent_message_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/submit_custom_world_agent_message_procedure.rs index c5e8def1..efb1c0b3 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/submit_custom_world_agent_message_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/submit_custom_world_agent_message_procedure.rs @@ -2,17 +2,23 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; -use super::custom_world_agent_message_submit_input_type::CustomWorldAgentMessageSubmitInput; use super::custom_world_agent_operation_procedure_result_type::CustomWorldAgentOperationProcedureResult; +use super::custom_world_agent_message_submit_input_type::CustomWorldAgentMessageSubmitInput; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] -struct SubmitCustomWorldAgentMessageArgs { + struct SubmitCustomWorldAgentMessageArgs { pub input: CustomWorldAgentMessageSubmitInput, } + impl __sdk::InModule for SubmitCustomWorldAgentMessageArgs { type Module = super::RemoteModule; } @@ -22,19 +28,16 @@ impl __sdk::InModule for SubmitCustomWorldAgentMessageArgs { /// /// Implemented for [`super::RemoteProcedures`]. pub trait submit_custom_world_agent_message { - fn submit_custom_world_agent_message(&self, input: CustomWorldAgentMessageSubmitInput) { - self.submit_custom_world_agent_message_then(input, |_, _| {}); + fn submit_custom_world_agent_message(&self, input: CustomWorldAgentMessageSubmitInput, +) { + self.submit_custom_world_agent_message_then(input, |_, _| {}); } fn submit_custom_world_agent_message_then( &self, input: CustomWorldAgentMessageSubmitInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ); } @@ -43,17 +46,13 @@ impl submit_custom_world_agent_message for super::RemoteProcedures { &self, input: CustomWorldAgentMessageSubmitInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ) { - self.imp - .invoke_procedure_with_callback::<_, CustomWorldAgentOperationProcedureResult>( - "submit_custom_world_agent_message", - SubmitCustomWorldAgentMessageArgs { input }, - __callback, - ); + self.imp.invoke_procedure_with_callback::<_, CustomWorldAgentOperationProcedureResult>( + "submit_custom_world_agent_message", + SubmitCustomWorldAgentMessageArgs { input, }, + __callback, + ); } } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/submit_puzzle_agent_message_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/submit_puzzle_agent_message_procedure.rs index b5b2b090..4bca696e 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/submit_puzzle_agent_message_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/submit_puzzle_agent_message_procedure.rs @@ -2,17 +2,23 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; -use super::puzzle_agent_message_submit_input_type::PuzzleAgentMessageSubmitInput; use super::puzzle_agent_session_procedure_result_type::PuzzleAgentSessionProcedureResult; +use super::puzzle_agent_message_submit_input_type::PuzzleAgentMessageSubmitInput; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] -struct SubmitPuzzleAgentMessageArgs { + struct SubmitPuzzleAgentMessageArgs { pub input: PuzzleAgentMessageSubmitInput, } + impl __sdk::InModule for SubmitPuzzleAgentMessageArgs { type Module = super::RemoteModule; } @@ -22,19 +28,16 @@ impl __sdk::InModule for SubmitPuzzleAgentMessageArgs { /// /// Implemented for [`super::RemoteProcedures`]. pub trait submit_puzzle_agent_message { - fn submit_puzzle_agent_message(&self, input: PuzzleAgentMessageSubmitInput) { - self.submit_puzzle_agent_message_then(input, |_, _| {}); + fn submit_puzzle_agent_message(&self, input: PuzzleAgentMessageSubmitInput, +) { + self.submit_puzzle_agent_message_then(input, |_, _| {}); } fn submit_puzzle_agent_message_then( &self, input: PuzzleAgentMessageSubmitInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ); } @@ -43,17 +46,13 @@ impl submit_puzzle_agent_message for super::RemoteProcedures { &self, input: PuzzleAgentMessageSubmitInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ) { - self.imp - .invoke_procedure_with_callback::<_, PuzzleAgentSessionProcedureResult>( - "submit_puzzle_agent_message", - SubmitPuzzleAgentMessageArgs { input }, - __callback, - ); + self.imp.invoke_procedure_with_callback::<_, PuzzleAgentSessionProcedureResult>( + "submit_puzzle_agent_message", + SubmitPuzzleAgentMessageArgs { input, }, + __callback, + ); } } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/submit_puzzle_leaderboard_entry_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/submit_puzzle_leaderboard_entry_procedure.rs index 7df24564..b28cb51d 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/submit_puzzle_leaderboard_entry_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/submit_puzzle_leaderboard_entry_procedure.rs @@ -2,17 +2,23 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; -use super::puzzle_leaderboard_submit_input_type::PuzzleLeaderboardSubmitInput; use super::puzzle_run_procedure_result_type::PuzzleRunProcedureResult; +use super::puzzle_leaderboard_submit_input_type::PuzzleLeaderboardSubmitInput; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] -struct SubmitPuzzleLeaderboardEntryArgs { + struct SubmitPuzzleLeaderboardEntryArgs { pub input: PuzzleLeaderboardSubmitInput, } + impl __sdk::InModule for SubmitPuzzleLeaderboardEntryArgs { type Module = super::RemoteModule; } @@ -22,19 +28,16 @@ impl __sdk::InModule for SubmitPuzzleLeaderboardEntryArgs { /// /// Implemented for [`super::RemoteProcedures`]. pub trait submit_puzzle_leaderboard_entry { - fn submit_puzzle_leaderboard_entry(&self, input: PuzzleLeaderboardSubmitInput) { - self.submit_puzzle_leaderboard_entry_then(input, |_, _| {}); + fn submit_puzzle_leaderboard_entry(&self, input: PuzzleLeaderboardSubmitInput, +) { + self.submit_puzzle_leaderboard_entry_then(input, |_, _| {}); } fn submit_puzzle_leaderboard_entry_then( &self, input: PuzzleLeaderboardSubmitInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ); } @@ -43,17 +46,13 @@ impl submit_puzzle_leaderboard_entry for super::RemoteProcedures { &self, input: PuzzleLeaderboardSubmitInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ) { - self.imp - .invoke_procedure_with_callback::<_, PuzzleRunProcedureResult>( - "submit_puzzle_leaderboard_entry", - SubmitPuzzleLeaderboardEntryArgs { input }, - __callback, - ); + self.imp.invoke_procedure_with_callback::<_, PuzzleRunProcedureResult>( + "submit_puzzle_leaderboard_entry", + SubmitPuzzleLeaderboardEntryArgs { input, }, + __callback, + ); } } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/swap_puzzle_pieces_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/swap_puzzle_pieces_procedure.rs index 9c6a1337..dfcb2750 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/swap_puzzle_pieces_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/swap_puzzle_pieces_procedure.rs @@ -2,17 +2,23 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::puzzle_run_procedure_result_type::PuzzleRunProcedureResult; use super::puzzle_run_swap_input_type::PuzzleRunSwapInput; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] -struct SwapPuzzlePiecesArgs { + struct SwapPuzzlePiecesArgs { pub input: PuzzleRunSwapInput, } + impl __sdk::InModule for SwapPuzzlePiecesArgs { type Module = super::RemoteModule; } @@ -22,19 +28,16 @@ impl __sdk::InModule for SwapPuzzlePiecesArgs { /// /// Implemented for [`super::RemoteProcedures`]. pub trait swap_puzzle_pieces { - fn swap_puzzle_pieces(&self, input: PuzzleRunSwapInput) { - self.swap_puzzle_pieces_then(input, |_, _| {}); + fn swap_puzzle_pieces(&self, input: PuzzleRunSwapInput, +) { + self.swap_puzzle_pieces_then(input, |_, _| {}); } fn swap_puzzle_pieces_then( &self, input: PuzzleRunSwapInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ); } @@ -43,17 +46,13 @@ impl swap_puzzle_pieces for super::RemoteProcedures { &self, input: PuzzleRunSwapInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ) { - self.imp - .invoke_procedure_with_callback::<_, PuzzleRunProcedureResult>( - "swap_puzzle_pieces", - SwapPuzzlePiecesArgs { input }, - __callback, - ); + self.imp.invoke_procedure_with_callback::<_, PuzzleRunProcedureResult>( + "swap_puzzle_pieces", + SwapPuzzlePiecesArgs { input, }, + __callback, + ); } } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/treasure_interaction_action_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/treasure_interaction_action_type.rs index a9b61b0e..77e48673 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/treasure_interaction_action_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/treasure_interaction_action_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -13,8 +18,12 @@ pub enum TreasureInteractionAction { Leave, Secure, + } + + impl __sdk::InModule for TreasureInteractionAction { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/treasure_record_procedure_result_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/treasure_record_procedure_result_type.rs index d0ac0175..212c4042 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/treasure_record_procedure_result_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/treasure_record_procedure_result_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::treasure_record_snapshot_type::TreasureRecordSnapshot; @@ -10,10 +15,12 @@ use super::treasure_record_snapshot_type::TreasureRecordSnapshot; #[sats(crate = __lib)] pub struct TreasureRecordProcedureResult { pub ok: bool, - pub record: Option, - pub error_message: Option, + pub record: Option::, + pub error_message: Option::, } + impl __sdk::InModule for TreasureRecordProcedureResult { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/treasure_record_snapshot_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/treasure_record_snapshot_type.rs index 8d69d10e..29677339 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/treasure_record_snapshot_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/treasure_record_snapshot_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::runtime_item_reward_item_snapshot_type::RuntimeItemRewardItemSnapshot; use super::treasure_interaction_action_type::TreasureInteractionAction; @@ -16,18 +21,20 @@ pub struct TreasureRecordSnapshot { pub actor_user_id: String, pub encounter_id: String, pub encounter_name: String, - pub scene_id: Option, - pub scene_name: Option, + pub scene_id: Option::, + pub scene_name: Option::, pub action: TreasureInteractionAction, - pub reward_items: Vec, + pub reward_items: Vec::, pub reward_hp: u32, pub reward_mana: u32, pub reward_currency: u32, - pub story_hint: Option, + pub story_hint: Option::, pub created_at_micros: i64, pub updated_at_micros: i64, } + impl __sdk::InModule for TreasureRecordSnapshot { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/treasure_record_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/treasure_record_type.rs index 9bcd8f6d..e757734f 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/treasure_record_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/treasure_record_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::runtime_item_reward_item_snapshot_type::RuntimeItemRewardItemSnapshot; use super::treasure_interaction_action_type::TreasureInteractionAction; @@ -16,22 +21,24 @@ pub struct TreasureRecord { pub actor_user_id: String, pub encounter_id: String, pub encounter_name: String, - pub scene_id: Option, - pub scene_name: Option, + pub scene_id: Option::, + pub scene_name: Option::, pub action: TreasureInteractionAction, - pub reward_items: Vec, + pub reward_items: Vec::, pub reward_hp: u32, pub reward_mana: u32, pub reward_currency: u32, - pub story_hint: Option, + pub story_hint: Option::, pub created_at: __sdk::Timestamp, pub updated_at: __sdk::Timestamp, } + impl __sdk::InModule for TreasureRecord { type Module = super::RemoteModule; } + /// Column accessor struct for the table `TreasureRecord`. /// /// Provides typed access to columns for query building. @@ -42,15 +49,14 @@ pub struct TreasureRecordCols { pub actor_user_id: __sdk::__query_builder::Col, pub encounter_id: __sdk::__query_builder::Col, pub encounter_name: __sdk::__query_builder::Col, - pub scene_id: __sdk::__query_builder::Col>, - pub scene_name: __sdk::__query_builder::Col>, + pub scene_id: __sdk::__query_builder::Col>, + pub scene_name: __sdk::__query_builder::Col>, pub action: __sdk::__query_builder::Col, - pub reward_items: - __sdk::__query_builder::Col>, + pub reward_items: __sdk::__query_builder::Col>, pub reward_hp: __sdk::__query_builder::Col, pub reward_mana: __sdk::__query_builder::Col, pub reward_currency: __sdk::__query_builder::Col, - pub story_hint: __sdk::__query_builder::Col>, + pub story_hint: __sdk::__query_builder::Col>, pub created_at: __sdk::__query_builder::Col, pub updated_at: __sdk::__query_builder::Col, } @@ -75,6 +81,7 @@ impl __sdk::__query_builder::HasCols for TreasureRecord { story_hint: __sdk::__query_builder::Col::new(table_name, "story_hint"), created_at: __sdk::__query_builder::Col::new(table_name, "created_at"), updated_at: __sdk::__query_builder::Col::new(table_name, "updated_at"), + } } } @@ -96,17 +103,13 @@ impl __sdk::__query_builder::HasIxCols for TreasureRecord { TreasureRecordIxCols { actor_user_id: __sdk::__query_builder::IxCol::new(table_name, "actor_user_id"), encounter_id: __sdk::__query_builder::IxCol::new(table_name, "encounter_id"), - runtime_session_id: __sdk::__query_builder::IxCol::new( - table_name, - "runtime_session_id", - ), + runtime_session_id: __sdk::__query_builder::IxCol::new(table_name, "runtime_session_id"), story_session_id: __sdk::__query_builder::IxCol::new(table_name, "story_session_id"), - treasure_record_id: __sdk::__query_builder::IxCol::new( - table_name, - "treasure_record_id", - ), + treasure_record_id: __sdk::__query_builder::IxCol::new(table_name, "treasure_record_id"), + } } } impl __sdk::__query_builder::CanBeLookupTable for TreasureRecord {} + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/treasure_resolve_input_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/treasure_resolve_input_type.rs index ef1083df..9893c678 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/treasure_resolve_input_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/treasure_resolve_input_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::runtime_item_reward_item_snapshot_type::RuntimeItemRewardItemSnapshot; use super::treasure_interaction_action_type::TreasureInteractionAction; @@ -16,18 +21,20 @@ pub struct TreasureResolveInput { pub actor_user_id: String, pub encounter_id: String, pub encounter_name: String, - pub scene_id: Option, - pub scene_name: Option, + pub scene_id: Option::, + pub scene_name: Option::, pub action: TreasureInteractionAction, - pub reward_items: Vec, + pub reward_items: Vec::, pub reward_hp: u32, pub reward_mana: u32, pub reward_currency: u32, - pub story_hint: Option, + pub story_hint: Option::, pub created_at_micros: i64, pub updated_at_micros: i64, } + impl __sdk::InModule for TreasureResolveInput { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/turn_in_quest_reducer.rs b/server-rs/crates/spacetime-client/src/module_bindings/turn_in_quest_reducer.rs index 4306a47f..2e9e88fd 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/turn_in_quest_reducer.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/turn_in_quest_reducer.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::quest_turn_in_input_type::QuestTurnInInput; @@ -14,8 +19,10 @@ pub(super) struct TurnInQuestArgs { impl From for super::Reducer { fn from(args: TurnInQuestArgs) -> Self { - Self::TurnInQuest { input: args.input } - } + Self::TurnInQuest { + input: args.input, +} +} } impl __sdk::InModule for TurnInQuestArgs { @@ -33,8 +40,9 @@ pub trait turn_in_quest { /// The reducer will run asynchronously in the future, /// and this method provides no way to listen for its completion status. /// /// Use [`turn_in_quest:turn_in_quest_then`] to run a callback after the reducer completes. - fn turn_in_quest(&self, input: QuestTurnInInput) -> __sdk::Result<()> { - self.turn_in_quest_then(input, |_, _| {}) + fn turn_in_quest(&self, input: QuestTurnInInput, +) -> __sdk::Result<()> { + self.turn_in_quest_then(input, |_, _| {}) } /// Request that the remote module invoke the reducer `turn_in_quest` to run as soon as possible, @@ -62,7 +70,7 @@ impl turn_in_quest for super::RemoteReducers { + Send + 'static, ) -> __sdk::Result<()> { - self.imp - .invoke_reducer_with_callback(TurnInQuestArgs { input }, callback) + self.imp.invoke_reducer_with_callback(TurnInQuestArgs { input, }, callback) } } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/unequip_inventory_item_input_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/unequip_inventory_item_input_type.rs index 227f40ec..20ca72d7 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/unequip_inventory_item_input_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/unequip_inventory_item_input_type.rs @@ -2,7 +2,13 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; + #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -10,6 +16,8 @@ pub struct UnequipInventoryItemInput { pub slot_id: String, } + impl __sdk::InModule for UnequipInventoryItemInput { type Module = super::RemoteModule; } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/unpublish_custom_world_profile_and_return_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/unpublish_custom_world_profile_and_return_procedure.rs index b87880a7..45e7a4a1 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/unpublish_custom_world_profile_and_return_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/unpublish_custom_world_profile_and_return_procedure.rs @@ -2,17 +2,23 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::custom_world_library_mutation_result_type::CustomWorldLibraryMutationResult; use super::custom_world_profile_unpublish_input_type::CustomWorldProfileUnpublishInput; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] -struct UnpublishCustomWorldProfileAndReturnArgs { + struct UnpublishCustomWorldProfileAndReturnArgs { pub input: CustomWorldProfileUnpublishInput, } + impl __sdk::InModule for UnpublishCustomWorldProfileAndReturnArgs { type Module = super::RemoteModule; } @@ -22,19 +28,16 @@ impl __sdk::InModule for UnpublishCustomWorldProfileAndReturnArgs { /// /// Implemented for [`super::RemoteProcedures`]. pub trait unpublish_custom_world_profile_and_return { - fn unpublish_custom_world_profile_and_return(&self, input: CustomWorldProfileUnpublishInput) { - self.unpublish_custom_world_profile_and_return_then(input, |_, _| {}); + fn unpublish_custom_world_profile_and_return(&self, input: CustomWorldProfileUnpublishInput, +) { + self.unpublish_custom_world_profile_and_return_then(input, |_, _| {}); } fn unpublish_custom_world_profile_and_return_then( &self, input: CustomWorldProfileUnpublishInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ); } @@ -43,17 +46,13 @@ impl unpublish_custom_world_profile_and_return for super::RemoteProcedures { &self, input: CustomWorldProfileUnpublishInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ) { - self.imp - .invoke_procedure_with_callback::<_, CustomWorldLibraryMutationResult>( - "unpublish_custom_world_profile_and_return", - UnpublishCustomWorldProfileAndReturnArgs { input }, - __callback, - ); + self.imp.invoke_procedure_with_callback::<_, CustomWorldLibraryMutationResult>( + "unpublish_custom_world_profile_and_return", + UnpublishCustomWorldProfileAndReturnArgs { input, }, + __callback, + ); } } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/unpublish_custom_world_profile_reducer.rs b/server-rs/crates/spacetime-client/src/module_bindings/unpublish_custom_world_profile_reducer.rs index 05274f62..adedc423 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/unpublish_custom_world_profile_reducer.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/unpublish_custom_world_profile_reducer.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::custom_world_profile_unpublish_input_type::CustomWorldProfileUnpublishInput; @@ -14,8 +19,10 @@ pub(super) struct UnpublishCustomWorldProfileArgs { impl From for super::Reducer { fn from(args: UnpublishCustomWorldProfileArgs) -> Self { - Self::UnpublishCustomWorldProfile { input: args.input } - } + Self::UnpublishCustomWorldProfile { + input: args.input, +} +} } impl __sdk::InModule for UnpublishCustomWorldProfileArgs { @@ -33,11 +40,9 @@ pub trait unpublish_custom_world_profile { /// The reducer will run asynchronously in the future, /// and this method provides no way to listen for its completion status. /// /// Use [`unpublish_custom_world_profile:unpublish_custom_world_profile_then`] to run a callback after the reducer completes. - fn unpublish_custom_world_profile( - &self, - input: CustomWorldProfileUnpublishInput, - ) -> __sdk::Result<()> { - self.unpublish_custom_world_profile_then(input, |_, _| {}) + fn unpublish_custom_world_profile(&self, input: CustomWorldProfileUnpublishInput, +) -> __sdk::Result<()> { + self.unpublish_custom_world_profile_then(input, |_, _| {}) } /// Request that the remote module invoke the reducer `unpublish_custom_world_profile` to run as soon as possible, @@ -65,7 +70,7 @@ impl unpublish_custom_world_profile for super::RemoteReducers { + Send + 'static, ) -> __sdk::Result<()> { - self.imp - .invoke_reducer_with_callback(UnpublishCustomWorldProfileArgs { input }, callback) + self.imp.invoke_reducer_with_callback(UnpublishCustomWorldProfileArgs { input, }, callback) } } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/update_puzzle_work_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/update_puzzle_work_procedure.rs index 6710b4da..14a134df 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/update_puzzle_work_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/update_puzzle_work_procedure.rs @@ -2,17 +2,23 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::puzzle_work_procedure_result_type::PuzzleWorkProcedureResult; use super::puzzle_work_upsert_input_type::PuzzleWorkUpsertInput; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] -struct UpdatePuzzleWorkArgs { + struct UpdatePuzzleWorkArgs { pub input: PuzzleWorkUpsertInput, } + impl __sdk::InModule for UpdatePuzzleWorkArgs { type Module = super::RemoteModule; } @@ -22,19 +28,16 @@ impl __sdk::InModule for UpdatePuzzleWorkArgs { /// /// Implemented for [`super::RemoteProcedures`]. pub trait update_puzzle_work { - fn update_puzzle_work(&self, input: PuzzleWorkUpsertInput) { - self.update_puzzle_work_then(input, |_, _| {}); + fn update_puzzle_work(&self, input: PuzzleWorkUpsertInput, +) { + self.update_puzzle_work_then(input, |_, _| {}); } fn update_puzzle_work_then( &self, input: PuzzleWorkUpsertInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ); } @@ -43,17 +46,13 @@ impl update_puzzle_work for super::RemoteProcedures { &self, input: PuzzleWorkUpsertInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ) { - self.imp - .invoke_procedure_with_callback::<_, PuzzleWorkProcedureResult>( - "update_puzzle_work", - UpdatePuzzleWorkArgs { input }, - __callback, - ); + self.imp.invoke_procedure_with_callback::<_, PuzzleWorkProcedureResult>( + "update_puzzle_work", + UpdatePuzzleWorkArgs { input, }, + __callback, + ); } } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/upsert_auth_store_snapshot_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/upsert_auth_store_snapshot_procedure.rs index 31460874..cc7f5700 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/upsert_auth_store_snapshot_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/upsert_auth_store_snapshot_procedure.rs @@ -2,17 +2,23 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::auth_store_snapshot_procedure_result_type::AuthStoreSnapshotProcedureResult; use super::auth_store_snapshot_upsert_input_type::AuthStoreSnapshotUpsertInput; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] -struct UpsertAuthStoreSnapshotArgs { + struct UpsertAuthStoreSnapshotArgs { pub input: AuthStoreSnapshotUpsertInput, } + impl __sdk::InModule for UpsertAuthStoreSnapshotArgs { type Module = super::RemoteModule; } @@ -22,19 +28,16 @@ impl __sdk::InModule for UpsertAuthStoreSnapshotArgs { /// /// Implemented for [`super::RemoteProcedures`]. pub trait upsert_auth_store_snapshot { - fn upsert_auth_store_snapshot(&self, input: AuthStoreSnapshotUpsertInput) { - self.upsert_auth_store_snapshot_then(input, |_, _| {}); + fn upsert_auth_store_snapshot(&self, input: AuthStoreSnapshotUpsertInput, +) { + self.upsert_auth_store_snapshot_then(input, |_, _| {}); } fn upsert_auth_store_snapshot_then( &self, input: AuthStoreSnapshotUpsertInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ); } @@ -43,17 +46,13 @@ impl upsert_auth_store_snapshot for super::RemoteProcedures { &self, input: AuthStoreSnapshotUpsertInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ) { - self.imp - .invoke_procedure_with_callback::<_, AuthStoreSnapshotProcedureResult>( - "upsert_auth_store_snapshot", - UpsertAuthStoreSnapshotArgs { input }, - __callback, - ); + self.imp.invoke_procedure_with_callback::<_, AuthStoreSnapshotProcedureResult>( + "upsert_auth_store_snapshot", + UpsertAuthStoreSnapshotArgs { input, }, + __callback, + ); } } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/upsert_chapter_progression_and_return_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/upsert_chapter_progression_and_return_procedure.rs index abc39bfe..2182ec5a 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/upsert_chapter_progression_and_return_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/upsert_chapter_progression_and_return_procedure.rs @@ -2,17 +2,23 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; -use super::chapter_progression_input_type::ChapterProgressionInput; use super::chapter_progression_procedure_result_type::ChapterProgressionProcedureResult; +use super::chapter_progression_input_type::ChapterProgressionInput; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] -struct UpsertChapterProgressionAndReturnArgs { + struct UpsertChapterProgressionAndReturnArgs { pub input: ChapterProgressionInput, } + impl __sdk::InModule for UpsertChapterProgressionAndReturnArgs { type Module = super::RemoteModule; } @@ -22,19 +28,16 @@ impl __sdk::InModule for UpsertChapterProgressionAndReturnArgs { /// /// Implemented for [`super::RemoteProcedures`]. pub trait upsert_chapter_progression_and_return { - fn upsert_chapter_progression_and_return(&self, input: ChapterProgressionInput) { - self.upsert_chapter_progression_and_return_then(input, |_, _| {}); + fn upsert_chapter_progression_and_return(&self, input: ChapterProgressionInput, +) { + self.upsert_chapter_progression_and_return_then(input, |_, _| {}); } fn upsert_chapter_progression_and_return_then( &self, input: ChapterProgressionInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ); } @@ -43,17 +46,13 @@ impl upsert_chapter_progression_and_return for super::RemoteProcedures { &self, input: ChapterProgressionInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ) { - self.imp - .invoke_procedure_with_callback::<_, ChapterProgressionProcedureResult>( - "upsert_chapter_progression_and_return", - UpsertChapterProgressionAndReturnArgs { input }, - __callback, - ); + self.imp.invoke_procedure_with_callback::<_, ChapterProgressionProcedureResult>( + "upsert_chapter_progression_and_return", + UpsertChapterProgressionAndReturnArgs { input, }, + __callback, + ); } } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/upsert_chapter_progression_reducer.rs b/server-rs/crates/spacetime-client/src/module_bindings/upsert_chapter_progression_reducer.rs index 0cb4bb7a..a01fe892 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/upsert_chapter_progression_reducer.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/upsert_chapter_progression_reducer.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::chapter_progression_input_type::ChapterProgressionInput; @@ -14,8 +19,10 @@ pub(super) struct UpsertChapterProgressionArgs { impl From for super::Reducer { fn from(args: UpsertChapterProgressionArgs) -> Self { - Self::UpsertChapterProgression { input: args.input } - } + Self::UpsertChapterProgression { + input: args.input, +} +} } impl __sdk::InModule for UpsertChapterProgressionArgs { @@ -33,8 +40,9 @@ pub trait upsert_chapter_progression { /// The reducer will run asynchronously in the future, /// and this method provides no way to listen for its completion status. /// /// Use [`upsert_chapter_progression:upsert_chapter_progression_then`] to run a callback after the reducer completes. - fn upsert_chapter_progression(&self, input: ChapterProgressionInput) -> __sdk::Result<()> { - self.upsert_chapter_progression_then(input, |_, _| {}) + fn upsert_chapter_progression(&self, input: ChapterProgressionInput, +) -> __sdk::Result<()> { + self.upsert_chapter_progression_then(input, |_, _| {}) } /// Request that the remote module invoke the reducer `upsert_chapter_progression` to run as soon as possible, @@ -62,7 +70,7 @@ impl upsert_chapter_progression for super::RemoteReducers { + Send + 'static, ) -> __sdk::Result<()> { - self.imp - .invoke_reducer_with_callback(UpsertChapterProgressionArgs { input }, callback) + self.imp.invoke_reducer_with_callback(UpsertChapterProgressionArgs { input, }, callback) } } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/upsert_custom_world_agent_operation_progress_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/upsert_custom_world_agent_operation_progress_procedure.rs index 554b95b2..5f0475ad 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/upsert_custom_world_agent_operation_progress_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/upsert_custom_world_agent_operation_progress_procedure.rs @@ -2,17 +2,23 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::custom_world_agent_operation_procedure_result_type::CustomWorldAgentOperationProcedureResult; use super::custom_world_agent_operation_progress_input_type::CustomWorldAgentOperationProgressInput; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] -struct UpsertCustomWorldAgentOperationProgressArgs { + struct UpsertCustomWorldAgentOperationProgressArgs { pub input: CustomWorldAgentOperationProgressInput, } + impl __sdk::InModule for UpsertCustomWorldAgentOperationProgressArgs { type Module = super::RemoteModule; } @@ -22,22 +28,16 @@ impl __sdk::InModule for UpsertCustomWorldAgentOperationProgressArgs { /// /// Implemented for [`super::RemoteProcedures`]. pub trait upsert_custom_world_agent_operation_progress { - fn upsert_custom_world_agent_operation_progress( - &self, - input: CustomWorldAgentOperationProgressInput, - ) { - self.upsert_custom_world_agent_operation_progress_then(input, |_, _| {}); + fn upsert_custom_world_agent_operation_progress(&self, input: CustomWorldAgentOperationProgressInput, +) { + self.upsert_custom_world_agent_operation_progress_then(input, |_, _| {}); } fn upsert_custom_world_agent_operation_progress_then( &self, input: CustomWorldAgentOperationProgressInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ); } @@ -46,17 +46,13 @@ impl upsert_custom_world_agent_operation_progress for super::RemoteProcedures { &self, input: CustomWorldAgentOperationProgressInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ) { - self.imp - .invoke_procedure_with_callback::<_, CustomWorldAgentOperationProcedureResult>( - "upsert_custom_world_agent_operation_progress", - UpsertCustomWorldAgentOperationProgressArgs { input }, - __callback, - ); + self.imp.invoke_procedure_with_callback::<_, CustomWorldAgentOperationProcedureResult>( + "upsert_custom_world_agent_operation_progress", + UpsertCustomWorldAgentOperationProgressArgs { input, }, + __callback, + ); } } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/upsert_custom_world_profile_and_return_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/upsert_custom_world_profile_and_return_procedure.rs index 343e1807..e7720c06 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/upsert_custom_world_profile_and_return_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/upsert_custom_world_profile_and_return_procedure.rs @@ -2,17 +2,23 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::custom_world_library_mutation_result_type::CustomWorldLibraryMutationResult; use super::custom_world_profile_upsert_input_type::CustomWorldProfileUpsertInput; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] -struct UpsertCustomWorldProfileAndReturnArgs { + struct UpsertCustomWorldProfileAndReturnArgs { pub input: CustomWorldProfileUpsertInput, } + impl __sdk::InModule for UpsertCustomWorldProfileAndReturnArgs { type Module = super::RemoteModule; } @@ -22,19 +28,16 @@ impl __sdk::InModule for UpsertCustomWorldProfileAndReturnArgs { /// /// Implemented for [`super::RemoteProcedures`]. pub trait upsert_custom_world_profile_and_return { - fn upsert_custom_world_profile_and_return(&self, input: CustomWorldProfileUpsertInput) { - self.upsert_custom_world_profile_and_return_then(input, |_, _| {}); + fn upsert_custom_world_profile_and_return(&self, input: CustomWorldProfileUpsertInput, +) { + self.upsert_custom_world_profile_and_return_then(input, |_, _| {}); } fn upsert_custom_world_profile_and_return_then( &self, input: CustomWorldProfileUpsertInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ); } @@ -43,17 +46,13 @@ impl upsert_custom_world_profile_and_return for super::RemoteProcedures { &self, input: CustomWorldProfileUpsertInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ) { - self.imp - .invoke_procedure_with_callback::<_, CustomWorldLibraryMutationResult>( - "upsert_custom_world_profile_and_return", - UpsertCustomWorldProfileAndReturnArgs { input }, - __callback, - ); + self.imp.invoke_procedure_with_callback::<_, CustomWorldLibraryMutationResult>( + "upsert_custom_world_profile_and_return", + UpsertCustomWorldProfileAndReturnArgs { input, }, + __callback, + ); } } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/upsert_custom_world_profile_reducer.rs b/server-rs/crates/spacetime-client/src/module_bindings/upsert_custom_world_profile_reducer.rs index e343b491..353bc46f 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/upsert_custom_world_profile_reducer.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/upsert_custom_world_profile_reducer.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::custom_world_profile_upsert_input_type::CustomWorldProfileUpsertInput; @@ -14,8 +19,10 @@ pub(super) struct UpsertCustomWorldProfileArgs { impl From for super::Reducer { fn from(args: UpsertCustomWorldProfileArgs) -> Self { - Self::UpsertCustomWorldProfile { input: args.input } - } + Self::UpsertCustomWorldProfile { + input: args.input, +} +} } impl __sdk::InModule for UpsertCustomWorldProfileArgs { @@ -33,11 +40,9 @@ pub trait upsert_custom_world_profile { /// The reducer will run asynchronously in the future, /// and this method provides no way to listen for its completion status. /// /// Use [`upsert_custom_world_profile:upsert_custom_world_profile_then`] to run a callback after the reducer completes. - fn upsert_custom_world_profile( - &self, - input: CustomWorldProfileUpsertInput, - ) -> __sdk::Result<()> { - self.upsert_custom_world_profile_then(input, |_, _| {}) + fn upsert_custom_world_profile(&self, input: CustomWorldProfileUpsertInput, +) -> __sdk::Result<()> { + self.upsert_custom_world_profile_then(input, |_, _| {}) } /// Request that the remote module invoke the reducer `upsert_custom_world_profile` to run as soon as possible, @@ -65,7 +70,7 @@ impl upsert_custom_world_profile for super::RemoteReducers { + Send + 'static, ) -> __sdk::Result<()> { - self.imp - .invoke_reducer_with_callback(UpsertCustomWorldProfileArgs { input }, callback) + self.imp.invoke_reducer_with_callback(UpsertCustomWorldProfileArgs { input, }, callback) } } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/upsert_npc_state_and_return_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/upsert_npc_state_and_return_procedure.rs index b7f3a28d..46ad5903 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/upsert_npc_state_and_return_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/upsert_npc_state_and_return_procedure.rs @@ -2,17 +2,23 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::npc_state_procedure_result_type::NpcStateProcedureResult; use super::npc_state_upsert_input_type::NpcStateUpsertInput; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] -struct UpsertNpcStateAndReturnArgs { + struct UpsertNpcStateAndReturnArgs { pub input: NpcStateUpsertInput, } + impl __sdk::InModule for UpsertNpcStateAndReturnArgs { type Module = super::RemoteModule; } @@ -22,19 +28,16 @@ impl __sdk::InModule for UpsertNpcStateAndReturnArgs { /// /// Implemented for [`super::RemoteProcedures`]. pub trait upsert_npc_state_and_return { - fn upsert_npc_state_and_return(&self, input: NpcStateUpsertInput) { - self.upsert_npc_state_and_return_then(input, |_, _| {}); + fn upsert_npc_state_and_return(&self, input: NpcStateUpsertInput, +) { + self.upsert_npc_state_and_return_then(input, |_, _| {}); } fn upsert_npc_state_and_return_then( &self, input: NpcStateUpsertInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ); } @@ -43,17 +46,13 @@ impl upsert_npc_state_and_return for super::RemoteProcedures { &self, input: NpcStateUpsertInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ) { - self.imp - .invoke_procedure_with_callback::<_, NpcStateProcedureResult>( - "upsert_npc_state_and_return", - UpsertNpcStateAndReturnArgs { input }, - __callback, - ); + self.imp.invoke_procedure_with_callback::<_, NpcStateProcedureResult>( + "upsert_npc_state_and_return", + UpsertNpcStateAndReturnArgs { input, }, + __callback, + ); } } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/upsert_npc_state_reducer.rs b/server-rs/crates/spacetime-client/src/module_bindings/upsert_npc_state_reducer.rs index f363770d..1a6f10c7 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/upsert_npc_state_reducer.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/upsert_npc_state_reducer.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::npc_state_upsert_input_type::NpcStateUpsertInput; @@ -14,8 +19,10 @@ pub(super) struct UpsertNpcStateArgs { impl From for super::Reducer { fn from(args: UpsertNpcStateArgs) -> Self { - Self::UpsertNpcState { input: args.input } - } + Self::UpsertNpcState { + input: args.input, +} +} } impl __sdk::InModule for UpsertNpcStateArgs { @@ -33,8 +40,9 @@ pub trait upsert_npc_state { /// The reducer will run asynchronously in the future, /// and this method provides no way to listen for its completion status. /// /// Use [`upsert_npc_state:upsert_npc_state_then`] to run a callback after the reducer completes. - fn upsert_npc_state(&self, input: NpcStateUpsertInput) -> __sdk::Result<()> { - self.upsert_npc_state_then(input, |_, _| {}) + fn upsert_npc_state(&self, input: NpcStateUpsertInput, +) -> __sdk::Result<()> { + self.upsert_npc_state_then(input, |_, _| {}) } /// Request that the remote module invoke the reducer `upsert_npc_state` to run as soon as possible, @@ -62,7 +70,7 @@ impl upsert_npc_state for super::RemoteReducers { + Send + 'static, ) -> __sdk::Result<()> { - self.imp - .invoke_reducer_with_callback(UpsertNpcStateArgs { input }, callback) + self.imp.invoke_reducer_with_callback(UpsertNpcStateArgs { input, }, callback) } } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/upsert_platform_browse_history_and_return_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/upsert_platform_browse_history_and_return_procedure.rs index 614a6d05..eb863a2d 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/upsert_platform_browse_history_and_return_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/upsert_platform_browse_history_and_return_procedure.rs @@ -2,17 +2,23 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::runtime_browse_history_procedure_result_type::RuntimeBrowseHistoryProcedureResult; use super::runtime_browse_history_sync_input_type::RuntimeBrowseHistorySyncInput; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] -struct UpsertPlatformBrowseHistoryAndReturnArgs { + struct UpsertPlatformBrowseHistoryAndReturnArgs { pub input: RuntimeBrowseHistorySyncInput, } + impl __sdk::InModule for UpsertPlatformBrowseHistoryAndReturnArgs { type Module = super::RemoteModule; } @@ -22,19 +28,16 @@ impl __sdk::InModule for UpsertPlatformBrowseHistoryAndReturnArgs { /// /// Implemented for [`super::RemoteProcedures`]. pub trait upsert_platform_browse_history_and_return { - fn upsert_platform_browse_history_and_return(&self, input: RuntimeBrowseHistorySyncInput) { - self.upsert_platform_browse_history_and_return_then(input, |_, _| {}); + fn upsert_platform_browse_history_and_return(&self, input: RuntimeBrowseHistorySyncInput, +) { + self.upsert_platform_browse_history_and_return_then(input, |_, _| {}); } fn upsert_platform_browse_history_and_return_then( &self, input: RuntimeBrowseHistorySyncInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ); } @@ -43,17 +46,13 @@ impl upsert_platform_browse_history_and_return for super::RemoteProcedures { &self, input: RuntimeBrowseHistorySyncInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ) { - self.imp - .invoke_procedure_with_callback::<_, RuntimeBrowseHistoryProcedureResult>( - "upsert_platform_browse_history_and_return", - UpsertPlatformBrowseHistoryAndReturnArgs { input }, - __callback, - ); + self.imp.invoke_procedure_with_callback::<_, RuntimeBrowseHistoryProcedureResult>( + "upsert_platform_browse_history_and_return", + UpsertPlatformBrowseHistoryAndReturnArgs { input, }, + __callback, + ); } } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/upsert_runtime_setting_and_return_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/upsert_runtime_setting_and_return_procedure.rs index 119eab70..f820971c 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/upsert_runtime_setting_and_return_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/upsert_runtime_setting_and_return_procedure.rs @@ -2,17 +2,23 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::runtime_setting_procedure_result_type::RuntimeSettingProcedureResult; use super::runtime_setting_upsert_input_type::RuntimeSettingUpsertInput; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] -struct UpsertRuntimeSettingAndReturnArgs { + struct UpsertRuntimeSettingAndReturnArgs { pub input: RuntimeSettingUpsertInput, } + impl __sdk::InModule for UpsertRuntimeSettingAndReturnArgs { type Module = super::RemoteModule; } @@ -22,19 +28,16 @@ impl __sdk::InModule for UpsertRuntimeSettingAndReturnArgs { /// /// Implemented for [`super::RemoteProcedures`]. pub trait upsert_runtime_setting_and_return { - fn upsert_runtime_setting_and_return(&self, input: RuntimeSettingUpsertInput) { - self.upsert_runtime_setting_and_return_then(input, |_, _| {}); + fn upsert_runtime_setting_and_return(&self, input: RuntimeSettingUpsertInput, +) { + self.upsert_runtime_setting_and_return_then(input, |_, _| {}); } fn upsert_runtime_setting_and_return_then( &self, input: RuntimeSettingUpsertInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ); } @@ -43,17 +46,13 @@ impl upsert_runtime_setting_and_return for super::RemoteProcedures { &self, input: RuntimeSettingUpsertInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ) { - self.imp - .invoke_procedure_with_callback::<_, RuntimeSettingProcedureResult>( - "upsert_runtime_setting_and_return", - UpsertRuntimeSettingAndReturnArgs { input }, - __callback, - ); + self.imp.invoke_procedure_with_callback::<_, RuntimeSettingProcedureResult>( + "upsert_runtime_setting_and_return", + UpsertRuntimeSettingAndReturnArgs { input, }, + __callback, + ); } } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/upsert_runtime_snapshot_and_return_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/upsert_runtime_snapshot_and_return_procedure.rs index eceae785..f86a0f7d 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/upsert_runtime_snapshot_and_return_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/upsert_runtime_snapshot_and_return_procedure.rs @@ -2,17 +2,23 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::runtime_snapshot_procedure_result_type::RuntimeSnapshotProcedureResult; use super::runtime_snapshot_upsert_input_type::RuntimeSnapshotUpsertInput; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] -struct UpsertRuntimeSnapshotAndReturnArgs { + struct UpsertRuntimeSnapshotAndReturnArgs { pub input: RuntimeSnapshotUpsertInput, } + impl __sdk::InModule for UpsertRuntimeSnapshotAndReturnArgs { type Module = super::RemoteModule; } @@ -22,19 +28,16 @@ impl __sdk::InModule for UpsertRuntimeSnapshotAndReturnArgs { /// /// Implemented for [`super::RemoteProcedures`]. pub trait upsert_runtime_snapshot_and_return { - fn upsert_runtime_snapshot_and_return(&self, input: RuntimeSnapshotUpsertInput) { - self.upsert_runtime_snapshot_and_return_then(input, |_, _| {}); + fn upsert_runtime_snapshot_and_return(&self, input: RuntimeSnapshotUpsertInput, +) { + self.upsert_runtime_snapshot_and_return_then(input, |_, _| {}); } fn upsert_runtime_snapshot_and_return_then( &self, input: RuntimeSnapshotUpsertInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ); } @@ -43,17 +46,13 @@ impl upsert_runtime_snapshot_and_return for super::RemoteProcedures { &self, input: RuntimeSnapshotUpsertInput, - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + __callback: impl FnOnce(&super::ProcedureEventContext, Result) + Send + 'static, ) { - self.imp - .invoke_procedure_with_callback::<_, RuntimeSnapshotProcedureResult>( - "upsert_runtime_snapshot_and_return", - UpsertRuntimeSnapshotAndReturnArgs { input }, - __callback, - ); + self.imp.invoke_procedure_with_callback::<_, RuntimeSnapshotProcedureResult>( + "upsert_runtime_snapshot_and_return", + UpsertRuntimeSnapshotAndReturnArgs { input, }, + __callback, + ); } } + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/user_account_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/user_account_type.rs index 87b8e5f3..49cf4cd1 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/user_account_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/user_account_type.rs @@ -2,7 +2,13 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; + #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] @@ -11,8 +17,8 @@ pub struct UserAccount { pub public_user_code: String, pub username: String, pub display_name: String, - pub phone_number_masked: Option, - pub phone_number_e_164: Option, + pub phone_number_masked: Option::, + pub phone_number_e_164: Option::, pub login_method: String, pub binding_status: String, pub wechat_bound: bool, @@ -21,10 +27,12 @@ pub struct UserAccount { pub token_version: u64, } + impl __sdk::InModule for UserAccount { type Module = super::RemoteModule; } + /// Column accessor struct for the table `UserAccount`. /// /// Provides typed access to columns for query building. @@ -33,8 +41,8 @@ pub struct UserAccountCols { pub public_user_code: __sdk::__query_builder::Col, pub username: __sdk::__query_builder::Col, pub display_name: __sdk::__query_builder::Col, - pub phone_number_masked: __sdk::__query_builder::Col>, - pub phone_number_e_164: __sdk::__query_builder::Col>, + pub phone_number_masked: __sdk::__query_builder::Col>, + pub phone_number_e_164: __sdk::__query_builder::Col>, pub login_method: __sdk::__query_builder::Col, pub binding_status: __sdk::__query_builder::Col, pub wechat_bound: __sdk::__query_builder::Col, @@ -51,20 +59,15 @@ impl __sdk::__query_builder::HasCols for UserAccount { public_user_code: __sdk::__query_builder::Col::new(table_name, "public_user_code"), username: __sdk::__query_builder::Col::new(table_name, "username"), display_name: __sdk::__query_builder::Col::new(table_name, "display_name"), - phone_number_masked: __sdk::__query_builder::Col::new( - table_name, - "phone_number_masked", - ), + phone_number_masked: __sdk::__query_builder::Col::new(table_name, "phone_number_masked"), phone_number_e_164: __sdk::__query_builder::Col::new(table_name, "phone_number_e_164"), login_method: __sdk::__query_builder::Col::new(table_name, "login_method"), binding_status: __sdk::__query_builder::Col::new(table_name, "binding_status"), wechat_bound: __sdk::__query_builder::Col::new(table_name, "wechat_bound"), password_hash: __sdk::__query_builder::Col::new(table_name, "password_hash"), - password_login_enabled: __sdk::__query_builder::Col::new( - table_name, - "password_login_enabled", - ), + password_login_enabled: __sdk::__query_builder::Col::new(table_name, "password_login_enabled"), token_version: __sdk::__query_builder::Col::new(table_name, "token_version"), + } } } @@ -85,8 +88,10 @@ impl __sdk::__query_builder::HasIxCols for UserAccount { public_user_code: __sdk::__query_builder::IxCol::new(table_name, "public_user_code"), user_id: __sdk::__query_builder::IxCol::new(table_name, "user_id"), username: __sdk::__query_builder::IxCol::new(table_name, "username"), + } } } impl __sdk::__query_builder::CanBeLookupTable for UserAccount {} + diff --git a/server-rs/crates/spacetime-client/src/module_bindings/user_browse_history_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/user_browse_history_type.rs index 3a6cf1ae..85e114be 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/user_browse_history_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/user_browse_history_type.rs @@ -2,7 +2,12 @@ // 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 spacetimedb_sdk::__codegen::{ + self as __sdk, + __lib, + __sats, + __ws, +}; use super::runtime_browse_history_theme_mode_type::RuntimeBrowseHistoryThemeMode; @@ -16,7 +21,7 @@ pub struct UserBrowseHistory { pub world_name: String, pub subtitle: String, pub summary_text: String, - pub cover_image_src: Option, + pub cover_image_src: Option::, pub theme_mode: RuntimeBrowseHistoryThemeMode, pub author_display_name: String, pub visited_at: __sdk::Timestamp, @@ -24,10 +29,12 @@ pub struct UserBrowseHistory { pub updated_at: __sdk::Timestamp, } + impl __sdk::InModule for UserBrowseHistory { type Module = super::RemoteModule; } + /// Column accessor struct for the table `UserBrowseHistory`. /// /// Provides typed access to columns for query building. @@ -39,7 +46,7 @@ pub struct UserBrowseHistoryCols { pub world_name: __sdk::__query_builder::Col, pub subtitle: __sdk::__query_builder::Col, pub summary_text: __sdk::__query_builder::Col, - pub cover_image_src: __sdk::__query_builder::Col>, + pub cover_image_src: __sdk::__query_builder::Col>, pub theme_mode: __sdk::__query_builder::Col, pub author_display_name: __sdk::__query_builder::Col, pub visited_at: __sdk::__query_builder::Col, @@ -60,13 +67,11 @@ impl __sdk::__query_builder::HasCols for UserBrowseHistory { summary_text: __sdk::__query_builder::Col::new(table_name, "summary_text"), cover_image_src: __sdk::__query_builder::Col::new(table_name, "cover_image_src"), theme_mode: __sdk::__query_builder::Col::new(table_name, "theme_mode"), - author_display_name: __sdk::__query_builder::Col::new( - table_name, - "author_display_name", - ), + author_display_name: __sdk::__query_builder::Col::new(table_name, "author_display_name"), visited_at: __sdk::__query_builder::Col::new(table_name, "visited_at"), created_at: __sdk::__query_builder::Col::new(table_name, "created_at"), updated_at: __sdk::__query_builder::Col::new(table_name, "updated_at"), + } } } @@ -85,8 +90,10 @@ impl __sdk::__query_builder::HasIxCols for UserBrowseHistory { UserBrowseHistoryIxCols { browse_history_id: __sdk::__query_builder::IxCol::new(table_name, "browse_history_id"), user_id: __sdk::__query_builder::IxCol::new(table_name, "user_id"), + } } } impl __sdk::__query_builder::CanBeLookupTable for UserBrowseHistory {} + diff --git a/server-rs/crates/spacetime-client/src/story_runtime.rs b/server-rs/crates/spacetime-client/src/story_runtime.rs index d08af363..dfe0b60f 100644 --- a/server-rs/crates/spacetime-client/src/story_runtime.rs +++ b/server-rs/crates/spacetime-client/src/story_runtime.rs @@ -1,9 +1,11 @@ +use module_inventory::{RuntimeInventorySlotRecord, RuntimeInventoryStateRecord}; use module_runtime_story::StoryRuntimeProjectionSource; -use serde_json::Value; +use serde_json::{Map, Value, json}; use shared_contracts::{ runtime_story::RuntimeStoryOptionView, story::{StoryEventPayload, StorySessionPayload}, }; +use std::collections::HashMap; use super::*; @@ -20,14 +22,25 @@ impl SpacetimeClient { )); } - let runtime_snapshot = - self.get_runtime_snapshot(actor_user_id) - .await? - .ok_or_else(|| { - SpacetimeClientError::Runtime("当前用户缺少 runtime snapshot".to_string()) - })?; + let runtime_snapshot = self + .get_runtime_snapshot(actor_user_id.clone()) + .await? + .ok_or_else(|| { + SpacetimeClientError::Runtime("当前用户缺少 runtime snapshot".to_string()) + })?; assert_runtime_snapshot_matches_story_session(&story_state.session, &runtime_snapshot)?; + let inventory_state = self + .get_runtime_inventory_state( + story_state.session.runtime_session_id.clone(), + actor_user_id, + ) + .await?; + let game_state = build_projection_game_state( + runtime_snapshot.game_state, + Some(inventory_state), + &story_state.session.actor_user_id, + )?; let current_story = runtime_snapshot.current_story.as_ref(); let latest_narrative_text = story_state.session.latest_narrative_text.clone(); let server_version = runtime_snapshot.version.max(story_state.session.version); @@ -39,7 +52,7 @@ impl SpacetimeClient { .into_iter() .map(build_story_event_payload) .collect(), - game_state: runtime_snapshot.game_state, + game_state, options: read_runtime_story_options(current_story)?, server_version, current_narrative_text: read_current_story_text(current_story) @@ -50,6 +63,200 @@ impl SpacetimeClient { } } +fn build_projection_game_state( + mut game_state: Value, + inventory_state: Option, + expected_actor_user_id: &str, +) -> Result { + let Some(inventory_state) = inventory_state else { + return Ok(game_state); + }; + + assert_runtime_inventory_matches_game_state( + &game_state, + &inventory_state, + expected_actor_user_id, + )?; + write_runtime_inventory_state(&mut game_state, inventory_state); + + Ok(game_state) +} + +fn assert_runtime_inventory_matches_game_state( + game_state: &Value, + inventory_state: &RuntimeInventoryStateRecord, + expected_actor_user_id: &str, +) -> Result<(), SpacetimeClientError> { + let Some(runtime_session_id) = game_state + .as_object() + .and_then(|state| state.get("runtimeSessionId")) + .and_then(Value::as_str) + .map(str::trim) + .filter(|value| !value.is_empty()) + else { + return Err(SpacetimeClientError::Runtime( + "runtime snapshot 缺少 runtimeSessionId".to_string(), + )); + }; + + if inventory_state.runtime_session_id != runtime_session_id { + return Err(SpacetimeClientError::Runtime( + "runtime inventory state 与 runtime snapshot 不匹配".to_string(), + )); + } + + if inventory_state.actor_user_id != expected_actor_user_id { + return Err(SpacetimeClientError::Runtime( + "runtime inventory state 不属于当前用户".to_string(), + )); + } + + Ok(()) +} + +fn write_runtime_inventory_state( + game_state: &mut Value, + inventory_state: RuntimeInventoryStateRecord, +) { + let existing_items = collect_existing_runtime_items(game_state); + let equipment_items = inventory_state + .equipment_items + .into_iter() + .map(|slot| runtime_inventory_slot_to_game_item(slot, &existing_items)) + .collect::>(); + let backpack_items = inventory_state + .backpack_items + .into_iter() + .map(|slot| runtime_inventory_slot_to_game_item(slot, &existing_items)) + .collect::>(); + + let root = ensure_game_state_object(game_state); + root.insert("playerInventory".to_string(), Value::Array(backpack_items)); + root.insert( + "playerEquipment".to_string(), + Value::Object(build_runtime_equipment_map(equipment_items)), + ); +} + +fn collect_existing_runtime_items(game_state: &Value) -> HashMap> { + let mut items = HashMap::new(); + let Some(root) = game_state.as_object() else { + return items; + }; + + if let Some(inventory) = root.get("playerInventory").and_then(Value::as_array) { + for item in inventory { + collect_existing_runtime_item(&mut items, item); + } + } + + if let Some(equipment) = root.get("playerEquipment").and_then(Value::as_object) { + for item in equipment.values() { + collect_existing_runtime_item(&mut items, item); + } + } + + items +} + +fn collect_existing_runtime_item(items: &mut HashMap>, item: &Value) { + let Some(object) = item.as_object() else { + return; + }; + let Some(item_id) = object + .get("id") + .and_then(Value::as_str) + .map(str::trim) + .filter(|value| !value.is_empty()) + else { + return; + }; + + items.insert(item_id.to_string(), object.clone()); +} + +fn build_runtime_equipment_map(equipment_items: Vec) -> Map { + let mut equipment = Map::from_iter([ + ("weapon".to_string(), Value::Null), + ("armor".to_string(), Value::Null), + ("relic".to_string(), Value::Null), + ]); + + for item in equipment_items { + let Some(slot_id) = item + .as_object() + .and_then(|object| object.get("equipmentSlotId")) + .and_then(Value::as_str) + .map(str::trim) + .filter(|value| matches!(*value, "weapon" | "armor" | "relic")) + else { + continue; + }; + equipment.insert(slot_id.to_string(), item); + } + + equipment +} + +fn runtime_inventory_slot_to_game_item( + slot: RuntimeInventorySlotRecord, + existing_items: &HashMap>, +) -> Value { + let mut item = existing_items + .get(&slot.item_id) + .cloned() + .unwrap_or_else(Map::new); + + item.insert("id".to_string(), Value::String(slot.item_id)); + item.insert("category".to_string(), Value::String(slot.category)); + item.insert("name".to_string(), Value::String(slot.name)); + if let Some(description) = slot.description { + item.insert("description".to_string(), Value::String(description)); + } else { + item.remove("description"); + } + item.insert("quantity".to_string(), json!(slot.quantity)); + item.insert("rarity".to_string(), Value::String(slot.rarity)); + item.insert( + "tags".to_string(), + Value::Array(slot.tags.into_iter().map(Value::String).collect()), + ); + item.insert("stackable".to_string(), Value::Bool(slot.stackable)); + item.insert("stackKey".to_string(), Value::String(slot.stack_key)); + if let Some(equipment_slot_id) = slot.equipment_slot_id { + item.insert( + "equipmentSlotId".to_string(), + Value::String(equipment_slot_id), + ); + } else { + item.remove("equipmentSlotId"); + } + item.insert("sourceKind".to_string(), Value::String(slot.source_kind)); + if let Some(source_reference_id) = slot.source_reference_id { + item.insert( + "sourceReferenceId".to_string(), + Value::String(source_reference_id), + ); + } else { + item.remove("sourceReferenceId"); + } + item.insert("inventorySlotId".to_string(), Value::String(slot.slot_id)); + item.insert("inventorySlotKey".to_string(), Value::String(slot.slot_key)); + item.insert("createdAt".to_string(), Value::String(slot.created_at)); + item.insert("updatedAt".to_string(), Value::String(slot.updated_at)); + + Value::Object(item) +} + +fn ensure_game_state_object(game_state: &mut Value) -> &mut Map { + if !game_state.is_object() { + *game_state = Value::Object(Map::new()); + } + game_state + .as_object_mut() + .expect("projection game_state should be object") +} + fn assert_runtime_snapshot_matches_story_session( session: &StorySessionRecord, snapshot: &RuntimeSnapshotRecord, @@ -159,6 +366,73 @@ mod tests { assert!(error.to_string().contains("不匹配")); } + #[test] + fn projection_game_state_overlays_runtime_inventory_facade_state() { + let game_state = build_projection_game_state( + json!({ + "runtimeSessionId": "runtime_1", + "playerInventory": [{ + "id": "potion-typed", + "name": "旧药", + "useProfile": { "heal": 8 } + }], + "playerEquipment": { "weapon": null, "armor": null, "relic": null } + }), + Some(runtime_inventory_state_record()), + "user_1", + ) + .expect("inventory overlay should build"); + + assert_eq!( + game_state["playerInventory"][0]["id"], + json!("potion-typed") + ); + assert_eq!( + game_state["playerInventory"][0]["inventorySlotId"], + json!("invslot_backpack") + ); + assert_eq!( + game_state["playerInventory"][0]["useProfile"]["heal"], + json!(8) + ); + assert_eq!( + game_state["playerEquipment"]["weapon"]["id"], + json!("blade-typed") + ); + assert_eq!(game_state["playerEquipment"]["armor"], Value::Null); + assert_eq!(game_state["playerEquipment"]["relic"], Value::Null); + } + + #[test] + fn projection_game_state_rejects_mismatched_inventory_runtime_session() { + let mut inventory_state = runtime_inventory_state_record(); + inventory_state.runtime_session_id = "runtime_other".to_string(); + + let error = build_projection_game_state( + json!({ "runtimeSessionId": "runtime_1" }), + Some(inventory_state), + "user_1", + ) + .expect_err("mismatched inventory state should fail"); + + assert!(error.to_string().contains("不匹配")); + } + + #[test] + fn projection_game_state_rejects_mismatched_inventory_actor() { + let mut inventory_state = runtime_inventory_state_record(); + inventory_state.actor_user_id = "user_other".to_string(); + + let error = build_projection_game_state( + json!({ "runtimeSessionId": "runtime_1" }), + Some(inventory_state), + "user_1", + ) + .expect_err("mismatched inventory actor should fail"); + + assert!(error.to_string().contains("不属于当前用户")); + } + #[test] fn current_story_options_parse_runtime_story_options() { let options = read_runtime_story_options(Some(&json!({ @@ -224,4 +498,49 @@ mod tests { updated_at_micros: 3, } } + + fn runtime_inventory_state_record() -> RuntimeInventoryStateRecord { + RuntimeInventoryStateRecord { + runtime_session_id: "runtime_1".to_string(), + actor_user_id: "user_1".to_string(), + backpack_items: vec![RuntimeInventorySlotRecord { + slot_id: "invslot_backpack".to_string(), + container_kind: "backpack".to_string(), + slot_key: "invslot_backpack".to_string(), + item_id: "potion-typed".to_string(), + category: "消耗品".to_string(), + name: "疗伤药".to_string(), + description: Some("用于恢复少量气血。".to_string()), + quantity: 2, + rarity: "common".to_string(), + tags: vec!["healing".to_string()], + stackable: true, + stack_key: "potion-typed".to_string(), + equipment_slot_id: None, + source_kind: "treasure_reward".to_string(), + source_reference_id: Some("treasure_1".to_string()), + created_at: "1.000000Z".to_string(), + updated_at: "2.000000Z".to_string(), + }], + equipment_items: vec![RuntimeInventorySlotRecord { + slot_id: "invslot_weapon".to_string(), + container_kind: "equipment".to_string(), + slot_key: "weapon".to_string(), + item_id: "blade-typed".to_string(), + category: "武器".to_string(), + name: "逐风短剑".to_string(), + description: None, + quantity: 1, + rarity: "rare".to_string(), + tags: vec!["weapon".to_string(), "快剑".to_string()], + stackable: false, + stack_key: "blade-typed".to_string(), + equipment_slot_id: Some("weapon".to_string()), + source_kind: "story_reward".to_string(), + source_reference_id: None, + created_at: "1.000000Z".to_string(), + updated_at: "2.000000Z".to_string(), + }], + } + } } diff --git a/server-rs/crates/spacetime-module/src/auth/mapper.rs b/server-rs/crates/spacetime-module/src/auth/mapper.rs new file mode 100644 index 00000000..64e7e5f6 --- /dev/null +++ b/server-rs/crates/spacetime-module/src/auth/mapper.rs @@ -0,0 +1,86 @@ +use serde::{Deserialize, Serialize}; +use serde_json::Value; + +pub(super) fn sanitize_identity_component(value: &str) -> String { + let sanitized = value + .chars() + .map(|character| { + if character.is_ascii_alphanumeric() { + character + } else { + '_' + } + }) + .collect::(); + sanitized.trim_matches('_').to_string() +} + +#[derive(Deserialize, Serialize)] +pub(super) struct PersistentAuthStoreSnapshot { + #[serde(default = "default_next_user_id")] + pub(super) next_user_id: u64, + pub(super) users_by_username: std::collections::HashMap, + #[serde(default)] + pub(super) phone_to_user_id: std::collections::HashMap, + pub(super) sessions_by_id: std::collections::HashMap, + #[serde(default)] + pub(super) session_id_by_refresh_token_hash: std::collections::HashMap, + pub(super) wechat_identity_by_provider_uid: + std::collections::HashMap, + #[serde(default)] + pub(super) user_id_by_provider_union_id: std::collections::HashMap, +} + +fn default_next_user_id() -> u64 { + 1 +} + +#[derive(Deserialize, Serialize)] +pub(super) struct StoredPasswordUserSnapshot { + pub(super) user: AuthUserSnapshot, + pub(super) password_hash: String, + #[serde(default)] + pub(super) password_login_enabled: bool, + pub(super) phone_number: Option, +} + +#[derive(Deserialize, Serialize)] +pub(super) struct AuthUserSnapshot { + pub(super) id: String, + pub(super) public_user_code: String, + pub(super) username: String, + pub(super) display_name: String, + pub(super) phone_number_masked: Option, + pub(super) login_method: String, + pub(super) binding_status: String, + pub(super) wechat_bound: bool, + pub(super) token_version: u64, +} + +#[derive(Deserialize, Serialize)] +pub(super) struct StoredWechatIdentitySnapshot { + pub(super) user_id: String, + pub(super) provider_uid: String, + pub(super) provider_union_id: Option, + pub(super) display_name: Option, + pub(super) avatar_url: Option, +} + +#[derive(Deserialize, Serialize)] +pub(super) struct StoredRefreshSessionSnapshot { + pub(super) session: RefreshSessionSnapshot, +} + +#[derive(Deserialize, Serialize)] +pub(super) struct RefreshSessionSnapshot { + pub(super) session_id: String, + pub(super) user_id: String, + pub(super) refresh_token_hash: String, + pub(super) issued_by_provider: String, + pub(super) client_info: Value, + pub(super) expires_at: String, + pub(super) revoked_at: Option, + pub(super) created_at: String, + pub(super) updated_at: String, + pub(super) last_seen_at: String, +} diff --git a/server-rs/crates/spacetime-module/src/auth/mod.rs b/server-rs/crates/spacetime-module/src/auth/mod.rs new file mode 100644 index 00000000..fc02198b --- /dev/null +++ b/server-rs/crates/spacetime-module/src/auth/mod.rs @@ -0,0 +1,6 @@ +mod mapper; +mod procedures; +mod tables; + +pub use procedures::*; +pub use tables::*; diff --git a/server-rs/crates/spacetime-module/src/auth.rs b/server-rs/crates/spacetime-module/src/auth/procedures.rs similarity index 74% rename from server-rs/crates/spacetime-module/src/auth.rs rename to server-rs/crates/spacetime-module/src/auth/procedures.rs index cde789e7..3150680f 100644 --- a/server-rs/crates/spacetime-module/src/auth.rs +++ b/server-rs/crates/spacetime-module/src/auth/procedures.rs @@ -1,6 +1,16 @@ -use crate::*; -use serde::{Deserialize, Serialize}; -use serde_json::Value; +use crate::{ProcedureContext, ReducerContext, SpacetimeType, Table, Timestamp}; + +use super::{ + mapper::{ + AuthUserSnapshot, PersistentAuthStoreSnapshot, RefreshSessionSnapshot, + StoredPasswordUserSnapshot, StoredRefreshSessionSnapshot, StoredWechatIdentitySnapshot, + sanitize_identity_component, + }, + tables::{ + AuthIdentity, AuthStoreSnapshot, RefreshSession, UserAccount, auth_identity, + auth_store_snapshot, refresh_session, user_account, + }, +}; const AUTH_STORE_SNAPSHOT_ID: &str = "default"; @@ -37,71 +47,6 @@ pub struct AuthStoreSnapshotImportProcedureResult { pub error_message: Option, } -#[spacetimedb::table(accessor = auth_store_snapshot)] -pub struct AuthStoreSnapshot { - #[primary_key] - pub(crate) snapshot_id: String, - pub(crate) snapshot_json: String, - pub(crate) updated_at: Timestamp, -} - -#[spacetimedb::table( - accessor = user_account, - index(accessor = by_user_account_username, btree(columns = [username])), - index(accessor = by_user_account_public_code, btree(columns = [public_user_code])) -)] -pub struct UserAccount { - #[primary_key] - pub(crate) user_id: String, - pub(crate) public_user_code: String, - pub(crate) username: String, - pub(crate) display_name: String, - pub(crate) phone_number_masked: Option, - pub(crate) phone_number_e164: Option, - pub(crate) login_method: String, - pub(crate) binding_status: String, - pub(crate) wechat_bound: bool, - pub(crate) password_hash: String, - pub(crate) password_login_enabled: bool, - pub(crate) token_version: u64, -} - -#[spacetimedb::table( - accessor = auth_identity, - index(accessor = by_auth_identity_user_id, btree(columns = [user_id])), - index(accessor = by_auth_identity_provider_uid, btree(columns = [provider, provider_uid])) -)] -pub struct AuthIdentity { - #[primary_key] - pub(crate) identity_id: String, - pub(crate) user_id: String, - pub(crate) provider: String, - pub(crate) provider_uid: String, - pub(crate) provider_union_id: Option, - pub(crate) phone_e164: Option, - pub(crate) display_name: Option, - pub(crate) avatar_url: Option, -} - -#[spacetimedb::table( - accessor = refresh_session, - index(accessor = by_refresh_session_user_id, btree(columns = [user_id])), - index(accessor = by_refresh_session_token_hash, btree(columns = [refresh_token_hash])) -)] -pub struct RefreshSession { - #[primary_key] - pub(crate) session_id: String, - pub(crate) user_id: String, - pub(crate) refresh_token_hash: String, - pub(crate) issued_by_provider: String, - pub(crate) client_info_json: String, - pub(crate) expires_at: String, - pub(crate) revoked_at: Option, - pub(crate) created_at: String, - pub(crate) updated_at: String, - pub(crate) last_seen_at: String, -} - // Axum 启动恢复认证状态时读取当前快照;记录不存在代表尚未产生登录态。 #[spacetimedb::procedure] pub fn get_auth_store_snapshot(ctx: &mut ProcedureContext) -> AuthStoreSnapshotProcedureResult { @@ -157,7 +102,7 @@ pub fn import_auth_store_snapshot( } } -// Axum ??????????????? module-auth ???????????????? +// Axum 启动时可从正式表重新导出 module-auth 使用的整份认证快照。 #[spacetimedb::procedure] pub fn export_auth_store_snapshot_from_tables( ctx: &mut ProcedureContext, @@ -409,8 +354,8 @@ fn export_auth_store_snapshot_from_tables_tx( let mut sessions_by_id = std::collections::HashMap::new(); let mut session_id_by_refresh_token_hash = std::collections::HashMap::new(); for session in sessions { - let client_info = serde_json::from_str::(&session.client_info_json) - .map_err(|error| format!("refresh session ????? JSON ?????{error}"))?; + let client_info = serde_json::from_str::(&session.client_info_json) + .map_err(|error| format!("refresh session 客户端信息 JSON 解析失败:{error}"))?; session_id_by_refresh_token_hash.insert( session.refresh_token_hash.clone(), session.session_id.clone(), @@ -443,8 +388,8 @@ fn export_auth_store_snapshot_from_tables_tx( wechat_identity_by_provider_uid, user_id_by_provider_union_id, }; - let snapshot_json = - serde_json::to_string_pretty(&snapshot).map_err(|error| format!("?????????????{error}"))?; + let snapshot_json = serde_json::to_string_pretty(&snapshot) + .map_err(|error| format!("序列化认证快照失败:{error}"))?; Ok(AuthStoreSnapshotRecord { snapshot_json: Some(snapshot_json), @@ -469,87 +414,3 @@ fn clear_auth_target_tables(ctx: &ReducerContext) { ctx.db.user_account().user_id().delete(&row.user_id); } } - -fn sanitize_identity_component(value: &str) -> String { - let sanitized = value - .chars() - .map(|character| { - if character.is_ascii_alphanumeric() { - character - } else { - '_' - } - }) - .collect::(); - sanitized.trim_matches('_').to_string() -} - -#[derive(Deserialize, Serialize)] -struct PersistentAuthStoreSnapshot { - #[serde(default = "default_next_user_id")] - next_user_id: u64, - users_by_username: std::collections::HashMap, - #[serde(default)] - phone_to_user_id: std::collections::HashMap, - sessions_by_id: std::collections::HashMap, - #[serde(default)] - session_id_by_refresh_token_hash: std::collections::HashMap, - wechat_identity_by_provider_uid: - std::collections::HashMap, - #[serde(default)] - user_id_by_provider_union_id: std::collections::HashMap, -} - -fn default_next_user_id() -> u64 { - 1 -} - -#[derive(Deserialize, Serialize)] -struct StoredPasswordUserSnapshot { - user: AuthUserSnapshot, - password_hash: String, - #[serde(default)] - password_login_enabled: bool, - phone_number: Option, -} - -#[derive(Deserialize, Serialize)] -struct AuthUserSnapshot { - id: String, - public_user_code: String, - username: String, - display_name: String, - phone_number_masked: Option, - login_method: String, - binding_status: String, - wechat_bound: bool, - token_version: u64, -} - -#[derive(Deserialize, Serialize)] -struct StoredWechatIdentitySnapshot { - user_id: String, - provider_uid: String, - provider_union_id: Option, - display_name: Option, - avatar_url: Option, -} - -#[derive(Deserialize, Serialize)] -struct StoredRefreshSessionSnapshot { - session: RefreshSessionSnapshot, -} - -#[derive(Deserialize, Serialize)] -struct RefreshSessionSnapshot { - session_id: String, - user_id: String, - refresh_token_hash: String, - issued_by_provider: String, - client_info: Value, - expires_at: String, - revoked_at: Option, - created_at: String, - updated_at: String, - last_seen_at: String, -} diff --git a/server-rs/crates/spacetime-module/src/auth/tables.rs b/server-rs/crates/spacetime-module/src/auth/tables.rs new file mode 100644 index 00000000..3cd84fb4 --- /dev/null +++ b/server-rs/crates/spacetime-module/src/auth/tables.rs @@ -0,0 +1,66 @@ +use crate::Timestamp; + +#[spacetimedb::table(accessor = auth_store_snapshot)] +pub struct AuthStoreSnapshot { + #[primary_key] + pub(crate) snapshot_id: String, + pub(crate) snapshot_json: String, + pub(crate) updated_at: Timestamp, +} + +#[spacetimedb::table( + accessor = user_account, + index(accessor = by_user_account_username, btree(columns = [username])), + index(accessor = by_user_account_public_code, btree(columns = [public_user_code])) +)] +pub struct UserAccount { + #[primary_key] + pub(crate) user_id: String, + pub(crate) public_user_code: String, + pub(crate) username: String, + pub(crate) display_name: String, + pub(crate) phone_number_masked: Option, + pub(crate) phone_number_e164: Option, + pub(crate) login_method: String, + pub(crate) binding_status: String, + pub(crate) wechat_bound: bool, + pub(crate) password_hash: String, + pub(crate) password_login_enabled: bool, + pub(crate) token_version: u64, +} + +#[spacetimedb::table( + accessor = auth_identity, + index(accessor = by_auth_identity_user_id, btree(columns = [user_id])), + index(accessor = by_auth_identity_provider_uid, btree(columns = [provider, provider_uid])) +)] +pub struct AuthIdentity { + #[primary_key] + pub(crate) identity_id: String, + pub(crate) user_id: String, + pub(crate) provider: String, + pub(crate) provider_uid: String, + pub(crate) provider_union_id: Option, + pub(crate) phone_e164: Option, + pub(crate) display_name: Option, + pub(crate) avatar_url: Option, +} + +#[spacetimedb::table( + accessor = refresh_session, + index(accessor = by_refresh_session_user_id, btree(columns = [user_id])), + index(accessor = by_refresh_session_token_hash, btree(columns = [refresh_token_hash])) +)] +pub struct RefreshSession { + #[primary_key] + pub(crate) session_id: String, + pub(crate) user_id: String, + pub(crate) refresh_token_hash: String, + pub(crate) issued_by_provider: String, + pub(crate) client_info_json: String, + pub(crate) expires_at: String, + pub(crate) revoked_at: Option, + pub(crate) created_at: String, + pub(crate) updated_at: String, + pub(crate) last_seen_at: String, +} diff --git a/server-rs/crates/spacetime-module/src/big_fish/events.rs b/server-rs/crates/spacetime-module/src/big_fish/events.rs index 1ff52b09..cd2745e1 100644 --- a/server-rs/crates/spacetime-module/src/big_fish/events.rs +++ b/server-rs/crates/spacetime-module/src/big_fish/events.rs @@ -37,7 +37,10 @@ pub(crate) fn emit_big_fish_publish_readiness_event( publish_ready, blockers, occurred_at_micros, - } = event; + } = event + else { + return Ok(()); + }; let blockers_json = serde_json::to_string(&blockers) .map_err(|error| format!("big_fish.publish_readiness.blockers 序列化失败: {error}"))?; diff --git a/server-rs/crates/spacetime-module/src/big_fish/mod.rs b/server-rs/crates/spacetime-module/src/big_fish/mod.rs index 8b7f8ec8..89ea3549 100644 --- a/server-rs/crates/spacetime-module/src/big_fish/mod.rs +++ b/server-rs/crates/spacetime-module/src/big_fish/mod.rs @@ -1,9 +1,11 @@ mod assets; mod events; +mod runtime; mod session; mod tables; pub use assets::*; pub(crate) use events::*; +pub use runtime::*; pub use session::*; pub use tables::*; diff --git a/server-rs/crates/spacetime-module/src/big_fish/runtime.rs b/server-rs/crates/spacetime-module/src/big_fish/runtime.rs new file mode 100644 index 00000000..fefdadd4 --- /dev/null +++ b/server-rs/crates/spacetime-module/src/big_fish/runtime.rs @@ -0,0 +1,231 @@ +use crate::big_fish::tables::{ + BigFishCreationSession, BigFishRuntimeRun, big_fish_creation_session, big_fish_runtime_run, +}; +use crate::*; +use module_big_fish::{ + StartBigFishRunCommand, SubmitBigFishInputCommand, deserialize_runtime_snapshot, + serialize_runtime_snapshot, start_big_fish_run as start_big_fish_run_domain, + submit_big_fish_input as submit_big_fish_input_domain, +}; + +#[spacetimedb::procedure] +pub fn start_big_fish_run( + ctx: &mut ProcedureContext, + input: BigFishRunStartInput, +) -> BigFishRunProcedureResult { + match ctx.try_with_tx(|tx| start_big_fish_run_tx(tx, input.clone())) { + Ok(run) => BigFishRunProcedureResult { + ok: true, + run_json: Some(serialize_big_fish_run_json(&run)), + error_message: None, + }, + Err(message) => BigFishRunProcedureResult { + ok: false, + run_json: None, + error_message: Some(message), + }, + } +} + +#[spacetimedb::procedure] +pub fn get_big_fish_run( + ctx: &mut ProcedureContext, + input: BigFishRunGetInput, +) -> BigFishRunProcedureResult { + match ctx.try_with_tx(|tx| get_big_fish_run_tx(tx, input.clone())) { + Ok(run) => BigFishRunProcedureResult { + ok: true, + run_json: Some(serialize_big_fish_run_json(&run)), + error_message: None, + }, + Err(message) => BigFishRunProcedureResult { + ok: false, + run_json: None, + error_message: Some(message), + }, + } +} + +#[spacetimedb::procedure] +pub fn submit_big_fish_input( + ctx: &mut ProcedureContext, + input: BigFishInputSubmitInput, +) -> BigFishRunProcedureResult { + match ctx.try_with_tx(|tx| submit_big_fish_input_tx(tx, input.clone())) { + Ok(run) => BigFishRunProcedureResult { + ok: true, + run_json: Some(serialize_big_fish_run_json(&run)), + error_message: None, + }, + Err(message) => BigFishRunProcedureResult { + ok: false, + run_json: None, + error_message: Some(message), + }, + } +} + +pub(crate) fn start_big_fish_run_tx( + ctx: &ReducerContext, + input: BigFishRunStartInput, +) -> Result { + validate_run_start_input(&input).map_err(|error| error.to_string())?; + if ctx + .db + .big_fish_runtime_run() + .run_id() + .find(&input.run_id) + .is_some() + { + return Err("big_fish_runtime_run.run_id 已存在".to_string()); + } + + let session = ctx + .db + .big_fish_creation_session() + .session_id() + .find(&input.session_id) + .ok_or_else(|| "big_fish_creation_session 不存在".to_string())?; + ensure_big_fish_session_playable(&session, &input.owner_user_id)?; + let draft = session + .draft_json + .as_deref() + .map(deserialize_draft) + .transpose() + .map_err(|error| format!("big_fish.draft_json 非法: {error}"))?; + let work_level_count = draft + .as_ref() + .map(|value| value.runtime_params.level_count) + .or_else(|| Some(BIG_FISH_DEFAULT_LEVEL_COUNT)); + let result = start_big_fish_run_domain(StartBigFishRunCommand { + run_id: input.run_id, + session_id: input.session_id, + owner_user_id: input.owner_user_id.clone(), + draft, + work_level_count, + started_at_micros: input.started_at_micros, + }) + .map_err(|error| error.to_string())?; + + insert_big_fish_runtime_run( + ctx, + &result.snapshot, + &input.owner_user_id, + input.started_at_micros, + )?; + Ok(result.snapshot) +} + +pub(crate) fn get_big_fish_run_tx( + ctx: &ReducerContext, + input: BigFishRunGetInput, +) -> Result { + validate_run_get_input(&input).map_err(|error| error.to_string())?; + let row = get_owned_big_fish_run_row(ctx, &input.run_id, &input.owner_user_id)?; + deserialize_runtime_snapshot(&row.snapshot_json) + .map_err(|error| format!("big_fish.runtime_snapshot_json 非法: {error}")) +} + +pub(crate) fn submit_big_fish_input_tx( + ctx: &ReducerContext, + input: BigFishInputSubmitInput, +) -> Result { + validate_input_submit_input(&input).map_err(|error| error.to_string())?; + let row = get_owned_big_fish_run_row(ctx, &input.run_id, &input.owner_user_id)?; + let current_snapshot = deserialize_runtime_snapshot(&row.snapshot_json) + .map_err(|error| format!("big_fish.runtime_snapshot_json 非法: {error}"))?; + let result = submit_big_fish_input_domain(SubmitBigFishInputCommand { + owner_user_id: input.owner_user_id, + x: input.x, + y: input.y, + submitted_at_micros: input.submitted_at_micros, + current_snapshot, + }) + .map_err(|error| error.to_string())?; + + replace_big_fish_runtime_run(ctx, &row, &result.snapshot, input.submitted_at_micros)?; + Ok(result.snapshot) +} + +fn ensure_big_fish_session_playable( + session: &BigFishCreationSession, + player_user_id: &str, +) -> Result<(), String> { + if session.owner_user_id == player_user_id { + return Ok(()); + } + if session.stage == BigFishCreationStage::Published { + return Ok(()); + } + Err("未发布的大鱼吃小鱼作品不允许非作者启动运行态".to_string()) +} + +fn get_owned_big_fish_run_row( + ctx: &ReducerContext, + run_id: &str, + owner_user_id: &str, +) -> Result { + let row = ctx + .db + .big_fish_runtime_run() + .run_id() + .find(&run_id.to_string()) + .ok_or_else(|| "big_fish_runtime_run 不存在".to_string())?; + if row.owner_user_id != owner_user_id { + return Err("无权访问该 big_fish_runtime_run".to_string()); + } + Ok(row) +} + +fn insert_big_fish_runtime_run( + ctx: &ReducerContext, + run: &BigFishRuntimeSnapshot, + owner_user_id: &str, + created_at_micros: i64, +) -> Result<(), String> { + let timestamp = Timestamp::from_micros_since_unix_epoch(created_at_micros); + ctx.db.big_fish_runtime_run().insert(BigFishRuntimeRun { + run_id: run.run_id.clone(), + session_id: run.session_id.clone(), + owner_user_id: owner_user_id.to_string(), + status: run.status, + snapshot_json: serialize_runtime_snapshot(run) + .map_err(|error| format!("big_fish.runtime_snapshot 序列化失败: {error}"))?, + last_input_x: run.last_input.x, + last_input_y: run.last_input.y, + tick: run.tick, + created_at: timestamp, + updated_at: timestamp, + }); + Ok(()) +} + +fn replace_big_fish_runtime_run( + ctx: &ReducerContext, + current: &BigFishRuntimeRun, + run: &BigFishRuntimeSnapshot, + updated_at_micros: i64, +) -> Result<(), String> { + ctx.db + .big_fish_runtime_run() + .run_id() + .delete(¤t.run_id); + ctx.db.big_fish_runtime_run().insert(BigFishRuntimeRun { + run_id: run.run_id.clone(), + session_id: run.session_id.clone(), + owner_user_id: current.owner_user_id.clone(), + status: run.status, + snapshot_json: serialize_runtime_snapshot(run) + .map_err(|error| format!("big_fish.runtime_snapshot 序列化失败: {error}"))?, + last_input_x: run.last_input.x, + last_input_y: run.last_input.y, + tick: run.tick, + created_at: current.created_at, + updated_at: Timestamp::from_micros_since_unix_epoch(updated_at_micros), + }); + Ok(()) +} + +fn serialize_big_fish_run_json(run: &BigFishRuntimeSnapshot) -> String { + serialize_runtime_snapshot(run).unwrap_or_else(|_| "{}".to_string()) +} diff --git a/server-rs/crates/spacetime-module/src/big_fish/session.rs b/server-rs/crates/spacetime-module/src/big_fish/session.rs index fbef0200..05b04e22 100644 --- a/server-rs/crates/spacetime-module/src/big_fish/session.rs +++ b/server-rs/crates/spacetime-module/src/big_fish/session.rs @@ -1,4 +1,6 @@ -use crate::big_fish::tables::{big_fish_agent_message, big_fish_creation_session}; +use crate::big_fish::tables::{ + big_fish_agent_message, big_fish_creation_session, big_fish_runtime_run, +}; use crate::runtime::{ ProfilePlayedWorkUpsertInput, add_profile_observed_play_time, upsert_profile_played_work, }; @@ -326,7 +328,7 @@ pub(crate) fn delete_big_fish_work_tx( .filter(|row| row.owner_user_id == input.owner_user_id) .ok_or_else(|| "big_fish_creation_session 不存在".to_string())?; - // 删除作品时同步清理 Agent 消息与素材槽;最终游玩模拟已经迁到前端,不再写后端运行快照。 + // 中文注释:删除作品时同步清理 Agent 消息、素材槽和后端运行态快照,避免失去来源会话的 run 残留。 ctx.db .big_fish_creation_session() .session_id() @@ -352,6 +354,15 @@ pub(crate) fn delete_big_fish_work_tx( { ctx.db.big_fish_asset_slot().slot_id().delete(&slot.slot_id); } + for run in ctx + .db + .big_fish_runtime_run() + .iter() + .filter(|row| row.session_id == input.session_id) + .collect::>() + { + ctx.db.big_fish_runtime_run().run_id().delete(&run.run_id); + } list_big_fish_works_tx( ctx, BigFishWorksListInput { diff --git a/server-rs/crates/spacetime-module/src/big_fish/tables.rs b/server-rs/crates/spacetime-module/src/big_fish/tables.rs index 7e82cd91..31183469 100644 --- a/server-rs/crates/spacetime-module/src/big_fish/tables.rs +++ b/server-rs/crates/spacetime-module/src/big_fish/tables.rs @@ -52,3 +52,22 @@ pub struct BigFishAssetSlot { pub(crate) prompt_snapshot: String, pub(crate) updated_at: Timestamp, } + +#[spacetimedb::table( + accessor = big_fish_runtime_run, + index(accessor = by_big_fish_run_owner_user_id, btree(columns = [owner_user_id])), + index(accessor = by_big_fish_run_session_id, btree(columns = [session_id])) +)] +pub struct BigFishRuntimeRun { + #[primary_key] + pub(crate) run_id: String, + pub(crate) session_id: String, + pub(crate) owner_user_id: String, + pub(crate) status: BigFishRunStatus, + pub(crate) snapshot_json: String, + pub(crate) last_input_x: f32, + pub(crate) last_input_y: f32, + pub(crate) tick: u64, + pub(crate) created_at: Timestamp, + pub(crate) updated_at: Timestamp, +} diff --git a/server-rs/crates/spacetime-module/src/custom_world/mod.rs b/server-rs/crates/spacetime-module/src/custom_world/mod.rs index 32802668..ec2b8224 100644 --- a/server-rs/crates/spacetime-module/src/custom_world/mod.rs +++ b/server-rs/crates/spacetime-module/src/custom_world/mod.rs @@ -1,3 +1,4 @@ +use crate::*; use std::collections::HashSet; #[spacetimedb::table( @@ -13,6 +14,10 @@ pub struct CustomWorldProfile { profile_id: String, // 当前 profile 承接 library / publish / enter-world 的正式世界工件真相。 owner_user_id: String, + // 作品公开编号是稳定分享键,第一次发布时分配,后续重复发布沿用。 + public_work_code: Option, + // 作者公开叙世号在发布时固化到作品真相,供广场读模型与搜索结果直接展示。 + author_public_user_code: Option, source_agent_session_id: Option, publication_status: CustomWorldPublicationStatus, world_name: String, @@ -104,6 +109,7 @@ pub struct CustomWorldAgentMessage { created_at: Timestamp, } +#[derive(Clone)] #[spacetimedb::table( accessor = custom_world_agent_operation, index(accessor = by_custom_world_agent_operation_session_id, btree(columns = [session_id])) @@ -151,13 +157,16 @@ pub struct CustomWorldDraftCard { accessor = custom_world_gallery_entry, public, index(accessor = by_custom_world_gallery_owner_user_id, btree(columns = [owner_user_id])), - index(accessor = by_custom_world_gallery_theme_mode, btree(columns = [theme_mode])) + index(accessor = by_custom_world_gallery_theme_mode, btree(columns = [theme_mode])), + index(accessor = by_custom_world_gallery_public_work_code, btree(columns = [public_work_code])) )] pub struct CustomWorldGalleryEntry { #[primary_key] profile_id: String, // 画廊是公开订阅读模型,不再运行时从 profile 即席拼装。 owner_user_id: String, + public_work_code: String, + author_public_user_code: String, author_display_name: String, world_name: String, subtitle: String, @@ -169,7 +178,7 @@ pub struct CustomWorldGalleryEntry { published_at: Timestamp, updated_at: Timestamp, } - +// Agent 会话首版只负责把可持久化创作状态落进 SpacetimeDB,LLM 采集与卡片生成后续再接入。 #[spacetimedb::procedure] pub fn create_custom_world_agent_session( ctx: &mut ProcedureContext, @@ -209,6 +218,25 @@ pub fn get_custom_world_agent_session( } } +#[spacetimedb::procedure] +pub fn delete_custom_world_agent_session( + ctx: &mut ProcedureContext, + input: CustomWorldAgentSessionGetInput, +) -> CustomWorldWorksListResult { + match ctx.try_with_tx(|tx| delete_custom_world_agent_session_tx(tx, input.clone())) { + Ok(items) => CustomWorldWorksListResult { + ok: true, + items, + error_message: None, + }, + Err(message) => CustomWorldWorksListResult { + ok: false, + items: Vec::new(), + error_message: Some(message), + }, + } +} + #[spacetimedb::procedure] pub fn submit_custom_world_agent_message( ctx: &mut ProcedureContext, @@ -228,6 +256,25 @@ pub fn submit_custom_world_agent_message( } } +#[spacetimedb::procedure] +pub fn finalize_custom_world_agent_message_turn( + ctx: &mut ProcedureContext, + input: CustomWorldAgentMessageFinalizeInput, +) -> CustomWorldAgentOperationProcedureResult { + match ctx.try_with_tx(|tx| finalize_custom_world_agent_message_turn_tx(tx, input.clone())) { + Ok(operation) => CustomWorldAgentOperationProcedureResult { + ok: true, + operation: Some(operation), + error_message: None, + }, + Err(message) => CustomWorldAgentOperationProcedureResult { + ok: false, + operation: None, + error_message: Some(message), + }, + } +} + #[spacetimedb::procedure] pub fn get_custom_world_agent_operation( ctx: &mut ProcedureContext, @@ -247,95 +294,24 @@ pub fn get_custom_world_agent_operation( } } -fn continue_story_tx( - ctx: &ReducerContext, - input: StoryContinueInput, -) -> Result<(StorySessionSnapshot, StoryEventSnapshot), String> { - validate_story_continue_input(&input).map_err(|error| error.to_string())?; - - let current = ctx - .db - .story_session() - .story_session_id() - .find(&input.story_session_id) - .ok_or_else(|| "story_session 不存在,无法继续推进".to_string())?; - - let current_snapshot = StorySessionSnapshot { - story_session_id: current.story_session_id.clone(), - runtime_session_id: current.runtime_session_id.clone(), - actor_user_id: current.actor_user_id.clone(), - world_profile_id: current.world_profile_id.clone(), - initial_prompt: current.initial_prompt.clone(), - opening_summary: current.opening_summary.clone(), - latest_narrative_text: current.latest_narrative_text.clone(), - latest_choice_function_id: current.latest_choice_function_id.clone(), - status: current.status, - version: current.version, - created_at_micros: current.created_at.to_micros_since_unix_epoch(), - updated_at_micros: current.updated_at.to_micros_since_unix_epoch(), - }; - - let (next_snapshot, event_snapshot) = - apply_story_continue(current_snapshot, input).map_err(|error| error.to_string())?; - - ctx.db - .story_session() - .story_session_id() - .delete(¤t.story_session_id); - - ctx.db.story_session().insert(StorySession { - story_session_id: next_snapshot.story_session_id.clone(), - runtime_session_id: next_snapshot.runtime_session_id.clone(), - actor_user_id: next_snapshot.actor_user_id.clone(), - world_profile_id: next_snapshot.world_profile_id.clone(), - initial_prompt: next_snapshot.initial_prompt.clone(), - opening_summary: next_snapshot.opening_summary.clone(), - latest_narrative_text: next_snapshot.latest_narrative_text.clone(), - latest_choice_function_id: next_snapshot.latest_choice_function_id.clone(), - status: next_snapshot.status, - version: next_snapshot.version, - created_at: Timestamp::from_micros_since_unix_epoch(next_snapshot.created_at_micros), - updated_at: Timestamp::from_micros_since_unix_epoch(next_snapshot.updated_at_micros), - }); - - ctx.db.story_event().insert(StoryEvent { - event_id: event_snapshot.event_id.clone(), - story_session_id: event_snapshot.story_session_id.clone(), - event_kind: event_snapshot.event_kind, - narrative_text: event_snapshot.narrative_text.clone(), - choice_function_id: event_snapshot.choice_function_id.clone(), - created_at: Timestamp::from_micros_since_unix_epoch(event_snapshot.created_at_micros), - }); - - Ok((next_snapshot, event_snapshot)) +#[spacetimedb::procedure] +pub fn upsert_custom_world_agent_operation_progress( + ctx: &mut ProcedureContext, + input: CustomWorldAgentOperationProgressInput, +) -> CustomWorldAgentOperationProcedureResult { + match ctx.try_with_tx(|tx| upsert_custom_world_agent_operation_progress_tx(tx, input.clone())) { + Ok(operation) => CustomWorldAgentOperationProcedureResult { + ok: true, + operation: Some(operation), + error_message: None, + }, + Err(message) => CustomWorldAgentOperationProcedureResult { + ok: false, + operation: None, + error_message: Some(message), + }, + } } - -fn get_story_session_state_tx( - ctx: &ReducerContext, - input: StorySessionStateInput, -) -> Result<(StorySessionSnapshot, Vec), String> { - validate_story_session_state_input(&input).map_err(|error| error.to_string())?; - - let session = ctx - .db - .story_session() - .story_session_id() - .find(&input.story_session_id) - .ok_or_else(|| "story_session 不存在".to_string())?; - - let session_snapshot = build_story_session_snapshot_from_row(&session); - let mut events = ctx - .db - .story_event() - .iter() - .filter(|row| row.story_session_id == input.story_session_id) - .map(|row| build_story_event_snapshot_from_row(&row)) - .collect::>(); - events.sort_by_key(|event| (event.created_at_micros, event.event_id.clone())); - - Ok((session_snapshot, events)) -} - fn create_custom_world_agent_session_tx( ctx: &ReducerContext, input: CustomWorldAgentSessionCreateInput, @@ -428,6 +404,101 @@ fn get_custom_world_agent_session_tx( Ok(build_custom_world_agent_session_snapshot(ctx, &session)) } +fn delete_custom_world_agent_session_tx( + ctx: &ReducerContext, + input: CustomWorldAgentSessionGetInput, +) -> Result, String> { + validate_custom_world_agent_session_get_input(&input).map_err(|error| error.to_string())?; + + let session = ctx + .db + .custom_world_agent_session() + .session_id() + .find(&input.session_id) + .filter(|row| row.owner_user_id == input.owner_user_id) + .ok_or_else(|| "custom_world_agent_session 不存在".to_string())?; + + if session.stage == RpgAgentStage::Published { + let published_profile = ctx + .db + .custom_world_profile() + .iter() + .find(|row| { + row.owner_user_id == input.owner_user_id + && row.source_agent_session_id.as_deref() == Some(input.session_id.as_str()) + && row.deleted_at.is_none() + }) + .ok_or_else(|| "已发布 RPG 作品缺少关联 profile,无法删除".to_string())?; + + // 作品卡可能只携带源 Agent sessionId。这里把“按 session 删除已发布作品” + // 收敛为 profile 软删除,避免前端误入草稿删除接口时继续暴露 procedure 分叉。 + delete_custom_world_profile_record( + ctx, + CustomWorldProfileDeleteInput { + profile_id: published_profile.profile_id, + owner_user_id: input.owner_user_id.clone(), + deleted_at_micros: ctx.timestamp.to_micros_since_unix_epoch(), + }, + )?; + + return list_custom_world_work_snapshots( + ctx, + CustomWorldWorksListInput { + owner_user_id: input.owner_user_id, + }, + ); + } + + // 删除纯 Agent 草稿时同步清理消息、操作与草稿卡,避免作品列表消失后残留孤儿数据。 + ctx.db + .custom_world_agent_session() + .session_id() + .delete(&session.session_id); + for message in ctx + .db + .custom_world_agent_message() + .iter() + .filter(|row| row.session_id == input.session_id) + .collect::>() + { + ctx.db + .custom_world_agent_message() + .message_id() + .delete(&message.message_id); + } + for operation in ctx + .db + .custom_world_agent_operation() + .iter() + .filter(|row| row.session_id == input.session_id) + .collect::>() + { + ctx.db + .custom_world_agent_operation() + .operation_id() + .delete(&operation.operation_id); + } + for card in ctx + .db + .custom_world_draft_card() + .iter() + .filter(|row| row.session_id == input.session_id) + .collect::>() + { + ctx.db + .custom_world_draft_card() + .card_id() + .delete(&card.card_id); + } + + list_custom_world_work_snapshots( + ctx, + CustomWorldWorksListInput { + owner_user_id: input.owner_user_id, + }, + ) +} + fn submit_custom_world_agent_message_tx( ctx: &ReducerContext, input: CustomWorldAgentMessageSubmitInput, @@ -438,7 +509,7 @@ fn submit_custom_world_agent_message_tx( return Err("forced failure".to_string()); } - let session = ctx + let _session = ctx .db .custom_world_agent_session() .session_id() @@ -455,36 +526,18 @@ fn submit_custom_world_agent_message_tx( { return Err("custom_world_agent_message.message_id 已存在".to_string()); } - if let Some(existing_operation) = ctx + if ctx .db .custom_world_agent_operation() .operation_id() .find(&input.operation_id) + .is_some() { - let can_reuse_running_draft_operation = input.action.trim() == "draft_foundation" - && existing_operation.session_id == input.session_id - && existing_operation.operation_type == RpgAgentOperationType::DraftFoundation - && matches!( - existing_operation.status, - RpgAgentOperationStatus::Queued | RpgAgentOperationStatus::Running - ); - if !can_reuse_running_draft_operation { - return Err("custom_world_agent_operation.operation_id 已存在".to_string()); - } + return Err("custom_world_agent_operation.operation_id 已存在".to_string()); } let submitted_at = Timestamp::from_micros_since_unix_epoch(input.submitted_at_micros); let user_message_text = input.user_message_text.trim().to_string(); - let assistant_message_id = format!("assistant-{}", input.operation_id); - if ctx - .db - .custom_world_agent_message() - .message_id() - .find(&assistant_message_id) - .is_some() - { - return Err("custom_world_agent_message.assistant_message_id 已存在".to_string()); - } ctx.db .custom_world_agent_message() @@ -498,96 +551,21 @@ fn submit_custom_world_agent_message_tx( created_at: submitted_at, }); - let user_message_count = ctx - .db - .custom_world_agent_message() - .iter() - .filter(|row| { - row.session_id == input.session_id && matches!(row.role, RpgAgentMessageRole::User) - }) - .count() as u32; - let next_turn = session.current_turn.saturating_add(1); - let (next_stage, next_progress_percent, next_readiness_json, next_pending_clarifications_json) = - if user_message_count >= 2 { - ( - RpgAgentStage::FoundationReview, - 100, - r#"{"isReady":true,"completedKeys":["seed_input"],"missingKeys":[]}"#.to_string(), - "[]".to_string(), - ) - } else { - ( - RpgAgentStage::Clarifying, - session.progress_percent.max(20), - session.creator_intent_readiness_json.clone(), - format!( - r#"[{{"id":"clarify-{next_turn}","label":"补充核心设定","question":"请继续补充这个世界的玩家身份、主题氛围或核心冲突。","targetKey":"core_conflict","priority":1}}]"# - ), - ) - }; - let assistant_reply = "已记录这条设定。我会先把它当作新的世界线索收进当前草稿,你可以继续补充玩家身份、主题氛围、核心冲突、关键关系或标志性元素。".to_string(); - ctx.db .custom_world_agent_operation() .insert(CustomWorldAgentOperation { operation_id: input.operation_id.clone(), session_id: input.session_id.clone(), operation_type: RpgAgentOperationType::ProcessMessage, - status: RpgAgentOperationStatus::Completed, - phase_label: "消息已处理".to_string(), - phase_detail: if next_stage == RpgAgentStage::FoundationReview { - "当前上下文已达到最小 foundation_review 门槛。".to_string() - } else { - "当前上下文已记录,继续收集世界关键锚点。".to_string() - }, - progress: 100, + status: RpgAgentOperationStatus::Running, + phase_label: "消息处理中".to_string(), + phase_detail: "已记录用户消息,等待大模型生成本轮回复。".to_string(), + progress: 10, error_message: None, created_at: submitted_at, updated_at: submitted_at, }); - ctx.db - .custom_world_agent_message() - .insert(CustomWorldAgentMessage { - message_id: assistant_message_id, - session_id: input.session_id.clone(), - role: RpgAgentMessageRole::Assistant, - kind: RpgAgentMessageKind::Chat, - text: assistant_reply.clone(), - related_operation_id: Some(input.operation_id.clone()), - created_at: submitted_at, - }); - - ctx.db - .custom_world_agent_session() - .session_id() - .update(CustomWorldAgentSession { - session_id: session.session_id.clone(), - owner_user_id: session.owner_user_id.clone(), - seed_text: session.seed_text.clone(), - current_turn: next_turn, - progress_percent: next_progress_percent, - stage: next_stage, - focus_card_id: session.focus_card_id.clone(), - anchor_content_json: session.anchor_content_json.clone(), - creator_intent_json: session.creator_intent_json.clone(), - creator_intent_readiness_json: next_readiness_json, - anchor_pack_json: session.anchor_pack_json.clone(), - lock_state_json: session.lock_state_json.clone(), - draft_profile_json: session.draft_profile_json.clone(), - last_assistant_reply: Some(assistant_reply), - publish_gate_json: session.publish_gate_json.clone(), - result_preview_json: session.result_preview_json.clone(), - pending_clarifications_json: next_pending_clarifications_json, - quality_findings_json: session.quality_findings_json.clone(), - suggested_actions_json: session.suggested_actions_json.clone(), - recommended_replies_json: session.recommended_replies_json.clone(), - asset_coverage_json: session.asset_coverage_json.clone(), - checkpoints_json: session.checkpoints_json.clone(), - created_at: session.created_at, - updated_at: submitted_at, - }); - get_custom_world_agent_operation_tx( ctx, CustomWorldAgentOperationGetInput { @@ -622,6 +600,166 @@ fn get_custom_world_agent_operation_tx( Ok(build_custom_world_agent_operation_snapshot(&operation)) } +fn upsert_custom_world_agent_operation_progress_tx( + ctx: &ReducerContext, + input: CustomWorldAgentOperationProgressInput, +) -> Result { + validate_custom_world_agent_operation_progress_input(&input) + .map_err(|error| error.to_string())?; + ctx.db + .custom_world_agent_session() + .session_id() + .find(&input.session_id) + .filter(|row| row.owner_user_id == input.owner_user_id) + .ok_or_else(|| "custom_world_agent_session 不存在".to_string())?; + + let timestamp = Timestamp::from_micros_since_unix_epoch(input.updated_at_micros); + let operation = if let Some(current) = ctx + .db + .custom_world_agent_operation() + .operation_id() + .find(&input.operation_id) + { + if current.session_id != input.session_id { + return Err("custom_world_agent_operation.session_id 不匹配".to_string()); + } + let next = rebuild_custom_world_agent_operation_row( + ¤t, + CustomWorldAgentOperationPatch { + status: Some(input.operation_status), + phase_label: Some(input.phase_label.clone()), + phase_detail: Some(input.phase_detail.clone()), + progress: Some(input.operation_progress), + error_message: Some(input.error_message.clone()), + updated_at_micros: Some(input.updated_at_micros), + }, + )?; + replace_custom_world_agent_operation(ctx, ¤t, next.clone()); + next + } else { + ctx.db + .custom_world_agent_operation() + .insert(CustomWorldAgentOperation { + operation_id: input.operation_id.clone(), + session_id: input.session_id.clone(), + operation_type: input.operation_type, + status: input.operation_status, + phase_label: input.phase_label.clone(), + phase_detail: input.phase_detail.clone(), + progress: input.operation_progress, + error_message: input.error_message.clone(), + created_at: timestamp, + updated_at: timestamp, + }) + }; + + Ok(build_custom_world_agent_operation_snapshot(&operation)) +} + +fn finalize_custom_world_agent_message_turn_tx( + ctx: &ReducerContext, + input: CustomWorldAgentMessageFinalizeInput, +) -> Result { + validate_custom_world_agent_message_finalize_input(&input) + .map_err(|error| error.to_string())?; + + let session = ctx + .db + .custom_world_agent_session() + .session_id() + .find(&input.session_id) + .filter(|row| row.owner_user_id == input.owner_user_id) + .ok_or_else(|| "custom_world_agent_session 不存在".to_string())?; + + let operation = ctx + .db + .custom_world_agent_operation() + .operation_id() + .find(&input.operation_id) + .filter(|row| row.session_id == input.session_id) + .ok_or_else(|| "custom_world_agent_operation 不存在".to_string())?; + + let updated_at = Timestamp::from_micros_since_unix_epoch(input.updated_at_micros); + + let next_session = if input.operation_status == RpgAgentOperationStatus::Failed { + rebuild_custom_world_agent_session_row( + &session, + CustomWorldAgentSessionPatch { + updated_at_micros: Some(input.updated_at_micros), + ..CustomWorldAgentSessionPatch::default() + }, + )? + } else { + let assistant_message_id = input.assistant_message_id.clone().ok_or_else(|| { + "custom_world_agent_message.assistant_message_id 不能为空".to_string() + })?; + let assistant_reply_text = input + .assistant_reply_text + .clone() + .ok_or_else(|| "custom_world_agent_message.text 不能为空".to_string())?; + + if ctx + .db + .custom_world_agent_message() + .message_id() + .find(&assistant_message_id) + .is_some() + { + return Err("custom_world_agent_message.assistant_message_id 已存在".to_string()); + } + + ctx.db + .custom_world_agent_message() + .insert(CustomWorldAgentMessage { + message_id: assistant_message_id, + session_id: input.session_id.clone(), + role: RpgAgentMessageRole::Assistant, + kind: RpgAgentMessageKind::Chat, + text: assistant_reply_text.clone(), + related_operation_id: Some(input.operation_id.clone()), + created_at: updated_at, + }); + + rebuild_custom_world_agent_session_row( + &session, + CustomWorldAgentSessionPatch { + current_turn: Some(session.current_turn.saturating_add(1)), + progress_percent: Some(input.progress_percent), + stage: Some(input.stage), + focus_card_id: Some(input.focus_card_id.clone()), + anchor_content_json: Some(input.anchor_content_json.clone()), + creator_intent_json: Some(input.creator_intent_json.clone()), + creator_intent_readiness_json: Some(input.creator_intent_readiness_json.clone()), + anchor_pack_json: Some(input.anchor_pack_json.clone()), + draft_profile_json: Some(input.draft_profile_json.clone()), + last_assistant_reply: Some(Some(assistant_reply_text)), + pending_clarifications_json: Some(input.pending_clarifications_json.clone()), + quality_findings_json: Some(input.quality_findings_json.clone()), + suggested_actions_json: Some(input.suggested_actions_json.clone()), + recommended_replies_json: Some(input.recommended_replies_json.clone()), + asset_coverage_json: Some(input.asset_coverage_json.clone()), + updated_at_micros: Some(input.updated_at_micros), + ..CustomWorldAgentSessionPatch::default() + }, + )? + }; + replace_custom_world_agent_session(ctx, &session, next_session); + + let next_operation = rebuild_custom_world_agent_operation_row( + &operation, + CustomWorldAgentOperationPatch { + status: Some(input.operation_status), + phase_label: Some(input.phase_label.clone()), + phase_detail: Some(input.phase_detail.clone()), + progress: Some(input.operation_progress), + error_message: Some(input.error_message.clone()), + updated_at_micros: Some(input.updated_at_micros), + }, + )?; + replace_custom_world_agent_operation(ctx, &operation, next_operation.clone()); + + Ok(build_custom_world_agent_operation_snapshot(&next_operation)) +} // M5 Stage 2 先把 library profile upsert 固定成最小正式写入口;已发布作品在这里同步刷新 gallery 投影。 #[spacetimedb::reducer] pub fn upsert_custom_world_profile( @@ -717,7 +855,7 @@ pub fn unpublish_custom_world_profile_and_return( #[spacetimedb::procedure] pub fn delete_custom_world_profile_and_return( ctx: &mut ProcedureContext, - input: CustomWorldProfileDeleteInput, + input: module_custom_world::CustomWorldProfileDeleteInput, ) -> CustomWorldProfileListResult { match ctx.try_with_tx(|tx| { delete_custom_world_profile_record(tx, input.clone())?; @@ -764,7 +902,7 @@ pub fn list_custom_world_profiles( pub fn list_custom_world_gallery_entries( ctx: &mut ProcedureContext, ) -> CustomWorldGalleryListResult { - match ctx.try_with_tx(|tx| Ok::<_, String>(list_custom_world_gallery_snapshots(tx))) { + match ctx.try_with_tx(|tx| list_custom_world_gallery_snapshots(tx)) { Ok(entries) => CustomWorldGalleryListResult { ok: true, entries, @@ -821,30 +959,32 @@ pub fn get_custom_world_gallery_detail( } #[spacetimedb::procedure] -pub fn list_custom_world_works( +pub fn get_custom_world_gallery_detail_by_code( ctx: &mut ProcedureContext, - input: CustomWorldWorksListInput, -) -> CustomWorldWorksListResult { - match ctx.try_with_tx(|tx| list_custom_world_work_snapshots(tx, input.clone())) { - Ok(items) => CustomWorldWorksListResult { + input: module_custom_world::CustomWorldGalleryDetailByCodeInput, +) -> CustomWorldLibraryMutationResult { + match ctx.try_with_tx(|tx| get_custom_world_gallery_detail_record_by_code(tx, input.clone())) { + Ok((entry, gallery_entry)) => CustomWorldLibraryMutationResult { ok: true, - items, + entry, + gallery_entry, error_message: None, }, - Err(message) => CustomWorldWorksListResult { + Err(message) => CustomWorldLibraryMutationResult { ok: false, - items: Vec::new(), + entry: None, + gallery_entry: None, error_message: Some(message), }, } } #[spacetimedb::procedure] -pub fn delete_custom_world_agent_session( +pub fn list_custom_world_works( ctx: &mut ProcedureContext, - input: CustomWorldAgentSessionGetInput, + input: CustomWorldWorksListInput, ) -> CustomWorldWorksListResult { - match ctx.try_with_tx(|tx| delete_custom_world_agent_session_tx(tx, input.clone())) { + match ctx.try_with_tx(|tx| list_custom_world_work_snapshots(tx, input.clone())) { Ok(items) => CustomWorldWorksListResult { ok: true, items, @@ -896,66 +1036,6 @@ pub fn execute_custom_world_agent_action( } } -// procedure 面向 Axum 同步拉取浏览历史,继续沿用旧 Node 的 visitedAt 倒序输出语义。 -#[spacetimedb::procedure] -pub fn list_platform_browse_history( - ctx: &mut ProcedureContext, - input: RuntimeBrowseHistoryListInput, -) -> RuntimeBrowseHistoryProcedureResult { - match ctx.try_with_tx(|tx| list_platform_browse_history_rows(tx, input.clone())) { - Ok(entries) => RuntimeBrowseHistoryProcedureResult { - ok: true, - entries, - error_message: None, - }, - Err(message) => RuntimeBrowseHistoryProcedureResult { - ok: false, - entries: Vec::new(), - error_message: Some(message), - }, - } -} - -// procedure 面向 Axum 承接 browse history 的单条/批量 POST,同步返回当前用户的完整列表。 -#[spacetimedb::procedure] -pub fn upsert_platform_browse_history_and_return( - ctx: &mut ProcedureContext, - input: RuntimeBrowseHistorySyncInput, -) -> RuntimeBrowseHistoryProcedureResult { - match ctx.try_with_tx(|tx| upsert_platform_browse_history_rows(tx, input.clone())) { - Ok(entries) => RuntimeBrowseHistoryProcedureResult { - ok: true, - entries, - error_message: None, - }, - Err(message) => RuntimeBrowseHistoryProcedureResult { - ok: false, - entries: Vec::new(), - error_message: Some(message), - }, - } -} - -// procedure 面向 Axum 清空当前用户浏览历史,并直接返回空列表响应。 -#[spacetimedb::procedure] -pub fn clear_platform_browse_history_and_return( - ctx: &mut ProcedureContext, - input: RuntimeBrowseHistoryClearInput, -) -> RuntimeBrowseHistoryProcedureResult { - match ctx.try_with_tx(|tx| clear_platform_browse_history_rows(tx, input.clone())) { - Ok(entries) => RuntimeBrowseHistoryProcedureResult { - ok: true, - entries, - error_message: None, - }, - Err(message) => RuntimeBrowseHistoryProcedureResult { - ok: false, - entries: Vec::new(), - error_message: Some(message), - }, - } -} - // Stage 3 先把 published profile compile 作为独立 procedure 暴露,避免把编译逻辑和表写入、发布动作强耦合。 #[spacetimedb::procedure] pub fn compile_custom_world_published_profile( @@ -1003,7 +1083,6 @@ pub fn publish_custom_world_world( }, } } - fn upsert_custom_world_profile_record( ctx: &ReducerContext, input: CustomWorldProfileUpsertInput, @@ -1043,6 +1122,8 @@ fn upsert_custom_world_profile_record( CustomWorldProfile { profile_id: existing.profile_id.clone(), owner_user_id: existing.owner_user_id.clone(), + public_work_code: existing.public_work_code.clone(), + author_public_user_code: existing.author_public_user_code.clone(), source_agent_session_id: input.source_agent_session_id.clone(), publication_status: existing.publication_status, world_name: input.world_name.clone(), @@ -1063,6 +1144,8 @@ fn upsert_custom_world_profile_record( None => CustomWorldProfile { profile_id: input.profile_id.clone(), owner_user_id: input.owner_user_id.clone(), + public_work_code: input.public_work_code.clone(), + author_public_user_code: input.author_public_user_code.clone(), source_agent_session_id: input.source_agent_session_id.clone(), publication_status: CustomWorldPublicationStatus::Draft, world_name: input.world_name.clone(), @@ -1134,6 +1217,8 @@ fn publish_custom_world_world_record( CustomWorldProfileUpsertInput { profile_id: compiled_record.profile_id.clone(), owner_user_id: compiled_record.owner_user_id.clone(), + public_work_code: input.public_work_code.clone(), + author_public_user_code: Some(input.author_public_user_code.clone()), source_agent_session_id: Some(input.session_id.clone()), world_name: compiled_record.world_name.clone(), subtitle: compiled_record.subtitle.clone(), @@ -1153,6 +1238,8 @@ fn publish_custom_world_world_record( CustomWorldProfilePublishInput { profile_id: compiled_record.profile_id.clone(), owner_user_id: compiled_record.owner_user_id.clone(), + public_work_code: input.public_work_code.clone(), + author_public_user_code: input.author_public_user_code.clone(), author_display_name: compiled_record.author_display_name.clone(), published_at_micros: input.published_at_micros, }, @@ -1198,6 +1285,11 @@ fn publish_custom_world_profile_record( let next_row = CustomWorldProfile { profile_id: existing.profile_id.clone(), owner_user_id: existing.owner_user_id.clone(), + public_work_code: existing + .public_work_code + .clone() + .or_else(|| Some(build_public_work_code_from_profile_id(&existing.profile_id))), + author_public_user_code: Some(input.author_public_user_code.clone()), source_agent_session_id: existing.source_agent_session_id.clone(), publication_status: CustomWorldPublicationStatus::Published, world_name: existing.world_name.clone(), @@ -1259,6 +1351,8 @@ fn unpublish_custom_world_profile_record( let next_row = CustomWorldProfile { profile_id: existing.profile_id.clone(), owner_user_id: existing.owner_user_id.clone(), + public_work_code: existing.public_work_code.clone(), + author_public_user_code: existing.author_public_user_code.clone(), source_agent_session_id: existing.source_agent_session_id.clone(), publication_status: CustomWorldPublicationStatus::Draft, world_name: existing.world_name.clone(), @@ -1283,7 +1377,7 @@ fn unpublish_custom_world_profile_record( fn delete_custom_world_profile_record( ctx: &ReducerContext, - input: CustomWorldProfileDeleteInput, + input: module_custom_world::CustomWorldProfileDeleteInput, ) -> Result<(), String> { validate_custom_world_profile_delete_input(&input).map_err(|error| error.to_string())?; @@ -1316,6 +1410,8 @@ fn delete_custom_world_profile_record( let next_row = CustomWorldProfile { profile_id: existing.profile_id.clone(), owner_user_id: existing.owner_user_id.clone(), + public_work_code: existing.public_work_code.clone(), + author_public_user_code: existing.author_public_user_code.clone(), source_agent_session_id: existing.source_agent_session_id.clone(), publication_status: CustomWorldPublicationStatus::Draft, world_name: existing.world_name.clone(), @@ -1358,7 +1454,9 @@ fn list_custom_world_profile_snapshots( fn list_custom_world_gallery_snapshots( ctx: &ReducerContext, -) -> Vec { +) -> Result, String> { + sync_missing_custom_world_gallery_entries(ctx)?; + let mut entries = ctx .db .custom_world_gallery_entry() @@ -1373,7 +1471,7 @@ fn list_custom_world_gallery_snapshots( .then(right.updated_at_micros.cmp(&left.updated_at_micros)) }); - entries + Ok(entries) } fn get_custom_world_library_detail_record( @@ -1452,6 +1550,48 @@ fn get_custom_world_gallery_detail_record( )) } +fn get_custom_world_gallery_detail_record_by_code( + ctx: &ReducerContext, + input: module_custom_world::CustomWorldGalleryDetailByCodeInput, +) -> Result< + ( + Option, + Option, + ), + String, +> { + validate_custom_world_gallery_detail_by_code_input(&input) + .map_err(|error| error.to_string())?; + + let normalized_public_work_code = normalize_public_work_code(&input.public_work_code) + .ok_or_else(|| "public_work_code 格式不正确".to_string())?; + + let gallery_entry = ctx + .db + .custom_world_gallery_entry() + .iter() + .find(|row| row.public_work_code == normalized_public_work_code); + + let profile = gallery_entry.as_ref().and_then(|row| { + ctx.db + .custom_world_profile() + .profile_id() + .find(&row.profile_id) + .filter(|profile_row| { + profile_row.owner_user_id == row.owner_user_id + && profile_row.publication_status == CustomWorldPublicationStatus::Published + && profile_row.deleted_at.is_none() + }) + }); + + Ok(( + profile.as_ref().map(build_custom_world_profile_snapshot), + gallery_entry + .as_ref() + .map(build_custom_world_gallery_entry_snapshot), + )) +} + fn list_custom_world_work_snapshots( ctx: &ReducerContext, input: CustomWorldWorksListInput, @@ -1574,7 +1714,8 @@ fn should_include_custom_world_agent_session_work( } if ctx.db.custom_world_agent_message().iter().any(|message| { - message.session_id == session.session_id && matches!(message.role, RpgAgentMessageRole::User) + message.session_id == session.session_id + && matches!(message.role, RpgAgentMessageRole::User) }) { return true; } @@ -1585,9 +1726,7 @@ fn should_include_custom_world_agent_session_work( .any(|card| card.session_id == session.session_id) } -fn custom_world_agent_session_has_direct_work_content( - session: &CustomWorldAgentSession, -) -> bool { +fn custom_world_agent_session_has_direct_work_content(session: &CustomWorldAgentSession) -> bool { // 创建会话时写入的助手欢迎语和空 `{}` draftProfile 不算草稿内容; // 这里只承认用户显式输入的 seed 或已经生成出的真实草稿阶段。 !session.seed_text.trim().is_empty() @@ -1622,100 +1761,6 @@ fn should_include_custom_world_profile_work( }) } -fn delete_custom_world_agent_session_tx( - ctx: &ReducerContext, - input: CustomWorldAgentSessionGetInput, -) -> Result, String> { - validate_custom_world_agent_session_get_input(&input).map_err(|error| error.to_string())?; - - let session = ctx - .db - .custom_world_agent_session() - .session_id() - .find(&input.session_id) - .filter(|row| row.owner_user_id == input.owner_user_id) - .ok_or_else(|| "custom_world_agent_session 不存在".to_string())?; - if session.stage == RpgAgentStage::Published { - let published_profile = ctx - .db - .custom_world_profile() - .iter() - .find(|row| { - row.owner_user_id == input.owner_user_id - && row.source_agent_session_id.as_deref() == Some(input.session_id.as_str()) - && row.deleted_at.is_none() - }) - .ok_or_else(|| "已发布 RPG 作品缺少关联 profile,无法删除".to_string())?; - - // 作品卡可能只携带源 Agent sessionId。这里把“按 session 删除已发布作品”收敛为 - // profile 软删除,避免前端误入草稿删除接口时把业务分支放大成上游 502。 - delete_custom_world_profile_record( - ctx, - CustomWorldProfileDeleteInput { - profile_id: published_profile.profile_id, - owner_user_id: input.owner_user_id.clone(), - deleted_at_micros: ctx.timestamp.to_micros_since_unix_epoch(), - }, - )?; - - return list_custom_world_work_snapshots( - ctx, - CustomWorldWorksListInput { - owner_user_id: input.owner_user_id, - }, - ); - } - - // 删除纯 Agent 草稿时同步清理消息、操作与草稿卡,避免作品列表消失后残留孤儿数据。 - ctx.db - .custom_world_agent_session() - .session_id() - .delete(&session.session_id); - for message in ctx - .db - .custom_world_agent_message() - .iter() - .filter(|row| row.session_id == input.session_id) - .collect::>() - { - ctx.db - .custom_world_agent_message() - .message_id() - .delete(&message.message_id); - } - for operation in ctx - .db - .custom_world_agent_operation() - .iter() - .filter(|row| row.session_id == input.session_id) - .collect::>() - { - ctx.db - .custom_world_agent_operation() - .operation_id() - .delete(&operation.operation_id); - } - for card in ctx - .db - .custom_world_draft_card() - .iter() - .filter(|row| row.session_id == input.session_id) - .collect::>() - { - ctx.db - .custom_world_draft_card() - .card_id() - .delete(&card.card_id); - } - - list_custom_world_work_snapshots( - ctx, - CustomWorldWorksListInput { - owner_user_id: input.owner_user_id, - }, - ) -} - fn get_custom_world_agent_card_detail_tx( ctx: &ReducerContext, input: CustomWorldAgentCardDetailGetInput, @@ -1754,14 +1799,22 @@ fn execute_custom_world_agent_action_tx( .filter(|row| row.owner_user_id == input.owner_user_id) .ok_or_else(|| "custom_world_agent_session 不存在".to_string())?; - if ctx + if let Some(existing_operation) = ctx .db .custom_world_agent_operation() .operation_id() .find(&input.operation_id) - .is_some() { - return Err("custom_world_agent_operation.operation_id 已存在".to_string()); + let can_reuse_running_draft_operation = input.action.trim() == "draft_foundation" + && existing_operation.session_id == input.session_id + && existing_operation.operation_type == RpgAgentOperationType::DraftFoundation + && matches!( + existing_operation.status, + RpgAgentOperationStatus::Queued | RpgAgentOperationStatus::Running + ); + if !can_reuse_running_draft_operation { + return Err("custom_world_agent_operation.operation_id 已存在".to_string()); + } } let payload = parse_optional_session_object(input.payload_json.as_deref()).unwrap_or_default(); @@ -1773,169 +1826,17 @@ fn execute_custom_world_agent_action_tx( } "publish_world" => execute_publish_world_action(ctx, &session, &input, &payload), "revert_checkpoint" => execute_revert_checkpoint_action(ctx, &session, &input, &payload), - "generate_characters" | "generate_landmarks" => { - execute_generate_entities_action(ctx, &session, &input, &payload) - } - "generate_role_assets" | "generate_scene_assets" => { - execute_prepare_asset_studio_action(ctx, &session, &input, &payload) - } - "sync_role_assets" => execute_sync_role_assets_action(ctx, &session, &input, &payload), - "sync_scene_assets" => execute_sync_scene_assets_action(ctx, &session, &input, &payload), - "expand_long_tail" => execute_placeholder_custom_world_action(ctx, &session, &input), + "generate_characters" + | "generate_landmarks" + | "generate_role_assets" + | "sync_role_assets" + | "generate_scene_assets" + | "sync_scene_assets" + | "expand_long_tail" => execute_placeholder_custom_world_action(ctx, &session, &input), other => Err(format!("custom world action `{other}` 当前尚未支持")), } } -fn execute_generate_entities_action( - ctx: &ReducerContext, - session: &CustomWorldAgentSession, - input: &CustomWorldAgentActionExecuteInput, - payload: &JsonMap, -) -> Result { - ensure_refining_stage(session.stage, input.action.as_str())?; - - let mut draft_profile = parse_optional_session_object(session.draft_profile_json.as_deref()) - .ok_or_else(|| format!("{} requires an existing draft foundation", input.action))?; - // 结果页只消费服务端 resultPreview,这里必须先写回草稿真相再刷新预览。 - let (payload_key, profile_key, card_kind, operation_type, entity_label) = - resolve_generate_entities_target(input.action.as_str(), payload)?; - let generated_entities = payload - .get(payload_key) - .and_then(JsonValue::as_array) - .cloned() - .ok_or_else(|| format!("{} requires payload.{payload_key}", input.action))?; - if generated_entities.is_empty() { - return Err(format!("{} requires at least one generated entity", input.action)); - } - - let mut appended_entities = Vec::new(); - for (index, entity) in generated_entities.into_iter().enumerate() { - let normalized_entity = ensure_generated_entity_id(entity, card_kind, index); - if normalized_entity.as_object().is_none() { - return Err(format!("{payload_key} entries must be objects")); - } - upsert_generated_entity_card( - ctx, - &session.session_id, - card_kind, - &normalized_entity, - input.submitted_at_micros, - )?; - appended_entities.push(normalized_entity); - } - - let entries = draft_profile - .entry(profile_key.to_string()) - .or_insert_with(|| JsonValue::Array(Vec::new())) - .as_array_mut() - .ok_or_else(|| format!("draftProfile.{profile_key} must be array"))?; - entries.extend(appended_entities.iter().cloned()); - - let gate = summarize_publish_gate_from_json( - &session.session_id, - session.stage, - Some(&draft_profile), - &parse_json_array_or_empty(&session.quality_findings_json), - ); - let quality_findings = parse_json_array_or_empty(&session.quality_findings_json); - let next_session = rebuild_custom_world_agent_session_row( - session, - CustomWorldAgentSessionPatch { - draft_profile_json: Some(Some(serialize_json_value(&JsonValue::Object( - draft_profile.clone(), - ))?)), - last_assistant_reply: Some(Some(format!( - "已新增 {} 个{},并刷新结果预览。", - appended_entities.len(), - entity_label, - ))), - publish_gate_json: Some(Some(serialize_json_value(&publish_gate_to_json_value( - &gate, - ))?)), - result_preview_json: Some(build_result_preview_json( - Some(&draft_profile), - &gate, - &quality_findings, - input.submitted_at_micros, - )?), - checkpoints_json: Some(append_checkpoint_json( - &session.checkpoints_json, - &build_session_checkpoint_value( - input.action.as_str(), - &format!("新增{}", entity_label), - session, - ), - )?), - updated_at_micros: Some(input.submitted_at_micros), - ..CustomWorldAgentSessionPatch::default() - }, - )?; - replace_custom_world_agent_session(ctx, session, next_session); - - append_custom_world_action_result_message( - ctx, - &session.session_id, - &input.operation_id, - &format!("已新增 {} 个{}。", appended_entities.len(), entity_label), - input.submitted_at_micros, - ); - - let operation = build_and_insert_custom_world_operation( - ctx, - &input.operation_id, - &session.session_id, - operation_type, - "新增内容已写入", - &format!( - "{} 已追加到 draftProfile.{},resultPreview 已刷新。", - entity_label, profile_key, - ), - input.submitted_at_micros, - ); - - Ok(build_custom_world_agent_operation_snapshot(&operation)) -} - -fn resolve_generate_entities_target( - action: &str, - payload: &JsonMap, -) -> Result<( - &'static str, - &'static str, - RpgAgentDraftCardKind, - RpgAgentOperationType, - &'static str, -), String> { - match action { - "generate_characters" => { - let profile_key = match payload.get("roleType").and_then(JsonValue::as_str).map(str::trim) { - Some("playable") => "playableNpcs", - _ => "storyNpcs", - }; - let entity_label = if profile_key == "playableNpcs" { - "可扮演角色" - } else { - "场景角色" - }; - Ok(( - "generatedCharacters", - profile_key, - RpgAgentDraftCardKind::Character, - RpgAgentOperationType::GenerateCharacters, - entity_label, - )) - } - "generate_landmarks" => Ok(( - "generatedLandmarks", - "landmarks", - RpgAgentDraftCardKind::Landmark, - RpgAgentOperationType::GenerateLandmarks, - "场景", - )), - other => Err(format!("custom world action `{other}` 当前尚未支持生成实体")), - } -} - fn execute_draft_foundation_action( ctx: &ReducerContext, session: &CustomWorldAgentSession, @@ -2000,24 +1901,7 @@ fn execute_draft_foundation_action( updated_at, ); - if let Some(existing_operation) = ctx - .db - .custom_world_agent_operation() - .operation_id() - .find(&input.operation_id) - { - if existing_operation.session_id != session.session_id - || existing_operation.operation_type != RpgAgentOperationType::DraftFoundation - { - return Err("custom_world_agent_operation 与 draft_foundation 写回不匹配".to_string()); - } - ctx.db - .custom_world_agent_operation() - .operation_id() - .delete(&input.operation_id); - } - - let operation = build_and_insert_custom_world_operation( + let operation = complete_custom_world_operation( ctx, &input.operation_id, &session.session_id, @@ -2025,7 +1909,7 @@ fn execute_draft_foundation_action( "底稿已整理", "第一版 foundation draft 已写入会话与世界卡。", updated_at, - ); + )?; Ok(build_custom_world_agent_operation_snapshot(&operation)) } @@ -2335,16 +2219,22 @@ fn execute_publish_world_action( .get("legacyResultProfile") .map(serialize_json_value) .transpose()?; + let author_public_user_code = read_optional_text_field(payload, &["authorPublicUserCode"]) + .unwrap_or_else(|| build_public_user_code_from_owner_user_id(&session.owner_user_id)); + let author_display_name = read_optional_text_field(payload, &["authorDisplayName"]) + .unwrap_or_else(|| "创作者".to_string()); let publish_result = publish_custom_world_world_record( ctx, CustomWorldPublishWorldInput { session_id: session.session_id.clone(), profile_id, owner_user_id: session.owner_user_id.clone(), + public_work_code: None, + author_public_user_code, draft_profile_json: serialize_json_value(&JsonValue::Object(draft_profile.clone()))?, legacy_result_profile_json, setting_text, - author_display_name: "创作者".to_string(), + author_display_name, published_at_micros: input.submitted_at_micros, }, )?; @@ -2488,146 +2378,6 @@ fn execute_revert_checkpoint_action( Ok(build_custom_world_agent_operation_snapshot(&operation)) } - -fn execute_prepare_asset_studio_action( - ctx: &ReducerContext, - session: &CustomWorldAgentSession, - input: &CustomWorldAgentActionExecuteInput, - payload: &JsonMap, -) -> Result { - ensure_draft_refining_stage(session.stage, input.action.as_str())?; - let draft_profile = parse_optional_session_object(session.draft_profile_json.as_deref()) - .ok_or_else(|| format!("{} requires an existing draft foundation", input.action))?; - let (focus_id, operation_type, message_text, phase_label, phase_detail) = - if input.action == "generate_role_assets" { - let role_id = read_first_payload_text(payload, "roleIds", "roleId") - .ok_or_else(|| "generate_role_assets requires roleIds".to_string())?; - let role = find_profile_entity_by_id(&draft_profile, &["playableNpcs", "storyNpcs"], &role_id) - .ok_or_else(|| "未找到目标角色,无法进入角色资产工坊。".to_string())?; - let role_name = read_optional_text_field(role, &["name"]).unwrap_or_else(|| "角色".to_string()); - ( - role_id, - RpgAgentOperationType::GenerateRoleAssets, - format!("已为「{}」准备好角色资产工坊,先生成主图候选,再补核心动作。", role_name), - "角色资产工坊已就绪", - format!("「{}」现在可以开始生成主图和动作。", role_name), - ) - } else { - let scene_id = read_first_payload_text(payload, "sceneIds", "sceneId") - .ok_or_else(|| "generate_scene_assets requires sceneIds".to_string())?; - let scene_kind = payload.get("sceneKind").and_then(JsonValue::as_str).map(str::trim).unwrap_or("landmark"); - let scene = if scene_kind == "camp" { - draft_profile.get("camp").and_then(JsonValue::as_object) - } else { - find_profile_entity_by_id(&draft_profile, &["landmarks"], &scene_id) - } - .ok_or_else(|| "未找到目标场景,无法进入场景资产工坊。".to_string())?; - let scene_name = read_optional_text_field(scene, &["name"]) - .unwrap_or_else(|| if scene_kind == "camp" { "开局营地" } else { "未命名场景" }.to_string()); - ( - scene_id, - RpgAgentOperationType::GenerateSceneAssets, - format!("已为「{}」准备好场景图工坊,保存生成结果后会自动同步回当前草稿。", scene_name), - "场景资产工坊已就绪", - format!("「{}」现在可以继续生成和确认正式场景图。", scene_name), - ) - }; - let next_session = rebuild_custom_world_agent_session_row( - session, - CustomWorldAgentSessionPatch { - stage: Some(RpgAgentStage::VisualRefining), - focus_card_id: Some(Some(focus_id)), - last_assistant_reply: Some(Some(message_text.clone())), - updated_at_micros: Some(input.submitted_at_micros), - ..CustomWorldAgentSessionPatch::default() - }, - )?; - replace_custom_world_agent_session(ctx, session, next_session); - append_custom_world_action_result_message(ctx, &session.session_id, &input.operation_id, &message_text, input.submitted_at_micros); - let operation = build_and_insert_custom_world_operation(ctx, &input.operation_id, &session.session_id, operation_type, phase_label, &phase_detail, input.submitted_at_micros); - Ok(build_custom_world_agent_operation_snapshot(&operation)) -} - -fn execute_sync_role_assets_action( - ctx: &ReducerContext, - session: &CustomWorldAgentSession, - input: &CustomWorldAgentActionExecuteInput, - payload: &JsonMap, -) -> Result { - ensure_draft_refining_stage(session.stage, "sync_role_assets")?; - let mut draft_profile = parse_optional_session_object(session.draft_profile_json.as_deref()) - .ok_or_else(|| "sync_role_assets requires an existing draft foundation".to_string())?; - let role_id = read_required_payload_text(payload, "roleId", "sync_role_assets requires roleId")?; - let portrait_path = read_required_payload_text(payload, "portraitPath", "sync_role_assets requires portraitPath")?; - let generated_visual_asset_id = read_required_payload_text(payload, "generatedVisualAssetId", "sync_role_assets requires generatedVisualAssetId")?; - let generated_animation_set_id = payload.get("generatedAnimationSetId").and_then(JsonValue::as_str).map(str::trim).filter(|value| !value.is_empty()).map(ToOwned::to_owned); - let animation_map = payload.get("animationMap").cloned(); - let updated_role = apply_role_asset_publish_result(&mut draft_profile, &role_id, &portrait_path, &generated_visual_asset_id, generated_animation_set_id.as_deref(), animation_map)?; - let role_name = read_optional_text_field(&updated_role, &["name"]).unwrap_or_else(|| "当前角色".to_string()); - let asset_status = resolve_role_asset_status(&updated_role); - let asset_status_label = resolve_role_asset_status_label(asset_status).to_string(); - upsert_asset_role_card(ctx, &session.session_id, &role_id, &updated_role, asset_status, &asset_status_label, input.submitted_at_micros)?; - let gate = summarize_publish_gate_from_json(&input.session_id, RpgAgentStage::VisualRefining, Some(&draft_profile), &parse_json_array_or_empty(&session.quality_findings_json)); - let next_session = rebuild_custom_world_agent_session_row( - session, - CustomWorldAgentSessionPatch { - stage: Some(RpgAgentStage::VisualRefining), - focus_card_id: Some(Some(role_id.clone())), - draft_profile_json: Some(Some(serialize_json_value(&JsonValue::Object(draft_profile.clone()))?)), - last_assistant_reply: Some(Some(format!("已把「{}」的角色资产写回草稿,当前状态:{}。", role_name, asset_status_label))), - publish_gate_json: Some(Some(serialize_json_value(&publish_gate_to_json_value(&gate))?)), - result_preview_json: Some(build_result_preview_json(Some(&draft_profile), &gate, &parse_json_array_or_empty(&session.quality_findings_json), input.submitted_at_micros)?), - checkpoints_json: Some(append_checkpoint_json(&session.checkpoints_json, &build_session_checkpoint_value("sync-role-assets", &format!("同步角色资产 {}", role_name), session))?), - asset_coverage_json: Some(build_asset_coverage_json(&draft_profile)?), - updated_at_micros: Some(input.submitted_at_micros), - ..CustomWorldAgentSessionPatch::default() - }, - )?; - replace_custom_world_agent_session(ctx, session, next_session); - append_custom_world_action_result_message(ctx, &session.session_id, &input.operation_id, &format!("已把「{}」的角色资产写回草稿,当前状态:{}。", role_name, asset_status_label), input.submitted_at_micros); - let operation = build_and_insert_custom_world_operation(ctx, &input.operation_id, &session.session_id, RpgAgentOperationType::SyncRoleAssets, "角色资产已同步", &format!("「{}」的资产状态已更新为{}。", role_name, asset_status_label), input.submitted_at_micros); - Ok(build_custom_world_agent_operation_snapshot(&operation)) -} - -fn execute_sync_scene_assets_action( - ctx: &ReducerContext, - session: &CustomWorldAgentSession, - input: &CustomWorldAgentActionExecuteInput, - payload: &JsonMap, -) -> Result { - ensure_draft_refining_stage(session.stage, "sync_scene_assets")?; - let mut draft_profile = parse_optional_session_object(session.draft_profile_json.as_deref()) - .ok_or_else(|| "sync_scene_assets requires an existing draft foundation".to_string())?; - let scene_id = read_required_payload_text(payload, "sceneId", "sync_scene_assets requires sceneId")?; - let scene_kind = read_required_payload_text(payload, "sceneKind", "sync_scene_assets requires sceneKind")?; - let image_src = read_required_payload_text(payload, "imageSrc", "sync_scene_assets requires imageSrc")?; - let generated_scene_asset_id = read_required_payload_text(payload, "generatedSceneAssetId", "sync_scene_assets requires generatedSceneAssetId")?; - let updated_scene = apply_scene_asset_publish_result(&mut draft_profile, &scene_id, &scene_kind, &image_src, &generated_scene_asset_id, payload.get("generatedScenePrompt").cloned().unwrap_or(JsonValue::Null), payload.get("generatedSceneModel").cloned().unwrap_or(JsonValue::Null))?; - let scene_name = read_optional_text_field(&updated_scene, &["name"]).unwrap_or_else(|| if scene_kind == "camp" { "开局营地" } else { "当前场景" }.to_string()); - upsert_asset_scene_card(ctx, &session.session_id, &scene_id, &scene_kind, &updated_scene, input.submitted_at_micros)?; - let gate = summarize_publish_gate_from_json(&input.session_id, RpgAgentStage::VisualRefining, Some(&draft_profile), &parse_json_array_or_empty(&session.quality_findings_json)); - let next_session = rebuild_custom_world_agent_session_row( - session, - CustomWorldAgentSessionPatch { - stage: Some(RpgAgentStage::VisualRefining), - focus_card_id: Some(Some(scene_id.clone())), - draft_profile_json: Some(Some(serialize_json_value(&JsonValue::Object(draft_profile.clone()))?)), - last_assistant_reply: Some(Some(format!("已把「{}」的场景图写回草稿,并同步刷新地点卡与幕背景状态。", scene_name))), - publish_gate_json: Some(Some(serialize_json_value(&publish_gate_to_json_value(&gate))?)), - result_preview_json: Some(build_result_preview_json(Some(&draft_profile), &gate, &parse_json_array_or_empty(&session.quality_findings_json), input.submitted_at_micros)?), - checkpoints_json: Some(append_checkpoint_json(&session.checkpoints_json, &build_session_checkpoint_value("sync-scene-assets", &format!("同步场景资产 {}", scene_name), session))?), - asset_coverage_json: Some(build_asset_coverage_json(&draft_profile)?), - updated_at_micros: Some(input.submitted_at_micros), - ..CustomWorldAgentSessionPatch::default() - }, - )?; - replace_custom_world_agent_session(ctx, session, next_session); - append_custom_world_action_result_message(ctx, &session.session_id, &input.operation_id, &format!("已把「{}」的场景图写回草稿,并同步刷新地点卡与幕背景状态。", scene_name), input.submitted_at_micros); - let operation = build_and_insert_custom_world_operation(ctx, &input.operation_id, &session.session_id, RpgAgentOperationType::SyncSceneAssets, "场景资产已同步", &format!("「{}」的场景图已经进入当前草稿。", scene_name), input.submitted_at_micros); - Ok(build_custom_world_agent_operation_snapshot(&operation)) -} - - fn execute_placeholder_custom_world_action( ctx: &ReducerContext, session: &CustomWorldAgentSession, @@ -2659,6 +2409,7 @@ fn execute_placeholder_custom_world_action( #[derive(Clone, Debug, Default)] struct CustomWorldAgentSessionPatch { + current_turn: Option, progress_percent: Option, stage: Option, focus_card_id: Option>, @@ -2680,6 +2431,16 @@ struct CustomWorldAgentSessionPatch { updated_at_micros: Option, } +#[derive(Clone, Debug, Default)] +struct CustomWorldAgentOperationPatch { + status: Option, + phase_label: Option, + phase_detail: Option, + progress: Option, + error_message: Option>, + updated_at_micros: Option, +} + fn build_custom_world_publish_gate_from_session( session: &CustomWorldAgentSession, ) -> CustomWorldPublishGateSnapshot { @@ -3133,7 +2894,7 @@ fn rebuild_custom_world_agent_session_row( session_id: current.session_id.clone(), owner_user_id: current.owner_user_id.clone(), seed_text: current.seed_text.clone(), - current_turn: current.current_turn, + current_turn: patch.current_turn.unwrap_or(current.current_turn), progress_percent: patch.progress_percent.unwrap_or(current.progress_percent), stage: patch.stage.unwrap_or(current.stage), focus_card_id: patch @@ -3193,6 +2954,44 @@ fn rebuild_custom_world_agent_session_row( }) } +fn rebuild_custom_world_agent_operation_row( + current: &CustomWorldAgentOperation, + patch: CustomWorldAgentOperationPatch, +) -> Result { + let phase_label = patch + .phase_label + .unwrap_or_else(|| current.phase_label.clone()); + let progress = patch.progress.unwrap_or(current.progress); + validate_custom_world_agent_operation_fields( + ¤t.operation_id, + ¤t.session_id, + &phase_label, + progress, + ) + .map_err(|error| error.to_string())?; + + Ok(CustomWorldAgentOperation { + operation_id: current.operation_id.clone(), + session_id: current.session_id.clone(), + operation_type: current.operation_type, + status: patch.status.unwrap_or(current.status), + phase_label, + phase_detail: patch + .phase_detail + .unwrap_or_else(|| current.phase_detail.clone()), + progress, + error_message: patch + .error_message + .unwrap_or_else(|| current.error_message.clone()), + created_at: current.created_at, + updated_at: Timestamp::from_micros_since_unix_epoch( + patch + .updated_at_micros + .unwrap_or_else(|| current.updated_at.to_micros_since_unix_epoch()), + ), + }) +} + fn replace_custom_world_agent_session( ctx: &ReducerContext, current: &CustomWorldAgentSession, @@ -3205,6 +3004,18 @@ fn replace_custom_world_agent_session( ctx.db.custom_world_agent_session().insert(next); } +fn replace_custom_world_agent_operation( + ctx: &ReducerContext, + current: &CustomWorldAgentOperation, + next: CustomWorldAgentOperation, +) { + ctx.db + .custom_world_agent_operation() + .operation_id() + .delete(¤t.operation_id); + ctx.db.custom_world_agent_operation().insert(next); +} + fn replace_custom_world_draft_card( ctx: &ReducerContext, current: &CustomWorldDraftCard, @@ -3217,6 +3028,53 @@ fn replace_custom_world_draft_card( ctx.db.custom_world_draft_card().insert(next); } +fn complete_custom_world_operation( + ctx: &ReducerContext, + operation_id: &str, + session_id: &str, + operation_type: RpgAgentOperationType, + phase_label: &str, + phase_detail: &str, + timestamp_micros: i64, +) -> Result { + if let Some(current) = ctx + .db + .custom_world_agent_operation() + .operation_id() + .find(&operation_id.to_string()) + { + if current.session_id != session_id { + return Err("custom_world_agent_operation.session_id 不匹配".to_string()); + } + if current.operation_type != operation_type { + return Err("custom_world_agent_operation.operation_type 不匹配".to_string()); + } + let next = rebuild_custom_world_agent_operation_row( + ¤t, + CustomWorldAgentOperationPatch { + status: Some(RpgAgentOperationStatus::Completed), + phase_label: Some(phase_label.to_string()), + phase_detail: Some(phase_detail.to_string()), + progress: Some(100), + error_message: Some(None), + updated_at_micros: Some(timestamp_micros), + }, + )?; + replace_custom_world_agent_operation(ctx, ¤t, next.clone()); + return Ok(next); + } + + Ok(build_and_insert_custom_world_operation( + ctx, + operation_id, + session_id, + operation_type, + phase_label, + phase_detail, + timestamp_micros, + )) +} + fn build_and_insert_custom_world_operation( ctx: &ReducerContext, operation_id: &str, @@ -3266,7 +3124,13 @@ fn upsert_world_foundation_card( draft_profile: &JsonMap, updated_at_micros: i64, ) -> Result<(), String> { - let card_id = "world-foundation".to_string(); + let card_id = build_world_foundation_card_id(session_id); + let existing_card = ctx + .db + .custom_world_draft_card() + .card_id() + .find(&card_id) + .filter(|row| row.session_id == session_id); let title = read_optional_text_field(draft_profile, &["name", "title"]) .unwrap_or_else(|| "世界底稿".to_string()); let subtitle = read_optional_text_field(draft_profile, &["subtitle"]).unwrap_or_default(); @@ -3288,13 +3152,7 @@ fn upsert_world_foundation_card( "warningMessages": [], }))?; - if let Some(existing) = ctx - .db - .custom_world_draft_card() - .card_id() - .find(&card_id) - .filter(|row| row.session_id == session_id) - { + if let Some(existing) = existing_card { replace_custom_world_draft_card( ctx, &existing, @@ -3345,282 +3203,6 @@ fn upsert_world_foundation_card( Ok(()) } -fn upsert_generated_entity_card( - ctx: &ReducerContext, - session_id: &str, - kind: RpgAgentDraftCardKind, - entity: &JsonValue, - updated_at_micros: i64, -) -> Result<(), String> { - let entity_object = entity - .as_object() - .ok_or_else(|| "generated entity must be object".to_string())?; - let name = read_optional_text_field(entity_object, &["name"]) - .unwrap_or_else(|| "未命名对象".to_string()); - let card_id = build_generated_entity_card_id(kind, name.as_str(), 0); - let subtitle = match kind { - RpgAgentDraftCardKind::Character => { - read_optional_text_field(entity_object, &["role", "relationToPlayer", "publicMask"]) - .unwrap_or_else(|| "新角色".to_string()) - } - RpgAgentDraftCardKind::Landmark => { - read_optional_text_field(entity_object, &["purpose", "mood"]) - .unwrap_or_else(|| "新地点".to_string()) - } - _ => "新增对象".to_string(), - }; - let summary = read_optional_text_field( - entity_object, - &[ - "summary", - "description", - "publicMask", - "secret", - "hiddenHook", - ], - ) - .unwrap_or_else(|| "新增内容已写入世界草稿。".to_string()); - let linked_ids = entity_object - .get("threadIds") - .or_else(|| entity_object.get("characterIds")) - .cloned() - .unwrap_or_else(|| JsonValue::Array(Vec::new())); - let detail_payload_json = serialize_json_value(&json!({ - "id": card_id, - "kind": kind.as_str(), - "title": name, - "sections": build_generated_entity_detail_sections(entity_object, kind), - "linkedIds": linked_ids, - "locked": false, - "editable": true, - "editableSectionIds": ["summary"], - "warningMessages": [], - }))?; - - if let Some(existing) = ctx - .db - .custom_world_draft_card() - .card_id() - .find(&card_id) - .filter(|row| row.session_id == session_id) - { - replace_custom_world_draft_card( - ctx, - &existing, - CustomWorldDraftCard { - card_id: existing.card_id.clone(), - session_id: existing.session_id.clone(), - kind, - status: RpgAgentDraftCardStatus::Draft, - title: name, - subtitle, - summary, - linked_ids_json: serialize_json_value(&linked_ids)?, - warning_count: 0, - asset_status: None, - asset_status_label: None, - detail_payload_json: Some(detail_payload_json), - created_at: existing.created_at, - updated_at: Timestamp::from_micros_since_unix_epoch(updated_at_micros), - }, - ); - } else { - ctx.db - .custom_world_draft_card() - .insert(CustomWorldDraftCard { - card_id, - session_id: session_id.to_string(), - kind, - status: RpgAgentDraftCardStatus::Draft, - title: name, - subtitle, - summary, - linked_ids_json: serialize_json_value(&linked_ids)?, - warning_count: 0, - asset_status: None, - asset_status_label: None, - detail_payload_json: Some(detail_payload_json), - created_at: Timestamp::from_micros_since_unix_epoch(updated_at_micros), - updated_at: Timestamp::from_micros_since_unix_epoch(updated_at_micros), - }); - } - - Ok(()) -} - -fn build_generated_entity_detail_sections( - entity: &JsonMap, - kind: RpgAgentDraftCardKind, -) -> Vec { - let mut sections = vec![json!({ - "id": "summary", - "label": "摘要", - "value": read_optional_text_field(entity, &["summary", "description", "publicMask"]) - .unwrap_or_default(), - })]; - if kind == RpgAgentDraftCardKind::Character { - sections.push(json!({ - "id": "relationToPlayer", - "label": "玩家关系", - "value": read_optional_text_field(entity, &["relationToPlayer", "role"]).unwrap_or_default(), - })); - sections.push(json!({ - "id": "hiddenHook", - "label": "隐藏钩子", - "value": read_optional_text_field(entity, &["hiddenHook"]).unwrap_or_default(), - })); - } else if kind == RpgAgentDraftCardKind::Landmark { - sections.push(json!({ - "id": "purpose", - "label": "用途", - "value": read_optional_text_field(entity, &["purpose"]).unwrap_or_default(), - })); - sections.push(json!({ - "id": "secret", - "label": "秘密", - "value": read_optional_text_field(entity, &["secret"]).unwrap_or_default(), - })); - } - sections -} - -fn ensure_generated_entity_id( - mut entity: JsonValue, - kind: RpgAgentDraftCardKind, - index: usize, -) -> JsonValue { - if let Some(object) = entity.as_object_mut() { - let name = object - .get("name") - .and_then(JsonValue::as_str) - .map(str::trim) - .filter(|value| !value.is_empty()) - .unwrap_or("entry") - .to_string(); - object.entry("id".to_string()).or_insert_with(|| { - JsonValue::String(build_generated_entity_card_id(kind, &name, index)) - }); - } - entity -} - -fn build_generated_entity_card_id(kind: RpgAgentDraftCardKind, name: &str, index: usize) -> String { - let prefix = match kind { - RpgAgentDraftCardKind::Character => "character", - RpgAgentDraftCardKind::Landmark => "landmark", - _ => "card", - }; - let slug = name - .trim() - .to_lowercase() - .chars() - .map(|ch| { - if ch.is_ascii_alphanumeric() || ('\u{4e00}'..='\u{9fa5}').contains(&ch) { - ch - } else { - '-' - } - }) - .collect::() - .trim_matches('-') - .to_string(); - format!( - "{}-{}-{}", - prefix, - if slug.is_empty() { - "entry" - } else { - slug.as_str() - }, - index + 1 - ) -} - -fn read_payload_string_array(payload: &JsonMap, key: &str) -> Vec { - payload - .get(key) - .and_then(JsonValue::as_array) - .into_iter() - .flatten() - .filter_map(JsonValue::as_str) - .map(str::trim) - .filter(|value| !value.is_empty()) - .map(ToOwned::to_owned) - .collect() -} - -fn remove_profile_entities_by_ids( - draft_profile: &mut JsonMap, - key: &str, - ids: &[String], -) -> Result, String> { - let Some(value) = draft_profile.get_mut(key) else { - return Ok(Vec::new()); - }; - let entries = value - .as_array_mut() - .ok_or_else(|| format!("draftProfile.{key} must be array"))?; - let mut removed_names = Vec::new(); - entries.retain(|entry| { - let entry_id = entry.get("id").and_then(JsonValue::as_str).unwrap_or_default(); - let should_remove = ids.iter().any(|id| id == entry_id); - if should_remove { - if let Some(name) = entry.get("name").and_then(JsonValue::as_str) { - removed_names.push(name.to_string()); - } - } - !should_remove - }); - Ok(removed_names) -} - -fn remove_deleted_landmark_connections(draft_profile: &mut JsonMap, ids: &[String]) { - let Some(landmarks) = draft_profile - .get_mut("landmarks") - .and_then(JsonValue::as_array_mut) - else { - return; - }; - for landmark in landmarks { - if let Some(connections) = landmark - .get_mut("connections") - .and_then(JsonValue::as_array_mut) - { - connections.retain(|connection| { - let target_id = connection - .get("targetLandmarkId") - .and_then(JsonValue::as_str) - .unwrap_or_default(); - !ids.iter().any(|id| id == target_id) - }); - } - } -} - -fn delete_draft_card_by_entity_id(ctx: &ReducerContext, session_id: &str, entity_id: &str) { - let target = ctx - .db - .custom_world_draft_card() - .iter() - .find(|card| { - card.session_id == session_id - && (card.card_id == entity_id - || card - .detail_payload_json - .as_deref() - .and_then(|json_text| serde_json::from_str::(json_text).ok()) - .and_then(|value| value.get("id").and_then(JsonValue::as_str).map(str::to_string)) - .as_deref() - == Some(entity_id)) - }); - if let Some(card) = target { - ctx.db - .custom_world_draft_card() - .card_id() - .delete(&card.card_id); - } -} - fn build_world_foundation_card_id(session_id: &str) -> String { // `custom_world_draft_card.card_id` 是全局主键,世界底稿卡必须带上会话维度,避免多会话写入时触发唯一键冲突。 format!("custom-world:{session_id}:world-foundation") @@ -3790,130 +3372,6 @@ fn parse_json_array_or_empty(raw: &str) -> Vec { .unwrap_or_default() } -fn read_first_payload_text(payload: &JsonMap, array_key: &str, scalar_key: &str) -> Option { - payload.get(array_key).and_then(JsonValue::as_array).and_then(|values| values.first()).and_then(JsonValue::as_str) - .or_else(|| payload.get(scalar_key).and_then(JsonValue::as_str)) - .map(str::trim).filter(|value| !value.is_empty()).map(ToOwned::to_owned) -} - -fn find_profile_entity_by_id<'a>(profile: &'a JsonMap, fields: &[&str], entity_id: &str) -> Option<&'a JsonMap> { - for field in fields { - if let Some(entries) = profile.get(*field).and_then(JsonValue::as_array) { - for entry in entries { - let Some(object) = entry.as_object() else { continue; }; - if read_optional_text_field(object, &["id"]).as_deref() == Some(entity_id) { return Some(object); } - } - } - } - None -} - -fn apply_role_asset_publish_result(profile: &mut JsonMap, role_id: &str, portrait_path: &str, generated_visual_asset_id: &str, generated_animation_set_id: Option<&str>, animation_map: Option) -> Result, String> { - for field in ["playableNpcs", "storyNpcs"] { - let Some(entries) = profile.get_mut(field).and_then(JsonValue::as_array_mut) else { continue; }; - for entry in entries { - let Some(object) = entry.as_object_mut() else { continue; }; - if read_optional_text_field(object, &["id"]).as_deref() != Some(role_id) { continue; } - object.insert("imageSrc".to_string(), JsonValue::String(portrait_path.to_string())); - object.insert("generatedVisualAssetId".to_string(), JsonValue::String(generated_visual_asset_id.to_string())); - if let Some(asset_id) = generated_animation_set_id { object.insert("generatedAnimationSetId".to_string(), JsonValue::String(asset_id.to_string())); } - if let Some(map) = animation_map { object.insert("animationMap".to_string(), map); } - return Ok(object.clone()); - } - } - Err("目标角色不存在,无法同步角色资产。".to_string()) -} - -fn apply_scene_asset_publish_result(profile: &mut JsonMap, scene_id: &str, scene_kind: &str, image_src: &str, generated_scene_asset_id: &str, generated_scene_prompt: JsonValue, generated_scene_model: JsonValue) -> Result, String> { - let updated_scene = if scene_kind == "camp" { - let camp = profile.get_mut("camp").and_then(JsonValue::as_object_mut).ok_or_else(|| "目标营地不存在,无法同步场景资产。".to_string())?; - if read_optional_text_field(camp, &["id"]).as_deref() != Some(scene_id) { return Err("目标营地不存在,无法同步场景资产。".to_string()); } - camp.insert("imageSrc".to_string(), JsonValue::String(image_src.to_string())); - camp.insert("generatedSceneAssetId".to_string(), JsonValue::String(generated_scene_asset_id.to_string())); - camp.insert("generatedScenePrompt".to_string(), generated_scene_prompt); - camp.insert("generatedSceneModel".to_string(), generated_scene_model); - camp.clone() - } else { - let landmarks = profile.get_mut("landmarks").and_then(JsonValue::as_array_mut).ok_or_else(|| "目标地点不存在,无法同步场景资产。".to_string())?; - let mut updated = None; - for entry in landmarks { - let Some(object) = entry.as_object_mut() else { continue; }; - if read_optional_text_field(object, &["id"]).as_deref() != Some(scene_id) { continue; } - object.insert("imageSrc".to_string(), JsonValue::String(image_src.to_string())); - object.insert("generatedSceneAssetId".to_string(), JsonValue::String(generated_scene_asset_id.to_string())); - object.insert("generatedScenePrompt".to_string(), generated_scene_prompt.clone()); - object.insert("generatedSceneModel".to_string(), generated_scene_model.clone()); - updated = Some(object.clone()); - break; - } - updated.ok_or_else(|| "目标地点不存在,无法同步场景资产。".to_string())? - }; - update_scene_chapter_acts_for_scene(profile, scene_id, image_src, generated_scene_asset_id); - Ok(updated_scene) -} - -fn update_scene_chapter_acts_for_scene(profile: &mut JsonMap, scene_id: &str, image_src: &str, generated_scene_asset_id: &str) { - // 中文注释:当前结果页与发布链路以 sceneChapterBlueprints 为主,旧 sceneChapters 仅作兼容;同步场景资产时两边都要写,避免开局场景幕图只落在旧字段。 - for field in ["sceneChapterBlueprints", "sceneChapters"] { - let Some(chapters) = profile.get_mut(field).and_then(JsonValue::as_array_mut) else { continue; }; - for chapter in chapters { - let Some(chapter_object) = chapter.as_object_mut() else { continue; }; - let is_target_scene = read_optional_text_field(chapter_object, &["sceneId"]).as_deref() == Some(scene_id) - || chapter_object - .get("linkedLandmarkIds") - .and_then(JsonValue::as_array) - .map(|ids| ids.iter().any(|id| id.as_str().map(str::trim) == Some(scene_id))) - .unwrap_or(false); - if !is_target_scene { continue; } - let Some(acts) = chapter_object.get_mut("acts").and_then(JsonValue::as_array_mut) else { continue; }; - for act in acts { - if let Some(act_object) = act.as_object_mut() { - act_object.insert("backgroundImageSrc".to_string(), JsonValue::String(image_src.to_string())); - act_object.insert("backgroundAssetId".to_string(), JsonValue::String(generated_scene_asset_id.to_string())); - } - } - } - } -} - -fn resolve_role_asset_status(role: &JsonMap) -> CustomWorldRoleAssetStatus { - let has_portrait = read_optional_text_field(role, &["imageSrc"]).is_some() && read_optional_text_field(role, &["generatedVisualAssetId"]).is_some(); - if !has_portrait { return CustomWorldRoleAssetStatus::Missing; } - let has_animation_set = read_optional_text_field(role, &["generatedAnimationSetId"]).is_some(); - let has_animation_map = role.get("animationMap").and_then(JsonValue::as_object).map(|map| !map.is_empty()).unwrap_or(false); - if has_animation_set && has_animation_map { CustomWorldRoleAssetStatus::Complete } else if has_animation_set { CustomWorldRoleAssetStatus::AnimationsReady } else { CustomWorldRoleAssetStatus::VisualReady } -} - -fn resolve_role_asset_status_label(status: CustomWorldRoleAssetStatus) -> &'static str { - match status { CustomWorldRoleAssetStatus::Complete => "动作已就绪", CustomWorldRoleAssetStatus::AnimationsReady => "动作补齐中", CustomWorldRoleAssetStatus::VisualReady => "主图已就绪", CustomWorldRoleAssetStatus::Missing => "待生成主图" } -} - -fn build_asset_coverage_json(profile: &JsonMap) -> Result { - serialize_json_value(&json!({"roleAssets": [], "sceneAssets": [], "allRoleAssetsReady": false, "allSceneAssetsReady": false, "profileId": read_optional_text_field(profile, &["id"])})) -} - -fn upsert_asset_role_card(ctx: &ReducerContext, session_id: &str, role_id: &str, role: &JsonMap, asset_status: CustomWorldRoleAssetStatus, asset_status_label: &str, updated_at_micros: i64) -> Result<(), String> { - let title = read_optional_text_field(role, &["name"]).unwrap_or_else(|| "角色".to_string()); - let subtitle = read_optional_text_field(role, &["role", "relationToPlayer", "publicMask"]).unwrap_or_else(|| asset_status_label.to_string()); - let summary = read_optional_text_field(role, &["summary", "description", "publicMask"]).unwrap_or_else(|| "角色资产已写回草稿。".to_string()); - upsert_asset_card(ctx, session_id, role_id, RpgAgentDraftCardKind::Character, &title, &subtitle, &summary, Some(asset_status), Some(asset_status_label), updated_at_micros) -} - -fn upsert_asset_scene_card(ctx: &ReducerContext, session_id: &str, scene_id: &str, scene_kind: &str, scene: &JsonMap, updated_at_micros: i64) -> Result<(), String> { - let kind = if scene_kind == "camp" { RpgAgentDraftCardKind::Camp } else { RpgAgentDraftCardKind::Landmark }; - let title = read_optional_text_field(scene, &["name"]).unwrap_or_else(|| if scene_kind == "camp" { "开局营地" } else { "场景" }.to_string()); - let subtitle = read_optional_text_field(scene, &["purpose", "mood"]).unwrap_or_else(|| "场景资产已就绪".to_string()); - let summary = read_optional_text_field(scene, &["summary", "description", "publicMask"]).unwrap_or_else(|| "场景图已写回草稿。".to_string()); - upsert_asset_card(ctx, session_id, scene_id, kind, &title, &subtitle, &summary, None, Some("场景图已就绪"), updated_at_micros) -} - -fn upsert_asset_card(ctx: &ReducerContext, session_id: &str, card_id: &str, kind: RpgAgentDraftCardKind, title: &str, subtitle: &str, summary: &str, asset_status: Option, asset_status_label: Option<&str>, updated_at_micros: i64) -> Result<(), String> { - let detail_payload = json!({"id": card_id, "kind": kind.as_str(), "title": title, "sections": [{"id": "summary", "label": "摘要", "value": summary}], "linkedIds": [], "locked": false, "editable": true, "editableSectionIds": ["summary"], "warningMessages": []}); - let next = CustomWorldDraftCard { card_id: card_id.to_string(), session_id: session_id.to_string(), kind, status: RpgAgentDraftCardStatus::Draft, title: title.to_string(), subtitle: subtitle.to_string(), summary: summary.to_string(), linked_ids_json: "[]".to_string(), warning_count: 0, asset_status, asset_status_label: asset_status_label.map(ToOwned::to_owned), detail_payload_json: Some(serialize_json_value(&detail_payload)?), created_at: Timestamp::from_micros_since_unix_epoch(updated_at_micros), updated_at: Timestamp::from_micros_since_unix_epoch(updated_at_micros) }; - if let Some(existing) = ctx.db.custom_world_draft_card().card_id().find(&card_id.to_string()).filter(|entry| entry.session_id == session_id) { replace_custom_world_draft_card(ctx, &existing, CustomWorldDraftCard { created_at: existing.created_at, ..next }); } else { ctx.db.custom_world_draft_card().insert(next); } - Ok(()) -} - fn serialize_json_value(value: &JsonValue) -> Result { serde_json::to_string(value).map_err(|error| format!("JSON 序列化失败: {error}")) } @@ -4250,6 +3708,12 @@ fn sync_custom_world_gallery_entry_from_profile( let row = CustomWorldGalleryEntry { profile_id: profile.profile_id.clone(), owner_user_id: profile.owner_user_id.clone(), + public_work_code: profile.public_work_code.clone().ok_or_else(|| { + "published profile 缺少 public_work_code,无法同步 gallery".to_string() + })?, + author_public_user_code: profile.author_public_user_code.clone().ok_or_else(|| { + "published profile 缺少 author_public_user_code,无法同步 gallery".to_string() + })?, author_display_name: profile.author_display_name.clone(), world_name: profile.world_name.clone(), subtitle: profile.subtitle.clone(), @@ -4267,10 +3731,118 @@ fn sync_custom_world_gallery_entry_from_profile( Ok(build_custom_world_gallery_entry_snapshot(&inserted)) } +fn sync_missing_custom_world_gallery_entries(ctx: &ReducerContext) -> Result<(), String> { + let published_profiles = ctx + .db + .custom_world_profile() + .iter() + .filter(|profile| { + profile.publication_status == CustomWorldPublicationStatus::Published + && profile.deleted_at.is_none() + }) + .collect::>(); + + for profile in published_profiles { + if profile.published_at.is_none() { + continue; + } + + let existing_gallery_entry = ctx + .db + .custom_world_gallery_entry() + .profile_id() + .find(&profile.profile_id) + .filter(|entry| entry.owner_user_id == profile.owner_user_id); + + if existing_gallery_entry.is_some() + && profile.public_work_code.is_some() + && profile.author_public_user_code.is_some() + { + continue; + } + + let profile_with_public_fields = ensure_custom_world_profile_public_fields(ctx, &profile); + sync_custom_world_gallery_entry_from_profile(ctx, &profile_with_public_fields)?; + } + + Ok(()) +} + +fn ensure_custom_world_profile_public_fields( + ctx: &ReducerContext, + profile: &CustomWorldProfile, +) -> CustomWorldProfile { + if profile.public_work_code.is_some() && profile.author_public_user_code.is_some() { + return build_custom_world_profile_row_copy(profile); + } + + ctx.db + .custom_world_profile() + .profile_id() + .delete(&profile.profile_id); + + let next_row = CustomWorldProfile { + profile_id: profile.profile_id.clone(), + owner_user_id: profile.owner_user_id.clone(), + public_work_code: profile + .public_work_code + .clone() + .or_else(|| Some(build_public_work_code_from_profile_id(&profile.profile_id))), + author_public_user_code: profile.author_public_user_code.clone().or_else(|| { + Some(build_public_user_code_from_owner_user_id( + &profile.owner_user_id, + )) + }), + source_agent_session_id: profile.source_agent_session_id.clone(), + publication_status: profile.publication_status, + world_name: profile.world_name.clone(), + subtitle: profile.subtitle.clone(), + summary_text: profile.summary_text.clone(), + theme_mode: profile.theme_mode, + cover_image_src: profile.cover_image_src.clone(), + profile_payload_json: profile.profile_payload_json.clone(), + playable_npc_count: profile.playable_npc_count, + landmark_count: profile.landmark_count, + author_display_name: profile.author_display_name.clone(), + published_at: profile.published_at, + deleted_at: profile.deleted_at, + created_at: profile.created_at, + updated_at: profile.updated_at, + }; + + ctx.db.custom_world_profile().insert(next_row) +} + +fn build_custom_world_profile_row_copy(profile: &CustomWorldProfile) -> CustomWorldProfile { + CustomWorldProfile { + profile_id: profile.profile_id.clone(), + owner_user_id: profile.owner_user_id.clone(), + public_work_code: profile.public_work_code.clone(), + author_public_user_code: profile.author_public_user_code.clone(), + source_agent_session_id: profile.source_agent_session_id.clone(), + publication_status: profile.publication_status, + world_name: profile.world_name.clone(), + subtitle: profile.subtitle.clone(), + summary_text: profile.summary_text.clone(), + theme_mode: profile.theme_mode, + cover_image_src: profile.cover_image_src.clone(), + profile_payload_json: profile.profile_payload_json.clone(), + playable_npc_count: profile.playable_npc_count, + landmark_count: profile.landmark_count, + author_display_name: profile.author_display_name.clone(), + published_at: profile.published_at, + deleted_at: profile.deleted_at, + created_at: profile.created_at, + updated_at: profile.updated_at, + } +} + fn build_custom_world_profile_snapshot(row: &CustomWorldProfile) -> CustomWorldProfileSnapshot { CustomWorldProfileSnapshot { profile_id: row.profile_id.clone(), owner_user_id: row.owner_user_id.clone(), + public_work_code: row.public_work_code.clone(), + author_public_user_code: row.author_public_user_code.clone(), source_agent_session_id: row.source_agent_session_id.clone(), publication_status: row.publication_status, world_name: row.world_name.clone(), @@ -4423,6 +3995,8 @@ fn build_custom_world_gallery_entry_snapshot( CustomWorldGalleryEntrySnapshot { profile_id: row.profile_id.clone(), owner_user_id: row.owner_user_id.clone(), + public_work_code: row.public_work_code.clone(), + author_public_user_code: row.author_public_user_code.clone(), author_display_name: row.author_display_name.clone(), world_name: row.world_name.clone(), subtitle: row.subtitle.clone(), @@ -4435,3 +4009,408 @@ fn build_custom_world_gallery_entry_snapshot( updated_at_micros: row.updated_at.to_micros_since_unix_epoch(), } } + +// 作品公开号保持稳定公开语义,本期先由 profile_id 派生 deterministic fallback, +// 后续若引入独立 sequence 表,可无痛替换生成来源而不影响读写接口。 +fn build_public_work_code_from_profile_id(profile_id: &str) -> String { + let digits = profile_id + .chars() + .filter(|character| character.is_ascii_digit()) + .collect::(); + let normalized_digits = if digits.is_empty() { + let checksum = profile_id.bytes().fold(0u32, |accumulator, value| { + accumulator.wrapping_mul(131) + u32::from(value) + }); + format!("{:08}", checksum % 100_000_000) + } else { + format!("{:0>8}", &digits[digits.len().saturating_sub(8)..]) + }; + + format!("CW-{normalized_digits}") +} + +fn build_public_user_code_from_owner_user_id(owner_user_id: &str) -> String { + owner_user_id + .trim_start_matches("user_") + .parse::() + .ok() + .map(|sequence| format!("SY-{sequence:08}")) + .unwrap_or_else(|| "SY-00000000".to_string()) +} + +fn normalize_public_work_code(input: &str) -> Option { + let normalized = input + .trim() + .chars() + .filter(|character| character.is_ascii_alphanumeric()) + .collect::() + .to_ascii_uppercase(); + let digits = normalized.strip_prefix("CW").unwrap_or(&normalized); + if digits.is_empty() + || digits.len() > 8 + || !digits.chars().all(|character| character.is_ascii_digit()) + { + return None; + } + + Some(format!("CW-{digits:0>8}")) +} + +#[cfg(test)] +mod tests { + use super::*; + + fn build_test_custom_world_agent_session( + seed_text: &str, + stage: RpgAgentStage, + draft_profile_json: Option<&str>, + ) -> CustomWorldAgentSession { + CustomWorldAgentSession { + session_id: "session-1".to_string(), + owner_user_id: "user-1".to_string(), + seed_text: seed_text.to_string(), + current_turn: 0, + progress_percent: 0, + stage, + focus_card_id: None, + anchor_content_json: "{}".to_string(), + creator_intent_json: None, + creator_intent_readiness_json: "{}".to_string(), + anchor_pack_json: None, + lock_state_json: None, + draft_profile_json: draft_profile_json.map(str::to_string), + last_assistant_reply: None, + publish_gate_json: None, + result_preview_json: None, + pending_clarifications_json: "[]".to_string(), + quality_findings_json: "[]".to_string(), + suggested_actions_json: "[]".to_string(), + recommended_replies_json: "[]".to_string(), + asset_coverage_json: "{}".to_string(), + checkpoints_json: "[]".to_string(), + created_at: Timestamp::from_micros_since_unix_epoch(1), + updated_at: Timestamp::from_micros_since_unix_epoch(1), + } + } + + #[test] + fn resolve_stable_agent_draft_profile_id_prefers_legacy_result_profile_id() { + let session = build_test_custom_world_agent_session( + "seed", + RpgAgentStage::ObjectRefining, + Some(r#"{"id":"drifted-profile","legacyResultProfile":{"id":"stable-profile"}}"#), + ); + + assert_eq!( + resolve_stable_agent_draft_profile_id(&session), + Some("stable-profile".to_string()) + ); + } + + #[test] + fn custom_world_agent_session_direct_work_content_ignores_empty_created_session() { + let empty_session = + build_test_custom_world_agent_session("", RpgAgentStage::CollectingIntent, Some("{}")); + let seeded_session = build_test_custom_world_agent_session( + "想做一个海雾群岛", + RpgAgentStage::CollectingIntent, + Some("{}"), + ); + let drafted_session = + build_test_custom_world_agent_session("", RpgAgentStage::ObjectRefining, Some("{}")); + let profile_session = build_test_custom_world_agent_session( + "", + RpgAgentStage::CollectingIntent, + Some(r#"{"worldHook":"海雾会吞掉记错航线的人。"}"#), + ); + + assert!(!custom_world_agent_session_has_direct_work_content( + &empty_session, + )); + assert!(custom_world_agent_session_has_direct_work_content( + &seeded_session, + )); + assert!(custom_world_agent_session_has_direct_work_content( + &drafted_session, + )); + assert!(custom_world_agent_session_has_direct_work_content( + &profile_session, + )); + } + + #[test] + fn same_agent_draft_profile_candidate_requires_same_owner_active_draft_and_session() { + let matching = CustomWorldProfile { + profile_id: "profile-1".to_string(), + owner_user_id: "user-1".to_string(), + public_work_code: None, + author_public_user_code: None, + source_agent_session_id: Some("session-1".to_string()), + publication_status: CustomWorldPublicationStatus::Draft, + world_name: "潮雾列岛".to_string(), + subtitle: String::new(), + summary_text: String::new(), + theme_mode: CustomWorldThemeMode::Mythic, + cover_image_src: None, + profile_payload_json: "{}".to_string(), + playable_npc_count: 0, + landmark_count: 0, + author_display_name: "玩家".to_string(), + published_at: None, + deleted_at: None, + created_at: Timestamp::from_micros_since_unix_epoch(1), + updated_at: Timestamp::from_micros_since_unix_epoch(1), + }; + let deleted = CustomWorldProfile { + profile_id: "profile-1".to_string(), + owner_user_id: "user-1".to_string(), + public_work_code: None, + author_public_user_code: None, + source_agent_session_id: Some("session-1".to_string()), + publication_status: CustomWorldPublicationStatus::Draft, + world_name: "潮雾列岛".to_string(), + subtitle: String::new(), + summary_text: String::new(), + theme_mode: CustomWorldThemeMode::Mythic, + cover_image_src: None, + profile_payload_json: "{}".to_string(), + playable_npc_count: 0, + landmark_count: 0, + author_display_name: "玩家".to_string(), + published_at: None, + deleted_at: Some(Timestamp::from_micros_since_unix_epoch(2)), + created_at: Timestamp::from_micros_since_unix_epoch(1), + updated_at: Timestamp::from_micros_since_unix_epoch(1), + }; + let published = CustomWorldProfile { + profile_id: "profile-1".to_string(), + owner_user_id: "user-1".to_string(), + public_work_code: Some("CW-00000001".to_string()), + author_public_user_code: Some("SY-00000001".to_string()), + source_agent_session_id: Some("session-1".to_string()), + publication_status: CustomWorldPublicationStatus::Published, + world_name: "潮雾列岛".to_string(), + subtitle: String::new(), + summary_text: String::new(), + theme_mode: CustomWorldThemeMode::Mythic, + cover_image_src: None, + profile_payload_json: "{}".to_string(), + playable_npc_count: 0, + landmark_count: 0, + author_display_name: "玩家".to_string(), + published_at: None, + deleted_at: None, + created_at: Timestamp::from_micros_since_unix_epoch(1), + updated_at: Timestamp::from_micros_since_unix_epoch(1), + }; + + assert!(is_same_agent_draft_profile_candidate( + &matching, + "user-1", + "session-1", + )); + assert!(!is_same_agent_draft_profile_candidate( + &matching, + "user-2", + "session-1", + )); + assert!(!is_same_agent_draft_profile_candidate( + &matching, + "user-1", + "session-2", + )); + assert!(!is_same_agent_draft_profile_candidate( + &deleted, + "user-1", + "session-1", + )); + assert!(!is_same_agent_draft_profile_candidate( + &published, + "user-1", + "session-1", + )); + } + + #[test] + fn custom_world_works_hides_compiled_draft_profile_when_agent_session_is_active() { + fn build_test_custom_world_profile( + profile_id: &str, + source_agent_session_id: Option<&str>, + publication_status: CustomWorldPublicationStatus, + ) -> CustomWorldProfile { + CustomWorldProfile { + profile_id: profile_id.to_string(), + owner_user_id: "user-1".to_string(), + public_work_code: if publication_status == CustomWorldPublicationStatus::Published { + Some("CW-00000001".to_string()) + } else { + None + }, + author_public_user_code: None, + source_agent_session_id: source_agent_session_id.map(str::to_string), + publication_status, + world_name: "潮雾列岛".to_string(), + subtitle: String::new(), + summary_text: String::new(), + theme_mode: CustomWorldThemeMode::Mythic, + cover_image_src: None, + profile_payload_json: "{}".to_string(), + playable_npc_count: 0, + landmark_count: 0, + author_display_name: "玩家".to_string(), + published_at: if publication_status == CustomWorldPublicationStatus::Published { + Some(Timestamp::from_micros_since_unix_epoch(2)) + } else { + None + }, + deleted_at: None, + created_at: Timestamp::from_micros_since_unix_epoch(1), + updated_at: Timestamp::from_micros_since_unix_epoch(1), + } + } + + let draft_profile = build_test_custom_world_profile( + "profile-1", + Some("session-1"), + CustomWorldPublicationStatus::Draft, + ); + let orphan_draft_profile = build_test_custom_world_profile( + "profile-2", + Some("session-2"), + CustomWorldPublicationStatus::Draft, + ); + let published_profile = build_test_custom_world_profile( + "profile-3", + Some("session-1"), + CustomWorldPublicationStatus::Published, + ); + let mut active_agent_session_ids = HashSet::new(); + active_agent_session_ids.insert("session-1".to_string()); + + assert!(!should_include_custom_world_profile_work( + &draft_profile, + &active_agent_session_ids, + )); + assert!(should_include_custom_world_profile_work( + &orphan_draft_profile, + &active_agent_session_ids, + )); + assert!(should_include_custom_world_profile_work( + &published_profile, + &active_agent_session_ids, + )); + } + + #[test] + fn custom_world_works_keeps_compiled_draft_profile_without_active_agent_session() { + let draft_profile = CustomWorldProfile { + profile_id: "profile-1".to_string(), + owner_user_id: "user-1".to_string(), + public_work_code: None, + author_public_user_code: None, + source_agent_session_id: Some("session-1".to_string()), + publication_status: CustomWorldPublicationStatus::Draft, + world_name: "潮雾列岛".to_string(), + subtitle: String::new(), + summary_text: String::new(), + theme_mode: CustomWorldThemeMode::Mythic, + cover_image_src: None, + profile_payload_json: "{}".to_string(), + playable_npc_count: 0, + landmark_count: 0, + author_display_name: "玩家".to_string(), + published_at: None, + deleted_at: None, + created_at: Timestamp::from_micros_since_unix_epoch(1), + updated_at: Timestamp::from_micros_since_unix_epoch(1), + }; + let mut active_agent_session_ids = HashSet::new(); + + assert!(should_include_custom_world_profile_work( + &draft_profile, + &active_agent_session_ids, + )); + + active_agent_session_ids.insert("session-2".to_string()); + assert!(should_include_custom_world_profile_work( + &draft_profile, + &active_agent_session_ids, + )); + } + + #[test] + fn summarize_publish_gate_accepts_current_agent_result_schema() { + let draft_profile = serde_json::from_str::( + r#"{ + "id":"agent-draft-session-1", + "settingText":"海雾会吞掉记错航线的人。", + "creatorIntent":{"playerPremise":"玩家是带着旧航海日志返乡的守灯人。"}, + "anchorContent":{ + "worldPromise":{"hook":"在失真的海图上追查一场被篡改的沉船事故。"}, + "playerEntryPoint":{ + "openingIdentity":"被停职返乡的守灯人", + "openingProblem":"灯塔记录被人改写", + "entryMotivation":"查清父亲沉船真相" + } + }, + "coreConflicts":["群岛议会试图掩盖沉船真相。"], + "sceneChapterBlueprints":[ + { + "id":"scene-chapter-1", + "sceneId":"landmark-1", + "title":"失灯港", + "acts":[ + { + "id":"act-1", + "title":"第一幕" + } + ] + } + ] + }"#, + ) + .expect("draft profile should be valid json") + .as_object() + .cloned() + .expect("draft profile should be object"); + + let gate = summarize_publish_gate_from_json( + "session-1", + RpgAgentStage::ReadyToPublish, + Some(&draft_profile), + &[], + ); + + assert!(gate.publish_ready); + assert_eq!(gate.blocker_count, 0); + assert!(gate.blockers.is_empty()); + } + + #[test] + fn ensure_minimal_draft_profile_includes_scene_chapter_blueprints_slot() { + let profile = ensure_minimal_draft_profile(JsonMap::new(), "旧航路群岛"); + + assert_eq!( + profile.get("sceneChapterBlueprints"), + Some(&JsonValue::Array(Vec::new())) + ); + } + + #[test] + fn draft_foundation_payload_must_contain_external_draft_profile() { + let payload = JsonMap::new(); + + let result = payload + .get("draftProfile") + .and_then(JsonValue::as_object) + .cloned() + .ok_or_else(|| { + "draft_foundation requires externally generated payload.draftProfile".to_string() + }); + + assert_eq!( + result.expect_err("missing draftProfile should be rejected"), + "draft_foundation requires externally generated payload.draftProfile" + ); + } +} diff --git a/server-rs/crates/spacetime-module/src/gameplay/mod.rs b/server-rs/crates/spacetime-module/src/gameplay/mod.rs index dfee8a93..fc248815 100644 --- a/server-rs/crates/spacetime-module/src/gameplay/mod.rs +++ b/server-rs/crates/spacetime-module/src/gameplay/mod.rs @@ -1,3 +1,12 @@ +use crate::*; +use module_combat::resolve_combat_action as resolve_battle_state_action; +use module_inventory::apply_inventory_mutation as apply_inventory_slot_mutation; +use module_npc::resolve_npc_interaction as resolve_npc_interaction_domain; +use module_quest::{ + acknowledge_quest_completion as acknowledge_quest_record_completion, + apply_quest_signal as apply_quest_record_signal, +}; + #[spacetimedb::table(accessor = player_progression)] pub struct PlayerProgression { #[primary_key] @@ -874,6 +883,79 @@ pub fn get_story_session_state( } } +fn continue_story_tx( + ctx: &ReducerContext, + input: StoryContinueInput, +) -> Result<(StorySessionSnapshot, StoryEventSnapshot), String> { + validate_story_continue_input(&input).map_err(|error| error.to_string())?; + + let current = ctx + .db + .story_session() + .story_session_id() + .find(&input.story_session_id) + .ok_or_else(|| "story_session 不存在,无法继续推进".to_string())?; + + let current_snapshot = build_story_session_snapshot_from_row(¤t); + let (next_snapshot, event_snapshot) = + apply_story_continue(current_snapshot, input).map_err(|error| error.to_string())?; + + ctx.db + .story_session() + .story_session_id() + .delete(¤t.story_session_id); + ctx.db.story_session().insert(StorySession { + story_session_id: next_snapshot.story_session_id.clone(), + runtime_session_id: next_snapshot.runtime_session_id.clone(), + actor_user_id: next_snapshot.actor_user_id.clone(), + world_profile_id: next_snapshot.world_profile_id.clone(), + initial_prompt: next_snapshot.initial_prompt.clone(), + opening_summary: next_snapshot.opening_summary.clone(), + latest_narrative_text: next_snapshot.latest_narrative_text.clone(), + latest_choice_function_id: next_snapshot.latest_choice_function_id.clone(), + status: next_snapshot.status, + version: next_snapshot.version, + created_at: Timestamp::from_micros_since_unix_epoch(next_snapshot.created_at_micros), + updated_at: Timestamp::from_micros_since_unix_epoch(next_snapshot.updated_at_micros), + }); + + ctx.db.story_event().insert(StoryEvent { + event_id: event_snapshot.event_id.clone(), + story_session_id: event_snapshot.story_session_id.clone(), + event_kind: event_snapshot.event_kind, + narrative_text: event_snapshot.narrative_text.clone(), + choice_function_id: event_snapshot.choice_function_id.clone(), + created_at: Timestamp::from_micros_since_unix_epoch(event_snapshot.created_at_micros), + }); + + Ok((next_snapshot, event_snapshot)) +} + +fn get_story_session_state_tx( + ctx: &ReducerContext, + input: StorySessionStateInput, +) -> Result<(StorySessionSnapshot, Vec), String> { + validate_story_session_state_input(&input).map_err(|error| error.to_string())?; + + let session = ctx + .db + .story_session() + .story_session_id() + .find(&input.story_session_id) + .ok_or_else(|| "story_session 不存在".to_string())?; + + let session_snapshot = build_story_session_snapshot_from_row(&session); + let mut events = ctx + .db + .story_event() + .iter() + .filter(|row| row.story_session_id == input.story_session_id) + .map(|row| build_story_event_snapshot_from_row(&row)) + .collect::>(); + events.sort_by_key(|event| (event.created_at_micros, event.event_id.clone())); + + Ok((session_snapshot, events)) +} // 当前阶段先把 quest_record / quest_log 立成最小任务真相源,后续再把奖励结算和 story action 总分发接进来。 #[spacetimedb::reducer] diff --git a/server-rs/crates/spacetime-module/src/lib.rs b/server-rs/crates/spacetime-module/src/lib.rs index 8a5856bd..898a7f0c 100644 --- a/server-rs/crates/spacetime-module/src/lib.rs +++ b/server-rs/crates/spacetime-module/src/lib.rs @@ -15,26 +15,19 @@ pub use module_runtime::*; pub use module_runtime_item::*; pub use module_story::*; -use module_combat::resolve_combat_action as resolve_battle_state_action; -use module_inventory::apply_inventory_mutation as apply_inventory_slot_mutation; -use module_npc::resolve_npc_interaction as resolve_npc_interaction_domain; -use module_quest::{ - acknowledge_quest_completion as acknowledge_quest_record_completion, - apply_quest_signal as apply_quest_record_signal, -}; pub(crate) use serde_json::{Map as JsonMap, Value as JsonValue, json}; pub(crate) use shared_kernel::format_timestamp_micros; pub use spacetimedb::{ Identity, ProcedureContext, ReducerContext, SpacetimeType, Table, Timestamp, }; -use std::collections::HashSet; - mod ai; mod asset_metadata; mod auth; mod big_fish; +mod custom_world; mod domain_types; mod entry; +mod gameplay; mod migration; mod puzzle; mod runtime; @@ -43,6628 +36,9 @@ pub use ai::*; pub use asset_metadata::*; pub use auth::*; pub use big_fish::*; +pub use custom_world::*; pub use domain_types::*; pub use entry::*; +pub use gameplay::*; pub use migration::*; pub use runtime::*; - -#[spacetimedb::table(accessor = player_progression)] -pub struct PlayerProgression { - #[primary_key] - user_id: String, - level: u32, - current_level_xp: u32, - total_xp: u32, - xp_to_next_level: u32, - pending_level_ups: u32, - last_granted_source: Option, - created_at: Timestamp, - updated_at: Timestamp, -} - -#[spacetimedb::table( - accessor = chapter_progression, - index(accessor = by_chapter_progression_user_id, btree(columns = [user_id])), - index(accessor = by_chapter_progression_chapter_id, btree(columns = [chapter_id])), - index(accessor = by_chapter_progression_user_chapter, btree(columns = [user_id, chapter_id])) -)] -pub struct ChapterProgression { - #[primary_key] - chapter_progression_id: String, - user_id: String, - chapter_id: String, - chapter_index: u32, - total_chapters: u32, - entry_pseudo_level_millis: u32, - exit_pseudo_level_millis: u32, - entry_level: u32, - exit_level: u32, - planned_total_xp: u32, - planned_quest_xp: u32, - planned_hostile_xp: u32, - actual_quest_xp: u32, - actual_hostile_xp: u32, - expected_hostile_defeat_count: u32, - actual_hostile_defeat_count: u32, - level_at_entry: u32, - level_at_exit: Option, - pace_band: ChapterPaceBand, - created_at: Timestamp, - updated_at: Timestamp, -} - -#[spacetimedb::table( - accessor = npc_state, - index(accessor = by_runtime_session_id, btree(columns = [runtime_session_id])), - index(accessor = by_npc_id, btree(columns = [npc_id])), - index(accessor = by_runtime_session_npc, btree(columns = [runtime_session_id, npc_id])) -)] -pub struct NpcState { - #[primary_key] - npc_state_id: String, - runtime_session_id: String, - npc_id: String, - npc_name: String, - affinity: i32, - relation_state: NpcRelationState, - help_used: bool, - chatted_count: u32, - gifts_given: u32, - recruited: bool, - trade_stock_signature: Option, - revealed_facts: Vec, - known_attribute_rumors: Vec, - first_meaningful_contact_resolved: bool, - seen_backstory_chapter_ids: Vec, - stance_profile: NpcStanceProfile, - created_at: Timestamp, - updated_at: Timestamp, -} - -#[spacetimedb::table( - accessor = story_session, - index(accessor = by_runtime_session_id, btree(columns = [runtime_session_id])), - index(accessor = by_actor_user_id, btree(columns = [actor_user_id])) -)] -pub struct StorySession { - #[primary_key] - story_session_id: String, - runtime_session_id: String, - actor_user_id: String, - world_profile_id: String, - initial_prompt: String, - opening_summary: Option, - latest_narrative_text: String, - latest_choice_function_id: Option, - status: StorySessionStatus, - version: u32, - created_at: Timestamp, - updated_at: Timestamp, -} - -#[spacetimedb::table( - accessor = story_event, - index(accessor = by_story_session_id, btree(columns = [story_session_id])) -)] -pub struct StoryEvent { - #[primary_key] - event_id: String, - story_session_id: String, - event_kind: StoryEventKind, - narrative_text: String, - choice_function_id: Option, - created_at: Timestamp, -} - -#[spacetimedb::table( - accessor = inventory_slot, - index(accessor = by_inventory_runtime_session_id, btree(columns = [runtime_session_id])), - index(accessor = by_inventory_actor_user_id, btree(columns = [actor_user_id])), - index(accessor = by_inventory_container_slot, btree(columns = [container_kind, slot_key])), - index(accessor = by_inventory_item_id, btree(columns = [item_id])) -)] -pub struct InventorySlot { - #[primary_key] - slot_id: String, - runtime_session_id: String, - story_session_id: Option, - actor_user_id: String, - container_kind: InventoryContainerKind, - slot_key: String, - item_id: String, - category: String, - name: String, - description: Option, - quantity: u32, - rarity: InventoryItemRarity, - tags: Vec, - stackable: bool, - stack_key: String, - equipment_slot_id: Option, - source_kind: InventoryItemSourceKind, - source_reference_id: Option, - created_at: Timestamp, - updated_at: Timestamp, -} - -#[spacetimedb::table( - accessor = battle_state, - index(accessor = by_battle_story_session_id, btree(columns = [story_session_id])), - index(accessor = by_battle_runtime_session_id, btree(columns = [runtime_session_id])), - index(accessor = by_battle_actor_user_id, btree(columns = [actor_user_id])) -)] -pub struct BattleState { - #[primary_key] - battle_state_id: String, - story_session_id: String, - runtime_session_id: String, - actor_user_id: String, - chapter_id: Option, - target_npc_id: String, - target_name: String, - battle_mode: BattleMode, - status: BattleStatus, - player_hp: i32, - player_max_hp: i32, - player_mana: i32, - player_max_mana: i32, - target_hp: i32, - target_max_hp: i32, - experience_reward: u32, - reward_items: Vec, - turn_index: u32, - last_action_function_id: Option, - last_action_text: Option, - last_result_text: Option, - last_damage_dealt: i32, - last_damage_taken: i32, - last_outcome: CombatOutcome, - version: u32, - created_at: Timestamp, - updated_at: Timestamp, -} - -#[spacetimedb::table( - accessor = treasure_record, - index(accessor = by_treasure_story_session_id, btree(columns = [story_session_id])), - index(accessor = by_treasure_runtime_session_id, btree(columns = [runtime_session_id])), - index(accessor = by_treasure_actor_user_id, btree(columns = [actor_user_id])), - index(accessor = by_treasure_encounter_id, btree(columns = [encounter_id])) -)] -pub struct TreasureRecord { - #[primary_key] - treasure_record_id: String, - runtime_session_id: String, - story_session_id: String, - actor_user_id: String, - encounter_id: String, - encounter_name: String, - scene_id: Option, - scene_name: Option, - action: TreasureInteractionAction, - reward_items: Vec, - reward_hp: u32, - reward_mana: u32, - reward_currency: u32, - story_hint: Option, - created_at: Timestamp, - updated_at: Timestamp, -} - -#[spacetimedb::table( - accessor = quest_record, - index(accessor = by_runtime_session_id, btree(columns = [runtime_session_id])), - index(accessor = by_actor_user_id, btree(columns = [actor_user_id])), - index(accessor = by_issuer_npc_id, btree(columns = [issuer_npc_id])) -)] -pub struct QuestRecord { - #[primary_key] - quest_id: String, - runtime_session_id: String, - story_session_id: Option, - actor_user_id: String, - issuer_npc_id: String, - issuer_npc_name: String, - scene_id: Option, - chapter_id: Option, - act_id: Option, - thread_id: Option, - contract_id: Option, - title: String, - description: String, - summary: String, - objective: QuestObjectiveSnapshot, - progress: u32, - status: QuestStatus, - completion_notified: bool, - reward: QuestRewardSnapshot, - reward_text: String, - narrative_binding: QuestNarrativeBindingSnapshot, - steps: Vec, - active_step_id: Option, - visible_stage: u32, - hidden_flags: Vec, - discovered_fact_ids: Vec, - related_carrier_ids: Vec, - consequence_ids: Vec, - created_at: Timestamp, - updated_at: Timestamp, - completed_at: Option, - turned_in_at: Option, -} - -#[spacetimedb::table( - accessor = quest_log, - index(accessor = by_quest_id, btree(columns = [quest_id])), - index(accessor = by_runtime_session_id, btree(columns = [runtime_session_id])), - index(accessor = by_actor_user_id, btree(columns = [actor_user_id])) -)] -pub struct QuestLog { - #[primary_key] - log_id: String, - quest_id: String, - runtime_session_id: String, - actor_user_id: String, - event_kind: QuestLogEventKind, - status_after: QuestStatus, - signal_kind: Option, - signal: Option, - step_id: Option, - step_progress: Option, - created_at: Timestamp, -} - -#[spacetimedb::table( - accessor = custom_world_profile, - index(accessor = by_custom_world_profile_owner_user_id, btree(columns = [owner_user_id])), - index( - accessor = by_custom_world_profile_publication_status, - btree(columns = [publication_status]) - ) -)] -pub struct CustomWorldProfile { - #[primary_key] - profile_id: String, - // 当前 profile 承接 library / publish / enter-world 的正式世界工件真相。 - owner_user_id: String, - // 作品公开编号是稳定分享键,第一次发布时分配,后续重复发布沿用。 - public_work_code: Option, - // 作者公开叙世号在发布时固化到作品真相,供广场读模型与搜索结果直接展示。 - author_public_user_code: Option, - source_agent_session_id: Option, - publication_status: CustomWorldPublicationStatus, - world_name: String, - subtitle: String, - summary_text: String, - theme_mode: CustomWorldThemeMode, - cover_image_src: Option, - profile_payload_json: String, - playable_npc_count: u32, - landmark_count: u32, - author_display_name: String, - published_at: Option, - // 软删除后保留 profile 真相,供审计与幂等删除使用。 - deleted_at: Option, - created_at: Timestamp, - updated_at: Timestamp, -} - -#[spacetimedb::table( - accessor = custom_world_session, - index(accessor = by_custom_world_session_owner_user_id, btree(columns = [owner_user_id])) -)] -pub struct CustomWorldSession { - #[primary_key] - session_id: String, - // 这张表只承接旧 custom-world/sessions 传统问答流,不和 agent 会话混存。 - owner_user_id: String, - generation_mode: CustomWorldGenerationMode, - status: CustomWorldSessionStatus, - setting_text: String, - creator_intent_json: Option, - question_snapshot_json: String, - result_payload_json: Option, - last_error_message: Option, - created_at: Timestamp, - updated_at: Timestamp, -} - -#[spacetimedb::table( - accessor = custom_world_agent_session, - index( - accessor = by_custom_world_agent_session_owner_user_id, - btree(columns = [owner_user_id]) - ), - index(accessor = by_custom_world_agent_session_stage, btree(columns = [stage])) -)] -pub struct CustomWorldAgentSession { - #[primary_key] - session_id: String, - // Agent 会话只保留会话级聚合字段,消息、操作、卡片都拆到独立表。 - owner_user_id: String, - seed_text: String, - current_turn: u32, - progress_percent: u32, - stage: RpgAgentStage, - focus_card_id: Option, - anchor_content_json: String, - creator_intent_json: Option, - creator_intent_readiness_json: String, - anchor_pack_json: Option, - lock_state_json: Option, - draft_profile_json: Option, - last_assistant_reply: Option, - publish_gate_json: Option, - result_preview_json: Option, - pending_clarifications_json: String, - quality_findings_json: String, - suggested_actions_json: String, - recommended_replies_json: String, - asset_coverage_json: String, - checkpoints_json: String, - created_at: Timestamp, - updated_at: Timestamp, -} - -#[spacetimedb::table( - accessor = custom_world_agent_message, - index(accessor = by_custom_world_agent_message_session_id, btree(columns = [session_id])) -)] -pub struct CustomWorldAgentMessage { - #[primary_key] - message_id: String, - // 消息流水单独成表,避免继续塞回 session 大 JSON。 - session_id: String, - role: RpgAgentMessageRole, - kind: RpgAgentMessageKind, - text: String, - related_operation_id: Option, - created_at: Timestamp, -} - -#[derive(Clone)] -#[spacetimedb::table( - accessor = custom_world_agent_operation, - index(accessor = by_custom_world_agent_operation_session_id, btree(columns = [session_id])) -)] -pub struct CustomWorldAgentOperation { - #[primary_key] - operation_id: String, - // 异步操作单独建表,为 message stream / operation query 提供真相源。 - session_id: String, - operation_type: RpgAgentOperationType, - status: RpgAgentOperationStatus, - phase_label: String, - phase_detail: String, - progress: u32, - error_message: Option, - created_at: Timestamp, - updated_at: Timestamp, -} - -#[spacetimedb::table( - accessor = custom_world_draft_card, - index(accessor = by_custom_world_draft_card_session_id, btree(columns = [session_id])), - index(accessor = by_custom_world_draft_card_kind, btree(columns = [kind])) -)] -pub struct CustomWorldDraftCard { - #[primary_key] - card_id: String, - // 卡片实体从 agent session 拆出,后续 detail / update 都直接对这张表操作。 - session_id: String, - kind: RpgAgentDraftCardKind, - status: RpgAgentDraftCardStatus, - title: String, - subtitle: String, - summary: String, - linked_ids_json: String, - warning_count: u32, - asset_status: Option, - asset_status_label: Option, - detail_payload_json: Option, - created_at: Timestamp, - updated_at: Timestamp, -} - -#[spacetimedb::table( - accessor = custom_world_gallery_entry, - public, - index(accessor = by_custom_world_gallery_owner_user_id, btree(columns = [owner_user_id])), - index(accessor = by_custom_world_gallery_theme_mode, btree(columns = [theme_mode])), - index(accessor = by_custom_world_gallery_public_work_code, btree(columns = [public_work_code])) -)] -pub struct CustomWorldGalleryEntry { - #[primary_key] - profile_id: String, - // 画廊是公开订阅读模型,不再运行时从 profile 即席拼装。 - owner_user_id: String, - public_work_code: String, - author_public_user_code: String, - author_display_name: String, - world_name: String, - subtitle: String, - summary_text: String, - cover_image_src: Option, - theme_mode: CustomWorldThemeMode, - playable_npc_count: u32, - landmark_count: u32, - published_at: Timestamp, - updated_at: Timestamp, -} - -// 成长状态默认按 user_id 单行持久化;若尚未存在记录则返回 Lv.1 / 0 XP 的兼容初始值。 -#[spacetimedb::procedure] -pub fn get_player_progression_or_default( - ctx: &mut ProcedureContext, - input: PlayerProgressionGetInput, -) -> PlayerProgressionProcedureResult { - match ctx.try_with_tx(|tx| get_player_progression_snapshot_tx(tx, input.clone())) { - Ok(record) => PlayerProgressionProcedureResult { - ok: true, - record: Some(record), - error_message: None, - }, - Err(message) => PlayerProgressionProcedureResult { - ok: false, - record: None, - error_message: Some(message), - }, - } -} - -// 经验发放统一走 progression reducer,避免任务和战斗各自直接写等级字段。 -#[spacetimedb::reducer] -pub fn grant_player_progression_experience( - ctx: &ReducerContext, - input: PlayerProgressionGrantInput, -) -> Result<(), String> { - upsert_player_progression_after_grant_tx(ctx, input).map(|_| ()) -} - -#[spacetimedb::procedure] -pub fn grant_player_progression_experience_and_return( - ctx: &mut ProcedureContext, - input: PlayerProgressionGrantInput, -) -> PlayerProgressionProcedureResult { - match ctx.try_with_tx(|tx| upsert_player_progression_after_grant_tx(tx, input.clone())) { - Ok(record) => PlayerProgressionProcedureResult { - ok: true, - record: Some(record), - error_message: None, - }, - Err(message) => PlayerProgressionProcedureResult { - ok: false, - record: None, - error_message: Some(message), - }, - } -} - -// 章节计划在进入章节或编译章节预算时写入;当前先用单表同时承接计划值与实际记账值。 -#[spacetimedb::reducer] -pub fn upsert_chapter_progression( - ctx: &ReducerContext, - input: ChapterProgressionInput, -) -> Result<(), String> { - upsert_chapter_progression_snapshot_tx(ctx, input).map(|_| ()) -} - -#[spacetimedb::procedure] -pub fn upsert_chapter_progression_and_return( - ctx: &mut ProcedureContext, - input: ChapterProgressionInput, -) -> ChapterProgressionProcedureResult { - match ctx.try_with_tx(|tx| upsert_chapter_progression_snapshot_tx(tx, input.clone())) { - Ok(record) => ChapterProgressionProcedureResult { - ok: true, - record: Some(record), - error_message: None, - }, - Err(message) => ChapterProgressionProcedureResult { - ok: false, - record: None, - error_message: Some(message), - }, - } -} - -// 章节实际经验与击杀记账后续由 quest/combat 联动调用,这一轮先把真相写入口固定下来。 -#[spacetimedb::reducer] -pub fn apply_chapter_progression_ledger_entry( - ctx: &ReducerContext, - input: ChapterProgressionLedgerInput, -) -> Result<(), String> { - update_chapter_progression_ledger_tx(ctx, input).map(|_| ()) -} - -#[spacetimedb::procedure] -pub fn apply_chapter_progression_ledger_entry_and_return( - ctx: &mut ProcedureContext, - input: ChapterProgressionLedgerInput, -) -> ChapterProgressionProcedureResult { - match ctx.try_with_tx(|tx| update_chapter_progression_ledger_tx(tx, input.clone())) { - Ok(record) => ChapterProgressionProcedureResult { - ok: true, - record: Some(record), - error_message: None, - }, - Err(message) => ChapterProgressionProcedureResult { - ok: false, - record: None, - error_message: Some(message), - }, - } -} - -#[spacetimedb::procedure] -pub fn get_chapter_progression( - ctx: &mut ProcedureContext, - input: ChapterProgressionGetInput, -) -> ChapterProgressionProcedureResult { - match ctx.try_with_tx(|tx| get_chapter_progression_snapshot_tx(tx, input.clone())) { - Ok(record) => ChapterProgressionProcedureResult { - ok: true, - record: Some(record), - error_message: None, - }, - Err(message) => ChapterProgressionProcedureResult { - ok: false, - record: None, - error_message: Some(message), - }, - } -} - -// 当前阶段先把 inventory_slot 立成显式背包真相表,避免继续由多个 service 各自改 runtime snapshot JSON。 -#[spacetimedb::reducer] -pub fn apply_inventory_mutation( - ctx: &ReducerContext, - input: InventoryMutationInput, -) -> Result<(), String> { - apply_inventory_mutation_tx(ctx, input) -} - -fn apply_inventory_mutation_tx( - ctx: &ReducerContext, - input: InventoryMutationInput, -) -> Result<(), String> { - let current_slots = ctx - .db - .inventory_slot() - .iter() - .filter(|slot| { - slot.runtime_session_id == input.runtime_session_id - && slot.actor_user_id == input.actor_user_id - }) - .map(|row| build_inventory_slot_snapshot_from_row(&row)) - .collect::>(); - - let outcome = - apply_inventory_slot_mutation(current_slots, input).map_err(|error| error.to_string())?; - - for removed_slot_id in outcome.removed_slot_ids { - ctx.db.inventory_slot().slot_id().delete(&removed_slot_id); - } - - for slot in outcome.next_slots { - ctx.db.inventory_slot().slot_id().delete(&slot.slot_id); - ctx.db - .inventory_slot() - .insert(build_inventory_slot_row(slot)); - } - - Ok(()) -} - -// procedure 面向 Axum 同步读取当前 runtime_session 下某个玩家的背包真相态。 -#[spacetimedb::procedure] -pub fn get_runtime_inventory_state( - ctx: &mut ProcedureContext, - input: RuntimeInventoryStateQueryInput, -) -> RuntimeInventoryStateProcedureResult { - match ctx.try_with_tx(|tx| get_runtime_inventory_state_tx(tx, input.clone())) { - Ok(snapshot) => RuntimeInventoryStateProcedureResult { - ok: true, - snapshot: Some(snapshot), - error_message: None, - }, - Err(message) => RuntimeInventoryStateProcedureResult { - ok: false, - snapshot: None, - error_message: Some(message), - }, - } -} - -// M4 首轮先把 battle_state 作为战斗真相源落到 SpacetimeDB,避免继续把战斗状态埋在 runtime JSON 里。 -#[spacetimedb::reducer] -pub fn create_battle_state(ctx: &ReducerContext, input: BattleStateInput) -> Result<(), String> { - create_battle_state_record(ctx, input).map(|_| ()) -} - -// procedure 面向 Axum 同步创建 battle_state,返回当前最新战斗快照,避免 HTTP 层再次读取 private table。 -#[spacetimedb::procedure] -pub fn create_battle_state_and_return( - ctx: &mut ProcedureContext, - input: BattleStateInput, -) -> BattleStateProcedureResult { - match ctx.try_with_tx(|tx| create_battle_state_record(tx, input.clone())) { - Ok(snapshot) => BattleStateProcedureResult { - ok: true, - snapshot: Some(snapshot), - error_message: None, - }, - Err(message) => BattleStateProcedureResult { - ok: false, - snapshot: None, - error_message: Some(message), - }, - } -} - -// procedure 面向 Axum 读取单个 battle_state 真相态,当前只返回最新战斗快照。 -#[spacetimedb::procedure] -pub fn get_battle_state( - ctx: &mut ProcedureContext, - input: BattleStateQueryInput, -) -> BattleStateProcedureResult { - match ctx.try_with_tx(|tx| get_battle_state_record(tx, input.clone())) { - Ok(snapshot) => BattleStateProcedureResult { - ok: true, - snapshot: Some(snapshot), - error_message: None, - }, - Err(message) => BattleStateProcedureResult { - ok: false, - snapshot: None, - error_message: Some(message), - }, - } -} - -// M4 首轮只承接单行为战斗推进,不提前把 inventory / progression / story AI 续写耦进 reducer。 -#[spacetimedb::reducer] -pub fn resolve_combat_action( - ctx: &ReducerContext, - input: ResolveCombatActionInput, -) -> Result<(), String> { - resolve_battle_state_record(ctx, input).map(|_| ()) -} - -// procedure 面向 Axum 同步推进单次战斗动作,返回本次结算结果与 battle_state 最新快照。 -#[spacetimedb::procedure] -pub fn resolve_combat_action_and_return( - ctx: &mut ProcedureContext, - input: ResolveCombatActionInput, -) -> ResolveCombatActionProcedureResult { - match ctx.try_with_tx(|tx| resolve_battle_state_record(tx, input.clone())) { - Ok(result) => ResolveCombatActionProcedureResult { - ok: true, - result: Some(result), - error_message: None, - }, - Err(message) => ResolveCombatActionProcedureResult { - ok: false, - result: None, - error_message: Some(message), - }, - } -} - -fn create_battle_state_record( - ctx: &ReducerContext, - input: BattleStateInput, -) -> Result { - validate_battle_state_input(&input).map_err(|error| error.to_string())?; - - if ctx - .db - .battle_state() - .battle_state_id() - .find(&input.battle_state_id) - .is_some() - { - return Err("battle_state.battle_state_id 已存在".to_string()); - } - - let snapshot = build_battle_state_snapshot(input); - ctx.db - .battle_state() - .insert(build_battle_state_row(snapshot.clone())); - - Ok(snapshot) -} - -fn get_battle_state_record( - ctx: &ReducerContext, - input: BattleStateQueryInput, -) -> Result { - validate_battle_state_query_input(&input).map_err(|error| error.to_string())?; - - let row = ctx - .db - .battle_state() - .battle_state_id() - .find(&input.battle_state_id) - .ok_or_else(|| "battle_state 不存在".to_string())?; - - Ok(build_battle_state_snapshot_from_row(&row)) -} - -fn get_runtime_inventory_state_tx( - ctx: &ReducerContext, - input: RuntimeInventoryStateQueryInput, -) -> Result { - let validated_input = - build_runtime_inventory_state_query_input(input.runtime_session_id, input.actor_user_id) - .map_err(|error| error.to_string())?; - - // 这层只返回 inventory_slot 真相表的最小切片,不混入 story、quest、battle 的额外投影。 - let slots = ctx - .db - .inventory_slot() - .iter() - .filter(|row| { - row.runtime_session_id == validated_input.runtime_session_id - && row.actor_user_id == validated_input.actor_user_id - }) - .map(|row| build_inventory_slot_snapshot_from_row(&row)) - .collect::>(); - - Ok(build_runtime_inventory_state_snapshot( - validated_input, - slots, - )) -} - -fn resolve_battle_state_record( - ctx: &ReducerContext, - input: ResolveCombatActionInput, -) -> Result { - let current = ctx - .db - .battle_state() - .battle_state_id() - .find(&input.battle_state_id) - .ok_or_else(|| "battle_state 不存在,无法执行战斗动作".to_string())?; - - let result = resolve_battle_state_action(build_battle_state_snapshot_from_row(¤t), input) - .map_err(|error| error.to_string())?; - - ctx.db - .battle_state() - .battle_state_id() - .delete(¤t.battle_state_id); - ctx.db - .battle_state() - .insert(build_battle_state_row(result.snapshot.clone())); - - if result.outcome == CombatOutcome::Victory { - grant_battle_reward_items(ctx, &result.snapshot)?; - - if result.snapshot.experience_reward > 0 { - let updated_player = upsert_player_progression_after_grant_tx( - ctx, - PlayerProgressionGrantInput { - user_id: result.snapshot.actor_user_id.clone(), - amount: result.snapshot.experience_reward, - source: PlayerProgressionGrantSource::HostileNpc, - updated_at_micros: result.snapshot.updated_at_micros, - }, - )?; - - // 章节计划可能尚未初始化;此时不能阻断战斗胜利结算,只跳过章节账本写入。 - try_update_chapter_progression_ledger_tx( - ctx, - result.snapshot.actor_user_id.clone(), - result.snapshot.chapter_id.clone(), - ChapterProgressionLedgerInput { - user_id: result.snapshot.actor_user_id.clone(), - chapter_id: result.snapshot.chapter_id.clone().unwrap_or_default(), - granted_quest_xp: 0, - granted_hostile_xp: result.snapshot.experience_reward, - hostile_defeat_increment: 1, - level_at_exit: Some(updated_player.level), - updated_at_micros: result.snapshot.updated_at_micros, - }, - )?; - } - } - - Ok(result) -} - -// 当前阶段先把 npc_state 立成显式真相表,避免继续把关系状态藏在运行时 JSON 快照里。 -#[spacetimedb::reducer] -pub fn upsert_npc_state(ctx: &ReducerContext, input: NpcStateUpsertInput) -> Result<(), String> { - upsert_npc_state_record(ctx, input).map(|_| ()) -} - -// procedure 面向 Axum 同步 upsert 接口,返回最新 NPC 状态快照。 -#[spacetimedb::procedure] -pub fn upsert_npc_state_and_return( - ctx: &mut ProcedureContext, - input: NpcStateUpsertInput, -) -> NpcStateProcedureResult { - match ctx.try_with_tx(|tx| upsert_npc_state_record(tx, input.clone())) { - Ok(record) => NpcStateProcedureResult { - ok: true, - record: Some(record), - error_message: None, - }, - Err(message) => NpcStateProcedureResult { - ok: false, - record: None, - error_message: Some(message), - }, - } -} - -// 当前阶段只承接 NPC 关系状态的最小社交动作,不提前把背包、战斗和队伍副作用也塞进来。 -#[spacetimedb::reducer] -pub fn resolve_npc_social_action( - ctx: &ReducerContext, - input: ResolveNpcSocialActionInput, -) -> Result<(), String> { - resolve_npc_social_action_record(ctx, input).map(|_| ()) -} - -// procedure 面向 Axum 同步社交动作接口,返回动作后的 NPC 状态快照。 -#[spacetimedb::procedure] -pub fn resolve_npc_social_action_and_return( - ctx: &mut ProcedureContext, - input: ResolveNpcSocialActionInput, -) -> NpcStateProcedureResult { - match ctx.try_with_tx(|tx| resolve_npc_social_action_record(tx, input.clone())) { - Ok(record) => NpcStateProcedureResult { - ok: true, - record: Some(record), - error_message: None, - }, - Err(message) => NpcStateProcedureResult { - ok: false, - record: None, - error_message: Some(message), - }, - } -} - -// 当前阶段先冻结 NPC 正式交互统一入口,不直接在这里扩出队伍、战斗、背包等跨子域副作用。 -#[spacetimedb::reducer] -pub fn resolve_npc_interaction( - ctx: &ReducerContext, - input: ResolveNpcInteractionInput, -) -> Result<(), String> { - resolve_npc_interaction_record(ctx, input).map(|_| ()) -} - -#[spacetimedb::procedure] -pub fn resolve_npc_interaction_and_return( - ctx: &mut ProcedureContext, - input: ResolveNpcInteractionInput, -) -> NpcInteractionProcedureResult { - match ctx.try_with_tx(|tx| resolve_npc_interaction_record(tx, input.clone())) { - Ok(result) => NpcInteractionProcedureResult { - ok: true, - result: Some(result), - error_message: None, - }, - Err(message) => NpcInteractionProcedureResult { - ok: false, - result: None, - error_message: Some(message), - }, - } -} - -// fight / spar 的 battle_state 初始化属于聚合层编排,不回灌到 module-npc 纯领域 crate。 -#[spacetimedb::procedure] -pub fn resolve_npc_battle_interaction_and_return( - ctx: &mut ProcedureContext, - input: ResolveNpcBattleInteractionInput, -) -> NpcBattleInteractionProcedureResult { - match ctx.try_with_tx(|tx| resolve_npc_battle_interaction_tx(tx, input.clone())) { - Ok(result) => NpcBattleInteractionProcedureResult { - ok: true, - result: Some(result), - error_message: None, - }, - Err(message) => NpcBattleInteractionProcedureResult { - ok: false, - result: None, - error_message: Some(message), - }, - } -} - -// M4 首轮先把 story_session / story_event 作为显式会话真相源落到 SpacetimeDB,避免后续继续依赖大 JSON 覆盖式写法。 -#[spacetimedb::reducer] -pub fn begin_story_session(ctx: &ReducerContext, input: StorySessionInput) -> Result<(), String> { - begin_story_session_tx(ctx, input).map(|_| ()) -} - -// procedure 面向 Axum 同步创建故事会话,返回最新会话快照与开场事件,避免 HTTP 层再读 private table。 -#[spacetimedb::procedure] -pub fn begin_story_session_and_return( - ctx: &mut ProcedureContext, - input: StorySessionInput, -) -> StorySessionProcedureResult { - match ctx.try_with_tx(|tx| begin_story_session_tx(tx, input.clone())) { - Ok((session, event)) => StorySessionProcedureResult { - ok: true, - session: Some(session), - event: Some(event), - error_message: None, - }, - Err(message) => StorySessionProcedureResult { - ok: false, - session: None, - event: None, - error_message: Some(message), - }, - } -} - -fn begin_story_session_tx( - ctx: &ReducerContext, - input: StorySessionInput, -) -> Result<(StorySessionSnapshot, StoryEventSnapshot), String> { - validate_story_session_input(&input).map_err(|error| error.to_string())?; - - if ctx - .db - .story_session() - .story_session_id() - .find(&input.story_session_id) - .is_some() - { - return Err("story_session.story_session_id 已存在".to_string()); - } - - let snapshot = build_story_session_snapshot(input); - let started_event = build_story_started_event(&snapshot); - let created_at = Timestamp::from_micros_since_unix_epoch(snapshot.created_at_micros); - let updated_at = Timestamp::from_micros_since_unix_epoch(snapshot.updated_at_micros); - - ctx.db.story_session().insert(StorySession { - story_session_id: snapshot.story_session_id.clone(), - runtime_session_id: snapshot.runtime_session_id.clone(), - actor_user_id: snapshot.actor_user_id.clone(), - world_profile_id: snapshot.world_profile_id.clone(), - initial_prompt: snapshot.initial_prompt.clone(), - opening_summary: snapshot.opening_summary.clone(), - latest_narrative_text: snapshot.latest_narrative_text.clone(), - latest_choice_function_id: snapshot.latest_choice_function_id.clone(), - status: snapshot.status, - version: snapshot.version, - created_at, - updated_at, - }); - - ctx.db.story_event().insert(StoryEvent { - event_id: started_event.event_id.clone(), - story_session_id: started_event.story_session_id.clone(), - event_kind: started_event.event_kind, - narrative_text: started_event.narrative_text.clone(), - choice_function_id: started_event.choice_function_id.clone(), - created_at, - }); - - Ok((snapshot, started_event)) -} - -// M4 首轮继续把“故事推进”固定为事件追加 + 会话版本递增,为后续 resolve_story_action 接线提供最小基座。 -#[spacetimedb::reducer] -pub fn continue_story(ctx: &ReducerContext, input: StoryContinueInput) -> Result<(), String> { - continue_story_tx(ctx, input).map(|_| ()) -} - -// procedure 面向 Axum 同步推进故事会话,返回最新会话快照与本次事件,避免 HTTP 层再读 private table。 -#[spacetimedb::procedure] -pub fn continue_story_and_return( - ctx: &mut ProcedureContext, - input: StoryContinueInput, -) -> StorySessionProcedureResult { - match ctx.try_with_tx(|tx| continue_story_tx(tx, input.clone())) { - Ok((session, event)) => StorySessionProcedureResult { - ok: true, - session: Some(session), - event: Some(event), - error_message: None, - }, - Err(message) => StorySessionProcedureResult { - ok: false, - session: None, - event: None, - error_message: Some(message), - }, - } -} - -// procedure 面向 Axum 读取指定 story session 的最小真实状态,当前只返回 session + event 列表。 -#[spacetimedb::procedure] -pub fn get_story_session_state( - ctx: &mut ProcedureContext, - input: StorySessionStateInput, -) -> StorySessionStateProcedureResult { - match ctx.try_with_tx(|tx| get_story_session_state_tx(tx, input.clone())) { - Ok((session, events)) => StorySessionStateProcedureResult { - ok: true, - session: Some(session), - events, - error_message: None, - }, - Err(message) => StorySessionStateProcedureResult { - ok: false, - session: None, - events: Vec::new(), - error_message: Some(message), - }, - } -} - -// Stage 6 先把 Agent 会话骨架写入 SpacetimeDB,LLM 采集与卡片生成后续再接入。 -#[spacetimedb::procedure] -pub fn create_custom_world_agent_session( - ctx: &mut ProcedureContext, - input: CustomWorldAgentSessionCreateInput, -) -> CustomWorldAgentSessionProcedureResult { - match ctx.try_with_tx(|tx| create_custom_world_agent_session_tx(tx, input.clone())) { - Ok(session) => CustomWorldAgentSessionProcedureResult { - ok: true, - session: Some(session), - error_message: None, - }, - Err(message) => CustomWorldAgentSessionProcedureResult { - ok: false, - session: None, - error_message: Some(message), - }, - } -} - -// Stage 6 读取拆表后的最小 Agent session snapshot,供 Axum 兼容旧前端 contract。 -#[spacetimedb::procedure] -pub fn get_custom_world_agent_session( - ctx: &mut ProcedureContext, - input: CustomWorldAgentSessionGetInput, -) -> CustomWorldAgentSessionProcedureResult { - match ctx.try_with_tx(|tx| get_custom_world_agent_session_tx(tx, input.clone())) { - Ok(session) => CustomWorldAgentSessionProcedureResult { - ok: true, - session: Some(session), - error_message: None, - }, - Err(message) => CustomWorldAgentSessionProcedureResult { - ok: false, - session: None, - error_message: Some(message), - }, - } -} - -#[spacetimedb::procedure] -pub fn delete_custom_world_agent_session( - ctx: &mut ProcedureContext, - input: CustomWorldAgentSessionGetInput, -) -> CustomWorldWorksListResult { - match ctx.try_with_tx(|tx| delete_custom_world_agent_session_tx(tx, input.clone())) { - Ok(items) => CustomWorldWorksListResult { - ok: true, - items, - error_message: None, - }, - Err(message) => CustomWorldWorksListResult { - ok: false, - items: Vec::new(), - error_message: Some(message), - }, - } -} - -#[spacetimedb::procedure] -pub fn submit_custom_world_agent_message( - ctx: &mut ProcedureContext, - input: CustomWorldAgentMessageSubmitInput, -) -> CustomWorldAgentOperationProcedureResult { - match ctx.try_with_tx(|tx| submit_custom_world_agent_message_tx(tx, input.clone())) { - Ok(operation) => CustomWorldAgentOperationProcedureResult { - ok: true, - operation: Some(operation), - error_message: None, - }, - Err(message) => CustomWorldAgentOperationProcedureResult { - ok: false, - operation: None, - error_message: Some(message), - }, - } -} - -#[spacetimedb::procedure] -pub fn finalize_custom_world_agent_message_turn( - ctx: &mut ProcedureContext, - input: CustomWorldAgentMessageFinalizeInput, -) -> CustomWorldAgentOperationProcedureResult { - match ctx.try_with_tx(|tx| finalize_custom_world_agent_message_turn_tx(tx, input.clone())) { - Ok(operation) => CustomWorldAgentOperationProcedureResult { - ok: true, - operation: Some(operation), - error_message: None, - }, - Err(message) => CustomWorldAgentOperationProcedureResult { - ok: false, - operation: None, - error_message: Some(message), - }, - } -} - -#[spacetimedb::procedure] -pub fn get_custom_world_agent_operation( - ctx: &mut ProcedureContext, - input: CustomWorldAgentOperationGetInput, -) -> CustomWorldAgentOperationProcedureResult { - match ctx.try_with_tx(|tx| get_custom_world_agent_operation_tx(tx, input.clone())) { - Ok(operation) => CustomWorldAgentOperationProcedureResult { - ok: true, - operation: Some(operation), - error_message: None, - }, - Err(message) => CustomWorldAgentOperationProcedureResult { - ok: false, - operation: None, - error_message: Some(message), - }, - } -} - -#[spacetimedb::procedure] -pub fn upsert_custom_world_agent_operation_progress( - ctx: &mut ProcedureContext, - input: CustomWorldAgentOperationProgressInput, -) -> CustomWorldAgentOperationProcedureResult { - match ctx.try_with_tx(|tx| upsert_custom_world_agent_operation_progress_tx(tx, input.clone())) { - Ok(operation) => CustomWorldAgentOperationProcedureResult { - ok: true, - operation: Some(operation), - error_message: None, - }, - Err(message) => CustomWorldAgentOperationProcedureResult { - ok: false, - operation: None, - error_message: Some(message), - }, - } -} - -fn continue_story_tx( - ctx: &ReducerContext, - input: StoryContinueInput, -) -> Result<(StorySessionSnapshot, StoryEventSnapshot), String> { - validate_story_continue_input(&input).map_err(|error| error.to_string())?; - - let current = ctx - .db - .story_session() - .story_session_id() - .find(&input.story_session_id) - .ok_or_else(|| "story_session 不存在,无法继续推进".to_string())?; - - let current_snapshot = StorySessionSnapshot { - story_session_id: current.story_session_id.clone(), - runtime_session_id: current.runtime_session_id.clone(), - actor_user_id: current.actor_user_id.clone(), - world_profile_id: current.world_profile_id.clone(), - initial_prompt: current.initial_prompt.clone(), - opening_summary: current.opening_summary.clone(), - latest_narrative_text: current.latest_narrative_text.clone(), - latest_choice_function_id: current.latest_choice_function_id.clone(), - status: current.status, - version: current.version, - created_at_micros: current.created_at.to_micros_since_unix_epoch(), - updated_at_micros: current.updated_at.to_micros_since_unix_epoch(), - }; - - let (next_snapshot, event_snapshot) = - apply_story_continue(current_snapshot, input).map_err(|error| error.to_string())?; - - ctx.db - .story_session() - .story_session_id() - .delete(¤t.story_session_id); - - ctx.db.story_session().insert(StorySession { - story_session_id: next_snapshot.story_session_id.clone(), - runtime_session_id: next_snapshot.runtime_session_id.clone(), - actor_user_id: next_snapshot.actor_user_id.clone(), - world_profile_id: next_snapshot.world_profile_id.clone(), - initial_prompt: next_snapshot.initial_prompt.clone(), - opening_summary: next_snapshot.opening_summary.clone(), - latest_narrative_text: next_snapshot.latest_narrative_text.clone(), - latest_choice_function_id: next_snapshot.latest_choice_function_id.clone(), - status: next_snapshot.status, - version: next_snapshot.version, - created_at: Timestamp::from_micros_since_unix_epoch(next_snapshot.created_at_micros), - updated_at: Timestamp::from_micros_since_unix_epoch(next_snapshot.updated_at_micros), - }); - - ctx.db.story_event().insert(StoryEvent { - event_id: event_snapshot.event_id.clone(), - story_session_id: event_snapshot.story_session_id.clone(), - event_kind: event_snapshot.event_kind, - narrative_text: event_snapshot.narrative_text.clone(), - choice_function_id: event_snapshot.choice_function_id.clone(), - created_at: Timestamp::from_micros_since_unix_epoch(event_snapshot.created_at_micros), - }); - - Ok((next_snapshot, event_snapshot)) -} - -fn get_story_session_state_tx( - ctx: &ReducerContext, - input: StorySessionStateInput, -) -> Result<(StorySessionSnapshot, Vec), String> { - validate_story_session_state_input(&input).map_err(|error| error.to_string())?; - - let session = ctx - .db - .story_session() - .story_session_id() - .find(&input.story_session_id) - .ok_or_else(|| "story_session 不存在".to_string())?; - - let session_snapshot = build_story_session_snapshot_from_row(&session); - let mut events = ctx - .db - .story_event() - .iter() - .filter(|row| row.story_session_id == input.story_session_id) - .map(|row| build_story_event_snapshot_from_row(&row)) - .collect::>(); - events.sort_by_key(|event| (event.created_at_micros, event.event_id.clone())); - - Ok((session_snapshot, events)) -} - -fn create_custom_world_agent_session_tx( - ctx: &ReducerContext, - input: CustomWorldAgentSessionCreateInput, -) -> Result { - validate_custom_world_agent_session_create_input(&input).map_err(|error| error.to_string())?; - - if ctx - .db - .custom_world_agent_session() - .session_id() - .find(&input.session_id) - .is_some() - { - return Err("custom_world_agent_session.session_id 已存在".to_string()); - } - if ctx - .db - .custom_world_agent_message() - .message_id() - .find(&input.welcome_message_id) - .is_some() - { - return Err("custom_world_agent_message.message_id 已存在".to_string()); - } - - let created_at = Timestamp::from_micros_since_unix_epoch(input.created_at_micros); - ctx.db - .custom_world_agent_session() - .insert(CustomWorldAgentSession { - session_id: input.session_id.clone(), - owner_user_id: input.owner_user_id.clone(), - seed_text: input.seed_text.trim().to_string(), - current_turn: 0, - progress_percent: 0, - stage: RpgAgentStage::CollectingIntent, - focus_card_id: None, - anchor_content_json: input.anchor_content_json.clone(), - creator_intent_json: input.creator_intent_json.clone(), - creator_intent_readiness_json: input.creator_intent_readiness_json.clone(), - anchor_pack_json: input.anchor_pack_json.clone(), - lock_state_json: input.lock_state_json.clone(), - draft_profile_json: input.draft_profile_json.clone(), - last_assistant_reply: Some(input.welcome_message_text.trim().to_string()), - publish_gate_json: None, - result_preview_json: None, - pending_clarifications_json: input.pending_clarifications_json.clone(), - quality_findings_json: input.quality_findings_json.clone(), - suggested_actions_json: input.suggested_actions_json.clone(), - recommended_replies_json: input.recommended_replies_json.clone(), - asset_coverage_json: input.asset_coverage_json.clone(), - checkpoints_json: input.checkpoints_json.clone(), - created_at, - updated_at: created_at, - }); - ctx.db - .custom_world_agent_message() - .insert(CustomWorldAgentMessage { - message_id: input.welcome_message_id, - session_id: input.session_id.clone(), - role: RpgAgentMessageRole::Assistant, - kind: RpgAgentMessageKind::Chat, - text: input.welcome_message_text.trim().to_string(), - related_operation_id: None, - created_at, - }); - - get_custom_world_agent_session_tx( - ctx, - CustomWorldAgentSessionGetInput { - session_id: input.session_id, - owner_user_id: input.owner_user_id, - }, - ) -} - -fn get_custom_world_agent_session_tx( - ctx: &ReducerContext, - input: CustomWorldAgentSessionGetInput, -) -> Result { - validate_custom_world_agent_session_get_input(&input).map_err(|error| error.to_string())?; - - let session = ctx - .db - .custom_world_agent_session() - .session_id() - .find(&input.session_id) - .filter(|row| row.owner_user_id == input.owner_user_id) - .ok_or_else(|| "custom_world_agent_session 不存在".to_string())?; - - Ok(build_custom_world_agent_session_snapshot(ctx, &session)) -} - -fn delete_custom_world_agent_session_tx( - ctx: &ReducerContext, - input: CustomWorldAgentSessionGetInput, -) -> Result, String> { - validate_custom_world_agent_session_get_input(&input).map_err(|error| error.to_string())?; - - let session = ctx - .db - .custom_world_agent_session() - .session_id() - .find(&input.session_id) - .filter(|row| row.owner_user_id == input.owner_user_id) - .ok_or_else(|| "custom_world_agent_session 不存在".to_string())?; - - if session.stage == RpgAgentStage::Published { - let published_profile = ctx - .db - .custom_world_profile() - .iter() - .find(|row| { - row.owner_user_id == input.owner_user_id - && row.source_agent_session_id.as_deref() == Some(input.session_id.as_str()) - && row.deleted_at.is_none() - }) - .ok_or_else(|| "已发布 RPG 作品缺少关联 profile,无法删除".to_string())?; - - // 作品卡可能只携带源 Agent sessionId。这里把“按 session 删除已发布作品” - // 收敛为 profile 软删除,避免前端误入草稿删除接口时继续暴露 procedure 分叉。 - delete_custom_world_profile_record( - ctx, - CustomWorldProfileDeleteInput { - profile_id: published_profile.profile_id, - owner_user_id: input.owner_user_id.clone(), - deleted_at_micros: ctx.timestamp.to_micros_since_unix_epoch(), - }, - )?; - - return list_custom_world_work_snapshots( - ctx, - CustomWorldWorksListInput { - owner_user_id: input.owner_user_id, - }, - ); - } - - // 删除纯 Agent 草稿时同步清理消息、操作与草稿卡,避免作品列表消失后残留孤儿数据。 - ctx.db - .custom_world_agent_session() - .session_id() - .delete(&session.session_id); - for message in ctx - .db - .custom_world_agent_message() - .iter() - .filter(|row| row.session_id == input.session_id) - .collect::>() - { - ctx.db - .custom_world_agent_message() - .message_id() - .delete(&message.message_id); - } - for operation in ctx - .db - .custom_world_agent_operation() - .iter() - .filter(|row| row.session_id == input.session_id) - .collect::>() - { - ctx.db - .custom_world_agent_operation() - .operation_id() - .delete(&operation.operation_id); - } - for card in ctx - .db - .custom_world_draft_card() - .iter() - .filter(|row| row.session_id == input.session_id) - .collect::>() - { - ctx.db - .custom_world_draft_card() - .card_id() - .delete(&card.card_id); - } - - list_custom_world_work_snapshots( - ctx, - CustomWorldWorksListInput { - owner_user_id: input.owner_user_id, - }, - ) -} - -fn submit_custom_world_agent_message_tx( - ctx: &ReducerContext, - input: CustomWorldAgentMessageSubmitInput, -) -> Result { - validate_custom_world_agent_message_submit_input(&input).map_err(|error| error.to_string())?; - - if input.user_message_text.contains("__phase1_force_fail__") { - return Err("forced failure".to_string()); - } - - let _session = ctx - .db - .custom_world_agent_session() - .session_id() - .find(&input.session_id) - .filter(|row| row.owner_user_id == input.owner_user_id) - .ok_or_else(|| "custom_world_agent_session 不存在".to_string())?; - - if ctx - .db - .custom_world_agent_message() - .message_id() - .find(&input.user_message_id) - .is_some() - { - return Err("custom_world_agent_message.message_id 已存在".to_string()); - } - if ctx - .db - .custom_world_agent_operation() - .operation_id() - .find(&input.operation_id) - .is_some() - { - return Err("custom_world_agent_operation.operation_id 已存在".to_string()); - } - - let submitted_at = Timestamp::from_micros_since_unix_epoch(input.submitted_at_micros); - let user_message_text = input.user_message_text.trim().to_string(); - - ctx.db - .custom_world_agent_message() - .insert(CustomWorldAgentMessage { - message_id: input.user_message_id, - session_id: input.session_id.clone(), - role: RpgAgentMessageRole::User, - kind: RpgAgentMessageKind::Chat, - text: user_message_text, - related_operation_id: Some(input.operation_id.clone()), - created_at: submitted_at, - }); - - ctx.db - .custom_world_agent_operation() - .insert(CustomWorldAgentOperation { - operation_id: input.operation_id.clone(), - session_id: input.session_id.clone(), - operation_type: RpgAgentOperationType::ProcessMessage, - status: RpgAgentOperationStatus::Running, - phase_label: "消息处理中".to_string(), - phase_detail: "已记录用户消息,等待大模型生成本轮回复。".to_string(), - progress: 10, - error_message: None, - created_at: submitted_at, - updated_at: submitted_at, - }); - - get_custom_world_agent_operation_tx( - ctx, - CustomWorldAgentOperationGetInput { - session_id: input.session_id, - owner_user_id: input.owner_user_id, - operation_id: input.operation_id, - }, - ) -} - -fn get_custom_world_agent_operation_tx( - ctx: &ReducerContext, - input: CustomWorldAgentOperationGetInput, -) -> Result { - validate_custom_world_agent_operation_get_input(&input).map_err(|error| error.to_string())?; - - ctx.db - .custom_world_agent_session() - .session_id() - .find(&input.session_id) - .filter(|row| row.owner_user_id == input.owner_user_id) - .ok_or_else(|| "custom_world_agent_session 不存在".to_string())?; - - let operation = ctx - .db - .custom_world_agent_operation() - .operation_id() - .find(&input.operation_id) - .filter(|row| row.session_id == input.session_id) - .ok_or_else(|| "custom_world_agent_operation 不存在".to_string())?; - - Ok(build_custom_world_agent_operation_snapshot(&operation)) -} - -fn upsert_custom_world_agent_operation_progress_tx( - ctx: &ReducerContext, - input: CustomWorldAgentOperationProgressInput, -) -> Result { - validate_custom_world_agent_operation_progress_input(&input) - .map_err(|error| error.to_string())?; - ctx.db - .custom_world_agent_session() - .session_id() - .find(&input.session_id) - .filter(|row| row.owner_user_id == input.owner_user_id) - .ok_or_else(|| "custom_world_agent_session 不存在".to_string())?; - - let timestamp = Timestamp::from_micros_since_unix_epoch(input.updated_at_micros); - let operation = if let Some(current) = ctx - .db - .custom_world_agent_operation() - .operation_id() - .find(&input.operation_id) - { - if current.session_id != input.session_id { - return Err("custom_world_agent_operation.session_id 不匹配".to_string()); - } - let next = rebuild_custom_world_agent_operation_row( - ¤t, - CustomWorldAgentOperationPatch { - status: Some(input.operation_status), - phase_label: Some(input.phase_label.clone()), - phase_detail: Some(input.phase_detail.clone()), - progress: Some(input.operation_progress), - error_message: Some(input.error_message.clone()), - updated_at_micros: Some(input.updated_at_micros), - }, - )?; - replace_custom_world_agent_operation(ctx, ¤t, next.clone()); - next - } else { - ctx.db - .custom_world_agent_operation() - .insert(CustomWorldAgentOperation { - operation_id: input.operation_id.clone(), - session_id: input.session_id.clone(), - operation_type: input.operation_type, - status: input.operation_status, - phase_label: input.phase_label.clone(), - phase_detail: input.phase_detail.clone(), - progress: input.operation_progress, - error_message: input.error_message.clone(), - created_at: timestamp, - updated_at: timestamp, - }) - }; - - Ok(build_custom_world_agent_operation_snapshot(&operation)) -} - -fn finalize_custom_world_agent_message_turn_tx( - ctx: &ReducerContext, - input: CustomWorldAgentMessageFinalizeInput, -) -> Result { - validate_custom_world_agent_message_finalize_input(&input) - .map_err(|error| error.to_string())?; - - let session = ctx - .db - .custom_world_agent_session() - .session_id() - .find(&input.session_id) - .filter(|row| row.owner_user_id == input.owner_user_id) - .ok_or_else(|| "custom_world_agent_session 不存在".to_string())?; - - let operation = ctx - .db - .custom_world_agent_operation() - .operation_id() - .find(&input.operation_id) - .filter(|row| row.session_id == input.session_id) - .ok_or_else(|| "custom_world_agent_operation 不存在".to_string())?; - - let updated_at = Timestamp::from_micros_since_unix_epoch(input.updated_at_micros); - - let next_session = if input.operation_status == RpgAgentOperationStatus::Failed { - rebuild_custom_world_agent_session_row( - &session, - CustomWorldAgentSessionPatch { - updated_at_micros: Some(input.updated_at_micros), - ..CustomWorldAgentSessionPatch::default() - }, - )? - } else { - let assistant_message_id = input.assistant_message_id.clone().ok_or_else(|| { - "custom_world_agent_message.assistant_message_id 不能为空".to_string() - })?; - let assistant_reply_text = input - .assistant_reply_text - .clone() - .ok_or_else(|| "custom_world_agent_message.text 不能为空".to_string())?; - - if ctx - .db - .custom_world_agent_message() - .message_id() - .find(&assistant_message_id) - .is_some() - { - return Err("custom_world_agent_message.assistant_message_id 已存在".to_string()); - } - - ctx.db - .custom_world_agent_message() - .insert(CustomWorldAgentMessage { - message_id: assistant_message_id, - session_id: input.session_id.clone(), - role: RpgAgentMessageRole::Assistant, - kind: RpgAgentMessageKind::Chat, - text: assistant_reply_text.clone(), - related_operation_id: Some(input.operation_id.clone()), - created_at: updated_at, - }); - - rebuild_custom_world_agent_session_row( - &session, - CustomWorldAgentSessionPatch { - current_turn: Some(session.current_turn.saturating_add(1)), - progress_percent: Some(input.progress_percent), - stage: Some(input.stage), - focus_card_id: Some(input.focus_card_id.clone()), - anchor_content_json: Some(input.anchor_content_json.clone()), - creator_intent_json: Some(input.creator_intent_json.clone()), - creator_intent_readiness_json: Some(input.creator_intent_readiness_json.clone()), - anchor_pack_json: Some(input.anchor_pack_json.clone()), - draft_profile_json: Some(input.draft_profile_json.clone()), - last_assistant_reply: Some(Some(assistant_reply_text)), - pending_clarifications_json: Some(input.pending_clarifications_json.clone()), - quality_findings_json: Some(input.quality_findings_json.clone()), - suggested_actions_json: Some(input.suggested_actions_json.clone()), - recommended_replies_json: Some(input.recommended_replies_json.clone()), - asset_coverage_json: Some(input.asset_coverage_json.clone()), - updated_at_micros: Some(input.updated_at_micros), - ..CustomWorldAgentSessionPatch::default() - }, - )? - }; - replace_custom_world_agent_session(ctx, &session, next_session); - - let next_operation = rebuild_custom_world_agent_operation_row( - &operation, - CustomWorldAgentOperationPatch { - status: Some(input.operation_status), - phase_label: Some(input.phase_label.clone()), - phase_detail: Some(input.phase_detail.clone()), - progress: Some(input.operation_progress), - error_message: Some(input.error_message.clone()), - updated_at_micros: Some(input.updated_at_micros), - }, - )?; - replace_custom_world_agent_operation(ctx, &operation, next_operation.clone()); - - Ok(build_custom_world_agent_operation_snapshot(&next_operation)) -} - -// 当前阶段先把 quest_record / quest_log 立成最小任务真相源,后续再把奖励结算和 story action 总分发接进来。 -#[spacetimedb::reducer] -pub fn accept_quest(ctx: &ReducerContext, input: QuestRecordInput) -> Result<(), String> { - let snapshot = build_quest_record_snapshot(input).map_err(|error| error.to_string())?; - - if ctx - .db - .quest_record() - .quest_id() - .find(&snapshot.quest_id) - .is_some() - { - return Err("quest_record.quest_id 已存在".to_string()); - } - - ctx.db - .quest_record() - .insert(build_quest_record_row(snapshot.clone())); - append_quest_log( - ctx, - &snapshot, - QuestLogEventKind::Accepted, - None, - None, - None, - None, - snapshot.created_at_micros, - ); - - Ok(()) -} - -// 任务推进 reducer 只认 QuestProgressSignal,不直接掺入背包、成长和关系奖励发放。 -#[spacetimedb::reducer] -pub fn apply_quest_signal( - ctx: &ReducerContext, - input: QuestSignalApplyInput, -) -> Result<(), String> { - let signal = input.signal.clone(); - let current = ctx - .db - .quest_record() - .quest_id() - .find(&input.quest_id) - .ok_or_else(|| "quest_record 不存在,无法应用任务信号".to_string())?; - let outcome = apply_quest_record_signal(build_quest_record_snapshot_from_row(¤t), input) - .map_err(|error| error.to_string())?; - - if !outcome.changed { - return Ok(()); - } - - ctx.db.quest_record().quest_id().delete(¤t.quest_id); - ctx.db - .quest_record() - .insert(build_quest_record_row(outcome.next_record.clone())); - append_quest_log( - ctx, - &outcome.next_record, - if outcome.completed_now { - QuestLogEventKind::Completed - } else { - QuestLogEventKind::Progressed - }, - Some(outcome.signal_kind), - Some(signal), - outcome.changed_step_id, - outcome.changed_step_progress, - outcome.next_record.updated_at_micros, - ); - - Ok(()) -} - -#[spacetimedb::reducer] -pub fn acknowledge_quest_completion( - ctx: &ReducerContext, - input: QuestCompletionAckInput, -) -> Result<(), String> { - let current = ctx - .db - .quest_record() - .quest_id() - .find(&input.quest_id) - .ok_or_else(|| "quest_record 不存在,无法确认完成提示".to_string())?; - let outcome = - acknowledge_quest_record_completion(build_quest_record_snapshot_from_row(¤t), input) - .map_err(|error| error.to_string())?; - - if !outcome.changed { - return Ok(()); - } - - ctx.db.quest_record().quest_id().delete(¤t.quest_id); - ctx.db - .quest_record() - .insert(build_quest_record_row(outcome.next_record.clone())); - append_quest_log( - ctx, - &outcome.next_record, - QuestLogEventKind::CompletionAcknowledged, - None, - None, - None, - None, - outcome.next_record.updated_at_micros, - ); - - Ok(()) -} - -#[spacetimedb::reducer] -pub fn turn_in_quest(ctx: &ReducerContext, input: QuestTurnInInput) -> Result<(), String> { - let current = ctx - .db - .quest_record() - .quest_id() - .find(&input.quest_id) - .ok_or_else(|| "quest_record 不存在,无法交付任务".to_string())?; - let next = turn_in_quest_record(build_quest_record_snapshot_from_row(¤t), input) - .map_err(|error| error.to_string())?; - - ctx.db.quest_record().quest_id().delete(¤t.quest_id); - ctx.db - .quest_record() - .insert(build_quest_record_row(next.clone())); - append_quest_log( - ctx, - &next, - QuestLogEventKind::TurnedIn, - None, - None, - None, - None, - next.updated_at_micros, - ); - - let reward_experience = next.reward.experience.unwrap_or(0); - grant_quest_reward_items(ctx, &next)?; - if reward_experience > 0 { - let updated_player = upsert_player_progression_after_grant_tx( - ctx, - PlayerProgressionGrantInput { - user_id: next.actor_user_id.clone(), - amount: reward_experience, - source: PlayerProgressionGrantSource::Quest, - updated_at_micros: next.updated_at_micros, - }, - )?; - - // 章节计划缺失时先保持任务交付成功,避免成长联动反向阻断 quest 主链。 - try_update_chapter_progression_ledger_tx( - ctx, - next.actor_user_id.clone(), - next.chapter_id.clone(), - ChapterProgressionLedgerInput { - user_id: next.actor_user_id.clone(), - chapter_id: next.chapter_id.clone().unwrap_or_default(), - granted_quest_xp: reward_experience, - granted_hostile_xp: 0, - hostile_defeat_increment: 0, - level_at_exit: Some(updated_player.level), - updated_at_micros: next.updated_at_micros, - }, - )?; - } - - Ok(()) -} - -// M5 Stage 2 先把 library profile upsert 固定成最小正式写入口;已发布作品在这里同步刷新 gallery 投影。 -#[spacetimedb::reducer] -pub fn upsert_custom_world_profile( - ctx: &ReducerContext, - input: CustomWorldProfileUpsertInput, -) -> Result<(), String> { - upsert_custom_world_profile_record(ctx, input).map(|_| ()) -} - -// procedure 面向 Axum 返回 profile 与可能同步出的 gallery 投影,避免 HTTP 层再二次查询私有表。 -#[spacetimedb::procedure] -pub fn upsert_custom_world_profile_and_return( - ctx: &mut ProcedureContext, - input: CustomWorldProfileUpsertInput, -) -> CustomWorldLibraryMutationResult { - match ctx.try_with_tx(|tx| upsert_custom_world_profile_record(tx, input.clone())) { - Ok((entry, gallery_entry)) => CustomWorldLibraryMutationResult { - ok: true, - entry: Some(entry), - gallery_entry, - error_message: None, - }, - Err(message) => CustomWorldLibraryMutationResult { - ok: false, - entry: None, - gallery_entry: None, - error_message: Some(message), - }, - } -} - -// publish 负责同时推进 profile 发布态与 gallery 公开投影,避免公开列表继续运行时拼装。 -#[spacetimedb::reducer] -pub fn publish_custom_world_profile( - ctx: &ReducerContext, - input: CustomWorldProfilePublishInput, -) -> Result<(), String> { - publish_custom_world_profile_record(ctx, input).map(|_| ()) -} - -#[spacetimedb::procedure] -pub fn publish_custom_world_profile_and_return( - ctx: &mut ProcedureContext, - input: CustomWorldProfilePublishInput, -) -> CustomWorldLibraryMutationResult { - match ctx.try_with_tx(|tx| publish_custom_world_profile_record(tx, input.clone())) { - Ok((entry, gallery_entry)) => CustomWorldLibraryMutationResult { - ok: true, - entry: Some(entry), - gallery_entry, - error_message: None, - }, - Err(message) => CustomWorldLibraryMutationResult { - ok: false, - entry: None, - gallery_entry: None, - error_message: Some(message), - }, - } -} - -// unpublish 负责撤掉 gallery 投影,并把 profile 恢复为 draft。 -#[spacetimedb::reducer] -pub fn unpublish_custom_world_profile( - ctx: &ReducerContext, - input: CustomWorldProfileUnpublishInput, -) -> Result<(), String> { - unpublish_custom_world_profile_record(ctx, input).map(|_| ()) -} - -#[spacetimedb::procedure] -pub fn unpublish_custom_world_profile_and_return( - ctx: &mut ProcedureContext, - input: CustomWorldProfileUnpublishInput, -) -> CustomWorldLibraryMutationResult { - match ctx.try_with_tx(|tx| unpublish_custom_world_profile_record(tx, input.clone())) { - Ok((entry, gallery_entry)) => CustomWorldLibraryMutationResult { - ok: true, - entry: Some(entry), - gallery_entry, - error_message: None, - }, - Err(message) => CustomWorldLibraryMutationResult { - ok: false, - entry: None, - gallery_entry: None, - error_message: Some(message), - }, - } -} - -// 删除入口继续走 owner-only 软删除,不直接物理删除 profile 真相。 -#[spacetimedb::procedure] -pub fn delete_custom_world_profile_and_return( - ctx: &mut ProcedureContext, - input: module_custom_world::CustomWorldProfileDeleteInput, -) -> CustomWorldProfileListResult { - match ctx.try_with_tx(|tx| { - delete_custom_world_profile_record(tx, input.clone())?; - list_custom_world_profile_snapshots( - tx, - CustomWorldProfileListInput { - owner_user_id: input.owner_user_id.clone(), - }, - ) - }) { - Ok(entries) => CustomWorldProfileListResult { - ok: true, - entries, - error_message: None, - }, - Err(message) => CustomWorldProfileListResult { - ok: false, - entries: Vec::new(), - error_message: Some(message), - }, - } -} - -#[spacetimedb::procedure] -pub fn list_custom_world_profiles( - ctx: &mut ProcedureContext, - input: CustomWorldProfileListInput, -) -> CustomWorldProfileListResult { - match ctx.try_with_tx(|tx| list_custom_world_profile_snapshots(tx, input.clone())) { - Ok(entries) => CustomWorldProfileListResult { - ok: true, - entries, - error_message: None, - }, - Err(message) => CustomWorldProfileListResult { - ok: false, - entries: Vec::new(), - error_message: Some(message), - }, - } -} - -#[spacetimedb::procedure] -pub fn list_custom_world_gallery_entries( - ctx: &mut ProcedureContext, -) -> CustomWorldGalleryListResult { - match ctx.try_with_tx(|tx| list_custom_world_gallery_snapshots(tx)) { - Ok(entries) => CustomWorldGalleryListResult { - ok: true, - entries, - error_message: None, - }, - Err(message) => CustomWorldGalleryListResult { - ok: false, - entries: Vec::new(), - error_message: Some(message), - }, - } -} - -#[spacetimedb::procedure] -pub fn get_custom_world_library_detail( - ctx: &mut ProcedureContext, - input: CustomWorldLibraryDetailInput, -) -> CustomWorldLibraryMutationResult { - match ctx.try_with_tx(|tx| get_custom_world_library_detail_record(tx, input.clone())) { - Ok((entry, gallery_entry)) => CustomWorldLibraryMutationResult { - ok: true, - entry, - gallery_entry, - error_message: None, - }, - Err(message) => CustomWorldLibraryMutationResult { - ok: false, - entry: None, - gallery_entry: None, - error_message: Some(message), - }, - } -} - -#[spacetimedb::procedure] -pub fn get_custom_world_gallery_detail( - ctx: &mut ProcedureContext, - input: CustomWorldGalleryDetailInput, -) -> CustomWorldLibraryMutationResult { - match ctx.try_with_tx(|tx| get_custom_world_gallery_detail_record(tx, input.clone())) { - Ok((entry, gallery_entry)) => CustomWorldLibraryMutationResult { - ok: true, - entry, - gallery_entry, - error_message: None, - }, - Err(message) => CustomWorldLibraryMutationResult { - ok: false, - entry: None, - gallery_entry: None, - error_message: Some(message), - }, - } -} - -#[spacetimedb::procedure] -pub fn get_custom_world_gallery_detail_by_code( - ctx: &mut ProcedureContext, - input: module_custom_world::CustomWorldGalleryDetailByCodeInput, -) -> CustomWorldLibraryMutationResult { - match ctx.try_with_tx(|tx| get_custom_world_gallery_detail_record_by_code(tx, input.clone())) { - Ok((entry, gallery_entry)) => CustomWorldLibraryMutationResult { - ok: true, - entry, - gallery_entry, - error_message: None, - }, - Err(message) => CustomWorldLibraryMutationResult { - ok: false, - entry: None, - gallery_entry: None, - error_message: Some(message), - }, - } -} - -#[spacetimedb::procedure] -pub fn list_custom_world_works( - ctx: &mut ProcedureContext, - input: CustomWorldWorksListInput, -) -> CustomWorldWorksListResult { - match ctx.try_with_tx(|tx| list_custom_world_work_snapshots(tx, input.clone())) { - Ok(items) => CustomWorldWorksListResult { - ok: true, - items, - error_message: None, - }, - Err(message) => CustomWorldWorksListResult { - ok: false, - items: Vec::new(), - error_message: Some(message), - }, - } -} - -#[spacetimedb::procedure] -pub fn get_custom_world_agent_card_detail( - ctx: &mut ProcedureContext, - input: CustomWorldAgentCardDetailGetInput, -) -> CustomWorldDraftCardDetailResult { - match ctx.try_with_tx(|tx| get_custom_world_agent_card_detail_tx(tx, input.clone())) { - Ok(card) => CustomWorldDraftCardDetailResult { - ok: true, - card: Some(card), - error_message: None, - }, - Err(message) => CustomWorldDraftCardDetailResult { - ok: false, - card: None, - error_message: Some(message), - }, - } -} - -#[spacetimedb::procedure] -pub fn execute_custom_world_agent_action( - ctx: &mut ProcedureContext, - input: CustomWorldAgentActionExecuteInput, -) -> CustomWorldAgentActionExecuteResult { - match ctx.try_with_tx(|tx| execute_custom_world_agent_action_tx(tx, input.clone())) { - Ok(operation) => CustomWorldAgentActionExecuteResult { - ok: true, - operation: Some(operation), - error_message: None, - }, - Err(message) => CustomWorldAgentActionExecuteResult { - ok: false, - operation: None, - error_message: Some(message), - }, - } -} - -// Stage 3 先把 published profile compile 作为独立 procedure 暴露,避免把编译逻辑和表写入、发布动作强耦合。 -#[spacetimedb::procedure] -pub fn compile_custom_world_published_profile( - _ctx: &mut ProcedureContext, - input: CustomWorldPublishedProfileCompileInput, -) -> CustomWorldPublishedProfileCompileResult { - match build_custom_world_published_profile_compile_snapshot(input) { - Ok(record) => CustomWorldPublishedProfileCompileResult { - ok: true, - record: Some(record), - error_message: None, - }, - Err(error) => CustomWorldPublishedProfileCompileResult { - ok: false, - record: None, - error_message: Some(error.to_string()), - }, - } -} - -// Stage 4 把 publish_world 串成单事务主链:compile -> profile upsert -> profile publish -> session.stage 推进。 -#[spacetimedb::procedure] -pub fn publish_custom_world_world( - ctx: &mut ProcedureContext, - input: CustomWorldPublishWorldInput, -) -> CustomWorldPublishWorldResult { - match ctx.try_with_tx(|tx| publish_custom_world_world_record(tx, input.clone())) { - Ok((compiled_record, entry, gallery_entry, session_stage)) => { - CustomWorldPublishWorldResult { - ok: true, - compiled_record: Some(compiled_record), - entry: Some(entry), - gallery_entry, - session_stage: Some(session_stage), - error_message: None, - } - } - Err(message) => CustomWorldPublishWorldResult { - ok: false, - compiled_record: None, - entry: None, - gallery_entry: None, - session_stage: None, - error_message: Some(message), - }, - } -} - -// M4 首轮先把 treasure_record 固定成可审计的宝藏结算真相表,奖励写入与 story 归属关系由 reducer 显式校验。 -#[spacetimedb::reducer] -pub fn resolve_treasure_interaction( - ctx: &ReducerContext, - input: TreasureResolveInput, -) -> Result<(), String> { - upsert_treasure_record(ctx, input).map(|_| ()) -} - -// procedure 面向后续 Axum facade,同步返回最终 treasure_record 快照,避免 HTTP 层再额外读取私有表。 -#[spacetimedb::procedure] -pub fn resolve_treasure_interaction_and_return( - ctx: &mut ProcedureContext, - input: TreasureResolveInput, -) -> TreasureRecordProcedureResult { - match ctx.try_with_tx(|tx| upsert_treasure_record(tx, input.clone())) { - Ok(record) => TreasureRecordProcedureResult { - ok: true, - record: Some(record), - error_message: None, - }, - Err(message) => TreasureRecordProcedureResult { - ok: false, - record: None, - error_message: Some(message), - }, - } -} - -fn upsert_treasure_record( - ctx: &ReducerContext, - input: TreasureResolveInput, -) -> Result { - let snapshot = build_treasure_record_snapshot(input).map_err(|error| error.to_string())?; - let story_session = ctx - .db - .story_session() - .story_session_id() - .find(&snapshot.story_session_id) - .ok_or_else(|| { - "treasure_record.story_session_id 对应的 story_session 不存在".to_string() - })?; - - if story_session.runtime_session_id != snapshot.runtime_session_id { - return Err( - "treasure_record.runtime_session_id 必须与 story_session.runtime_session_id 一致" - .to_string(), - ); - } - - if story_session.actor_user_id != snapshot.actor_user_id { - return Err( - "treasure_record.actor_user_id 必须与 story_session.actor_user_id 一致".to_string(), - ); - } - - // treasure_record 首版按单次结算真相处理:同 id 重放直接返回已落库快照,避免记录更新和重复发奖脱节。 - if let Some(existing) = ctx - .db - .treasure_record() - .treasure_record_id() - .find(&snapshot.treasure_record_id) - { - return Ok(build_treasure_record_snapshot_from_row(&existing)); - } - - let updated_at = Timestamp::from_micros_since_unix_epoch(snapshot.updated_at_micros); - let created_at = Timestamp::from_micros_since_unix_epoch(snapshot.created_at_micros); - - ctx.db - .treasure_record() - .insert(build_treasure_record_row(&snapshot, created_at, updated_at)); - - grant_treasure_reward_items_to_inventory(ctx, &snapshot)?; - - Ok(snapshot) -} - -fn grant_treasure_reward_items_to_inventory( - ctx: &ReducerContext, - snapshot: &TreasureRecordSnapshot, -) -> Result<(), String> { - for (index, reward_item) in snapshot.reward_items.iter().cloned().enumerate() { - let inventory_item = build_inventory_item_snapshot_from_reward_item( - &snapshot.treasure_record_id, - reward_item, - ) - .map_err(|error| error.to_string())?; - let slot_id = build_treasure_inventory_slot_id(&snapshot.treasure_record_id, index); - let mutation_id = build_treasure_inventory_mutation_id(&snapshot.treasure_record_id, index); - - apply_inventory_mutation_tx( - ctx, - InventoryMutationInput { - mutation_id, - runtime_session_id: snapshot.runtime_session_id.clone(), - story_session_id: Some(snapshot.story_session_id.clone()), - actor_user_id: snapshot.actor_user_id.clone(), - mutation: InventoryMutation::GrantItem(module_inventory::GrantInventoryItemInput { - slot_id, - item: inventory_item, - }), - updated_at_micros: snapshot.updated_at_micros, - }, - )?; - } - - Ok(()) -} - -fn build_treasure_inventory_slot_id(treasure_record_id: &str, reward_index: usize) -> String { - format!( - "{}{}_{}", - INVENTORY_SLOT_ID_PREFIX, treasure_record_id, reward_index - ) -} - -fn build_treasure_inventory_mutation_id(treasure_record_id: &str, reward_index: usize) -> String { - format!( - "{}{}_{}", - INVENTORY_MUTATION_ID_PREFIX, treasure_record_id, reward_index - ) -} - -fn build_treasure_record_row( - snapshot: &TreasureRecordSnapshot, - created_at: Timestamp, - updated_at: Timestamp, -) -> TreasureRecord { - TreasureRecord { - treasure_record_id: snapshot.treasure_record_id.clone(), - runtime_session_id: snapshot.runtime_session_id.clone(), - story_session_id: snapshot.story_session_id.clone(), - actor_user_id: snapshot.actor_user_id.clone(), - encounter_id: snapshot.encounter_id.clone(), - encounter_name: snapshot.encounter_name.clone(), - scene_id: snapshot.scene_id.clone(), - scene_name: snapshot.scene_name.clone(), - action: snapshot.action, - reward_items: snapshot.reward_items.clone(), - reward_hp: snapshot.reward_hp, - reward_mana: snapshot.reward_mana, - reward_currency: snapshot.reward_currency, - story_hint: snapshot.story_hint.clone(), - created_at, - updated_at, - } -} - -fn upsert_custom_world_profile_record( - ctx: &ReducerContext, - input: CustomWorldProfileUpsertInput, -) -> Result< - ( - CustomWorldProfileSnapshot, - Option, - ), - String, -> { - validate_custom_world_profile_upsert_input(&input).map_err(|error| error.to_string())?; - - let updated_at = Timestamp::from_micros_since_unix_epoch(input.updated_at_micros); - let current = ctx - .db - .custom_world_profile() - .profile_id() - .find(&input.profile_id) - .filter(|row| row.owner_user_id == input.owner_user_id) - .or_else(|| { - input - .source_agent_session_id - .as_ref() - .and_then(|session_id| { - ctx.db.custom_world_profile().iter().find(|row| { - is_same_agent_draft_profile_candidate(row, &input.owner_user_id, session_id) - }) - }) - }); - - let next_row = match current { - Some(existing) => { - ctx.db - .custom_world_profile() - .profile_id() - .delete(&existing.profile_id); - CustomWorldProfile { - profile_id: existing.profile_id.clone(), - owner_user_id: existing.owner_user_id.clone(), - public_work_code: existing.public_work_code.clone(), - author_public_user_code: existing.author_public_user_code.clone(), - source_agent_session_id: input.source_agent_session_id.clone(), - publication_status: existing.publication_status, - world_name: input.world_name.clone(), - subtitle: input.subtitle.clone(), - summary_text: input.summary_text.clone(), - theme_mode: input.theme_mode, - cover_image_src: input.cover_image_src.clone(), - profile_payload_json: input.profile_payload_json.clone(), - playable_npc_count: input.playable_npc_count, - landmark_count: input.landmark_count, - author_display_name: input.author_display_name.clone(), - published_at: existing.published_at, - deleted_at: None, - created_at: existing.created_at, - updated_at, - } - } - None => CustomWorldProfile { - profile_id: input.profile_id.clone(), - owner_user_id: input.owner_user_id.clone(), - public_work_code: input.public_work_code.clone(), - author_public_user_code: input.author_public_user_code.clone(), - source_agent_session_id: input.source_agent_session_id.clone(), - publication_status: CustomWorldPublicationStatus::Draft, - world_name: input.world_name.clone(), - subtitle: input.subtitle.clone(), - summary_text: input.summary_text.clone(), - theme_mode: input.theme_mode, - cover_image_src: input.cover_image_src.clone(), - profile_payload_json: input.profile_payload_json.clone(), - playable_npc_count: input.playable_npc_count, - landmark_count: input.landmark_count, - author_display_name: input.author_display_name.clone(), - published_at: None, - deleted_at: None, - created_at: updated_at, - updated_at, - }, - }; - - let inserted = ctx.db.custom_world_profile().insert(next_row); - - let gallery_entry = if inserted.publication_status == CustomWorldPublicationStatus::Published { - Some(sync_custom_world_gallery_entry_from_profile( - ctx, &inserted, - )?) - } else { - ctx.db - .custom_world_gallery_entry() - .profile_id() - .delete(&inserted.profile_id); - None - }; - - Ok(( - build_custom_world_profile_snapshot(&inserted), - gallery_entry, - )) -} - -fn publish_custom_world_world_record( - ctx: &ReducerContext, - input: CustomWorldPublishWorldInput, -) -> Result< - ( - module_custom_world::CustomWorldPublishedProfileCompileSnapshot, - CustomWorldProfileSnapshot, - Option, - RpgAgentStage, - ), - String, -> { - validate_custom_world_publish_world_input(&input).map_err(|error| error.to_string())?; - - let compiled_record = build_custom_world_published_profile_compile_snapshot( - CustomWorldPublishedProfileCompileInput { - session_id: input.session_id.clone(), - profile_id: input.profile_id.clone(), - owner_user_id: input.owner_user_id.clone(), - draft_profile_json: input.draft_profile_json.clone(), - legacy_result_profile_json: input.legacy_result_profile_json.clone(), - setting_text: input.setting_text.clone(), - author_display_name: input.author_display_name.clone(), - updated_at_micros: input.published_at_micros, - }, - ) - .map_err(|error| error.to_string())?; - - let _ = upsert_custom_world_profile_record( - ctx, - CustomWorldProfileUpsertInput { - profile_id: compiled_record.profile_id.clone(), - owner_user_id: compiled_record.owner_user_id.clone(), - public_work_code: input.public_work_code.clone(), - author_public_user_code: Some(input.author_public_user_code.clone()), - source_agent_session_id: Some(input.session_id.clone()), - world_name: compiled_record.world_name.clone(), - subtitle: compiled_record.subtitle.clone(), - summary_text: compiled_record.summary_text.clone(), - theme_mode: compiled_record.theme_mode, - cover_image_src: compiled_record.cover_image_src.clone(), - profile_payload_json: compiled_record.compiled_profile_payload_json.clone(), - playable_npc_count: compiled_record.playable_npc_count, - landmark_count: compiled_record.landmark_count, - author_display_name: compiled_record.author_display_name.clone(), - updated_at_micros: input.published_at_micros, - }, - )?; - - let (entry, gallery_entry) = publish_custom_world_profile_record( - ctx, - CustomWorldProfilePublishInput { - profile_id: compiled_record.profile_id.clone(), - owner_user_id: compiled_record.owner_user_id.clone(), - public_work_code: input.public_work_code.clone(), - author_public_user_code: input.author_public_user_code.clone(), - author_display_name: compiled_record.author_display_name.clone(), - published_at_micros: input.published_at_micros, - }, - )?; - - let session_stage = mark_custom_world_agent_session_published( - ctx, - &input.session_id, - &input.owner_user_id, - input.published_at_micros, - )?; - - Ok((compiled_record, entry, gallery_entry, session_stage)) -} - -fn publish_custom_world_profile_record( - ctx: &ReducerContext, - input: CustomWorldProfilePublishInput, -) -> Result< - ( - CustomWorldProfileSnapshot, - Option, - ), - String, -> { - validate_custom_world_profile_publish_input(&input).map_err(|error| error.to_string())?; - - let existing = ctx - .db - .custom_world_profile() - .profile_id() - .find(&input.profile_id) - .filter(|row| row.owner_user_id == input.owner_user_id) - .ok_or_else(|| "custom_world_profile 不存在,无法发布".to_string())?; - - let published_at = Timestamp::from_micros_since_unix_epoch(input.published_at_micros); - - ctx.db - .custom_world_profile() - .profile_id() - .delete(&existing.profile_id); - - let next_row = CustomWorldProfile { - profile_id: existing.profile_id.clone(), - owner_user_id: existing.owner_user_id.clone(), - public_work_code: existing - .public_work_code - .clone() - .or_else(|| Some(build_public_work_code_from_profile_id(&existing.profile_id))), - author_public_user_code: Some(input.author_public_user_code.clone()), - source_agent_session_id: existing.source_agent_session_id.clone(), - publication_status: CustomWorldPublicationStatus::Published, - world_name: existing.world_name.clone(), - subtitle: existing.subtitle.clone(), - summary_text: existing.summary_text.clone(), - theme_mode: existing.theme_mode, - cover_image_src: existing.cover_image_src.clone(), - profile_payload_json: existing.profile_payload_json.clone(), - playable_npc_count: existing.playable_npc_count, - landmark_count: existing.landmark_count, - author_display_name: input.author_display_name.clone(), - published_at: Some(published_at), - deleted_at: None, - created_at: existing.created_at, - updated_at: published_at, - }; - - let inserted = ctx.db.custom_world_profile().insert(next_row); - let gallery_entry = sync_custom_world_gallery_entry_from_profile(ctx, &inserted)?; - - Ok(( - build_custom_world_profile_snapshot(&inserted), - Some(gallery_entry), - )) -} - -fn unpublish_custom_world_profile_record( - ctx: &ReducerContext, - input: CustomWorldProfileUnpublishInput, -) -> Result< - ( - CustomWorldProfileSnapshot, - Option, - ), - String, -> { - validate_custom_world_profile_unpublish_input(&input).map_err(|error| error.to_string())?; - - let existing = ctx - .db - .custom_world_profile() - .profile_id() - .find(&input.profile_id) - .filter(|row| row.owner_user_id == input.owner_user_id) - .ok_or_else(|| "custom_world_profile 不存在,无法取消发布".to_string())?; - - let updated_at = Timestamp::from_micros_since_unix_epoch(input.updated_at_micros); - - ctx.db - .custom_world_profile() - .profile_id() - .delete(&existing.profile_id); - - ctx.db - .custom_world_gallery_entry() - .profile_id() - .delete(&existing.profile_id); - - let next_row = CustomWorldProfile { - profile_id: existing.profile_id.clone(), - owner_user_id: existing.owner_user_id.clone(), - public_work_code: existing.public_work_code.clone(), - author_public_user_code: existing.author_public_user_code.clone(), - source_agent_session_id: existing.source_agent_session_id.clone(), - publication_status: CustomWorldPublicationStatus::Draft, - world_name: existing.world_name.clone(), - subtitle: existing.subtitle.clone(), - summary_text: existing.summary_text.clone(), - theme_mode: existing.theme_mode, - cover_image_src: existing.cover_image_src.clone(), - profile_payload_json: existing.profile_payload_json.clone(), - playable_npc_count: existing.playable_npc_count, - landmark_count: existing.landmark_count, - author_display_name: input.author_display_name.clone(), - published_at: None, - deleted_at: None, - created_at: existing.created_at, - updated_at, - }; - - let inserted = ctx.db.custom_world_profile().insert(next_row); - - Ok((build_custom_world_profile_snapshot(&inserted), None)) -} - -fn delete_custom_world_profile_record( - ctx: &ReducerContext, - input: module_custom_world::CustomWorldProfileDeleteInput, -) -> Result<(), String> { - validate_custom_world_profile_delete_input(&input).map_err(|error| error.to_string())?; - - let Some(existing) = ctx - .db - .custom_world_profile() - .profile_id() - .find(&input.profile_id) - .filter(|row| row.owner_user_id == input.owner_user_id) - else { - return Ok(()); - }; - - if existing.deleted_at.is_some() { - return Ok(()); - } - - let deleted_at = Timestamp::from_micros_since_unix_epoch(input.deleted_at_micros); - - ctx.db - .custom_world_profile() - .profile_id() - .delete(&existing.profile_id); - - ctx.db - .custom_world_gallery_entry() - .profile_id() - .delete(&existing.profile_id); - - let next_row = CustomWorldProfile { - profile_id: existing.profile_id.clone(), - owner_user_id: existing.owner_user_id.clone(), - public_work_code: existing.public_work_code.clone(), - author_public_user_code: existing.author_public_user_code.clone(), - source_agent_session_id: existing.source_agent_session_id.clone(), - publication_status: CustomWorldPublicationStatus::Draft, - world_name: existing.world_name.clone(), - subtitle: existing.subtitle.clone(), - summary_text: existing.summary_text.clone(), - theme_mode: existing.theme_mode, - cover_image_src: existing.cover_image_src.clone(), - profile_payload_json: existing.profile_payload_json.clone(), - playable_npc_count: existing.playable_npc_count, - landmark_count: existing.landmark_count, - author_display_name: existing.author_display_name.clone(), - published_at: None, - deleted_at: Some(deleted_at), - created_at: existing.created_at, - updated_at: deleted_at, - }; - - let _ = ctx.db.custom_world_profile().insert(next_row); - Ok(()) -} - -fn list_custom_world_profile_snapshots( - ctx: &ReducerContext, - input: CustomWorldProfileListInput, -) -> Result, String> { - validate_custom_world_profile_list_input(&input).map_err(|error| error.to_string())?; - - let mut entries = ctx - .db - .custom_world_profile() - .iter() - .filter(|row| row.owner_user_id == input.owner_user_id && row.deleted_at.is_none()) - .map(|row| build_custom_world_profile_snapshot(&row)) - .collect::>(); - - entries.sort_by(|left, right| right.updated_at_micros.cmp(&left.updated_at_micros)); - - Ok(entries) -} - -fn list_custom_world_gallery_snapshots( - ctx: &ReducerContext, -) -> Result, String> { - sync_missing_custom_world_gallery_entries(ctx)?; - - let mut entries = ctx - .db - .custom_world_gallery_entry() - .iter() - .map(|row| build_custom_world_gallery_entry_snapshot(&row)) - .collect::>(); - - entries.sort_by(|left, right| { - right - .published_at_micros - .cmp(&left.published_at_micros) - .then(right.updated_at_micros.cmp(&left.updated_at_micros)) - }); - - Ok(entries) -} - -fn get_custom_world_library_detail_record( - ctx: &ReducerContext, - input: CustomWorldLibraryDetailInput, -) -> Result< - ( - Option, - Option, - ), - String, -> { - validate_custom_world_library_detail_input(&input).map_err(|error| error.to_string())?; - - let profile = ctx - .db - .custom_world_profile() - .profile_id() - .find(&input.profile_id) - .filter(|row| row.owner_user_id == input.owner_user_id && row.deleted_at.is_none()); - - let gallery_entry = profile - .as_ref() - .filter(|row| row.publication_status == CustomWorldPublicationStatus::Published) - .and_then(|row| { - ctx.db - .custom_world_gallery_entry() - .profile_id() - .find(&row.profile_id) - .filter(|gallery_row| gallery_row.owner_user_id == row.owner_user_id) - }); - - Ok(( - profile.as_ref().map(build_custom_world_profile_snapshot), - gallery_entry - .as_ref() - .map(build_custom_world_gallery_entry_snapshot), - )) -} - -fn get_custom_world_gallery_detail_record( - ctx: &ReducerContext, - input: CustomWorldGalleryDetailInput, -) -> Result< - ( - Option, - Option, - ), - String, -> { - validate_custom_world_gallery_detail_input(&input).map_err(|error| error.to_string())?; - - let profile = ctx - .db - .custom_world_profile() - .profile_id() - .find(&input.profile_id) - .filter(|row| { - row.owner_user_id == input.owner_user_id - && row.publication_status == CustomWorldPublicationStatus::Published - && row.deleted_at.is_none() - }); - - let gallery_entry = ctx - .db - .custom_world_gallery_entry() - .profile_id() - .find(&input.profile_id) - .filter(|row| row.owner_user_id == input.owner_user_id); - - Ok(( - profile.as_ref().map(build_custom_world_profile_snapshot), - gallery_entry - .as_ref() - .map(build_custom_world_gallery_entry_snapshot), - )) -} - -fn get_custom_world_gallery_detail_record_by_code( - ctx: &ReducerContext, - input: module_custom_world::CustomWorldGalleryDetailByCodeInput, -) -> Result< - ( - Option, - Option, - ), - String, -> { - validate_custom_world_gallery_detail_by_code_input(&input) - .map_err(|error| error.to_string())?; - - let normalized_public_work_code = normalize_public_work_code(&input.public_work_code) - .ok_or_else(|| "public_work_code 格式不正确".to_string())?; - - let gallery_entry = ctx - .db - .custom_world_gallery_entry() - .iter() - .find(|row| row.public_work_code == normalized_public_work_code); - - let profile = gallery_entry.as_ref().and_then(|row| { - ctx.db - .custom_world_profile() - .profile_id() - .find(&row.profile_id) - .filter(|profile_row| { - profile_row.owner_user_id == row.owner_user_id - && profile_row.publication_status == CustomWorldPublicationStatus::Published - && profile_row.deleted_at.is_none() - }) - }); - - Ok(( - profile.as_ref().map(build_custom_world_profile_snapshot), - gallery_entry - .as_ref() - .map(build_custom_world_gallery_entry_snapshot), - )) -} - -fn list_custom_world_work_snapshots( - ctx: &ReducerContext, - input: CustomWorldWorksListInput, -) -> Result, String> { - validate_custom_world_works_list_input(&input).map_err(|error| error.to_string())?; - - let mut items = Vec::new(); - let mut active_agent_session_ids = HashSet::new(); - - for session in ctx.db.custom_world_agent_session().iter().filter(|row| { - row.owner_user_id == input.owner_user_id - && row.stage != RpgAgentStage::Published - && should_include_custom_world_agent_session_work(ctx, row) - }) { - active_agent_session_ids.insert(session.session_id.clone()); - let gate = build_custom_world_publish_gate_from_session(&session); - let draft_profile = parse_optional_session_object(session.draft_profile_json.as_deref()); - let title = resolve_session_work_title(&session, draft_profile.as_ref()); - let summary = resolve_session_work_summary(&session, draft_profile.as_ref()); - let stage_label = Some(resolve_rpg_agent_stage_label(session.stage).to_string()); - let subtitle = - resolve_session_work_subtitle(draft_profile.as_ref(), stage_label.as_deref()); - let (playable_npc_count, landmark_count) = - resolve_session_work_counts(ctx, &session, draft_profile.as_ref()); - - items.push(CustomWorldWorkSummarySnapshot { - work_id: format!("draft:{}", session.session_id), - source_type: "agent_session".to_string(), - status: "draft".to_string(), - title, - subtitle, - summary, - cover_image_src: resolve_session_work_cover_image_src(draft_profile.as_ref()), - cover_render_mode: None, - cover_character_image_srcs_json: "[]".to_string(), - updated_at_micros: session.updated_at.to_micros_since_unix_epoch(), - published_at_micros: None, - stage: Some(session.stage), - stage_label, - playable_npc_count, - landmark_count, - role_visual_ready_count: None, - role_animation_ready_count: None, - role_asset_summary_label: None, - session_id: Some(session.session_id.clone()), - profile_id: None, - can_resume: true, - can_enter_world: gate.can_enter_world, - blocker_count: gate.blocker_count, - publish_ready: gate.publish_ready, - }); - } - - for profile in ctx - .db - .custom_world_profile() - .iter() - .filter(|row| row.owner_user_id == input.owner_user_id && row.deleted_at.is_none()) - .filter(|row| should_include_custom_world_profile_work(row, &active_agent_session_ids)) - { - items.push(CustomWorldWorkSummarySnapshot { - work_id: format!("published:{}", profile.profile_id), - source_type: "published_profile".to_string(), - status: profile.publication_status.as_str().to_string(), - title: profile.world_name.clone(), - subtitle: profile.subtitle.clone(), - summary: profile.summary_text.clone(), - cover_image_src: profile.cover_image_src.clone(), - cover_render_mode: None, - cover_character_image_srcs_json: "[]".to_string(), - updated_at_micros: profile.updated_at.to_micros_since_unix_epoch(), - published_at_micros: profile - .published_at - .map(|value| value.to_micros_since_unix_epoch()), - stage: None, - stage_label: None, - playable_npc_count: profile.playable_npc_count, - landmark_count: profile.landmark_count, - role_visual_ready_count: None, - role_animation_ready_count: None, - role_asset_summary_label: None, - session_id: profile.source_agent_session_id.clone(), - profile_id: Some(profile.profile_id.clone()), - can_resume: false, - can_enter_world: profile.publication_status == CustomWorldPublicationStatus::Published, - blocker_count: 0, - publish_ready: true, - }); - } - - items.sort_by(|left, right| { - right - .updated_at_micros - .cmp(&left.updated_at_micros) - .then_with(|| { - let left_rank = if left.source_type == "agent_session" { - 0 - } else { - 1 - }; - let right_rank = if right.source_type == "agent_session" { - 0 - } else { - 1 - }; - left_rank.cmp(&right_rank) - }) - .then(left.work_id.cmp(&right.work_id)) - }); - - Ok(items) -} - -fn should_include_custom_world_agent_session_work( - ctx: &ReducerContext, - session: &CustomWorldAgentSession, -) -> bool { - if custom_world_agent_session_has_direct_work_content(session) { - return true; - } - - if ctx.db.custom_world_agent_message().iter().any(|message| { - message.session_id == session.session_id - && matches!(message.role, RpgAgentMessageRole::User) - }) { - return true; - } - - ctx.db - .custom_world_draft_card() - .iter() - .any(|card| card.session_id == session.session_id) -} - -fn custom_world_agent_session_has_direct_work_content(session: &CustomWorldAgentSession) -> bool { - // 创建会话时写入的助手欢迎语和空 `{}` draftProfile 不算草稿内容; - // 这里只承认用户显式输入的 seed 或已经生成出的真实草稿阶段。 - !session.seed_text.trim().is_empty() - || matches!( - session.stage, - RpgAgentStage::ObjectRefining - | RpgAgentStage::VisualRefining - | RpgAgentStage::LongTailReview - | RpgAgentStage::ReadyToPublish - | RpgAgentStage::Published - ) - || parse_optional_session_object(session.draft_profile_json.as_deref()) - .as_ref() - .is_some_and(|profile| !profile.is_empty()) -} - -fn should_include_custom_world_profile_work( - row: &CustomWorldProfile, - active_agent_session_ids: &HashSet, -) -> bool { - // 已发布 profile 是正式作品;即使来源会话还存在,也必须保留独立入口。 - if row.publication_status == CustomWorldPublicationStatus::Published { - return true; - } - - // 未发布 profile 若来源于仍可继续聊天的 Agent 会话,只是同一草稿的编译产物, - // works 里保留 agent_session 即可,避免草稿分组显示两份同名作品。 - row.source_agent_session_id - .as_ref() - .map_or(true, |session_id| { - !active_agent_session_ids.contains(session_id) - }) -} - -fn get_custom_world_agent_card_detail_tx( - ctx: &ReducerContext, - input: CustomWorldAgentCardDetailGetInput, -) -> Result { - validate_custom_world_agent_card_detail_get_input(&input).map_err(|error| error.to_string())?; - - ctx.db - .custom_world_agent_session() - .session_id() - .find(&input.session_id) - .filter(|row| row.owner_user_id == input.owner_user_id) - .ok_or_else(|| "custom_world_agent_session 不存在".to_string())?; - - let card = ctx - .db - .custom_world_draft_card() - .card_id() - .find(&input.card_id) - .filter(|row| row.session_id == input.session_id) - .ok_or_else(|| "custom_world_draft_card 不存在".to_string())?; - - build_custom_world_draft_card_detail_snapshot(&card) -} - -fn execute_custom_world_agent_action_tx( - ctx: &ReducerContext, - input: CustomWorldAgentActionExecuteInput, -) -> Result { - validate_custom_world_agent_action_execute_input(&input).map_err(|error| error.to_string())?; - - let session = ctx - .db - .custom_world_agent_session() - .session_id() - .find(&input.session_id) - .filter(|row| row.owner_user_id == input.owner_user_id) - .ok_or_else(|| "custom_world_agent_session 不存在".to_string())?; - - if let Some(existing_operation) = ctx - .db - .custom_world_agent_operation() - .operation_id() - .find(&input.operation_id) - { - let can_reuse_running_draft_operation = input.action.trim() == "draft_foundation" - && existing_operation.session_id == input.session_id - && existing_operation.operation_type == RpgAgentOperationType::DraftFoundation - && matches!( - existing_operation.status, - RpgAgentOperationStatus::Queued | RpgAgentOperationStatus::Running - ); - if !can_reuse_running_draft_operation { - return Err("custom_world_agent_operation.operation_id 已存在".to_string()); - } - } - - let payload = parse_optional_session_object(input.payload_json.as_deref()).unwrap_or_default(); - match input.action.trim() { - "draft_foundation" => execute_draft_foundation_action(ctx, &session, &input, &payload), - "update_draft_card" => execute_update_draft_card_action(ctx, &session, &input, &payload), - "sync_result_profile" => { - execute_sync_result_profile_action(ctx, &session, &input, &payload) - } - "publish_world" => execute_publish_world_action(ctx, &session, &input, &payload), - "revert_checkpoint" => execute_revert_checkpoint_action(ctx, &session, &input, &payload), - "generate_characters" - | "generate_landmarks" - | "generate_role_assets" - | "sync_role_assets" - | "generate_scene_assets" - | "sync_scene_assets" - | "expand_long_tail" => execute_placeholder_custom_world_action(ctx, &session, &input), - other => Err(format!("custom world action `{other}` 当前尚未支持")), - } -} - -fn execute_draft_foundation_action( - ctx: &ReducerContext, - session: &CustomWorldAgentSession, - input: &CustomWorldAgentActionExecuteInput, - payload: &JsonMap, -) -> Result { - if session.progress_percent < 100 { - return Err("draft_foundation requires progressPercent >= 100".to_string()); - } - - let updated_at = input.submitted_at_micros; - let draft_profile = payload - .get("draftProfile") - .and_then(JsonValue::as_object) - .cloned() - .ok_or_else(|| { - "draft_foundation requires externally generated payload.draftProfile".to_string() - })?; - - let draft_profile_json = serde_json::to_string(&JsonValue::Object(draft_profile.clone())) - .map_err(|error| format!("draft_foundation 无法序列化 draft_profile_json: {error}"))?; - let gate = summarize_publish_gate_from_json( - &input.session_id, - RpgAgentStage::ObjectRefining, - Some(&draft_profile), - &parse_json_array_or_empty(&session.quality_findings_json), - ); - let next_session = rebuild_custom_world_agent_session_row( - session, - CustomWorldAgentSessionPatch { - progress_percent: Some(100), - stage: Some(RpgAgentStage::ObjectRefining), - draft_profile_json: Some(Some(draft_profile_json.clone())), - last_assistant_reply: Some(Some( - "世界底稿已整理完成,接下来可以继续细化卡片和发布预览。".to_string(), - )), - publish_gate_json: Some(Some(serialize_json_value(&publish_gate_to_json_value( - &gate, - ))?)), - result_preview_json: Some(build_result_preview_json( - Some(&draft_profile), - &gate, - &parse_json_array_or_empty(&session.quality_findings_json), - updated_at, - )?), - checkpoints_json: Some(append_checkpoint_json( - &session.checkpoints_json, - &build_session_checkpoint_value("foundation-ready", "底稿整理完成", session), - )?), - updated_at_micros: Some(updated_at), - ..CustomWorldAgentSessionPatch::default() - }, - )?; - replace_custom_world_agent_session(ctx, session, next_session); - - upsert_world_foundation_card(ctx, &session.session_id, &draft_profile, updated_at)?; - append_custom_world_action_result_message( - ctx, - &session.session_id, - &input.operation_id, - "已整理出第一版世界底稿,并同步生成世界基础卡片。", - updated_at, - ); - - let operation = complete_custom_world_operation( - ctx, - &input.operation_id, - &session.session_id, - RpgAgentOperationType::DraftFoundation, - "底稿已整理", - "第一版 foundation draft 已写入会话与世界卡。", - updated_at, - )?; - - Ok(build_custom_world_agent_operation_snapshot(&operation)) -} - -fn execute_update_draft_card_action( - ctx: &ReducerContext, - session: &CustomWorldAgentSession, - input: &CustomWorldAgentActionExecuteInput, - payload: &JsonMap, -) -> Result { - ensure_refining_stage(session.stage, "update_draft_card")?; - - let card_id = - read_required_payload_text(payload, "cardId", "update_draft_card requires cardId")?; - let card = ctx - .db - .custom_world_draft_card() - .card_id() - .find(&card_id) - .filter(|row| row.session_id == session.session_id) - .ok_or_else(|| "update_draft_card target card does not exist".to_string())?; - let sections = payload - .get("sections") - .and_then(JsonValue::as_array) - .ok_or_else(|| "update_draft_card requires sections".to_string())?; - if sections.is_empty() { - return Err("update_draft_card requires sections".to_string()); - } - - let mut detail_object = - parse_optional_session_object(card.detail_payload_json.as_deref()).unwrap_or_default(); - let mut detail_sections = detail_object - .get("sections") - .and_then(JsonValue::as_array) - .cloned() - .unwrap_or_else(|| build_fallback_card_sections_json(&card)); - - for patch in sections { - let patch_object = patch - .as_object() - .ok_or_else(|| "update_draft_card.sections 必须是 object 数组".to_string())?; - let section_id = read_required_payload_text( - patch_object, - "sectionId", - "update_draft_card section.sectionId is required", - )?; - let value = patch_object - .get("value") - .and_then(JsonValue::as_str) - .unwrap_or_default() - .trim() - .to_string(); - - let mut updated = false; - for existing in &mut detail_sections { - if existing.get("id").and_then(JsonValue::as_str) == Some(section_id.as_str()) { - if let Some(object) = existing.as_object_mut() { - object.insert("value".to_string(), JsonValue::String(value.clone())); - } - updated = true; - break; - } - } - - if !updated { - detail_sections.push(json!({ - "id": section_id, - "label": section_id, - "value": value, - })); - } - } - - detail_object.insert("id".to_string(), JsonValue::String(card.card_id.clone())); - detail_object.insert( - "kind".to_string(), - JsonValue::String(card.kind.as_str().to_string()), - ); - detail_object.insert("title".to_string(), JsonValue::String(card.title.clone())); - detail_object.insert( - "sections".to_string(), - JsonValue::Array(detail_sections.clone()), - ); - detail_object.insert( - "linkedIds".to_string(), - serde_json::from_str::(&card.linked_ids_json) - .unwrap_or_else(|_| JsonValue::Array(Vec::new())), - ); - detail_object.insert("locked".to_string(), JsonValue::Bool(false)); - detail_object.insert("editable".to_string(), JsonValue::Bool(false)); - detail_object.insert( - "editableSectionIds".to_string(), - JsonValue::Array(Vec::new()), - ); - detail_object.insert("warningMessages".to_string(), JsonValue::Array(Vec::new())); - - let updated_title = extract_detail_section_value(&detail_sections, "title") - .unwrap_or_else(|| card.title.clone()); - let updated_subtitle = extract_detail_section_value(&detail_sections, "subtitle") - .unwrap_or_else(|| card.subtitle.clone()); - let updated_summary = extract_detail_section_value(&detail_sections, "summary") - .unwrap_or_else(|| card.summary.clone()); - let detail_payload_json = serde_json::to_string(&JsonValue::Object(detail_object)) - .map_err(|error| format!("update_draft_card 无法序列化 detail_payload_json: {error}"))?; - - replace_custom_world_draft_card( - ctx, - &card, - CustomWorldDraftCard { - card_id: card.card_id.clone(), - session_id: card.session_id.clone(), - kind: card.kind, - status: card.status, - title: updated_title.clone(), - subtitle: updated_subtitle.clone(), - summary: updated_summary.clone(), - linked_ids_json: card.linked_ids_json.clone(), - warning_count: card.warning_count, - asset_status: card.asset_status, - asset_status_label: card.asset_status_label.clone(), - detail_payload_json: Some(detail_payload_json), - created_at: card.created_at, - updated_at: Timestamp::from_micros_since_unix_epoch(input.submitted_at_micros), - }, - ); - - let next_session = sync_session_draft_profile_from_card_update( - session, - &card, - &updated_title, - &updated_subtitle, - &updated_summary, - input.submitted_at_micros, - )?; - replace_custom_world_agent_session(ctx, session, next_session); - - append_custom_world_action_result_message( - ctx, - &session.session_id, - &input.operation_id, - &format!("已更新卡片《{}》的草稿内容。", updated_title), - input.submitted_at_micros, - ); - - let operation = build_and_insert_custom_world_operation( - ctx, - &input.operation_id, - &session.session_id, - RpgAgentOperationType::UpdateDraftCard, - "卡片已更新", - &format!("卡片 {} 的 detail 与摘要字段已同步更新。", card_id), - input.submitted_at_micros, - ); - - Ok(build_custom_world_agent_operation_snapshot(&operation)) -} - -fn execute_sync_result_profile_action( - ctx: &ReducerContext, - session: &CustomWorldAgentSession, - input: &CustomWorldAgentActionExecuteInput, - payload: &JsonMap, -) -> Result { - ensure_result_profile_sync_stage(session.stage, "sync_result_profile")?; - let mut profile = payload - .get("profile") - .and_then(JsonValue::as_object) - .cloned() - .ok_or_else(|| "sync_result_profile requires profile".to_string())?; - if let Some(stable_profile_id) = resolve_stable_agent_draft_profile_id(session) { - // 结果页回写时必须沿用当前草稿的稳定身份,避免把同一草稿写成新条目。 - profile.insert( - "id".to_string(), - JsonValue::String(stable_profile_id.clone()), - ); - upsert_nested_result_profile_id(&mut profile, &stable_profile_id); - } - let draft_profile = ensure_minimal_draft_profile(profile, &session.seed_text); - let gate = summarize_publish_gate_from_json( - &session.session_id, - session.stage, - Some(&draft_profile), - &parse_json_array_or_empty(&session.quality_findings_json), - ); - - let next_session = rebuild_custom_world_agent_session_row( - session, - CustomWorldAgentSessionPatch { - draft_profile_json: Some(Some(serialize_json_value(&JsonValue::Object( - draft_profile.clone(), - ))?)), - last_assistant_reply: Some(Some("结果页草稿已同步回当前会话。".to_string())), - publish_gate_json: Some(Some(serialize_json_value(&publish_gate_to_json_value( - &gate, - ))?)), - result_preview_json: Some(build_result_preview_json( - Some(&draft_profile), - &gate, - &parse_json_array_or_empty(&session.quality_findings_json), - input.submitted_at_micros, - )?), - checkpoints_json: Some(append_checkpoint_json( - &session.checkpoints_json, - &build_session_checkpoint_value("sync-result-profile", "同步结果页草稿", session), - )?), - updated_at_micros: Some(input.submitted_at_micros), - ..CustomWorldAgentSessionPatch::default() - }, - )?; - replace_custom_world_agent_session(ctx, session, next_session); - - append_custom_world_action_result_message( - ctx, - &session.session_id, - &input.operation_id, - "结果页 profile 已回写当前会话,并重建预览。", - input.submitted_at_micros, - ); - - let operation = build_and_insert_custom_world_operation( - ctx, - &input.operation_id, - &session.session_id, - RpgAgentOperationType::SyncResultProfile, - "结果页已同步", - "draft_profile_json 与 result_preview 已更新。", - input.submitted_at_micros, - ); - - Ok(build_custom_world_agent_operation_snapshot(&operation)) -} - -fn resolve_stable_agent_draft_profile_id(session: &CustomWorldAgentSession) -> Option { - parse_optional_session_object(session.draft_profile_json.as_deref()) - .and_then(|profile| read_optional_text_field(&profile, &["legacyResultProfile.id", "id"])) -} - -fn upsert_nested_result_profile_id( - profile: &mut JsonMap, - stable_profile_id: &str, -) { - let legacy_result_profile = profile - .entry("legacyResultProfile".to_string()) - .or_insert_with(|| JsonValue::Object(JsonMap::new())); - if let Some(object) = legacy_result_profile.as_object_mut() { - object.insert( - "id".to_string(), - JsonValue::String(stable_profile_id.to_string()), - ); - } -} - -fn is_same_agent_draft_profile_candidate( - row: &CustomWorldProfile, - owner_user_id: &str, - source_agent_session_id: &str, -) -> bool { - row.owner_user_id == owner_user_id - && row.deleted_at.is_none() - && row.publication_status == CustomWorldPublicationStatus::Draft - && row.source_agent_session_id.as_deref() == Some(source_agent_session_id) -} - -fn execute_publish_world_action( - ctx: &ReducerContext, - session: &CustomWorldAgentSession, - input: &CustomWorldAgentActionExecuteInput, - payload: &JsonMap, -) -> Result { - ensure_publishable_stage(session.stage, "publish_world")?; - - let draft_profile = - if let Some(explicit) = payload.get("draftProfile").and_then(JsonValue::as_object) { - explicit.clone() - } else { - parse_optional_session_object(session.draft_profile_json.as_deref()) - .ok_or_else(|| "publish_world requires draft_profile_json".to_string())? - }; - let gate = summarize_publish_gate_from_json( - &session.session_id, - session.stage, - Some(&draft_profile), - &parse_json_array_or_empty(&session.quality_findings_json), - ); - if !gate.publish_ready { - return Err(format!( - "当前世界仍有 {} 个 blocker,暂时不能发布", - gate.blocker_count - )); - } - - let profile_id = payload - .get("profileId") - .and_then(JsonValue::as_str) - .map(str::trim) - .filter(|value| !value.is_empty()) - .map(ToOwned::to_owned) - .unwrap_or_else(|| gate.profile_id.clone()); - let setting_text = payload - .get("settingText") - .and_then(JsonValue::as_str) - .map(str::trim) - .filter(|value| !value.is_empty()) - .map(ToOwned::to_owned) - .unwrap_or_else(|| session.seed_text.clone()); - let legacy_result_profile_json = payload - .get("legacyResultProfile") - .map(serialize_json_value) - .transpose()?; - let author_public_user_code = read_optional_text_field(payload, &["authorPublicUserCode"]) - .unwrap_or_else(|| build_public_user_code_from_owner_user_id(&session.owner_user_id)); - let author_display_name = read_optional_text_field(payload, &["authorDisplayName"]) - .unwrap_or_else(|| "创作者".to_string()); - let publish_result = publish_custom_world_world_record( - ctx, - CustomWorldPublishWorldInput { - session_id: session.session_id.clone(), - profile_id, - owner_user_id: session.owner_user_id.clone(), - public_work_code: None, - author_public_user_code, - draft_profile_json: serialize_json_value(&JsonValue::Object(draft_profile.clone()))?, - legacy_result_profile_json, - setting_text, - author_display_name, - published_at_micros: input.submitted_at_micros, - }, - )?; - - append_custom_world_action_result_message( - ctx, - &session.session_id, - &input.operation_id, - &format!("正式世界档案已发布:{}。", publish_result.1.profile_id), - input.submitted_at_micros, - ); - - let operation = build_and_insert_custom_world_operation( - ctx, - &input.operation_id, - &session.session_id, - RpgAgentOperationType::PublishWorld, - "世界已发布", - &format!( - "正式世界档案已写入作品库:{}。", - publish_result.1.profile_id - ), - input.submitted_at_micros, - ); - - Ok(build_custom_world_agent_operation_snapshot(&operation)) -} - -fn execute_revert_checkpoint_action( - ctx: &ReducerContext, - session: &CustomWorldAgentSession, - input: &CustomWorldAgentActionExecuteInput, - payload: &JsonMap, -) -> Result { - ensure_long_tail_stage(session.stage, "revert_checkpoint")?; - let checkpoint_id = read_required_payload_text( - payload, - "checkpointId", - "revert_checkpoint requires checkpointId", - )?; - let checkpoint = parse_json_array_or_empty(&session.checkpoints_json) - .into_iter() - .find(|entry| { - entry - .get("checkpointId") - .and_then(JsonValue::as_str) - .map(str::trim) - == Some(checkpoint_id.as_str()) - }) - .ok_or_else(|| "revert_checkpoint target checkpoint does not exist".to_string())?; - let snapshot = checkpoint - .get("snapshot") - .and_then(JsonValue::as_object) - .cloned() - .ok_or_else(|| { - "revert_checkpoint target checkpoint does not contain a restorable snapshot".to_string() - })?; - - let restored_stage = snapshot - .get("stage") - .and_then(JsonValue::as_str) - .and_then(parse_rpg_agent_stage) - .unwrap_or(session.stage); - let restored_progress = snapshot - .get("progressPercent") - .and_then(JsonValue::as_u64) - .and_then(|value| u32::try_from(value).ok()) - .unwrap_or(session.progress_percent); - let restored_draft_profile = snapshot - .get("draftProfile") - .and_then(JsonValue::as_object) - .cloned(); - let restored_quality_findings = snapshot - .get("qualityFindings") - .and_then(JsonValue::as_array) - .cloned() - .unwrap_or_else(Vec::new); - let gate = summarize_publish_gate_from_json( - &session.session_id, - restored_stage, - restored_draft_profile.as_ref(), - &restored_quality_findings, - ); - - let next_session = rebuild_custom_world_agent_session_row( - session, - CustomWorldAgentSessionPatch { - progress_percent: Some(restored_progress), - stage: Some(restored_stage), - draft_profile_json: Some( - restored_draft_profile - .as_ref() - .map(|value| serialize_json_value(&JsonValue::Object(value.clone()))) - .transpose()?, - ), - last_assistant_reply: Some(Some( - "已恢复到所选 checkpoint 的世界草稿状态。".to_string(), - )), - quality_findings_json: Some(serialize_json_value(&JsonValue::Array( - restored_quality_findings, - ))?), - publish_gate_json: Some(Some(serialize_json_value(&publish_gate_to_json_value( - &gate, - ))?)), - result_preview_json: Some(build_result_preview_json( - restored_draft_profile.as_ref(), - &gate, - &parse_json_array_or_empty(&serialize_json_value(&JsonValue::Array( - snapshot - .get("qualityFindings") - .and_then(JsonValue::as_array) - .cloned() - .unwrap_or_else(Vec::new), - ))?), - input.submitted_at_micros, - )?), - updated_at_micros: Some(input.submitted_at_micros), - ..CustomWorldAgentSessionPatch::default() - }, - )?; - replace_custom_world_agent_session(ctx, session, next_session); - - append_custom_world_action_result_message( - ctx, - &session.session_id, - &input.operation_id, - "已恢复到所选 checkpoint。", - input.submitted_at_micros, - ); - - let operation = build_and_insert_custom_world_operation( - ctx, - &input.operation_id, - &session.session_id, - RpgAgentOperationType::RevertCheckpoint, - "已回滚 checkpoint", - &format!("会话已恢复到 checkpoint {}。", checkpoint_id), - input.submitted_at_micros, - ); - - Ok(build_custom_world_agent_operation_snapshot(&operation)) -} - -fn execute_placeholder_custom_world_action( - ctx: &ReducerContext, - session: &CustomWorldAgentSession, - input: &CustomWorldAgentActionExecuteInput, -) -> Result { - let operation_type = map_action_name_to_operation_type(input.action.as_str()) - .ok_or_else(|| format!("action {} 无法映射到 operation type", input.action))?; - append_custom_world_action_result_message( - ctx, - &session.session_id, - &input.operation_id, - &format!( - "动作 {} 已接入最小兼容占位,后续会继续补真实编排。", - input.action - ), - input.submitted_at_micros, - ); - let operation = build_and_insert_custom_world_operation( - ctx, - &input.operation_id, - &session.session_id, - operation_type, - "动作已完成", - &format!("{} 当前已走最小兼容闭环。", input.action), - input.submitted_at_micros, - ); - Ok(build_custom_world_agent_operation_snapshot(&operation)) -} - -#[derive(Clone, Debug, Default)] -struct CustomWorldAgentSessionPatch { - current_turn: Option, - progress_percent: Option, - stage: Option, - focus_card_id: Option>, - anchor_content_json: Option, - creator_intent_json: Option>, - creator_intent_readiness_json: Option, - anchor_pack_json: Option>, - lock_state_json: Option>, - draft_profile_json: Option>, - last_assistant_reply: Option>, - publish_gate_json: Option>, - result_preview_json: Option>, - pending_clarifications_json: Option, - quality_findings_json: Option, - suggested_actions_json: Option, - recommended_replies_json: Option, - asset_coverage_json: Option, - checkpoints_json: Option, - updated_at_micros: Option, -} - -#[derive(Clone, Debug, Default)] -struct CustomWorldAgentOperationPatch { - status: Option, - phase_label: Option, - phase_detail: Option, - progress: Option, - error_message: Option>, - updated_at_micros: Option, -} - -fn build_custom_world_publish_gate_from_session( - session: &CustomWorldAgentSession, -) -> CustomWorldPublishGateSnapshot { - let quality_findings = parse_json_array_or_empty(&session.quality_findings_json); - summarize_publish_gate_from_json( - &session.session_id, - session.stage, - parse_optional_session_object(session.draft_profile_json.as_deref()).as_ref(), - &quality_findings, - ) -} - -fn summarize_publish_gate_from_json( - session_id: &str, - stage: RpgAgentStage, - draft_profile: Option<&JsonMap>, - quality_findings: &[JsonValue], -) -> CustomWorldPublishGateSnapshot { - let profile_id = draft_profile - .and_then(|profile| read_optional_text_field(profile, &["legacyResultProfile.id", "id"])) - .unwrap_or_else(|| format!("agent-draft-{session_id}")); - let mut blockers = Vec::new(); - - if draft_profile.is_none() { - blockers.push(CustomWorldPublishBlockerSnapshot { - blocker_id: "publish_empty_draft".to_string(), - code: "publish_empty_draft".to_string(), - message: "当前世界草稿为空,无法发布。".to_string(), - }); - } - - if let Some(profile) = draft_profile { - if read_optional_text_field( - profile, - &[ - "worldHook", - "creatorIntent.worldHook", - "anchorContent.worldPromise", - "anchorContent.worldPromise.hook", - "settingText", - ], - ) - .is_none() - { - blockers.push(CustomWorldPublishBlockerSnapshot { - blocker_id: "publish_missing_world_hook".to_string(), - code: "publish_missing_world_hook".to_string(), - message: "当前世界缺少 world hook,发布前需要先补齐世界一句话钩子。".to_string(), - }); - } - if read_optional_text_field( - profile, - &[ - "playerPremise", - "creatorIntent.playerPremise", - "anchorContent.playerEntryPoint", - "anchorContent.playerEntryPoint.openingIdentity", - "anchorContent.playerEntryPoint.openingProblem", - "anchorContent.playerEntryPoint.entryMotivation", - ], - ) - .is_none() - { - blockers.push(CustomWorldPublishBlockerSnapshot { - blocker_id: "publish_missing_player_premise".to_string(), - code: "publish_missing_player_premise".to_string(), - message: "当前世界缺少玩家身份与切入前提,发布前需要先补齐玩家 premise。" - .to_string(), - }); - } - if !json_array_has_non_empty_text(profile.get("coreConflicts")) { - blockers.push(CustomWorldPublishBlockerSnapshot { - blocker_id: "publish_missing_core_conflict".to_string(), - code: "publish_missing_core_conflict".to_string(), - message: "当前世界缺少核心冲突,发布前需要先补齐核心冲突。".to_string(), - }); - } - let has_main_chapter = profile - .get("chapters") - .and_then(JsonValue::as_array) - .map(|value| !value.is_empty()) - .unwrap_or(false) - || profile - .get("sceneChapterBlueprints") - .and_then(JsonValue::as_array) - .map(|value| !value.is_empty()) - .unwrap_or(false) - || profile - .get("sceneChapters") - .and_then(JsonValue::as_array) - .map(|value| !value.is_empty()) - .unwrap_or(false); - if !has_main_chapter { - blockers.push(CustomWorldPublishBlockerSnapshot { - blocker_id: "publish_missing_main_chapter".to_string(), - code: "publish_missing_main_chapter".to_string(), - message: "当前世界还没有主线章节草稿,发布前至少要保留主线第一幕。".to_string(), - }); - } - let has_scene_act = profile - .get("sceneChapterBlueprints") - .or_else(|| profile.get("sceneChapters")) - .and_then(JsonValue::as_array) - .map(|chapters| { - chapters.iter().any(|chapter| { - chapter - .get("acts") - .and_then(JsonValue::as_array) - .map(|acts| !acts.is_empty()) - .unwrap_or(false) - }) - }) - .unwrap_or(false); - if !has_scene_act { - blockers.push(CustomWorldPublishBlockerSnapshot { - blocker_id: "publish_missing_first_act".to_string(), - code: "publish_missing_first_act".to_string(), - message: "当前世界还没有主线第一幕,发布前至少要保留一个场景幕。".to_string(), - }); - } - } - - for finding in quality_findings { - if finding.get("severity").and_then(JsonValue::as_str) == Some("blocker") { - blockers.push(CustomWorldPublishBlockerSnapshot { - blocker_id: finding - .get("id") - .and_then(JsonValue::as_str) - .unwrap_or("publish-quality-blocker") - .to_string(), - code: finding - .get("code") - .and_then(JsonValue::as_str) - .unwrap_or("publish_quality_blocker") - .to_string(), - message: finding - .get("message") - .and_then(JsonValue::as_str) - .unwrap_or("当前世界仍存在 blocker。") - .to_string(), - }); - } - } - - let blocker_count = blockers.len() as u32; - let publish_ready = blocker_count == 0; - CustomWorldPublishGateSnapshot { - profile_id, - blockers, - blocker_count, - publish_ready, - can_enter_world: stage == RpgAgentStage::Published && publish_ready, - } -} - -fn publish_gate_to_json_value(gate: &CustomWorldPublishGateSnapshot) -> JsonValue { - json!({ - "profileId": gate.profile_id, - "blockers": gate.blockers.iter().map(|entry| { - json!({ - "id": entry.blocker_id, - "code": entry.code, - "message": entry.message, - }) - }).collect::>(), - "blockerCount": gate.blocker_count, - "publishReady": gate.publish_ready, - "canEnterWorld": gate.can_enter_world, - }) -} - -fn build_result_preview_json( - draft_profile: Option<&JsonMap>, - gate: &CustomWorldPublishGateSnapshot, - quality_findings: &[JsonValue], - generated_at_micros: i64, -) -> Result, String> { - let Some(profile) = draft_profile else { - return Ok(None); - }; - - serialize_json_value(&json!({ - "preview": JsonValue::Object(profile.clone()), - "source": "session_preview", - "generatedAt": format_timestamp_micros(generated_at_micros), - "qualityFindings": quality_findings, - "blockers": gate.blockers.iter().map(|entry| { - json!({ - "id": entry.blocker_id, - "code": entry.code, - "message": entry.message, - }) - }).collect::>(), - "publishReady": gate.publish_ready, - "canEnterWorld": gate.can_enter_world, - })) - .map(Some) -} - -fn build_supported_actions_json( - stage: RpgAgentStage, - progress_percent: u32, - gate: &CustomWorldPublishGateSnapshot, - checkpoints: &[JsonValue], -) -> Vec { - let has_checkpoint = checkpoints - .iter() - .any(|entry| entry.get("snapshot").is_some()); - let draft_refining_enabled = matches!( - stage, - RpgAgentStage::ObjectRefining | RpgAgentStage::VisualRefining - ); - let long_tail_enabled = matches!( - stage, - RpgAgentStage::ObjectRefining - | RpgAgentStage::VisualRefining - | RpgAgentStage::LongTailReview - | RpgAgentStage::ReadyToPublish - ); - - vec![ - build_supported_action_json( - "draft_foundation", - progress_percent >= 100, - (progress_percent < 100).then(|| "draft_foundation requires progressPercent >= 100".to_string()), - ), - build_supported_action_json( - "update_draft_card", - draft_refining_enabled, - (!draft_refining_enabled).then(|| { - "update_draft_card is only available during object_refining or visual_refining" - .to_string() - }), - ), - build_supported_action_json( - "sync_result_profile", - draft_refining_enabled, - (!draft_refining_enabled).then(|| { - "sync_result_profile is only available during object_refining or visual_refining" - .to_string() - }), - ), - build_supported_action_json( - "generate_characters", - draft_refining_enabled, - (!draft_refining_enabled).then(|| { - "generate_characters is only available during object_refining or visual_refining" - .to_string() - }), - ), - build_supported_action_json( - "generate_landmarks", - draft_refining_enabled, - (!draft_refining_enabled).then(|| { - "generate_landmarks is only available during object_refining or visual_refining" - .to_string() - }), - ), - build_supported_action_json( - "generate_role_assets", - draft_refining_enabled, - (!draft_refining_enabled).then(|| { - "generate_role_assets is only available during object_refining or visual_refining" - .to_string() - }), - ), - build_supported_action_json( - "sync_role_assets", - draft_refining_enabled, - (!draft_refining_enabled).then(|| { - "sync_role_assets is only available during object_refining or visual_refining" - .to_string() - }), - ), - build_supported_action_json( - "generate_scene_assets", - draft_refining_enabled, - (!draft_refining_enabled).then(|| { - "generate_scene_assets is only available during object_refining or visual_refining" - .to_string() - }), - ), - build_supported_action_json( - "sync_scene_assets", - draft_refining_enabled, - (!draft_refining_enabled).then(|| { - "sync_scene_assets is only available during object_refining or visual_refining" - .to_string() - }), - ), - build_supported_action_json( - "expand_long_tail", - long_tail_enabled, - (!long_tail_enabled).then(|| { - "expand_long_tail is only available during object_refining, visual_refining, long_tail_review or ready_to_publish".to_string() - }), - ), - build_supported_action_json( - "publish_world", - long_tail_enabled && gate.publish_ready, - (!long_tail_enabled) - .then(|| { - "publish_world is only available during object_refining, visual_refining, long_tail_review or ready_to_publish".to_string() - }) - .or_else(|| (!gate.publish_ready).then(|| "publish_world requires publish gate without blockers".to_string())), - ), - build_supported_action_json( - "revert_checkpoint", - long_tail_enabled && has_checkpoint, - (!long_tail_enabled) - .then(|| { - "revert_checkpoint is only available during object_refining, visual_refining, long_tail_review or ready_to_publish".to_string() - }) - .or_else(|| (!has_checkpoint).then(|| "revert_checkpoint requires at least one restorable checkpoint snapshot".to_string())), - ), - ] -} - -fn build_supported_action_json(action: &str, enabled: bool, reason: Option) -> JsonValue { - json!({ - "action": action, - "enabled": enabled, - "reason": reason, - }) -} - -fn build_custom_world_draft_card_detail_snapshot( - card: &CustomWorldDraftCard, -) -> Result { - if let Some(detail_payload_json) = card.detail_payload_json.as_deref() { - let detail_value = - serde_json::from_str::(detail_payload_json).map_err(|error| { - format!("custom_world_draft_card.detail_payload_json 非法: {error}") - })?; - if let Some(object) = detail_value.as_object() { - let sections = object - .get("sections") - .and_then(JsonValue::as_array) - .map(|entries| { - entries - .iter() - .filter_map(|entry| { - let object = entry.as_object()?; - Some(CustomWorldDraftCardDetailSectionSnapshot { - section_id: object.get("id")?.as_str()?.to_string(), - label: object - .get("label") - .and_then(JsonValue::as_str) - .unwrap_or_default() - .to_string(), - value: object - .get("value") - .and_then(JsonValue::as_str) - .unwrap_or_default() - .to_string(), - }) - }) - .collect::>() - }) - .unwrap_or_else(|| build_fallback_card_sections(&card)); - - return Ok(CustomWorldDraftCardDetailSnapshot { - card_id: card.card_id.clone(), - kind: card.kind, - title: object - .get("title") - .and_then(JsonValue::as_str) - .unwrap_or(card.title.as_str()) - .to_string(), - sections, - linked_ids_json: card.linked_ids_json.clone(), - locked: object - .get("locked") - .and_then(JsonValue::as_bool) - .unwrap_or(false), - editable: object - .get("editable") - .and_then(JsonValue::as_bool) - .unwrap_or(false), - editable_section_ids_json: serialize_json_value( - object - .get("editableSectionIds") - .unwrap_or(&JsonValue::Array(Vec::new())), - )?, - warning_messages_json: serialize_json_value( - object - .get("warningMessages") - .unwrap_or(&JsonValue::Array(Vec::new())), - )?, - asset_status: card.asset_status, - asset_status_label: card.asset_status_label.clone(), - }); - } - } - - Ok(CustomWorldDraftCardDetailSnapshot { - card_id: card.card_id.clone(), - kind: card.kind, - title: card.title.clone(), - sections: build_fallback_card_sections(card), - linked_ids_json: card.linked_ids_json.clone(), - locked: false, - editable: false, - editable_section_ids_json: "[]".to_string(), - warning_messages_json: "[]".to_string(), - asset_status: card.asset_status, - asset_status_label: card.asset_status_label.clone(), - }) -} - -fn build_fallback_card_sections( - card: &CustomWorldDraftCard, -) -> Vec { - vec![ - CustomWorldDraftCardDetailSectionSnapshot { - section_id: "title".to_string(), - label: "标题".to_string(), - value: card.title.clone(), - }, - CustomWorldDraftCardDetailSectionSnapshot { - section_id: "subtitle".to_string(), - label: "副标题".to_string(), - value: card.subtitle.clone(), - }, - CustomWorldDraftCardDetailSectionSnapshot { - section_id: "summary".to_string(), - label: "摘要".to_string(), - value: card.summary.clone(), - }, - ] -} - -fn build_fallback_card_sections_json(card: &CustomWorldDraftCard) -> Vec { - build_fallback_card_sections(card) - .into_iter() - .map(|section| { - json!({ - "id": section.section_id, - "label": section.label, - "value": section.value, - }) - }) - .collect() -} - -fn rebuild_custom_world_agent_session_row( - current: &CustomWorldAgentSession, - patch: CustomWorldAgentSessionPatch, -) -> Result { - Ok(CustomWorldAgentSession { - session_id: current.session_id.clone(), - owner_user_id: current.owner_user_id.clone(), - seed_text: current.seed_text.clone(), - current_turn: patch.current_turn.unwrap_or(current.current_turn), - progress_percent: patch.progress_percent.unwrap_or(current.progress_percent), - stage: patch.stage.unwrap_or(current.stage), - focus_card_id: patch - .focus_card_id - .unwrap_or_else(|| current.focus_card_id.clone()), - anchor_content_json: patch - .anchor_content_json - .unwrap_or_else(|| current.anchor_content_json.clone()), - creator_intent_json: patch - .creator_intent_json - .unwrap_or_else(|| current.creator_intent_json.clone()), - creator_intent_readiness_json: patch - .creator_intent_readiness_json - .unwrap_or_else(|| current.creator_intent_readiness_json.clone()), - anchor_pack_json: patch - .anchor_pack_json - .unwrap_or_else(|| current.anchor_pack_json.clone()), - lock_state_json: patch - .lock_state_json - .unwrap_or_else(|| current.lock_state_json.clone()), - draft_profile_json: patch - .draft_profile_json - .unwrap_or_else(|| current.draft_profile_json.clone()), - last_assistant_reply: patch - .last_assistant_reply - .unwrap_or_else(|| current.last_assistant_reply.clone()), - publish_gate_json: patch - .publish_gate_json - .unwrap_or_else(|| current.publish_gate_json.clone()), - result_preview_json: patch - .result_preview_json - .unwrap_or_else(|| current.result_preview_json.clone()), - pending_clarifications_json: patch - .pending_clarifications_json - .unwrap_or_else(|| current.pending_clarifications_json.clone()), - quality_findings_json: patch - .quality_findings_json - .unwrap_or_else(|| current.quality_findings_json.clone()), - suggested_actions_json: patch - .suggested_actions_json - .unwrap_or_else(|| current.suggested_actions_json.clone()), - recommended_replies_json: patch - .recommended_replies_json - .unwrap_or_else(|| current.recommended_replies_json.clone()), - asset_coverage_json: patch - .asset_coverage_json - .unwrap_or_else(|| current.asset_coverage_json.clone()), - checkpoints_json: patch - .checkpoints_json - .unwrap_or_else(|| current.checkpoints_json.clone()), - created_at: current.created_at, - updated_at: Timestamp::from_micros_since_unix_epoch( - patch - .updated_at_micros - .unwrap_or_else(|| current.updated_at.to_micros_since_unix_epoch()), - ), - }) -} - -fn rebuild_custom_world_agent_operation_row( - current: &CustomWorldAgentOperation, - patch: CustomWorldAgentOperationPatch, -) -> Result { - let phase_label = patch - .phase_label - .unwrap_or_else(|| current.phase_label.clone()); - let progress = patch.progress.unwrap_or(current.progress); - validate_custom_world_agent_operation_fields( - ¤t.operation_id, - ¤t.session_id, - &phase_label, - progress, - ) - .map_err(|error| error.to_string())?; - - Ok(CustomWorldAgentOperation { - operation_id: current.operation_id.clone(), - session_id: current.session_id.clone(), - operation_type: current.operation_type, - status: patch.status.unwrap_or(current.status), - phase_label, - phase_detail: patch - .phase_detail - .unwrap_or_else(|| current.phase_detail.clone()), - progress, - error_message: patch - .error_message - .unwrap_or_else(|| current.error_message.clone()), - created_at: current.created_at, - updated_at: Timestamp::from_micros_since_unix_epoch( - patch - .updated_at_micros - .unwrap_or_else(|| current.updated_at.to_micros_since_unix_epoch()), - ), - }) -} - -fn replace_custom_world_agent_session( - ctx: &ReducerContext, - current: &CustomWorldAgentSession, - next: CustomWorldAgentSession, -) { - ctx.db - .custom_world_agent_session() - .session_id() - .delete(¤t.session_id); - ctx.db.custom_world_agent_session().insert(next); -} - -fn replace_custom_world_agent_operation( - ctx: &ReducerContext, - current: &CustomWorldAgentOperation, - next: CustomWorldAgentOperation, -) { - ctx.db - .custom_world_agent_operation() - .operation_id() - .delete(¤t.operation_id); - ctx.db.custom_world_agent_operation().insert(next); -} - -fn replace_custom_world_draft_card( - ctx: &ReducerContext, - current: &CustomWorldDraftCard, - next: CustomWorldDraftCard, -) { - ctx.db - .custom_world_draft_card() - .card_id() - .delete(¤t.card_id); - ctx.db.custom_world_draft_card().insert(next); -} - -fn complete_custom_world_operation( - ctx: &ReducerContext, - operation_id: &str, - session_id: &str, - operation_type: RpgAgentOperationType, - phase_label: &str, - phase_detail: &str, - timestamp_micros: i64, -) -> Result { - if let Some(current) = ctx - .db - .custom_world_agent_operation() - .operation_id() - .find(&operation_id.to_string()) - { - if current.session_id != session_id { - return Err("custom_world_agent_operation.session_id 不匹配".to_string()); - } - if current.operation_type != operation_type { - return Err("custom_world_agent_operation.operation_type 不匹配".to_string()); - } - let next = rebuild_custom_world_agent_operation_row( - ¤t, - CustomWorldAgentOperationPatch { - status: Some(RpgAgentOperationStatus::Completed), - phase_label: Some(phase_label.to_string()), - phase_detail: Some(phase_detail.to_string()), - progress: Some(100), - error_message: Some(None), - updated_at_micros: Some(timestamp_micros), - }, - )?; - replace_custom_world_agent_operation(ctx, ¤t, next.clone()); - return Ok(next); - } - - Ok(build_and_insert_custom_world_operation( - ctx, - operation_id, - session_id, - operation_type, - phase_label, - phase_detail, - timestamp_micros, - )) -} - -fn build_and_insert_custom_world_operation( - ctx: &ReducerContext, - operation_id: &str, - session_id: &str, - operation_type: RpgAgentOperationType, - phase_label: &str, - phase_detail: &str, - timestamp_micros: i64, -) -> CustomWorldAgentOperation { - let row = CustomWorldAgentOperation { - operation_id: operation_id.to_string(), - session_id: session_id.to_string(), - operation_type, - status: RpgAgentOperationStatus::Completed, - phase_label: phase_label.to_string(), - phase_detail: phase_detail.to_string(), - progress: 100, - error_message: None, - created_at: Timestamp::from_micros_since_unix_epoch(timestamp_micros), - updated_at: Timestamp::from_micros_since_unix_epoch(timestamp_micros), - }; - ctx.db.custom_world_agent_operation().insert(row) -} - -fn append_custom_world_action_result_message( - ctx: &ReducerContext, - session_id: &str, - operation_id: &str, - text: &str, - timestamp_micros: i64, -) { - let row = CustomWorldAgentMessage { - message_id: format!("message-action-{}-{}", operation_id, timestamp_micros), - session_id: session_id.to_string(), - role: RpgAgentMessageRole::Assistant, - kind: RpgAgentMessageKind::ActionResult, - text: text.to_string(), - related_operation_id: Some(operation_id.to_string()), - created_at: Timestamp::from_micros_since_unix_epoch(timestamp_micros), - }; - ctx.db.custom_world_agent_message().insert(row); -} - -fn upsert_world_foundation_card( - ctx: &ReducerContext, - session_id: &str, - draft_profile: &JsonMap, - updated_at_micros: i64, -) -> Result<(), String> { - let card_id = build_world_foundation_card_id(session_id); - let existing_card = ctx - .db - .custom_world_draft_card() - .card_id() - .find(&card_id) - .filter(|row| row.session_id == session_id); - let title = read_optional_text_field(draft_profile, &["name", "title"]) - .unwrap_or_else(|| "世界底稿".to_string()); - let subtitle = read_optional_text_field(draft_profile, &["subtitle"]).unwrap_or_default(); - let summary = read_optional_text_field(draft_profile, &["summary"]) - .unwrap_or_else(|| "第一版世界底稿已生成。".to_string()); - let detail_payload_json = serialize_json_value(&json!({ - "id": card_id, - "kind": "world", - "title": title, - "sections": [ - { "id": "title", "label": "标题", "value": read_optional_text_field(draft_profile, &["name", "title"]).unwrap_or_else(|| "世界底稿".to_string()) }, - { "id": "subtitle", "label": "副标题", "value": subtitle }, - { "id": "summary", "label": "摘要", "value": summary }, - ], - "linkedIds": [], - "locked": false, - "editable": false, - "editableSectionIds": [], - "warningMessages": [], - }))?; - - if let Some(existing) = existing_card { - replace_custom_world_draft_card( - ctx, - &existing, - CustomWorldDraftCard { - card_id: existing.card_id.clone(), - session_id: existing.session_id.clone(), - kind: RpgAgentDraftCardKind::World, - status: RpgAgentDraftCardStatus::Confirmed, - title: read_optional_text_field(draft_profile, &["name", "title"]) - .unwrap_or_else(|| "世界底稿".to_string()), - subtitle: read_optional_text_field(draft_profile, &["subtitle"]) - .unwrap_or_default(), - summary: read_optional_text_field(draft_profile, &["summary"]) - .unwrap_or_else(|| "第一版世界底稿已生成。".to_string()), - linked_ids_json: "[]".to_string(), - warning_count: 0, - asset_status: None, - asset_status_label: None, - detail_payload_json: Some(detail_payload_json), - created_at: existing.created_at, - updated_at: Timestamp::from_micros_since_unix_epoch(updated_at_micros), - }, - ); - } else { - ctx.db - .custom_world_draft_card() - .insert(CustomWorldDraftCard { - card_id, - session_id: session_id.to_string(), - kind: RpgAgentDraftCardKind::World, - status: RpgAgentDraftCardStatus::Confirmed, - title: read_optional_text_field(draft_profile, &["name", "title"]) - .unwrap_or_else(|| "世界底稿".to_string()), - subtitle: read_optional_text_field(draft_profile, &["subtitle"]) - .unwrap_or_default(), - summary: read_optional_text_field(draft_profile, &["summary"]) - .unwrap_or_else(|| "第一版世界底稿已生成。".to_string()), - linked_ids_json: "[]".to_string(), - warning_count: 0, - asset_status: None, - asset_status_label: None, - detail_payload_json: Some(detail_payload_json), - created_at: Timestamp::from_micros_since_unix_epoch(updated_at_micros), - updated_at: Timestamp::from_micros_since_unix_epoch(updated_at_micros), - }); - } - - Ok(()) -} - -fn build_world_foundation_card_id(session_id: &str) -> String { - // `custom_world_draft_card.card_id` 是全局主键,世界底稿卡必须带上会话维度,避免多会话写入时触发唯一键冲突。 - format!("custom-world:{session_id}:world-foundation") -} - -fn sync_session_draft_profile_from_card_update( - session: &CustomWorldAgentSession, - card: &CustomWorldDraftCard, - updated_title: &str, - updated_subtitle: &str, - updated_summary: &str, - updated_at_micros: i64, -) -> Result { - let mut draft_profile = parse_optional_session_object(session.draft_profile_json.as_deref()) - .unwrap_or_else(|| build_minimal_draft_profile_from_seed(&session.seed_text)); - if card.kind == RpgAgentDraftCardKind::World { - draft_profile.insert( - "name".to_string(), - JsonValue::String(updated_title.to_string()), - ); - draft_profile.insert( - "subtitle".to_string(), - JsonValue::String(updated_subtitle.to_string()), - ); - draft_profile.insert( - "summary".to_string(), - JsonValue::String(updated_summary.to_string()), - ); - } - - let gate = summarize_publish_gate_from_json( - &session.session_id, - session.stage, - Some(&draft_profile), - &parse_json_array_or_empty(&session.quality_findings_json), - ); - rebuild_custom_world_agent_session_row( - session, - CustomWorldAgentSessionPatch { - draft_profile_json: Some(Some(serialize_json_value(&JsonValue::Object( - draft_profile.clone(), - ))?)), - publish_gate_json: Some(Some(serialize_json_value(&publish_gate_to_json_value( - &gate, - ))?)), - result_preview_json: Some(build_result_preview_json( - Some(&draft_profile), - &gate, - &parse_json_array_or_empty(&session.quality_findings_json), - updated_at_micros, - )?), - last_assistant_reply: Some(Some(format!("卡片《{}》已更新。", updated_title))), - updated_at_micros: Some(updated_at_micros), - ..CustomWorldAgentSessionPatch::default() - }, - ) -} - -fn ensure_refining_stage(stage: RpgAgentStage, action: &str) -> Result<(), String> { - if matches!( - stage, - RpgAgentStage::ObjectRefining | RpgAgentStage::VisualRefining - ) { - Ok(()) - } else { - Err(format!( - "{action} is only available during object_refining or visual_refining" - )) - } -} - -fn ensure_result_profile_sync_stage(stage: RpgAgentStage, action: &str) -> Result<(), String> { - if matches!( - stage, - RpgAgentStage::ObjectRefining - | RpgAgentStage::VisualRefining - | RpgAgentStage::LongTailReview - | RpgAgentStage::ReadyToPublish - ) { - Ok(()) - } else { - Err(format!( - "{action} is only available during object_refining, visual_refining, long_tail_review or ready_to_publish" - )) - } -} - -fn ensure_long_tail_stage(stage: RpgAgentStage, action: &str) -> Result<(), String> { - if matches!( - stage, - RpgAgentStage::ObjectRefining - | RpgAgentStage::VisualRefining - | RpgAgentStage::LongTailReview - | RpgAgentStage::ReadyToPublish - ) { - Ok(()) - } else { - Err(format!( - "{action} is only available during object_refining, visual_refining, long_tail_review or ready_to_publish" - )) - } -} - -fn ensure_publishable_stage(stage: RpgAgentStage, action: &str) -> Result<(), String> { - ensure_long_tail_stage(stage, action) -} - -fn map_action_name_to_operation_type(action: &str) -> Option { - match action { - "draft_foundation" => Some(RpgAgentOperationType::DraftFoundation), - "update_draft_card" => Some(RpgAgentOperationType::UpdateDraftCard), - "sync_result_profile" => Some(RpgAgentOperationType::SyncResultProfile), - "generate_characters" => Some(RpgAgentOperationType::GenerateCharacters), - "generate_landmarks" => Some(RpgAgentOperationType::GenerateLandmarks), - "generate_role_assets" => Some(RpgAgentOperationType::GenerateRoleAssets), - "sync_role_assets" => Some(RpgAgentOperationType::SyncRoleAssets), - "generate_scene_assets" => Some(RpgAgentOperationType::GenerateSceneAssets), - "sync_scene_assets" => Some(RpgAgentOperationType::SyncSceneAssets), - "expand_long_tail" => Some(RpgAgentOperationType::ExpandLongTail), - "publish_world" => Some(RpgAgentOperationType::PublishWorld), - "revert_checkpoint" => Some(RpgAgentOperationType::RevertCheckpoint), - _ => None, - } -} - -fn parse_rpg_agent_stage(value: &str) -> Option { - match value.trim() { - "collecting_intent" => Some(RpgAgentStage::CollectingIntent), - "clarifying" => Some(RpgAgentStage::Clarifying), - "foundation_review" => Some(RpgAgentStage::FoundationReview), - "object_refining" => Some(RpgAgentStage::ObjectRefining), - "visual_refining" => Some(RpgAgentStage::VisualRefining), - "long_tail_review" => Some(RpgAgentStage::LongTailReview), - "ready_to_publish" => Some(RpgAgentStage::ReadyToPublish), - "published" => Some(RpgAgentStage::Published), - "error" => Some(RpgAgentStage::Error), - _ => None, - } -} - -fn resolve_rpg_agent_stage_label(stage: RpgAgentStage) -> &'static str { - match stage { - RpgAgentStage::CollectingIntent => "收集世界锚点", - RpgAgentStage::Clarifying => "补齐关键锚点", - RpgAgentStage::FoundationReview => "准备整理底稿", - RpgAgentStage::ObjectRefining => "待完善草稿", - RpgAgentStage::VisualRefining => "视觉工坊", - RpgAgentStage::LongTailReview => "扩展长尾", - RpgAgentStage::ReadyToPublish => "准备发布", - RpgAgentStage::Published => "已发布", - RpgAgentStage::Error => "发生错误", - } -} - -fn parse_optional_session_object(value: Option<&str>) -> Option> { - value - .map(str::trim) - .filter(|value| !value.is_empty()) - .and_then(|value| serde_json::from_str::(value).ok()) - .and_then(|value| value.as_object().cloned()) -} - -fn parse_json_array_or_empty(raw: &str) -> Vec { - serde_json::from_str::(raw) - .ok() - .and_then(|value| value.as_array().cloned()) - .unwrap_or_default() -} - -fn serialize_json_value(value: &JsonValue) -> Result { - serde_json::to_string(value).map_err(|error| format!("JSON 序列化失败: {error}")) -} - -fn read_required_payload_text( - payload: &JsonMap, - key: &str, - error_message: &str, -) -> Result { - payload - .get(key) - .and_then(JsonValue::as_str) - .map(str::trim) - .filter(|value| !value.is_empty()) - .map(ToOwned::to_owned) - .ok_or_else(|| error_message.to_string()) -} - -fn read_optional_text_field(object: &JsonMap, keys: &[&str]) -> Option { - for key in keys { - let mut current = JsonValue::Object(object.clone()); - let mut found = true; - for segment in key.split('.') { - if let Some(next) = current.get(segment) { - current = next.clone(); - } else { - found = false; - break; - } - } - if found { - if let Some(value) = current - .as_str() - .map(str::trim) - .filter(|value| !value.is_empty()) - { - return Some(value.to_string()); - } - } - } - None -} - -fn resolve_session_work_title( - session: &CustomWorldAgentSession, - draft_profile: Option<&JsonMap>, -) -> String { - draft_profile - .and_then(|profile| read_optional_text_field(profile, &["name", "title"])) - .or_else(|| { - let seed = session.seed_text.trim(); - (!seed.is_empty()).then(|| seed.to_string()) - }) - .unwrap_or_else(|| "未命名草稿".to_string()) -} - -fn resolve_session_work_summary( - session: &CustomWorldAgentSession, - draft_profile: Option<&JsonMap>, -) -> String { - draft_profile - .and_then(|profile| read_optional_text_field(profile, &["summary"])) - .or_else(|| { - let seed = session.seed_text.trim(); - (!seed.is_empty()).then(|| seed.to_string()) - }) - .unwrap_or_else(|| "还在收集你的世界锚点。".to_string()) -} - -fn resolve_session_work_subtitle( - draft_profile: Option<&JsonMap>, - stage_label: Option<&str>, -) -> String { - draft_profile - .and_then(|profile| read_optional_text_field(profile, &["subtitle"])) - .or_else(|| stage_label.map(ToOwned::to_owned)) - .unwrap_or_default() -} - -fn resolve_session_work_cover_image_src( - draft_profile: Option<&JsonMap>, -) -> Option { - let profile = draft_profile?; - if let Some(camp) = profile.get("camp").and_then(JsonValue::as_object) { - if let Some(image_src) = read_optional_text_field(camp, &["imageSrc"]) { - return Some(image_src); - } - } - if let Some(landmarks) = profile.get("landmarks").and_then(JsonValue::as_array) { - for landmark in landmarks { - if let Some(object) = landmark.as_object() { - if let Some(image_src) = read_optional_text_field(object, &["imageSrc"]) { - return Some(image_src); - } - } - } - } - None -} - -fn resolve_session_work_counts( - ctx: &ReducerContext, - session: &CustomWorldAgentSession, - draft_profile: Option<&JsonMap>, -) -> (u32, u32) { - if let Some(profile) = draft_profile { - let role_count = profile - .get("playableNpcs") - .and_then(JsonValue::as_array) - .map(|entries| entries.len() as u32) - .unwrap_or(0) - + profile - .get("storyNpcs") - .and_then(JsonValue::as_array) - .map(|entries| entries.len() as u32) - .unwrap_or(0); - let landmark_count = profile - .get("landmarks") - .and_then(JsonValue::as_array) - .map(|entries| entries.len() as u32) - .unwrap_or(0); - return (role_count, landmark_count); - } - - let mut role_count = 0u32; - let mut landmark_count = 0u32; - for card in ctx - .db - .custom_world_draft_card() - .iter() - .filter(|row| row.session_id == session.session_id) - { - match card.kind { - RpgAgentDraftCardKind::Character => { - role_count = role_count.saturating_add(1); - } - RpgAgentDraftCardKind::Landmark => { - landmark_count = landmark_count.saturating_add(1); - } - _ => {} - } - } - - (role_count, landmark_count) -} - -fn ensure_minimal_draft_profile( - mut profile: JsonMap, - seed_text: &str, -) -> JsonMap { - if read_optional_text_field(&profile, &["name", "title"]).is_none() { - profile.insert( - "name".to_string(), - JsonValue::String(seed_text.trim().to_string().if_empty("未命名草稿")), - ); - } - if read_optional_text_field(&profile, &["summary"]).is_none() { - profile.insert( - "summary".to_string(), - JsonValue::String( - (!seed_text.trim().is_empty()) - .then(|| seed_text.trim().to_string()) - .unwrap_or_else(|| "还在收集你的世界锚点。".to_string()), - ), - ); - } - profile - .entry("subtitle".to_string()) - .or_insert_with(|| JsonValue::String(String::new())); - profile - .entry("worldHook".to_string()) - .or_insert_with(|| JsonValue::String(String::new())); - profile - .entry("playerPremise".to_string()) - .or_insert_with(|| JsonValue::String(String::new())); - profile - .entry("coreConflicts".to_string()) - .or_insert_with(|| JsonValue::Array(Vec::new())); - profile - .entry("playableNpcs".to_string()) - .or_insert_with(|| JsonValue::Array(Vec::new())); - profile - .entry("storyNpcs".to_string()) - .or_insert_with(|| JsonValue::Array(Vec::new())); - profile - .entry("landmarks".to_string()) - .or_insert_with(|| JsonValue::Array(Vec::new())); - profile - .entry("chapters".to_string()) - .or_insert_with(|| JsonValue::Array(Vec::new())); - profile - .entry("sceneChapters".to_string()) - .or_insert_with(|| JsonValue::Array(Vec::new())); - profile - .entry("sceneChapterBlueprints".to_string()) - .or_insert_with(|| JsonValue::Array(Vec::new())); - profile -} - -fn build_minimal_draft_profile_from_seed(seed_text: &str) -> JsonMap { - ensure_minimal_draft_profile(JsonMap::new(), seed_text) -} - -fn build_session_checkpoint_value( - checkpoint_id_suffix: &str, - label: &str, - session: &CustomWorldAgentSession, -) -> JsonValue { - json!({ - "checkpointId": format!("checkpoint-{}-{}", session.session_id, checkpoint_id_suffix), - "createdAt": format_timestamp_micros(session.updated_at.to_micros_since_unix_epoch()), - "label": label, - "snapshot": { - "stage": session.stage.as_str(), - "progressPercent": session.progress_percent, - "draftProfile": parse_optional_session_object(session.draft_profile_json.as_deref()).map(JsonValue::Object), - "qualityFindings": parse_json_array_or_empty(&session.quality_findings_json), - } - }) -} - -fn append_checkpoint_json(current: &str, checkpoint: &JsonValue) -> Result { - let mut checkpoints = parse_json_array_or_empty(current); - checkpoints.push(checkpoint.clone()); - serialize_json_value(&JsonValue::Array(checkpoints)) -} - -fn extract_detail_section_value(sections: &[JsonValue], target_id: &str) -> Option { - sections.iter().find_map(|entry| { - let object = entry.as_object()?; - (object.get("id").and_then(JsonValue::as_str) == Some(target_id)).then(|| { - object - .get("value") - .and_then(JsonValue::as_str) - .unwrap_or_default() - .to_string() - }) - }) -} - -fn json_array_has_non_empty_text(value: Option<&JsonValue>) -> bool { - value - .and_then(JsonValue::as_array) - .map(|entries| { - entries.iter().any(|entry| { - entry - .as_str() - .map(str::trim) - .filter(|text| !text.is_empty()) - .is_some() - }) - }) - .unwrap_or(false) -} - -trait IfEmptyString { - fn if_empty(self, fallback: &str) -> String; -} - -impl IfEmptyString for String { - fn if_empty(self, fallback: &str) -> String { - if self.trim().is_empty() { - fallback.to_string() - } else { - self - } - } -} - -fn mark_custom_world_agent_session_published( - ctx: &ReducerContext, - session_id: &str, - owner_user_id: &str, - updated_at_micros: i64, -) -> Result { - let existing = ctx - .db - .custom_world_agent_session() - .session_id() - .find(&session_id.to_string()) - .filter(|row| row.owner_user_id == owner_user_id) - .ok_or_else(|| "custom_world_agent_session 不存在,无法推进到 published".to_string())?; - - ctx.db - .custom_world_agent_session() - .session_id() - .delete(&existing.session_id); - - let next_row = CustomWorldAgentSession { - session_id: existing.session_id.clone(), - owner_user_id: existing.owner_user_id.clone(), - seed_text: existing.seed_text.clone(), - current_turn: existing.current_turn, - progress_percent: existing.progress_percent, - stage: RpgAgentStage::Published, - focus_card_id: existing.focus_card_id.clone(), - anchor_content_json: existing.anchor_content_json.clone(), - creator_intent_json: existing.creator_intent_json.clone(), - creator_intent_readiness_json: existing.creator_intent_readiness_json.clone(), - anchor_pack_json: existing.anchor_pack_json.clone(), - lock_state_json: existing.lock_state_json.clone(), - draft_profile_json: existing.draft_profile_json.clone(), - last_assistant_reply: existing.last_assistant_reply.clone(), - publish_gate_json: existing.publish_gate_json.clone(), - result_preview_json: existing.result_preview_json.clone(), - pending_clarifications_json: existing.pending_clarifications_json.clone(), - quality_findings_json: existing.quality_findings_json.clone(), - suggested_actions_json: existing.suggested_actions_json.clone(), - recommended_replies_json: existing.recommended_replies_json.clone(), - asset_coverage_json: existing.asset_coverage_json.clone(), - checkpoints_json: existing.checkpoints_json.clone(), - created_at: existing.created_at, - updated_at: Timestamp::from_micros_since_unix_epoch(updated_at_micros), - }; - - ctx.db.custom_world_agent_session().insert(next_row); - - Ok(RpgAgentStage::Published) -} - -fn sync_custom_world_gallery_entry_from_profile( - ctx: &ReducerContext, - profile: &CustomWorldProfile, -) -> Result { - let published_at = profile - .published_at - .ok_or_else(|| "published profile 缺少 published_at,无法同步 gallery".to_string())?; - - ctx.db - .custom_world_gallery_entry() - .profile_id() - .delete(&profile.profile_id); - - let row = CustomWorldGalleryEntry { - profile_id: profile.profile_id.clone(), - owner_user_id: profile.owner_user_id.clone(), - public_work_code: profile.public_work_code.clone().ok_or_else(|| { - "published profile 缺少 public_work_code,无法同步 gallery".to_string() - })?, - author_public_user_code: profile.author_public_user_code.clone().ok_or_else(|| { - "published profile 缺少 author_public_user_code,无法同步 gallery".to_string() - })?, - author_display_name: profile.author_display_name.clone(), - world_name: profile.world_name.clone(), - subtitle: profile.subtitle.clone(), - summary_text: profile.summary_text.clone(), - cover_image_src: profile.cover_image_src.clone(), - theme_mode: profile.theme_mode, - playable_npc_count: profile.playable_npc_count, - landmark_count: profile.landmark_count, - published_at, - updated_at: profile.updated_at, - }; - - let inserted = ctx.db.custom_world_gallery_entry().insert(row); - - Ok(build_custom_world_gallery_entry_snapshot(&inserted)) -} - -fn sync_missing_custom_world_gallery_entries(ctx: &ReducerContext) -> Result<(), String> { - let published_profiles = ctx - .db - .custom_world_profile() - .iter() - .filter(|profile| { - profile.publication_status == CustomWorldPublicationStatus::Published - && profile.deleted_at.is_none() - }) - .collect::>(); - - for profile in published_profiles { - if profile.published_at.is_none() { - continue; - } - - let existing_gallery_entry = ctx - .db - .custom_world_gallery_entry() - .profile_id() - .find(&profile.profile_id) - .filter(|entry| entry.owner_user_id == profile.owner_user_id); - - if existing_gallery_entry.is_some() - && profile.public_work_code.is_some() - && profile.author_public_user_code.is_some() - { - continue; - } - - let profile_with_public_fields = ensure_custom_world_profile_public_fields(ctx, &profile); - sync_custom_world_gallery_entry_from_profile(ctx, &profile_with_public_fields)?; - } - - Ok(()) -} - -fn ensure_custom_world_profile_public_fields( - ctx: &ReducerContext, - profile: &CustomWorldProfile, -) -> CustomWorldProfile { - if profile.public_work_code.is_some() && profile.author_public_user_code.is_some() { - return build_custom_world_profile_row_copy(profile); - } - - ctx.db - .custom_world_profile() - .profile_id() - .delete(&profile.profile_id); - - let next_row = CustomWorldProfile { - profile_id: profile.profile_id.clone(), - owner_user_id: profile.owner_user_id.clone(), - public_work_code: profile - .public_work_code - .clone() - .or_else(|| Some(build_public_work_code_from_profile_id(&profile.profile_id))), - author_public_user_code: profile.author_public_user_code.clone().or_else(|| { - Some(build_public_user_code_from_owner_user_id( - &profile.owner_user_id, - )) - }), - source_agent_session_id: profile.source_agent_session_id.clone(), - publication_status: profile.publication_status, - world_name: profile.world_name.clone(), - subtitle: profile.subtitle.clone(), - summary_text: profile.summary_text.clone(), - theme_mode: profile.theme_mode, - cover_image_src: profile.cover_image_src.clone(), - profile_payload_json: profile.profile_payload_json.clone(), - playable_npc_count: profile.playable_npc_count, - landmark_count: profile.landmark_count, - author_display_name: profile.author_display_name.clone(), - published_at: profile.published_at, - deleted_at: profile.deleted_at, - created_at: profile.created_at, - updated_at: profile.updated_at, - }; - - ctx.db.custom_world_profile().insert(next_row) -} - -fn build_custom_world_profile_row_copy(profile: &CustomWorldProfile) -> CustomWorldProfile { - CustomWorldProfile { - profile_id: profile.profile_id.clone(), - owner_user_id: profile.owner_user_id.clone(), - public_work_code: profile.public_work_code.clone(), - author_public_user_code: profile.author_public_user_code.clone(), - source_agent_session_id: profile.source_agent_session_id.clone(), - publication_status: profile.publication_status, - world_name: profile.world_name.clone(), - subtitle: profile.subtitle.clone(), - summary_text: profile.summary_text.clone(), - theme_mode: profile.theme_mode, - cover_image_src: profile.cover_image_src.clone(), - profile_payload_json: profile.profile_payload_json.clone(), - playable_npc_count: profile.playable_npc_count, - landmark_count: profile.landmark_count, - author_display_name: profile.author_display_name.clone(), - published_at: profile.published_at, - deleted_at: profile.deleted_at, - created_at: profile.created_at, - updated_at: profile.updated_at, - } -} - -fn build_custom_world_profile_snapshot(row: &CustomWorldProfile) -> CustomWorldProfileSnapshot { - CustomWorldProfileSnapshot { - profile_id: row.profile_id.clone(), - owner_user_id: row.owner_user_id.clone(), - public_work_code: row.public_work_code.clone(), - author_public_user_code: row.author_public_user_code.clone(), - source_agent_session_id: row.source_agent_session_id.clone(), - publication_status: row.publication_status, - world_name: row.world_name.clone(), - subtitle: row.subtitle.clone(), - summary_text: row.summary_text.clone(), - theme_mode: row.theme_mode, - cover_image_src: row.cover_image_src.clone(), - profile_payload_json: row.profile_payload_json.clone(), - playable_npc_count: row.playable_npc_count, - landmark_count: row.landmark_count, - author_display_name: row.author_display_name.clone(), - published_at_micros: row - .published_at - .map(|value| value.to_micros_since_unix_epoch()), - deleted_at_micros: row - .deleted_at - .map(|value| value.to_micros_since_unix_epoch()), - created_at_micros: row.created_at.to_micros_since_unix_epoch(), - updated_at_micros: row.updated_at.to_micros_since_unix_epoch(), - } -} - -fn build_custom_world_agent_session_snapshot( - ctx: &ReducerContext, - row: &CustomWorldAgentSession, -) -> CustomWorldAgentSessionSnapshot { - let mut messages = ctx - .db - .custom_world_agent_message() - .iter() - .filter(|message| message.session_id == row.session_id) - .map(|message| build_custom_world_agent_message_snapshot(&message)) - .collect::>(); - messages.sort_by_key(|message| (message.created_at_micros, message.message_id.clone())); - - let mut draft_cards = ctx - .db - .custom_world_draft_card() - .iter() - .filter(|card| card.session_id == row.session_id) - .map(|card| build_custom_world_draft_card_snapshot(&card)) - .collect::>(); - draft_cards.sort_by_key(|card| (card.created_at_micros, card.card_id.clone())); - - let mut operations = ctx - .db - .custom_world_agent_operation() - .iter() - .filter(|operation| operation.session_id == row.session_id) - .map(|operation| build_custom_world_agent_operation_snapshot(&operation)) - .collect::>(); - operations - .sort_by_key(|operation| (operation.created_at_micros, operation.operation_id.clone())); - - CustomWorldAgentSessionSnapshot { - session_id: row.session_id.clone(), - owner_user_id: row.owner_user_id.clone(), - seed_text: row.seed_text.clone(), - current_turn: row.current_turn, - progress_percent: row.progress_percent, - stage: row.stage, - focus_card_id: row.focus_card_id.clone(), - anchor_content_json: row.anchor_content_json.clone(), - creator_intent_json: row.creator_intent_json.clone(), - creator_intent_readiness_json: row.creator_intent_readiness_json.clone(), - anchor_pack_json: row.anchor_pack_json.clone(), - lock_state_json: row.lock_state_json.clone(), - draft_profile_json: row.draft_profile_json.clone(), - last_assistant_reply: row.last_assistant_reply.clone(), - publish_gate_json: row.publish_gate_json.clone(), - result_preview_json: row.result_preview_json.clone(), - pending_clarifications_json: row.pending_clarifications_json.clone(), - quality_findings_json: row.quality_findings_json.clone(), - suggested_actions_json: row.suggested_actions_json.clone(), - recommended_replies_json: row.recommended_replies_json.clone(), - asset_coverage_json: row.asset_coverage_json.clone(), - checkpoints_json: row.checkpoints_json.clone(), - supported_actions_json: serialize_json_value(&JsonValue::Array( - build_supported_actions_json( - row.stage, - row.progress_percent, - &build_custom_world_publish_gate_from_session(row), - &parse_json_array_or_empty(&row.checkpoints_json), - ), - )) - .unwrap_or_else(|_| "[]".to_string()), - messages, - draft_cards, - operations, - created_at_micros: row.created_at.to_micros_since_unix_epoch(), - updated_at_micros: row.updated_at.to_micros_since_unix_epoch(), - } -} - -fn build_custom_world_agent_message_snapshot( - row: &CustomWorldAgentMessage, -) -> CustomWorldAgentMessageSnapshot { - CustomWorldAgentMessageSnapshot { - message_id: row.message_id.clone(), - session_id: row.session_id.clone(), - role: row.role, - kind: row.kind, - text: row.text.clone(), - related_operation_id: row.related_operation_id.clone(), - created_at_micros: row.created_at.to_micros_since_unix_epoch(), - } -} - -fn build_custom_world_agent_operation_snapshot( - row: &CustomWorldAgentOperation, -) -> CustomWorldAgentOperationSnapshot { - CustomWorldAgentOperationSnapshot { - operation_id: row.operation_id.clone(), - session_id: row.session_id.clone(), - operation_type: row.operation_type, - status: row.status, - phase_label: row.phase_label.clone(), - phase_detail: row.phase_detail.clone(), - progress: row.progress, - error_message: row.error_message.clone(), - created_at_micros: row.created_at.to_micros_since_unix_epoch(), - updated_at_micros: row.updated_at.to_micros_since_unix_epoch(), - } -} - -fn build_custom_world_draft_card_snapshot( - row: &CustomWorldDraftCard, -) -> CustomWorldDraftCardSnapshot { - CustomWorldDraftCardSnapshot { - card_id: row.card_id.clone(), - session_id: row.session_id.clone(), - kind: row.kind, - status: row.status, - title: row.title.clone(), - subtitle: row.subtitle.clone(), - summary: row.summary.clone(), - linked_ids_json: row.linked_ids_json.clone(), - warning_count: row.warning_count, - asset_status: row.asset_status, - asset_status_label: row.asset_status_label.clone(), - detail_payload_json: row.detail_payload_json.clone(), - created_at_micros: row.created_at.to_micros_since_unix_epoch(), - updated_at_micros: row.updated_at.to_micros_since_unix_epoch(), - } -} - -fn build_custom_world_gallery_entry_snapshot( - row: &CustomWorldGalleryEntry, -) -> CustomWorldGalleryEntrySnapshot { - CustomWorldGalleryEntrySnapshot { - profile_id: row.profile_id.clone(), - owner_user_id: row.owner_user_id.clone(), - public_work_code: row.public_work_code.clone(), - author_public_user_code: row.author_public_user_code.clone(), - author_display_name: row.author_display_name.clone(), - world_name: row.world_name.clone(), - subtitle: row.subtitle.clone(), - summary_text: row.summary_text.clone(), - cover_image_src: row.cover_image_src.clone(), - theme_mode: row.theme_mode, - playable_npc_count: row.playable_npc_count, - landmark_count: row.landmark_count, - published_at_micros: row.published_at.to_micros_since_unix_epoch(), - updated_at_micros: row.updated_at.to_micros_since_unix_epoch(), - } -} - -// 作品公开号保持稳定公开语义,本期先由 profile_id 派生 deterministic fallback, -// 后续若引入独立 sequence 表,可无痛替换生成来源而不影响读写接口。 -fn build_public_work_code_from_profile_id(profile_id: &str) -> String { - let digits = profile_id - .chars() - .filter(|character| character.is_ascii_digit()) - .collect::(); - let normalized_digits = if digits.is_empty() { - let checksum = profile_id.bytes().fold(0u32, |accumulator, value| { - accumulator.wrapping_mul(131) + u32::from(value) - }); - format!("{:08}", checksum % 100_000_000) - } else { - format!("{:0>8}", &digits[digits.len().saturating_sub(8)..]) - }; - - format!("CW-{normalized_digits}") -} - -fn build_public_user_code_from_owner_user_id(owner_user_id: &str) -> String { - owner_user_id - .trim_start_matches("user_") - .parse::() - .ok() - .map(|sequence| format!("SY-{sequence:08}")) - .unwrap_or_else(|| "SY-00000000".to_string()) -} - -fn normalize_public_work_code(input: &str) -> Option { - let normalized = input - .trim() - .chars() - .filter(|character| character.is_ascii_alphanumeric()) - .collect::() - .to_ascii_uppercase(); - let digits = normalized.strip_prefix("CW").unwrap_or(&normalized); - if digits.is_empty() - || digits.len() > 8 - || !digits.chars().all(|character| character.is_ascii_digit()) - { - return None; - } - - Some(format!("CW-{digits:0>8}")) -} - -fn build_quest_record_row(snapshot: QuestRecordSnapshot) -> QuestRecord { - QuestRecord { - quest_id: snapshot.quest_id, - runtime_session_id: snapshot.runtime_session_id, - story_session_id: snapshot.story_session_id, - actor_user_id: snapshot.actor_user_id, - issuer_npc_id: snapshot.issuer_npc_id, - issuer_npc_name: snapshot.issuer_npc_name, - scene_id: snapshot.scene_id, - chapter_id: snapshot.chapter_id, - act_id: snapshot.act_id, - thread_id: snapshot.thread_id, - contract_id: snapshot.contract_id, - title: snapshot.title, - description: snapshot.description, - summary: snapshot.summary, - objective: snapshot.objective, - progress: snapshot.progress, - status: snapshot.status, - completion_notified: snapshot.completion_notified, - reward: snapshot.reward, - reward_text: snapshot.reward_text, - narrative_binding: snapshot.narrative_binding, - steps: snapshot.steps, - active_step_id: snapshot.active_step_id, - visible_stage: snapshot.visible_stage, - hidden_flags: snapshot.hidden_flags, - discovered_fact_ids: snapshot.discovered_fact_ids, - related_carrier_ids: snapshot.related_carrier_ids, - consequence_ids: snapshot.consequence_ids, - created_at: Timestamp::from_micros_since_unix_epoch(snapshot.created_at_micros), - updated_at: Timestamp::from_micros_since_unix_epoch(snapshot.updated_at_micros), - completed_at: snapshot - .completed_at_micros - .map(Timestamp::from_micros_since_unix_epoch), - turned_in_at: snapshot - .turned_in_at_micros - .map(Timestamp::from_micros_since_unix_epoch), - } -} - -fn build_player_progression_row(snapshot: PlayerProgressionSnapshot) -> PlayerProgression { - PlayerProgression { - user_id: snapshot.user_id, - level: snapshot.level, - current_level_xp: snapshot.current_level_xp, - total_xp: snapshot.total_xp, - xp_to_next_level: snapshot.xp_to_next_level, - pending_level_ups: snapshot.pending_level_ups, - last_granted_source: snapshot.last_granted_source, - created_at: Timestamp::from_micros_since_unix_epoch(snapshot.created_at_micros), - updated_at: Timestamp::from_micros_since_unix_epoch(snapshot.updated_at_micros), - } -} - -fn build_player_progression_snapshot_from_row( - row: &PlayerProgression, -) -> PlayerProgressionSnapshot { - PlayerProgressionSnapshot { - user_id: row.user_id.clone(), - level: row.level, - current_level_xp: row.current_level_xp, - total_xp: row.total_xp, - xp_to_next_level: row.xp_to_next_level, - pending_level_ups: row.pending_level_ups, - last_granted_source: row.last_granted_source, - created_at_micros: row.created_at.to_micros_since_unix_epoch(), - updated_at_micros: row.updated_at.to_micros_since_unix_epoch(), - } -} - -fn build_chapter_progression_id(user_id: &str, chapter_id: &str) -> String { - format!("chapprog_{}_{}", user_id.trim(), chapter_id.trim()) -} - -fn build_chapter_progression_row(snapshot: ChapterProgressionSnapshot) -> ChapterProgression { - ChapterProgression { - chapter_progression_id: build_chapter_progression_id( - &snapshot.user_id, - &snapshot.chapter_id, - ), - user_id: snapshot.user_id, - chapter_id: snapshot.chapter_id, - chapter_index: snapshot.chapter_index, - total_chapters: snapshot.total_chapters, - entry_pseudo_level_millis: snapshot.entry_pseudo_level_millis, - exit_pseudo_level_millis: snapshot.exit_pseudo_level_millis, - entry_level: snapshot.entry_level, - exit_level: snapshot.exit_level, - planned_total_xp: snapshot.planned_total_xp, - planned_quest_xp: snapshot.planned_quest_xp, - planned_hostile_xp: snapshot.planned_hostile_xp, - actual_quest_xp: snapshot.actual_quest_xp, - actual_hostile_xp: snapshot.actual_hostile_xp, - expected_hostile_defeat_count: snapshot.expected_hostile_defeat_count, - actual_hostile_defeat_count: snapshot.actual_hostile_defeat_count, - level_at_entry: snapshot.level_at_entry, - level_at_exit: snapshot.level_at_exit, - pace_band: snapshot.pace_band, - created_at: Timestamp::from_micros_since_unix_epoch(snapshot.created_at_micros), - updated_at: Timestamp::from_micros_since_unix_epoch(snapshot.updated_at_micros), - } -} - -fn build_chapter_progression_snapshot_from_row( - row: &ChapterProgression, -) -> ChapterProgressionSnapshot { - ChapterProgressionSnapshot { - user_id: row.user_id.clone(), - chapter_id: row.chapter_id.clone(), - chapter_index: row.chapter_index, - total_chapters: row.total_chapters, - entry_pseudo_level_millis: row.entry_pseudo_level_millis, - exit_pseudo_level_millis: row.exit_pseudo_level_millis, - entry_level: row.entry_level, - exit_level: row.exit_level, - planned_total_xp: row.planned_total_xp, - planned_quest_xp: row.planned_quest_xp, - planned_hostile_xp: row.planned_hostile_xp, - actual_quest_xp: row.actual_quest_xp, - actual_hostile_xp: row.actual_hostile_xp, - expected_hostile_defeat_count: row.expected_hostile_defeat_count, - actual_hostile_defeat_count: row.actual_hostile_defeat_count, - level_at_entry: row.level_at_entry, - level_at_exit: row.level_at_exit, - pace_band: row.pace_band, - created_at_micros: row.created_at.to_micros_since_unix_epoch(), - updated_at_micros: row.updated_at.to_micros_since_unix_epoch(), - } -} - -fn build_quest_record_snapshot_from_row(row: &QuestRecord) -> QuestRecordSnapshot { - QuestRecordSnapshot { - quest_id: row.quest_id.clone(), - runtime_session_id: row.runtime_session_id.clone(), - story_session_id: row.story_session_id.clone(), - actor_user_id: row.actor_user_id.clone(), - issuer_npc_id: row.issuer_npc_id.clone(), - issuer_npc_name: row.issuer_npc_name.clone(), - scene_id: row.scene_id.clone(), - chapter_id: row.chapter_id.clone(), - act_id: row.act_id.clone(), - thread_id: row.thread_id.clone(), - contract_id: row.contract_id.clone(), - title: row.title.clone(), - description: row.description.clone(), - summary: row.summary.clone(), - objective: row.objective.clone(), - progress: row.progress, - status: row.status, - completion_notified: row.completion_notified, - reward: row.reward.clone(), - reward_text: row.reward_text.clone(), - narrative_binding: row.narrative_binding.clone(), - steps: row.steps.clone(), - active_step_id: row.active_step_id.clone(), - visible_stage: row.visible_stage, - hidden_flags: row.hidden_flags.clone(), - discovered_fact_ids: row.discovered_fact_ids.clone(), - related_carrier_ids: row.related_carrier_ids.clone(), - consequence_ids: row.consequence_ids.clone(), - created_at_micros: row.created_at.to_micros_since_unix_epoch(), - updated_at_micros: row.updated_at.to_micros_since_unix_epoch(), - completed_at_micros: row - .completed_at - .map(|value| value.to_micros_since_unix_epoch()), - turned_in_at_micros: row - .turned_in_at - .map(|value| value.to_micros_since_unix_epoch()), - } -} - -fn build_inventory_slot_row(snapshot: InventorySlotSnapshot) -> InventorySlot { - InventorySlot { - slot_id: snapshot.slot_id, - runtime_session_id: snapshot.runtime_session_id, - story_session_id: snapshot.story_session_id, - actor_user_id: snapshot.actor_user_id, - container_kind: snapshot.container_kind, - slot_key: snapshot.slot_key, - item_id: snapshot.item_id, - category: snapshot.category, - name: snapshot.name, - description: snapshot.description, - quantity: snapshot.quantity, - rarity: snapshot.rarity, - tags: snapshot.tags, - stackable: snapshot.stackable, - stack_key: snapshot.stack_key, - equipment_slot_id: snapshot.equipment_slot_id, - source_kind: snapshot.source_kind, - source_reference_id: snapshot.source_reference_id, - created_at: Timestamp::from_micros_since_unix_epoch(snapshot.created_at_micros), - updated_at: Timestamp::from_micros_since_unix_epoch(snapshot.updated_at_micros), - } -} - -fn build_inventory_slot_snapshot_from_row(row: &InventorySlot) -> InventorySlotSnapshot { - InventorySlotSnapshot { - slot_id: row.slot_id.clone(), - runtime_session_id: row.runtime_session_id.clone(), - story_session_id: row.story_session_id.clone(), - actor_user_id: row.actor_user_id.clone(), - container_kind: row.container_kind, - slot_key: row.slot_key.clone(), - item_id: row.item_id.clone(), - category: row.category.clone(), - name: row.name.clone(), - description: row.description.clone(), - quantity: row.quantity, - rarity: row.rarity, - tags: row.tags.clone(), - stackable: row.stackable, - stack_key: row.stack_key.clone(), - equipment_slot_id: row.equipment_slot_id, - source_kind: row.source_kind, - source_reference_id: row.source_reference_id.clone(), - created_at_micros: row.created_at.to_micros_since_unix_epoch(), - updated_at_micros: row.updated_at.to_micros_since_unix_epoch(), - } -} - -fn grant_quest_reward_items( - ctx: &ReducerContext, - snapshot: &QuestRecordSnapshot, -) -> Result<(), String> { - if !ctx - .db - .inventory_slot() - .iter() - .filter(|row| { - row.runtime_session_id == snapshot.runtime_session_id - && row.actor_user_id == snapshot.actor_user_id - }) - .all(|row| row.source_reference_id.as_deref() != Some(snapshot.quest_id.as_str())) - { - return Ok(()); - } - - for (index, reward_item) in snapshot.reward.items.clone().into_iter().enumerate() { - let inventory_item = - build_inventory_item_snapshot_from_quest_reward_item(&snapshot.quest_id, reward_item); - grant_inventory_item_to_actor( - ctx, - &snapshot.runtime_session_id, - snapshot.story_session_id.clone(), - &snapshot.actor_user_id, - inventory_item, - build_reward_seed(snapshot.updated_at_micros, index), - snapshot.updated_at_micros, - )?; - } - - Ok(()) -} - -fn grant_battle_reward_items( - ctx: &ReducerContext, - snapshot: &BattleStateSnapshot, -) -> Result<(), String> { - if snapshot.reward_items.is_empty() { - return Ok(()); - } - - if !ctx - .db - .inventory_slot() - .iter() - .filter(|row| { - row.runtime_session_id == snapshot.runtime_session_id - && row.actor_user_id == snapshot.actor_user_id - }) - .all(|row| row.source_reference_id.as_deref() != Some(snapshot.battle_state_id.as_str())) - { - return Ok(()); - } - - for (index, reward_item) in snapshot.reward_items.clone().into_iter().enumerate() { - let inventory_item = build_inventory_item_snapshot_from_battle_reward_item( - &snapshot.battle_state_id, - reward_item, - ); - grant_inventory_item_to_actor( - ctx, - &snapshot.runtime_session_id, - Some(snapshot.story_session_id.clone()), - &snapshot.actor_user_id, - inventory_item, - build_reward_seed(snapshot.updated_at_micros, index), - snapshot.updated_at_micros, - )?; - } - - Ok(()) -} - -fn grant_inventory_item_to_actor( - ctx: &ReducerContext, - runtime_session_id: &str, - story_session_id: Option, - actor_user_id: &str, - item: InventoryItemSnapshot, - seed_micros: i64, - updated_at_micros: i64, -) -> Result<(), String> { - let current_slots = ctx - .db - .inventory_slot() - .iter() - .filter(|row| { - row.runtime_session_id == runtime_session_id && row.actor_user_id == actor_user_id - }) - .map(|row| build_inventory_slot_snapshot_from_row(&row)) - .collect::>(); - let slot_id = generate_inventory_slot_id(seed_micros); - let mutation_id = generate_inventory_mutation_id(seed_micros); - let outcome = apply_inventory_slot_mutation( - current_slots, - InventoryMutationInput { - mutation_id, - runtime_session_id: runtime_session_id.to_string(), - story_session_id, - actor_user_id: actor_user_id.to_string(), - mutation: InventoryMutation::GrantItem(GrantInventoryItemInput { slot_id, item }), - updated_at_micros, - }, - ) - .map_err(|error| error.to_string())?; - - for removed_slot_id in outcome.removed_slot_ids { - ctx.db.inventory_slot().slot_id().delete(&removed_slot_id); - } - for slot in outcome.next_slots { - ctx.db.inventory_slot().slot_id().delete(&slot.slot_id); - ctx.db - .inventory_slot() - .insert(build_inventory_slot_row(slot)); - } - - Ok(()) -} - -fn build_inventory_item_snapshot_from_battle_reward_item( - battle_state_id: &str, - reward_item: RuntimeItemRewardItemSnapshot, -) -> InventoryItemSnapshot { - InventoryItemSnapshot { - item_id: reward_item.item_id, - category: reward_item.category, - name: reward_item.item_name, - description: reward_item.description, - quantity: reward_item.quantity, - rarity: map_runtime_reward_item_rarity(reward_item.rarity), - tags: reward_item.tags, - stackable: reward_item.stackable, - stack_key: reward_item.stack_key, - equipment_slot_id: reward_item - .equipment_slot_id - .map(map_runtime_reward_equipment_slot), - source_kind: InventoryItemSourceKind::CombatDrop, - source_reference_id: Some(battle_state_id.to_string()), - } -} - -fn build_inventory_item_snapshot_from_quest_reward_item( - quest_id: &str, - reward_item: QuestRewardItem, -) -> InventoryItemSnapshot { - InventoryItemSnapshot { - item_id: reward_item.item_id, - category: reward_item.category, - name: reward_item.name, - description: reward_item.description, - quantity: reward_item.quantity, - rarity: map_quest_reward_item_rarity(reward_item.rarity), - tags: reward_item.tags, - stackable: reward_item.stackable, - stack_key: reward_item.stack_key, - equipment_slot_id: reward_item - .equipment_slot_id - .map(map_quest_reward_equipment_slot), - source_kind: InventoryItemSourceKind::QuestReward, - source_reference_id: Some(quest_id.to_string()), - } -} - -fn map_quest_reward_item_rarity(rarity: QuestRewardItemRarity) -> InventoryItemRarity { - match rarity { - QuestRewardItemRarity::Common => InventoryItemRarity::Common, - QuestRewardItemRarity::Uncommon => InventoryItemRarity::Uncommon, - QuestRewardItemRarity::Rare => InventoryItemRarity::Rare, - QuestRewardItemRarity::Epic => InventoryItemRarity::Epic, - QuestRewardItemRarity::Legendary => InventoryItemRarity::Legendary, - } -} - -fn map_runtime_reward_item_rarity( - rarity: module_runtime_item::RuntimeItemRewardItemRarity, -) -> InventoryItemRarity { - match rarity { - module_runtime_item::RuntimeItemRewardItemRarity::Common => InventoryItemRarity::Common, - module_runtime_item::RuntimeItemRewardItemRarity::Uncommon => InventoryItemRarity::Uncommon, - module_runtime_item::RuntimeItemRewardItemRarity::Rare => InventoryItemRarity::Rare, - module_runtime_item::RuntimeItemRewardItemRarity::Epic => InventoryItemRarity::Epic, - module_runtime_item::RuntimeItemRewardItemRarity::Legendary => { - InventoryItemRarity::Legendary - } - } -} - -fn map_quest_reward_equipment_slot(slot: QuestRewardEquipmentSlot) -> InventoryEquipmentSlot { - match slot { - QuestRewardEquipmentSlot::Weapon => InventoryEquipmentSlot::Weapon, - QuestRewardEquipmentSlot::Armor => InventoryEquipmentSlot::Armor, - QuestRewardEquipmentSlot::Relic => InventoryEquipmentSlot::Relic, - } -} - -fn map_runtime_reward_equipment_slot( - slot: module_runtime_item::RuntimeItemEquipmentSlot, -) -> InventoryEquipmentSlot { - match slot { - module_runtime_item::RuntimeItemEquipmentSlot::Weapon => InventoryEquipmentSlot::Weapon, - module_runtime_item::RuntimeItemEquipmentSlot::Armor => InventoryEquipmentSlot::Armor, - module_runtime_item::RuntimeItemEquipmentSlot::Relic => InventoryEquipmentSlot::Relic, - } -} - -fn build_reward_seed(updated_at_micros: i64, index: usize) -> i64 { - updated_at_micros.saturating_add(index as i64 + 1) -} - -fn build_story_session_snapshot_from_row(row: &StorySession) -> StorySessionSnapshot { - StorySessionSnapshot { - story_session_id: row.story_session_id.clone(), - runtime_session_id: row.runtime_session_id.clone(), - actor_user_id: row.actor_user_id.clone(), - world_profile_id: row.world_profile_id.clone(), - initial_prompt: row.initial_prompt.clone(), - opening_summary: row.opening_summary.clone(), - latest_narrative_text: row.latest_narrative_text.clone(), - latest_choice_function_id: row.latest_choice_function_id.clone(), - status: row.status, - version: row.version, - created_at_micros: row.created_at.to_micros_since_unix_epoch(), - updated_at_micros: row.updated_at.to_micros_since_unix_epoch(), - } -} - -fn build_story_event_snapshot_from_row(row: &StoryEvent) -> StoryEventSnapshot { - StoryEventSnapshot { - event_id: row.event_id.clone(), - story_session_id: row.story_session_id.clone(), - event_kind: row.event_kind, - narrative_text: row.narrative_text.clone(), - choice_function_id: row.choice_function_id.clone(), - created_at_micros: row.created_at.to_micros_since_unix_epoch(), - } -} - -fn build_treasure_record_snapshot_from_row(row: &TreasureRecord) -> TreasureRecordSnapshot { - TreasureRecordSnapshot { - treasure_record_id: row.treasure_record_id.clone(), - runtime_session_id: row.runtime_session_id.clone(), - story_session_id: row.story_session_id.clone(), - actor_user_id: row.actor_user_id.clone(), - encounter_id: row.encounter_id.clone(), - encounter_name: row.encounter_name.clone(), - scene_id: row.scene_id.clone(), - scene_name: row.scene_name.clone(), - action: row.action, - reward_items: row.reward_items.clone(), - reward_hp: row.reward_hp, - reward_mana: row.reward_mana, - reward_currency: row.reward_currency, - story_hint: row.story_hint.clone(), - created_at_micros: row.created_at.to_micros_since_unix_epoch(), - updated_at_micros: row.updated_at.to_micros_since_unix_epoch(), - } -} - -fn append_quest_log( - ctx: &ReducerContext, - snapshot: &QuestRecordSnapshot, - event_kind: QuestLogEventKind, - signal_kind: Option, - signal: Option, - step_id: Option, - step_progress: Option, - created_at_micros: i64, -) { - ctx.db.quest_log().insert(QuestLog { - log_id: generate_quest_log_id(&snapshot.quest_id, event_kind, created_at_micros), - quest_id: snapshot.quest_id.clone(), - runtime_session_id: snapshot.runtime_session_id.clone(), - actor_user_id: snapshot.actor_user_id.clone(), - event_kind, - status_after: snapshot.status, - signal_kind, - signal, - step_id, - step_progress, - created_at: Timestamp::from_micros_since_unix_epoch(created_at_micros), - }); -} - -fn get_player_progression_snapshot_tx( - ctx: &ReducerContext, - input: PlayerProgressionGetInput, -) -> Result { - let user_id = input.user_id.trim().to_string(); - if user_id.is_empty() { - return Err("player_progression.user_id 不能为空".to_string()); - } - - if let Some(existing) = ctx.db.player_progression().user_id().find(&user_id) { - return Ok(build_player_progression_snapshot_from_row(&existing)); - } - - create_initial_player_progression(user_id, 0).map_err(|error| error.to_string()) -} - -fn upsert_player_progression_after_grant_tx( - ctx: &ReducerContext, - input: PlayerProgressionGrantInput, -) -> Result { - let current = if let Some(existing) = ctx.db.player_progression().user_id().find(&input.user_id) - { - build_player_progression_snapshot_from_row(&existing) - } else { - create_initial_player_progression(input.user_id.clone(), input.updated_at_micros) - .map_err(|error| error.to_string())? - }; - - let next = grant_player_experience(current, input).map_err(|error| error.to_string())?; - if ctx - .db - .player_progression() - .user_id() - .find(&next.user_id) - .is_some() - { - ctx.db.player_progression().user_id().delete(&next.user_id); - } - ctx.db - .player_progression() - .insert(build_player_progression_row(next.clone())); - Ok(next) -} - -fn get_chapter_progression_snapshot_tx( - ctx: &ReducerContext, - input: ChapterProgressionGetInput, -) -> Result { - let user_id = input.user_id.trim().to_string(); - let chapter_id = input.chapter_id.trim().to_string(); - if user_id.is_empty() { - return Err("chapter_progression.user_id 不能为空".to_string()); - } - if chapter_id.is_empty() { - return Err("chapter_progression.chapter_id 不能为空".to_string()); - } - - let row_id = build_chapter_progression_id(&user_id, &chapter_id); - let existing = ctx - .db - .chapter_progression() - .chapter_progression_id() - .find(&row_id) - .ok_or_else(|| "chapter_progression 不存在".to_string())?; - - Ok(build_chapter_progression_snapshot_from_row(&existing)) -} - -fn upsert_chapter_progression_snapshot_tx( - ctx: &ReducerContext, - input: ChapterProgressionInput, -) -> Result { - let snapshot = build_chapter_progression_snapshot(input).map_err(|error| error.to_string())?; - let row_id = build_chapter_progression_id(&snapshot.user_id, &snapshot.chapter_id); - if ctx - .db - .chapter_progression() - .chapter_progression_id() - .find(&row_id) - .is_some() - { - ctx.db - .chapter_progression() - .chapter_progression_id() - .delete(&row_id); - } - ctx.db - .chapter_progression() - .insert(build_chapter_progression_row(snapshot.clone())); - Ok(snapshot) -} - -fn update_chapter_progression_ledger_tx( - ctx: &ReducerContext, - input: ChapterProgressionLedgerInput, -) -> Result { - let row_id = build_chapter_progression_id(&input.user_id, &input.chapter_id); - let current = ctx - .db - .chapter_progression() - .chapter_progression_id() - .find(&row_id) - .ok_or_else(|| "chapter_progression 不存在,无法记账".to_string())?; - let next = apply_chapter_progression_ledger( - build_chapter_progression_snapshot_from_row(¤t), - input, - ) - .map_err(|error| error.to_string())?; - - ctx.db - .chapter_progression() - .chapter_progression_id() - .delete(&row_id); - ctx.db - .chapter_progression() - .insert(build_chapter_progression_row(next.clone())); - Ok(next) -} - -fn try_update_chapter_progression_ledger_tx( - ctx: &ReducerContext, - user_id: String, - chapter_id: Option, - input: ChapterProgressionLedgerInput, -) -> Result, String> { - let Some(chapter_id) = chapter_id.map(|value| value.trim().to_string()) else { - return Ok(None); - }; - - if chapter_id.is_empty() || user_id.trim().is_empty() { - return Ok(None); - } - - let row_id = build_chapter_progression_id(user_id.trim(), &chapter_id); - if ctx - .db - .chapter_progression() - .chapter_progression_id() - .find(&row_id) - .is_none() - { - return Ok(None); - } - - update_chapter_progression_ledger_tx(ctx, input).map(Some) -} - -fn build_battle_state_row(snapshot: BattleStateSnapshot) -> BattleState { - BattleState { - battle_state_id: snapshot.battle_state_id, - story_session_id: snapshot.story_session_id, - runtime_session_id: snapshot.runtime_session_id, - actor_user_id: snapshot.actor_user_id, - chapter_id: snapshot.chapter_id, - target_npc_id: snapshot.target_npc_id, - target_name: snapshot.target_name, - battle_mode: snapshot.battle_mode, - status: snapshot.status, - player_hp: snapshot.player_hp, - player_max_hp: snapshot.player_max_hp, - player_mana: snapshot.player_mana, - player_max_mana: snapshot.player_max_mana, - target_hp: snapshot.target_hp, - target_max_hp: snapshot.target_max_hp, - experience_reward: snapshot.experience_reward, - reward_items: snapshot.reward_items, - turn_index: snapshot.turn_index, - last_action_function_id: snapshot.last_action_function_id, - last_action_text: snapshot.last_action_text, - last_result_text: snapshot.last_result_text, - last_damage_dealt: snapshot.last_damage_dealt, - last_damage_taken: snapshot.last_damage_taken, - last_outcome: snapshot.last_outcome, - version: snapshot.version, - created_at: Timestamp::from_micros_since_unix_epoch(snapshot.created_at_micros), - updated_at: Timestamp::from_micros_since_unix_epoch(snapshot.updated_at_micros), - } -} - -fn build_battle_state_snapshot_from_row(row: &BattleState) -> BattleStateSnapshot { - BattleStateSnapshot { - battle_state_id: row.battle_state_id.clone(), - story_session_id: row.story_session_id.clone(), - runtime_session_id: row.runtime_session_id.clone(), - actor_user_id: row.actor_user_id.clone(), - chapter_id: row.chapter_id.clone(), - target_npc_id: row.target_npc_id.clone(), - target_name: row.target_name.clone(), - battle_mode: row.battle_mode, - status: row.status, - player_hp: row.player_hp, - player_max_hp: row.player_max_hp, - player_mana: row.player_mana, - player_max_mana: row.player_max_mana, - target_hp: row.target_hp, - target_max_hp: row.target_max_hp, - experience_reward: row.experience_reward, - reward_items: row.reward_items.clone(), - turn_index: row.turn_index, - last_action_function_id: row.last_action_function_id.clone(), - last_action_text: row.last_action_text.clone(), - last_result_text: row.last_result_text.clone(), - last_damage_dealt: row.last_damage_dealt, - last_damage_taken: row.last_damage_taken, - last_outcome: row.last_outcome, - version: row.version, - created_at_micros: row.created_at.to_micros_since_unix_epoch(), - updated_at_micros: row.updated_at.to_micros_since_unix_epoch(), - } -} - -fn upsert_npc_state_record( - ctx: &ReducerContext, - input: NpcStateUpsertInput, -) -> Result { - let npc_state_id = generate_npc_state_id(&input.runtime_session_id, &input.npc_id); - let existing = ctx.db.npc_state().npc_state_id().find(&npc_state_id); - let normalized = normalize_npc_state_snapshot( - input, - existing - .as_ref() - .map(|row| row.created_at.to_micros_since_unix_epoch()), - ) - .map_err(|error| error.to_string())?; - - if existing.is_some() { - ctx.db.npc_state().npc_state_id().delete(&npc_state_id); - } - ctx.db - .npc_state() - .insert(build_npc_state_row(normalized.clone())); - - Ok(normalized) -} - -fn resolve_npc_social_action_record( - ctx: &ReducerContext, - input: ResolveNpcSocialActionInput, -) -> Result { - let npc_state_id = generate_npc_state_id(&input.runtime_session_id, &input.npc_id); - let current = ctx - .db - .npc_state() - .npc_state_id() - .find(&npc_state_id) - .ok_or_else(|| "npc_state 不存在,无法执行社交动作".to_string())?; - let next = apply_npc_social_action(build_npc_state_snapshot_from_row(¤t), input) - .map_err(|error| error.to_string())?; - - ctx.db - .npc_state() - .npc_state_id() - .delete(¤t.npc_state_id); - ctx.db.npc_state().insert(build_npc_state_row(next.clone())); - - Ok(next) -} - -fn resolve_npc_interaction_record( - ctx: &ReducerContext, - input: ResolveNpcInteractionInput, -) -> Result { - let npc_state_id = generate_npc_state_id(&input.runtime_session_id, &input.npc_id); - let current = ctx - .db - .npc_state() - .npc_state_id() - .find(&npc_state_id) - .ok_or_else(|| "npc_state 不存在,无法执行交互".to_string())?; - let result = resolve_npc_interaction_domain(build_npc_state_snapshot_from_row(¤t), input) - .map_err(|error| error.to_string())?; - - ctx.db - .npc_state() - .npc_state_id() - .delete(¤t.npc_state_id); - ctx.db - .npc_state() - .insert(build_npc_state_row(result.npc_state.clone())); - - Ok(result) -} - -fn resolve_npc_battle_interaction_tx( - ctx: &ReducerContext, - input: ResolveNpcBattleInteractionInput, -) -> Result { - validate_npc_battle_interaction_input(&input)?; - - let interaction = resolve_npc_interaction_record(ctx, input.npc_interaction.clone())?; - let battle_mode = interaction - .battle_mode - .ok_or_else(|| "当前 NPC 交互没有产出 battle_mode,不能初始化 battle_state".to_string())?; - - let battle_state_id = input - .battle_state_id - .clone() - .unwrap_or_else(|| generate_battle_state_id(input.npc_interaction.updated_at_micros)); - if ctx - .db - .battle_state() - .battle_state_id() - .find(&battle_state_id) - .is_some() - { - return Err("battle_state.battle_state_id 已存在".to_string()); - } - - let battle_input = BattleStateInput { - battle_state_id, - story_session_id: input.story_session_id.trim().to_string(), - runtime_session_id: interaction.npc_state.runtime_session_id.clone(), - actor_user_id: input.actor_user_id.trim().to_string(), - chapter_id: None, - target_npc_id: interaction.npc_state.npc_id.clone(), - target_name: interaction.npc_state.npc_name.clone(), - battle_mode: map_npc_battle_mode(battle_mode), - player_hp: input.player_hp, - player_max_hp: input.player_max_hp, - player_mana: input.player_mana, - player_max_mana: input.player_max_mana, - target_hp: input.target_hp, - target_max_hp: input.target_max_hp, - experience_reward: input.experience_reward, - reward_items: input.reward_items.clone(), - created_at_micros: input.npc_interaction.updated_at_micros, - }; - validate_battle_state_input(&battle_input).map_err(|error| error.to_string())?; - - let battle_state = build_battle_state_snapshot(battle_input); - ctx.db - .battle_state() - .insert(build_battle_state_row(battle_state.clone())); - - Ok(NpcBattleInteractionResult { - interaction, - battle_state, - }) -} - -fn validate_npc_battle_interaction_input( - input: &ResolveNpcBattleInteractionInput, -) -> Result<(), String> { - if input.story_session_id.trim().is_empty() { - return Err("resolve_npc_battle_interaction.story_session_id 不能为空".to_string()); - } - if input.actor_user_id.trim().is_empty() { - return Err("resolve_npc_battle_interaction.actor_user_id 不能为空".to_string()); - } - if !matches!( - input.npc_interaction.interaction_function_id.trim(), - NPC_FIGHT_FUNCTION_ID | NPC_SPAR_FUNCTION_ID - ) { - return Err("resolve_npc_battle_interaction 只支持 npc_fight 或 npc_spar".to_string()); - } - - Ok(()) -} - -fn map_npc_battle_mode(mode: NpcInteractionBattleMode) -> BattleMode { - match mode { - NpcInteractionBattleMode::Fight => BattleMode::Fight, - NpcInteractionBattleMode::Spar => BattleMode::Spar, - } -} - -fn build_npc_state_row(snapshot: NpcStateSnapshot) -> NpcState { - NpcState { - npc_state_id: snapshot.npc_state_id, - runtime_session_id: snapshot.runtime_session_id, - npc_id: snapshot.npc_id, - npc_name: snapshot.npc_name, - affinity: snapshot.affinity, - relation_state: snapshot.relation_state, - help_used: snapshot.help_used, - chatted_count: snapshot.chatted_count, - gifts_given: snapshot.gifts_given, - recruited: snapshot.recruited, - trade_stock_signature: snapshot.trade_stock_signature, - revealed_facts: snapshot.revealed_facts, - known_attribute_rumors: snapshot.known_attribute_rumors, - first_meaningful_contact_resolved: snapshot.first_meaningful_contact_resolved, - seen_backstory_chapter_ids: snapshot.seen_backstory_chapter_ids, - stance_profile: snapshot.stance_profile, - created_at: Timestamp::from_micros_since_unix_epoch(snapshot.created_at_micros), - updated_at: Timestamp::from_micros_since_unix_epoch(snapshot.updated_at_micros), - } -} - -fn build_npc_state_snapshot_from_row(row: &NpcState) -> NpcStateSnapshot { - NpcStateSnapshot { - npc_state_id: row.npc_state_id.clone(), - runtime_session_id: row.runtime_session_id.clone(), - npc_id: row.npc_id.clone(), - npc_name: row.npc_name.clone(), - affinity: row.affinity, - relation_state: row.relation_state.clone(), - help_used: row.help_used, - chatted_count: row.chatted_count, - gifts_given: row.gifts_given, - recruited: row.recruited, - trade_stock_signature: row.trade_stock_signature.clone(), - revealed_facts: row.revealed_facts.clone(), - known_attribute_rumors: row.known_attribute_rumors.clone(), - first_meaningful_contact_resolved: row.first_meaningful_contact_resolved, - seen_backstory_chapter_ids: row.seen_backstory_chapter_ids.clone(), - stance_profile: row.stance_profile.clone(), - created_at_micros: row.created_at.to_micros_since_unix_epoch(), - updated_at_micros: row.updated_at.to_micros_since_unix_epoch(), - } -} - -#[cfg(test)] -mod tests { - use super::*; - - fn build_test_custom_world_agent_session( - seed_text: &str, - stage: RpgAgentStage, - draft_profile_json: Option<&str>, - ) -> CustomWorldAgentSession { - CustomWorldAgentSession { - session_id: "session-1".to_string(), - owner_user_id: "user-1".to_string(), - seed_text: seed_text.to_string(), - current_turn: 0, - progress_percent: 0, - stage, - focus_card_id: None, - anchor_content_json: "{}".to_string(), - creator_intent_json: None, - creator_intent_readiness_json: "{}".to_string(), - anchor_pack_json: None, - lock_state_json: None, - draft_profile_json: draft_profile_json.map(str::to_string), - last_assistant_reply: None, - publish_gate_json: None, - result_preview_json: None, - pending_clarifications_json: "[]".to_string(), - quality_findings_json: "[]".to_string(), - suggested_actions_json: "[]".to_string(), - recommended_replies_json: "[]".to_string(), - asset_coverage_json: "{}".to_string(), - checkpoints_json: "[]".to_string(), - created_at: Timestamp::from_micros_since_unix_epoch(1), - updated_at: Timestamp::from_micros_since_unix_epoch(1), - } - } - - #[test] - fn resolve_stable_agent_draft_profile_id_prefers_legacy_result_profile_id() { - let session = build_test_custom_world_agent_session( - "seed", - RpgAgentStage::ObjectRefining, - Some(r#"{"id":"drifted-profile","legacyResultProfile":{"id":"stable-profile"}}"#), - ); - - assert_eq!( - resolve_stable_agent_draft_profile_id(&session), - Some("stable-profile".to_string()) - ); - } - - #[test] - fn custom_world_agent_session_direct_work_content_ignores_empty_created_session() { - let empty_session = - build_test_custom_world_agent_session("", RpgAgentStage::CollectingIntent, Some("{}")); - let seeded_session = build_test_custom_world_agent_session( - "想做一个海雾群岛", - RpgAgentStage::CollectingIntent, - Some("{}"), - ); - let drafted_session = - build_test_custom_world_agent_session("", RpgAgentStage::ObjectRefining, Some("{}")); - let profile_session = build_test_custom_world_agent_session( - "", - RpgAgentStage::CollectingIntent, - Some(r#"{"worldHook":"海雾会吞掉记错航线的人。"}"#), - ); - - assert!(!custom_world_agent_session_has_direct_work_content( - &empty_session, - )); - assert!(custom_world_agent_session_has_direct_work_content( - &seeded_session, - )); - assert!(custom_world_agent_session_has_direct_work_content( - &drafted_session, - )); - assert!(custom_world_agent_session_has_direct_work_content( - &profile_session, - )); - } - - #[test] - fn same_agent_draft_profile_candidate_requires_same_owner_active_draft_and_session() { - let matching = CustomWorldProfile { - profile_id: "profile-1".to_string(), - owner_user_id: "user-1".to_string(), - public_work_code: None, - author_public_user_code: None, - source_agent_session_id: Some("session-1".to_string()), - publication_status: CustomWorldPublicationStatus::Draft, - world_name: "潮雾列岛".to_string(), - subtitle: String::new(), - summary_text: String::new(), - theme_mode: CustomWorldThemeMode::Mythic, - cover_image_src: None, - profile_payload_json: "{}".to_string(), - playable_npc_count: 0, - landmark_count: 0, - author_display_name: "玩家".to_string(), - published_at: None, - deleted_at: None, - created_at: Timestamp::from_micros_since_unix_epoch(1), - updated_at: Timestamp::from_micros_since_unix_epoch(1), - }; - let deleted = CustomWorldProfile { - profile_id: "profile-1".to_string(), - owner_user_id: "user-1".to_string(), - public_work_code: None, - author_public_user_code: None, - source_agent_session_id: Some("session-1".to_string()), - publication_status: CustomWorldPublicationStatus::Draft, - world_name: "潮雾列岛".to_string(), - subtitle: String::new(), - summary_text: String::new(), - theme_mode: CustomWorldThemeMode::Mythic, - cover_image_src: None, - profile_payload_json: "{}".to_string(), - playable_npc_count: 0, - landmark_count: 0, - author_display_name: "玩家".to_string(), - published_at: None, - deleted_at: Some(Timestamp::from_micros_since_unix_epoch(2)), - created_at: Timestamp::from_micros_since_unix_epoch(1), - updated_at: Timestamp::from_micros_since_unix_epoch(1), - }; - let published = CustomWorldProfile { - profile_id: "profile-1".to_string(), - owner_user_id: "user-1".to_string(), - public_work_code: Some("CW-00000001".to_string()), - author_public_user_code: Some("SY-00000001".to_string()), - source_agent_session_id: Some("session-1".to_string()), - publication_status: CustomWorldPublicationStatus::Published, - world_name: "潮雾列岛".to_string(), - subtitle: String::new(), - summary_text: String::new(), - theme_mode: CustomWorldThemeMode::Mythic, - cover_image_src: None, - profile_payload_json: "{}".to_string(), - playable_npc_count: 0, - landmark_count: 0, - author_display_name: "玩家".to_string(), - published_at: None, - deleted_at: None, - created_at: Timestamp::from_micros_since_unix_epoch(1), - updated_at: Timestamp::from_micros_since_unix_epoch(1), - }; - - assert!(is_same_agent_draft_profile_candidate( - &matching, - "user-1", - "session-1", - )); - assert!(!is_same_agent_draft_profile_candidate( - &matching, - "user-2", - "session-1", - )); - assert!(!is_same_agent_draft_profile_candidate( - &matching, - "user-1", - "session-2", - )); - assert!(!is_same_agent_draft_profile_candidate( - &deleted, - "user-1", - "session-1", - )); - assert!(!is_same_agent_draft_profile_candidate( - &published, - "user-1", - "session-1", - )); - } - - #[test] - fn custom_world_works_hides_compiled_draft_profile_when_agent_session_is_active() { - fn build_test_custom_world_profile( - profile_id: &str, - source_agent_session_id: Option<&str>, - publication_status: CustomWorldPublicationStatus, - ) -> CustomWorldProfile { - CustomWorldProfile { - profile_id: profile_id.to_string(), - owner_user_id: "user-1".to_string(), - public_work_code: if publication_status == CustomWorldPublicationStatus::Published { - Some("CW-00000001".to_string()) - } else { - None - }, - author_public_user_code: None, - source_agent_session_id: source_agent_session_id.map(str::to_string), - publication_status, - world_name: "潮雾列岛".to_string(), - subtitle: String::new(), - summary_text: String::new(), - theme_mode: CustomWorldThemeMode::Mythic, - cover_image_src: None, - profile_payload_json: "{}".to_string(), - playable_npc_count: 0, - landmark_count: 0, - author_display_name: "玩家".to_string(), - published_at: if publication_status == CustomWorldPublicationStatus::Published { - Some(Timestamp::from_micros_since_unix_epoch(2)) - } else { - None - }, - deleted_at: None, - created_at: Timestamp::from_micros_since_unix_epoch(1), - updated_at: Timestamp::from_micros_since_unix_epoch(1), - } - } - - let draft_profile = build_test_custom_world_profile( - "profile-1", - Some("session-1"), - CustomWorldPublicationStatus::Draft, - ); - let orphan_draft_profile = build_test_custom_world_profile( - "profile-2", - Some("session-2"), - CustomWorldPublicationStatus::Draft, - ); - let published_profile = build_test_custom_world_profile( - "profile-3", - Some("session-1"), - CustomWorldPublicationStatus::Published, - ); - let mut active_agent_session_ids = HashSet::new(); - active_agent_session_ids.insert("session-1".to_string()); - - assert!(!should_include_custom_world_profile_work( - &draft_profile, - &active_agent_session_ids, - )); - assert!(should_include_custom_world_profile_work( - &orphan_draft_profile, - &active_agent_session_ids, - )); - assert!(should_include_custom_world_profile_work( - &published_profile, - &active_agent_session_ids, - )); - } - - #[test] - fn custom_world_works_keeps_compiled_draft_profile_without_active_agent_session() { - let draft_profile = CustomWorldProfile { - profile_id: "profile-1".to_string(), - owner_user_id: "user-1".to_string(), - public_work_code: None, - author_public_user_code: None, - source_agent_session_id: Some("session-1".to_string()), - publication_status: CustomWorldPublicationStatus::Draft, - world_name: "潮雾列岛".to_string(), - subtitle: String::new(), - summary_text: String::new(), - theme_mode: CustomWorldThemeMode::Mythic, - cover_image_src: None, - profile_payload_json: "{}".to_string(), - playable_npc_count: 0, - landmark_count: 0, - author_display_name: "玩家".to_string(), - published_at: None, - deleted_at: None, - created_at: Timestamp::from_micros_since_unix_epoch(1), - updated_at: Timestamp::from_micros_since_unix_epoch(1), - }; - let mut active_agent_session_ids = HashSet::new(); - - assert!(should_include_custom_world_profile_work( - &draft_profile, - &active_agent_session_ids, - )); - - active_agent_session_ids.insert("session-2".to_string()); - assert!(should_include_custom_world_profile_work( - &draft_profile, - &active_agent_session_ids, - )); - } - - #[test] - fn summarize_publish_gate_accepts_current_agent_result_schema() { - let draft_profile = serde_json::from_str::( - r#"{ - "id":"agent-draft-session-1", - "settingText":"海雾会吞掉记错航线的人。", - "creatorIntent":{"playerPremise":"玩家是带着旧航海日志返乡的守灯人。"}, - "anchorContent":{ - "worldPromise":{"hook":"在失真的海图上追查一场被篡改的沉船事故。"}, - "playerEntryPoint":{ - "openingIdentity":"被停职返乡的守灯人", - "openingProblem":"灯塔记录被人改写", - "entryMotivation":"查清父亲沉船真相" - } - }, - "coreConflicts":["群岛议会试图掩盖沉船真相。"], - "sceneChapterBlueprints":[ - { - "id":"scene-chapter-1", - "sceneId":"landmark-1", - "title":"失灯港", - "acts":[ - { - "id":"act-1", - "title":"第一幕" - } - ] - } - ] - }"#, - ) - .expect("draft profile should be valid json") - .as_object() - .cloned() - .expect("draft profile should be object"); - - let gate = summarize_publish_gate_from_json( - "session-1", - RpgAgentStage::ReadyToPublish, - Some(&draft_profile), - &[], - ); - - assert!(gate.publish_ready); - assert_eq!(gate.blocker_count, 0); - assert!(gate.blockers.is_empty()); - } - - #[test] - fn ensure_minimal_draft_profile_includes_scene_chapter_blueprints_slot() { - let profile = ensure_minimal_draft_profile(JsonMap::new(), "旧航路群岛"); - - assert_eq!( - profile.get("sceneChapterBlueprints"), - Some(&JsonValue::Array(Vec::new())) - ); - } - - #[test] - fn draft_foundation_payload_must_contain_external_draft_profile() { - let payload = JsonMap::new(); - - let result = payload - .get("draftProfile") - .and_then(JsonValue::as_object) - .cloned() - .ok_or_else(|| { - "draft_foundation requires externally generated payload.draftProfile".to_string() - }); - - assert_eq!( - result.expect_err("missing draftProfile should be rejected"), - "draft_foundation requires externally generated payload.draftProfile" - ); - } -} diff --git a/server-rs/crates/spacetime-module/src/migration.rs b/server-rs/crates/spacetime-module/src/migration.rs index 201b096d..06766bbe 100644 --- a/server-rs/crates/spacetime-module/src/migration.rs +++ b/server-rs/crates/spacetime-module/src/migration.rs @@ -4,6 +4,7 @@ use spacetimedb_lib::sats::de::serde::DeserializeWrapper; use spacetimedb_lib::sats::ser::serde::SerializeWrapper; use std::collections::HashSet; +use crate::big_fish::big_fish_runtime_run; use crate::puzzle::{ puzzle_agent_message, puzzle_agent_session, puzzle_event, puzzle_leaderboard_entry, puzzle_runtime_run, puzzle_work_profile, @@ -147,6 +148,7 @@ macro_rules! migration_tables { big_fish_creation_session, big_fish_agent_message, big_fish_asset_slot, + big_fish_runtime_run, big_fish_event } }; diff --git a/server-rs/crates/spacetime-module/src/runtime/profile.rs b/server-rs/crates/spacetime-module/src/runtime/profile.rs index 4c81d551..426e8a56 100644 --- a/server-rs/crates/spacetime-module/src/runtime/profile.rs +++ b/server-rs/crates/spacetime-module/src/runtime/profile.rs @@ -590,7 +590,7 @@ pub(crate) fn sync_profile_projections_from_snapshot( let game_state_object = game_state.as_object(); let saved_at = Timestamp::from_micros_since_unix_epoch(snapshot.saved_at_micros); - if is_non_persistent_runtime_snapshot(&game_state) { + if module_runtime::is_non_persistent_runtime_snapshot(&game_state) { return Ok(()); } @@ -614,7 +614,7 @@ pub(crate) fn upsert_profile_played_work( } let played_at = Timestamp::from_micros_since_unix_epoch(input.played_at_micros); - let played_world_id = format!("{user_id}:{world_key}"); + let played_world_id = build_runtime_profile_played_world_id(user_id, world_key); let existing = ctx .db .profile_played_world() @@ -673,7 +673,7 @@ pub(crate) fn add_profile_observed_play_time( } let observed_at = Timestamp::from_micros_since_unix_epoch(observed_at_micros); - let played_world_id = format!("{user_id}:{world_key}"); + let played_world_id = build_runtime_profile_played_world_id(user_id, world_key); if let Some(existing) = ctx .db .profile_played_world() @@ -785,15 +785,17 @@ fn sync_profile_dashboard_from_snapshot( .as_ref() .map(|row| row.total_play_time_ms) .unwrap_or(0); - let next_wallet_balance = - read_non_negative_u64(game_state.and_then(|state| state.get("playerCurrency"))); + let next_wallet_balance = module_runtime::read_runtime_json_non_negative_u64( + game_state.and_then(|state| state.get("playerCurrency")), + ); let mut next_total_play_time_ms = previous_total_play_time_ms; if next_wallet_balance != previous_wallet_balance { ctx.db.profile_wallet_ledger().insert(ProfileWalletLedger { - wallet_ledger_id: format!( - "{}:{}:{}", - snapshot.user_id, snapshot.saved_at_micros, next_wallet_balance + wallet_ledger_id: build_runtime_profile_snapshot_wallet_ledger_id( + &snapshot.user_id, + snapshot.saved_at_micros, + next_wallet_balance, ), user_id: snapshot.user_id.clone(), amount_delta: next_wallet_balance as i64 - previous_wallet_balance as i64, @@ -803,14 +805,17 @@ fn sync_profile_dashboard_from_snapshot( }); } - if let Some(world_meta) = resolve_profile_world_snapshot_meta(game_state) { - let current_play_time_ms = read_non_negative_u64( + if let Some(world_meta) = + module_runtime::resolve_runtime_profile_world_snapshot_meta(game_state) + { + let current_play_time_ms = module_runtime::read_runtime_json_non_negative_u64( game_state .and_then(|state| state.get("runtimeStats")) .and_then(JsonValue::as_object) .and_then(|stats| stats.get("playTimeMs")), ); - let played_world_id = format!("{}:{}", snapshot.user_id, world_meta.world_key); + let played_world_id = + build_runtime_profile_played_world_id(&snapshot.user_id, &world_meta.world_key); let existing = ctx .db .profile_played_world() @@ -893,13 +898,15 @@ fn sync_profile_save_archive_from_snapshot( game_state: &JsonValue, saved_at: Timestamp, ) -> Result<(), String> { - let Some(archive_meta) = - resolve_profile_save_archive_meta(game_state, snapshot.current_story_json.as_deref()) - else { + let Some(archive_meta) = module_runtime::resolve_runtime_profile_save_archive_meta( + game_state, + snapshot.current_story_json.as_deref(), + ) else { return Ok(()); }; - let archive_id = format!("{}:{}", snapshot.user_id, archive_meta.world_key); + let archive_id = + build_runtime_profile_save_archive_id(&snapshot.user_id, &archive_meta.world_key); let existing = ctx.db.profile_save_archive().archive_id().find(&archive_id); let created_at = existing .as_ref() @@ -935,28 +942,6 @@ fn sync_profile_save_archive_from_snapshot( Ok(()) } -#[derive(Clone, Debug)] -struct ProfileWorldSnapshotMeta { - world_key: String, - owner_user_id: Option, - profile_id: Option, - world_type: Option, - world_title: String, - world_subtitle: String, -} - -#[derive(Clone, Debug)] -struct ProfileSaveArchiveMeta { - world_key: String, - owner_user_id: Option, - profile_id: Option, - world_type: Option, - world_name: String, - subtitle: String, - summary_text: String, - cover_image_src: Option, -} - pub(crate) fn build_profile_save_archive_snapshot_from_row( row: &ProfileSaveArchive, ) -> RuntimeProfileSaveArchiveSnapshot { @@ -980,196 +965,6 @@ pub(crate) fn build_profile_save_archive_snapshot_from_row( } } -fn read_non_negative_u64(value: Option<&JsonValue>) -> u64 { - match value { - Some(JsonValue::Number(number)) => { - if let Some(raw) = number.as_u64() { - raw - } else if let Some(raw) = number.as_i64() { - raw.max(0) as u64 - } else if let Some(raw) = number.as_f64() { - if raw.is_finite() && raw > 0.0 { - raw.floor() as u64 - } else { - 0 - } - } else { - 0 - } - } - Some(JsonValue::String(raw)) => raw.trim().parse::().ok().unwrap_or(0), - _ => 0, - } -} - -fn read_string_from_json(value: Option<&JsonValue>) -> Option { - value - .and_then(JsonValue::as_str) - .map(str::trim) - .filter(|value| !value.is_empty()) - .map(ToString::to_string) -} - -fn resolve_profile_world_snapshot_meta( - game_state: Option<&serde_json::Map>, -) -> Option { - let game_state = game_state?; - let custom_world_profile = game_state - .get("customWorldProfile") - .and_then(JsonValue::as_object); - - if let Some(custom_world_profile) = custom_world_profile { - let profile_id = read_string_from_json(custom_world_profile.get("id")); - let world_title = read_string_from_json(custom_world_profile.get("name")) - .or_else(|| read_string_from_json(custom_world_profile.get("title"))); - if profile_id.is_some() || world_title.is_some() { - let world_title = world_title.unwrap_or_else(|| "自定义世界".to_string()); - return Some(ProfileWorldSnapshotMeta { - world_key: profile_id - .as_ref() - .map(|profile_id| format!("custom:{profile_id}")) - .unwrap_or_else(|| format!("custom:{world_title}")), - owner_user_id: None, - profile_id, - world_type: Some("CUSTOM".to_string()), - world_title, - world_subtitle: read_string_from_json(custom_world_profile.get("summary")) - .or_else(|| read_string_from_json(custom_world_profile.get("settingText"))) - .unwrap_or_default(), - }); - } - } - - let world_type = read_string_from_json(game_state.get("worldType"))?; - let current_scene_preset = game_state - .get("currentScenePreset") - .and_then(JsonValue::as_object); - - Some(ProfileWorldSnapshotMeta { - world_key: format!("builtin:{world_type}"), - owner_user_id: None, - profile_id: None, - world_type: Some(world_type.clone()), - world_title: current_scene_preset - .and_then(|preset| read_string_from_json(preset.get("name"))) - .unwrap_or_else(|| build_builtin_world_title(&world_type)), - world_subtitle: current_scene_preset - .and_then(|preset| { - read_string_from_json(preset.get("summary")) - .or_else(|| read_string_from_json(preset.get("description"))) - }) - .unwrap_or_default(), - }) -} - -fn resolve_profile_save_archive_meta( - game_state: &JsonValue, - current_story_json: Option<&str>, -) -> Option { - if is_non_persistent_runtime_snapshot(game_state) { - return None; - } - - let game_state_object = game_state.as_object(); - let world_meta = resolve_profile_world_snapshot_meta(game_state_object)?; - let story_engine_memory = game_state_object - .and_then(|state| state.get("storyEngineMemory")) - .and_then(JsonValue::as_object); - let continue_game_digest = story_engine_memory - .and_then(|memory| read_string_from_json(memory.get("continueGameDigest"))); - let current_story_text = parse_optional_json_str(current_story_json) - .ok() - .flatten() - .and_then(|story| story.as_object().cloned()) - .and_then(|story| read_string_from_json(story.get("text"))); - let custom_world_profile = game_state_object - .and_then(|state| state.get("customWorldProfile")) - .and_then(JsonValue::as_object); - - if let Some(custom_world_profile) = custom_world_profile { - let world_name = read_string_from_json(custom_world_profile.get("name")) - .or_else(|| read_string_from_json(custom_world_profile.get("title"))) - .unwrap_or_else(|| world_meta.world_title.clone()); - let subtitle = read_string_from_json(custom_world_profile.get("summary")) - .or_else(|| read_string_from_json(custom_world_profile.get("settingText"))) - .unwrap_or_else(|| world_meta.world_subtitle.clone()); - let summary_text = continue_game_digest - .or(current_story_text) - .or_else(|| { - if subtitle.is_empty() { - None - } else { - Some(subtitle.clone()) - } - }) - .unwrap_or_else(|| DEFAULT_SAVE_ARCHIVE_SUMMARY_TEXT.to_string()); - - return Some(ProfileSaveArchiveMeta { - world_key: world_meta.world_key, - owner_user_id: world_meta.owner_user_id, - profile_id: world_meta.profile_id, - world_type: world_meta.world_type, - world_name, - subtitle, - summary_text, - cover_image_src: read_string_from_json(custom_world_profile.get("coverImageSrc")), - }); - } - - let summary_text = continue_game_digest - .or(current_story_text) - .or_else(|| { - if world_meta.world_subtitle.is_empty() { - None - } else { - Some(world_meta.world_subtitle.clone()) - } - }) - .unwrap_or_else(|| DEFAULT_SAVE_ARCHIVE_SUMMARY_TEXT.to_string()); - let current_scene_preset = game_state_object - .and_then(|state| state.get("currentScenePreset")) - .and_then(JsonValue::as_object); - - Some(ProfileSaveArchiveMeta { - world_key: world_meta.world_key, - owner_user_id: world_meta.owner_user_id, - profile_id: world_meta.profile_id, - world_type: world_meta.world_type, - world_name: world_meta.world_title, - subtitle: world_meta.world_subtitle.clone(), - summary_text, - cover_image_src: current_scene_preset - .and_then(|preset| read_string_from_json(preset.get("imageSrc"))), - }) -} - -fn is_non_persistent_runtime_snapshot(game_state: &JsonValue) -> bool { - let Some(game_state) = game_state.as_object() else { - return false; - }; - - if game_state - .get("runtimePersistenceDisabled") - .and_then(JsonValue::as_bool) - .unwrap_or(false) - { - return true; - } - - matches!( - read_string_from_json(game_state.get("runtimeMode")).as_deref(), - Some("preview") | Some("test") - ) -} - -fn build_builtin_world_title(world_type: &str) -> String { - match world_type { - "WUXIA" => "武侠世界".to_string(), - "XIANXIA" => "仙侠世界".to_string(), - _ => "叙事世界".to_string(), - } -} - fn get_profile_dashboard_snapshot( ctx: &ReducerContext, input: RuntimeProfileDashboardGetInput, @@ -1307,20 +1102,17 @@ fn create_profile_recharge_order_record( let (points_delta, membership_expires_at) = match product.kind { RuntimeProfileRechargeProductKind::Points => { let has_recharged = has_profile_points_recharged(ctx, &validated_input.user_id); - let bonus_points = if has_recharged { - 0 - } else { - product.bonus_points - }; - let points_delta = product.points_amount.saturating_add(bonus_points); + let points_delta = + resolve_runtime_profile_points_recharge_delta(&product, has_recharged); apply_profile_wallet_delta( ctx, &validated_input.user_id, points_delta, RuntimeProfileWalletLedgerSourceType::PointsRecharge, - &format!( - "{}:{}:{}", - validated_input.user_id, validated_input.created_at_micros, product.product_id + &build_runtime_profile_recharge_wallet_ledger_id( + &validated_input.user_id, + validated_input.created_at_micros, + &product.product_id, ), created_at, )?; @@ -1339,9 +1131,10 @@ fn create_profile_recharge_order_record( }; let order = ProfileRechargeOrder { - order_id: format!( - "recharge:{}:{}:{}", - validated_input.user_id, validated_input.created_at_micros, product.product_id + order_id: build_runtime_profile_recharge_order_id( + &validated_input.user_id, + validated_input.created_at_micros, + &product.product_id, ), user_id: validated_input.user_id.clone(), product_id: product.product_id.clone(), @@ -1416,25 +1209,25 @@ fn redeem_profile_referral_invite_code_record( &invitee_user_id, PROFILE_REFERRAL_REWARD_POINTS, RuntimeProfileWalletLedgerSourceType::InviteInviteeReward, - &format!( - "invitee:{}:{}", - invitee_user_id, validated_input.updated_at_micros + &build_runtime_profile_referral_invitee_ledger_id( + &invitee_user_id, + validated_input.updated_at_micros, ), bound_at, )?; let today_inviter_reward_count = count_today_profile_referral_inviter_rewards(ctx, &inviter_code.user_id, bound_at); let inviter_reward_granted = - today_inviter_reward_count < PROFILE_REFERRAL_DAILY_INVITER_REWARD_LIMIT; + should_grant_runtime_profile_inviter_reward(today_inviter_reward_count); let inviter_balance_after = if inviter_reward_granted { apply_profile_wallet_delta( ctx, &inviter_code.user_id, PROFILE_REFERRAL_REWARD_POINTS, RuntimeProfileWalletLedgerSourceType::InviteInviterReward, - &format!( - "inviter:{}:{}", - inviter_code.user_id, validated_input.updated_at_micros + &build_runtime_profile_referral_inviter_ledger_id( + &inviter_code.user_id, + validated_input.updated_at_micros, ), bound_at, )? @@ -1482,45 +1275,21 @@ fn redeem_profile_reward_code_record( .find(&code) .ok_or_else(|| "兑换码不存在".to_string())?; - if !redeem_code.enabled { - return Err("兑换码已停用".to_string()); - } - if redeem_code.reward_points == 0 { - return Err("兑换码奖励无效".to_string()); - } - let user_used_count = count_profile_redeem_code_user_usage(ctx, &code, &user_id); - match redeem_code.mode { - RuntimeProfileRedeemCodeMode::Public if user_used_count >= redeem_code.max_uses => { - return Err("兑换次数已用完".to_string()); - } - RuntimeProfileRedeemCodeMode::Unique - if redeem_code.global_used_count >= redeem_code.max_uses => - { - return Err("兑换次数已用完".to_string()); - } - RuntimeProfileRedeemCodeMode::Private => { - if !redeem_code - .allowed_user_ids - .iter() - .any(|item| item == &user_id) - { - return Err("该兑换码不适用于当前账号".to_string()); - } - if redeem_code.global_used_count >= redeem_code.max_uses { - return Err("兑换次数已用完".to_string()); - } - } - _ => {} - } + validate_runtime_profile_redeem_code_usage( + &build_profile_redeem_code_snapshot_from_row(&redeem_code), + &user_id, + user_used_count, + ) + .map_err(|error| error.to_string())?; - let usage_id = build_profile_redeem_code_usage_id( - ctx, + let usage_id = build_runtime_profile_redeem_code_usage_id( &code, &user_id, validated_input.redeemed_at_micros, + user_used_count, ); - let wallet_ledger_id = format!("{}:ledger", usage_id); + let wallet_ledger_id = build_runtime_profile_redeem_code_ledger_id(&usage_id); let wallet_balance = apply_profile_wallet_delta( ctx, &user_id, @@ -1669,7 +1438,7 @@ fn build_profile_referral_invite_center_snapshot( RuntimeReferralInviteCenterSnapshot { user_id: user_id.to_string(), invite_code: code.invite_code.clone(), - invite_link_path: format!("/?inviteCode={}", code.invite_code), + invite_link_path: build_runtime_profile_invite_link_path(&code.invite_code), invited_count, rewarded_invite_count, today_inviter_reward_count, @@ -1697,7 +1466,7 @@ fn ensure_profile_invite_code(ctx: &ReducerContext, user_id: &str) -> ProfileInv return row; } - let mut invite_code = build_profile_invite_code(user_id, 0); + let mut invite_code = build_runtime_profile_invite_code(user_id, 0); let mut salt = 1; while ctx .db @@ -1706,7 +1475,7 @@ fn ensure_profile_invite_code(ctx: &ReducerContext, user_id: &str) -> ProfileInv .find(&invite_code) .is_some() { - invite_code = build_profile_invite_code(user_id, salt); + invite_code = build_runtime_profile_invite_code(user_id, salt); salt += 1; } @@ -1718,21 +1487,12 @@ fn ensure_profile_invite_code(ctx: &ReducerContext, user_id: &str) -> ProfileInv }) } -fn build_profile_invite_code(user_id: &str, salt: u32) -> String { - let mut hash = 14_695_981_039_346_656_037u64; - for byte in user_id.as_bytes().iter().copied().chain(salt.to_le_bytes()) { - hash ^= byte as u64; - hash = hash.wrapping_mul(1_099_511_628_211); - } - format!("SY{:08X}", hash as u32) -} - fn count_today_profile_referral_inviter_rewards( ctx: &ReducerContext, user_id: &str, now: Timestamp, ) -> u32 { - let day_start_micros = (now.to_micros_since_unix_epoch() / 86_400_000_000) * 86_400_000_000; + let day_start_micros = runtime_profile_day_start_micros(now.to_micros_since_unix_epoch()); ctx.db .profile_wallet_ledger() .iter() @@ -1831,24 +1591,25 @@ fn apply_profile_membership_purchase( .user_id() .find(&user_id.to_string()); let purchased_at_micros = purchased_at.to_micros_since_unix_epoch(); - let start_at_micros = current - .as_ref() - .map(|row| row.expires_at.to_micros_since_unix_epoch()) - .filter(|expires_at_micros| *expires_at_micros > purchased_at_micros) - .unwrap_or(purchased_at_micros); - let expires_at = Timestamp::from_micros_since_unix_epoch( - start_at_micros.saturating_add(duration_days as i64 * 86_400_000_000), + let purchase_update = resolve_runtime_profile_membership_purchase_update( + current + .as_ref() + .map(|row| row.started_at.to_micros_since_unix_epoch()), + current + .as_ref() + .map(|row| row.expires_at.to_micros_since_unix_epoch()), + purchased_at_micros, + duration_days, ); - let created_at = current - .as_ref() - .map(|row| row.started_at) - .unwrap_or(purchased_at); + let expires_at = Timestamp::from_micros_since_unix_epoch(purchase_update.expires_at_micros); + let created_at = Timestamp::from_micros_since_unix_epoch(purchase_update.started_at_micros); + let current = current.map(|row| row.user_id); - if let Some(existing) = current { + if let Some(existing_user_id) = current { ctx.db .profile_membership() .user_id() - .delete(&existing.user_id); + .delete(&existing_user_id); } ctx.db.profile_membership().insert(ProfileMembership { @@ -1871,8 +1632,8 @@ fn apply_profile_wallet_delta( ledger_id: &str, created_at: Timestamp, ) -> Result { - let amount_delta = - i64::try_from(amount_delta).map_err(|_| "profile.wallet_amount 超出上限".to_string())?; + let amount_delta = convert_runtime_profile_wallet_unsigned_delta(amount_delta) + .map_err(|error| error.to_string())?; apply_profile_wallet_signed_delta( ctx, user_id, @@ -1898,10 +1659,12 @@ fn apply_profile_wallet_adjustment( ) .map_err(|error| error.to_string())?; let created_at = Timestamp::from_micros_since_unix_epoch(validated_input.created_at_micros); + let unsigned_delta = convert_runtime_profile_wallet_unsigned_delta(validated_input.amount) + .map_err(|error| error.to_string())?; let amount_delta = if consume { - -(validated_input.amount as i64) + -unsigned_delta } else { - validated_input.amount as i64 + unsigned_delta }; apply_profile_wallet_signed_delta( @@ -1947,15 +1710,8 @@ fn apply_profile_wallet_signed_delta( .user_id() .find(&user_id.to_string()); let previous_balance = current.as_ref().map(|row| row.wallet_balance).unwrap_or(0); - let next_balance = if amount_delta >= 0 { - previous_balance - .checked_add(amount_delta as u64) - .ok_or_else(|| "profile.wallet_balance 超出上限".to_string())? - } else { - previous_balance - .checked_sub(amount_delta.unsigned_abs()) - .ok_or_else(|| "叙世币余额不足".to_string())? - }; + let next_balance = calculate_runtime_profile_wallet_balance(previous_balance, amount_delta) + .map_err(|error| error.to_string())?; let created_state_at = current .as_ref() .map(|row| row.created_at) @@ -2034,19 +1790,6 @@ fn count_profile_redeem_code_user_usage(ctx: &ReducerContext, code: &str, user_i .count() as u32 } -fn build_profile_redeem_code_usage_id( - ctx: &ReducerContext, - code: &str, - user_id: &str, - redeemed_at_micros: i64, -) -> String { - let sequence = count_profile_redeem_code_user_usage(ctx, code, user_id); - format!( - "redeem:{}:{}:{}:{}", - code, user_id, redeemed_at_micros, sequence - ) -} - fn resolve_profile_redeem_code_allowed_user_ids( ctx: &ReducerContext, input: &RuntimeProfileRedeemCodeAdminUpsertInput, diff --git a/src/BigFishPlaygroundApp.tsx b/src/BigFishPlaygroundApp.tsx index b701f3c7..4f258cb0 100644 --- a/src/BigFishPlaygroundApp.tsx +++ b/src/BigFishPlaygroundApp.tsx @@ -1,224 +1,9 @@ -import { useCallback, useMemo, useState } from 'react'; - -import type { - BigFishAssetSlotResponse, - BigFishRuntimeEntityResponse, - BigFishRuntimeSnapshotResponse, - SubmitBigFishInputRequest, -} from '../packages/shared/src/contracts/bigFish'; -import { BigFishRuntimeShell } from './components/big-fish-runtime/BigFishRuntimeShell'; - -const BIG_FISH_BACKGROUND_IMAGE = - 'data:image/svg+xml;utf8,' + - encodeURIComponent(` - - - - - - - - - - - - - - - - - - - - -`); - -const WORLD_MIN_X = 60; -const WORLD_MAX_X = 780; -const WORLD_MIN_Y = 80; -const WORLD_MAX_Y = 1240; -const PLAYER_SPEED = 20; - -function clamp(value: number, min: number, max: number) { - return Math.max(min, Math.min(max, value)); -} - -function buildEntity( - entityId: string, - level: number, - x: number, - y: number, -): BigFishRuntimeEntityResponse { - return { - entityId, - level, - position: { x, y }, - radius: 12 + level * 5, - offscreenSeconds: 0, - }; -} - -function buildInitialRun(): BigFishRuntimeSnapshotResponse { - const leader = buildEntity('player-leader', 1, 360, 640); - return { - runId: `local-big-fish-run-${Date.now()}`, - sessionId: 'local-big-fish-session', - status: 'running', - tick: 0, - playerLevel: 1, - winLevel: 5, - leaderEntityId: leader.entityId, - ownedEntities: [leader], - wildEntities: [ - buildEntity('wild-small-1', 1, 250, 560), - buildEntity('wild-small-2', 1, 470, 760), - buildEntity('wild-mid-1', 2, 560, 520), - buildEntity('wild-mid-2', 3, 210, 820), - buildEntity('wild-boss-1', 5, 610, 930), - ], - cameraCenter: { ...leader.position }, - lastInput: { x: 0, y: 0 }, - eventLog: ['按住屏幕任意位置,再拖动控制方向。'], - updatedAt: new Date().toISOString(), - }; -} - -function distanceBetween( - first: BigFishRuntimeEntityResponse, - second: BigFishRuntimeEntityResponse, -) { - return Math.hypot( - first.position.x - second.position.x, - first.position.y - second.position.y, - ); -} - -function respawnWildEntity(entity: BigFishRuntimeEntityResponse, tick: number) { - const offset = tick * 37 + entity.level * 53; - return { - ...entity, - position: { - x: WORLD_MIN_X + (offset % Math.floor(WORLD_MAX_X - WORLD_MIN_X)), - y: WORLD_MIN_Y + ((offset * 7) % Math.floor(WORLD_MAX_Y - WORLD_MIN_Y)), - }, - }; -} - -function moveWildEntity(entity: BigFishRuntimeEntityResponse, tick: number) { - const phase = tick * 0.32 + entity.level * 1.7; - const speed = 6 + entity.level * 0.8; - const nextX = entity.position.x + Math.cos(phase) * speed; - const nextY = entity.position.y + Math.sin(phase * 0.73) * speed; - return { - ...entity, - position: { - x: clamp(nextX, WORLD_MIN_X, WORLD_MAX_X), - y: clamp(nextY, WORLD_MIN_Y, WORLD_MAX_Y), - }, - }; -} - -function applyLocalInput( - run: BigFishRuntimeSnapshotResponse, - input: SubmitBigFishInputRequest, -): BigFishRuntimeSnapshotResponse { - if (run.status !== 'running') { - return run; - } - - const leader = run.ownedEntities.find( - (entity) => entity.entityId === run.leaderEntityId, - ); - if (!leader) { - return run; - } - - const nextLeader = { - ...leader, - position: { - x: clamp(leader.position.x + input.x * PLAYER_SPEED, WORLD_MIN_X, WORLD_MAX_X), - y: clamp(leader.position.y + input.y * PLAYER_SPEED, WORLD_MIN_Y, WORLD_MAX_Y), - }, - }; - - let nextPlayerLevel = run.playerLevel; - const nextEvents = [...run.eventLog]; - const nextWildEntities = run.wildEntities.map((entity) => { - const movedEntity = moveWildEntity(entity, run.tick + 1); - const touched = distanceBetween(nextLeader, movedEntity) <= nextLeader.radius + movedEntity.radius; - if (!touched) { - return movedEntity; - } - - if (movedEntity.level <= nextPlayerLevel) { - nextPlayerLevel = Math.min(run.winLevel, nextPlayerLevel + 1); - nextEvents.push(`吞噬 Lv.${movedEntity.level},成长到 Lv.${nextPlayerLevel}`); - return respawnWildEntity(movedEntity, run.tick + nextPlayerLevel); - } - - nextEvents.push(`撞上 Lv.${movedEntity.level},暂时避开更大的鱼。`); - return movedEntity; - }); - - const scaledLeader = { - ...nextLeader, - level: nextPlayerLevel, - radius: 12 + nextPlayerLevel * 5, - }; - const status = nextPlayerLevel >= run.winLevel ? 'won' : 'running'; - if (status === 'won' && run.status !== 'won') { - nextEvents.push('已经成长为海域霸主。'); - } - - return { - ...run, - status, - tick: run.tick + 1, - playerLevel: nextPlayerLevel, - ownedEntities: [scaledLeader], - wildEntities: nextWildEntities, - cameraCenter: { ...scaledLeader.position }, - lastInput: input, - eventLog: nextEvents.slice(-5), - updatedAt: new Date().toISOString(), - }; -} +import { useEffect } from 'react'; export default function BigFishPlaygroundApp() { - const [run, setRun] = useState(buildInitialRun); - const assetSlots = useMemo( - () => [ - { - slotId: 'local-big-fish-background', - assetKind: 'stage_background', - status: 'ready', - assetUrl: BIG_FISH_BACKGROUND_IMAGE, - promptSnapshot: '本地直达入口占位海域背景', - updatedAt: new Date(0).toISOString(), - }, - ], - [], - ); - - const handleSubmitInput = useCallback((payload: SubmitBigFishInputRequest) => { - setRun((currentRun) => applyLocalInput(currentRun, payload)); - }, []); - - const handleRestart = useCallback(() => { - setRun(buildInitialRun()); - }, []); - - const handleExit = useCallback(() => { + useEffect(() => { window.location.assign('/'); }, []); - return ( - - ); + return null; } diff --git a/src/components/big-fish-runtime/BigFishRuntimeShell.test.tsx b/src/components/big-fish-runtime/BigFishRuntimeShell.test.tsx index c4775a94..1681ffdc 100644 --- a/src/components/big-fish-runtime/BigFishRuntimeShell.test.tsx +++ b/src/components/big-fish-runtime/BigFishRuntimeShell.test.tsx @@ -1,7 +1,7 @@ // @vitest-environment jsdom -import { act, fireEvent, render, screen } from '@testing-library/react'; -import { afterEach, describe, expect, test, vi } from 'vitest'; +import { act, fireEvent, render, screen, waitFor } from '@testing-library/react'; +import { describe, expect, test, vi } from 'vitest'; import type { BigFishRuntimeSnapshotResponse } from '../../../packages/shared/src/contracts/bigFish'; import { BigFishRuntimeShell } from './BigFishRuntimeShell'; @@ -49,10 +49,6 @@ function dispatchPointerEvent( } describe('BigFishRuntimeShell', () => { - afterEach(() => { - vi.useRealTimers(); - }); - test('renders restart and exit actions after a failed run', () => { const onBack = vi.fn(); const onRestart = vi.fn(); @@ -111,8 +107,7 @@ describe('BigFishRuntimeShell', () => { expect(screen.queryByRole('dialog', { name: '玩法规则' })).toBeNull(); }); - test('keeps moving in the last sampled direction after drag ends', () => { - vi.useFakeTimers(); + test('keeps moving in the last sampled direction after drag ends', async () => { const onSubmitInput = vi.fn(); const { container } = render( @@ -141,9 +136,10 @@ describe('BigFishRuntimeShell', () => { clientY: 100, }); }); - act(() => { - vi.advanceTimersByTime(100); + await waitFor(() => { + expect(onSubmitInput).toHaveBeenCalledWith({ x: 1, y: 0 }); }); + const callCountAfterDrag = onSubmitInput.mock.calls.length; act(() => { dispatchPointerEvent(stage, 'pointerup', { pointerId: 1, @@ -151,8 +147,9 @@ describe('BigFishRuntimeShell', () => { clientY: 100, }); }); - act(() => { - vi.advanceTimersByTime(220); + + await waitFor(() => { + expect(onSubmitInput.mock.calls.length).toBeGreaterThan(callCountAfterDrag); }); expect(onSubmitInput).toHaveBeenLastCalledWith({ x: 1, y: 0 }); diff --git a/src/components/platform-entry/PlatformEntryFlowShellImpl.tsx b/src/components/platform-entry/PlatformEntryFlowShellImpl.tsx index 6ce52d39..16672d6c 100644 --- a/src/components/platform-entry/PlatformEntryFlowShellImpl.tsx +++ b/src/components/platform-entry/PlatformEntryFlowShellImpl.tsx @@ -56,9 +56,9 @@ import { } from '../../services/big-fish-creation'; import { listBigFishGallery } from '../../services/big-fish-gallery'; import { - advanceLocalBigFishRuntimeRun, recordBigFishPlay, - startLocalBigFishRuntimeRun, + startBigFishRun as startBigFishRuntimeRun, + submitBigFishInput as submitBigFishRuntimeInput, } from '../../services/big-fish-runtime'; import { deleteBigFishWork, @@ -456,6 +456,7 @@ export function PlatformEntryFlowShellImpl({ >(null); const [bigFishRuntimeSessionSource, setBigFishRuntimeSessionSource] = useState(null); + const bigFishInputInFlightRef = useRef(false); const [isBigFishLoadingLibrary, setIsBigFishLoadingLibrary] = useState(false); const [bigFishGenerationState, setBigFishGenerationState] = useState(null); @@ -1182,7 +1183,7 @@ export function PlatformEntryFlowShellImpl({ } }, [bigFishRun, bigFishSession, selectionStage, setSelectionStage]); - const startBigFishRun = useCallback(() => { + const startBigFishRun = useCallback(async () => { if (!bigFishSession) { return; } @@ -1191,16 +1192,25 @@ export function PlatformEntryFlowShellImpl({ setBigFishError(null); setBigFishRuntimeShare(null); setBigFishRuntimeWork(null); - setBigFishRuntimeStartedAt(Date.now()); - setBigFishRuntimeSessionSource('draft'); - setBigFishRun(startLocalBigFishRuntimeRun({ session: bigFishSession })); - setSelectionStage('big-fish-runtime'); - void recordBigFishPlay(sessionId, { elapsedMs: 0 }).catch((error) => { + setBigFishRuntimeStartedAt(null); + setBigFishRun(null); + try { + const { run } = await startBigFishRuntimeRun(sessionId); + setBigFishRuntimeStartedAt(Date.now()); + setBigFishRuntimeSessionSource('draft'); + setBigFishRun(run); + setSelectionStage('big-fish-runtime'); + void recordBigFishPlay(sessionId, { elapsedMs: 0 }).catch((error) => { + setBigFishError( + resolveBigFishErrorMessage(error, '记录大鱼吃小鱼游玩失败。'), + ); + }); + void refreshBigFishShelf(); + } catch (error) { setBigFishError( - resolveBigFishErrorMessage(error, '记录大鱼吃小鱼游玩失败。'), + resolveBigFishErrorMessage(error, '启动大鱼吃小鱼玩法失败。'), ); - }); - void refreshBigFishShelf(); + } }, [ bigFishSession, refreshBigFishShelf, @@ -1208,7 +1218,7 @@ export function PlatformEntryFlowShellImpl({ setSelectionStage, ]); - const restartBigFishRun = useCallback(() => { + const restartBigFishRun = useCallback(async () => { if (!bigFishSession && !bigFishRun) { return; } @@ -1222,23 +1232,26 @@ export function PlatformEntryFlowShellImpl({ if (bigFishSession) { setBigFishRuntimeShare(null); } - setBigFishRuntimeStartedAt(Date.now()); - setBigFishRuntimeSessionSource(bigFishSession ? 'draft' : 'work'); - setBigFishRun( - startLocalBigFishRuntimeRun({ - session: bigFishSession, - work: bigFishRuntimeWork, - }), - ); - setSelectionStage('big-fish-runtime'); - void recordBigFishPlay(sessionId, { elapsedMs: 0 }).catch((error) => { + setBigFishRuntimeStartedAt(null); + setBigFishRun(null); + try { + const { run } = await startBigFishRuntimeRun(sessionId); + setBigFishRuntimeStartedAt(Date.now()); + setBigFishRuntimeSessionSource(bigFishSession ? 'draft' : 'work'); + setBigFishRun(run); + setSelectionStage('big-fish-runtime'); + void recordBigFishPlay(sessionId, { elapsedMs: 0 }).catch((error) => { + setBigFishError( + resolveBigFishErrorMessage(error, '记录大鱼吃小鱼游玩失败。'), + ); + }); + } catch (error) { setBigFishError( - resolveBigFishErrorMessage(error, '记录大鱼吃小鱼游玩失败。'), + resolveBigFishErrorMessage(error, '启动大鱼吃小鱼玩法失败。'), ); - }); + } }, [ bigFishRun, - bigFishRuntimeWork, bigFishSession, resolveBigFishErrorMessage, setSelectionStage, @@ -1327,18 +1340,31 @@ export function PlatformEntryFlowShellImpl({ ); const submitBigFishInput = useCallback( - (payload: SubmitBigFishInputRequest) => { - if (!bigFishRun || bigFishRun.status !== 'running') { + async (payload: SubmitBigFishInputRequest) => { + if ( + !bigFishRun || + bigFishRun.status !== 'running' || + bigFishInputInFlightRef.current + ) { return; } - setBigFishRun((currentRun) => - currentRun - ? advanceLocalBigFishRuntimeRun(currentRun, payload) - : currentRun, - ); + bigFishInputInFlightRef.current = true; + try { + const { run } = await submitBigFishRuntimeInput( + bigFishRun.runId, + payload, + ); + setBigFishRun(run); + } catch (error) { + setBigFishError( + resolveBigFishErrorMessage(error, '同步大鱼吃小鱼输入失败。'), + ); + } finally { + bigFishInputInFlightRef.current = false; + } }, - [bigFishRun], + [bigFishRun, resolveBigFishErrorMessage, setBigFishError], ); const reportBigFishObservedPlayTime = useCallback(() => { @@ -1793,7 +1819,7 @@ export function PlatformEntryFlowShellImpl({ ); const startBigFishRunFromWork = useCallback( - (item: BigFishWorkSummary) => { + async (item: BigFishWorkSummary) => { const sessionId = item.sourceSessionId?.trim(); if (!sessionId) { setBigFishError('当前作品缺少会话信息,暂时无法进入玩法。'); @@ -1808,18 +1834,27 @@ export function PlatformEntryFlowShellImpl({ title: item.title, publicWorkCode, }); - setBigFishRuntimeStartedAt(Date.now()); + setBigFishRuntimeStartedAt(null); setBigFishRuntimeSessionSource('work'); - setBigFishRun(startLocalBigFishRuntimeRun({ work: item })); - setSelectionStage('big-fish-runtime'); - pushAppHistoryPath( - buildPublicWorkStagePath('big-fish-runtime', publicWorkCode), - ); - void recordBigFishPlay(sessionId, { elapsedMs: 0 }).catch((error) => { - setBigFishError( - resolveBigFishErrorMessage(error, '记录大鱼吃小鱼游玩失败。'), + setBigFishRun(null); + try { + const { run } = await startBigFishRuntimeRun(sessionId); + setBigFishRuntimeStartedAt(Date.now()); + setBigFishRun(run); + setSelectionStage('big-fish-runtime'); + pushAppHistoryPath( + buildPublicWorkStagePath('big-fish-runtime', publicWorkCode), ); - }); + void recordBigFishPlay(sessionId, { elapsedMs: 0 }).catch((error) => { + setBigFishError( + resolveBigFishErrorMessage(error, '记录大鱼吃小鱼游玩失败。'), + ); + }); + } catch (error) { + setBigFishError( + resolveBigFishErrorMessage(error, '启动大鱼吃小鱼玩法失败。'), + ); + } }, [bigFishFlow, resolveBigFishErrorMessage, setSelectionStage], ); @@ -2018,10 +2053,10 @@ export function PlatformEntryFlowShellImpl({ (entry) => entry.sourceSessionId === sessionId, ); if (matchedEntry) { - startBigFishRunFromWork(matchedEntry); + void startBigFishRunFromWork(matchedEntry); return; } - startBigFishRunFromWork({ + void startBigFishRunFromWork({ workId: `big-fish:${sessionId}`, sourceSessionId: sessionId, ownerUserId: work.ownerUserId ?? '', diff --git a/src/components/rpg-entry/RpgEntryFlowShell.agent.interaction.test.tsx b/src/components/rpg-entry/RpgEntryFlowShell.agent.interaction.test.tsx index 8306d4c7..a4e233f1 100644 --- a/src/components/rpg-entry/RpgEntryFlowShell.agent.interaction.test.tsx +++ b/src/components/rpg-entry/RpgEntryFlowShell.agent.interaction.test.tsx @@ -17,7 +17,7 @@ import { getBigFishCreationSession, } from '../../services/big-fish-creation'; import { listBigFishGallery } from '../../services/big-fish-gallery'; -import { startLocalBigFishRuntimeRun } from '../../services/big-fish-runtime'; +import { startBigFishRun } from '../../services/big-fish-runtime'; import { listBigFishWorks } from '../../services/big-fish-works'; import { createPuzzleAgentSession, @@ -155,8 +155,9 @@ vi.mock('../../services/big-fish-gallery', () => ({ })); vi.mock('../../services/big-fish-runtime', () => ({ - advanceLocalBigFishRuntimeRun: vi.fn((run) => run), - startLocalBigFishRuntimeRun: vi.fn(), + recordBigFishPlay: vi.fn().mockResolvedValue({ items: [] }), + startBigFishRun: vi.fn(), + submitBigFishInput: vi.fn(), })); vi.mock('../../services/puzzle-agent', () => ({ @@ -1091,28 +1092,30 @@ beforeEach(() => { vi.mocked(listBigFishGallery).mockResolvedValue({ items: [], }); - vi.mocked(startLocalBigFishRuntimeRun).mockReturnValue({ - runId: 'big-fish-run-1', - sessionId: 'big-fish-session-public-1', - status: 'running', - tick: 0, - playerLevel: 1, - winLevel: 8, - leaderEntityId: 'owned-1', - ownedEntities: [ - { - entityId: 'owned-1', - level: 1, - position: { x: 0, y: 0 }, - radius: 12, - offscreenSeconds: 0, - }, - ], - wildEntities: [], - cameraCenter: { x: 0, y: 0 }, - lastInput: { x: 0, y: 0 }, - eventLog: ['机械鱼群开始巡游。'], - updatedAt: '2026-04-25T12:12:00.000Z', + vi.mocked(startBigFishRun).mockResolvedValue({ + run: { + runId: 'big-fish-run-1', + sessionId: 'big-fish-session-public-1', + status: 'running', + tick: 0, + playerLevel: 1, + winLevel: 8, + leaderEntityId: 'owned-1', + ownedEntities: [ + { + entityId: 'owned-1', + level: 1, + position: { x: 0, y: 0 }, + radius: 12, + offscreenSeconds: 0, + }, + ], + wildEntities: [], + cameraCenter: { x: 0, y: 0 }, + lastInput: { x: 0, y: 0 }, + eventLog: ['机械鱼群开始巡游。'], + updatedAt: '2026-04-25T12:12:00.000Z', + }, }); vi.mocked(listPuzzleWorks).mockResolvedValue({ items: [], @@ -2067,11 +2070,9 @@ test('public code search opens a published big fish work by BF code', async () = await user.click(screen.getByRole('button', { name: '搜索' })); await waitFor(() => { - expect(startLocalBigFishRuntimeRun).toHaveBeenCalledWith({ - work: expect.objectContaining({ - sourceSessionId: 'big-fish-session-public-1', - }), - }); + expect(startBigFishRun).toHaveBeenCalledWith( + 'big-fish-session-public-1', + ); }); expect(await screen.findByText('Lv.1/8 · 进行中')).toBeTruthy(); expect(getBigFishCreationSession).not.toHaveBeenCalledWith( diff --git a/src/components/rpg-runtime-shell/RpgRuntimeShell.test.tsx b/src/components/rpg-runtime-shell/RpgRuntimeShell.test.tsx index c71a385c..8fb1a421 100644 --- a/src/components/rpg-runtime-shell/RpgRuntimeShell.test.tsx +++ b/src/components/rpg-runtime-shell/RpgRuntimeShell.test.tsx @@ -4,10 +4,18 @@ import { render, screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { beforeEach, expect, test, vi } from 'vitest'; -import { AnimationState, WorldType, type GameState } from '../../types'; +import { + AnimationState, + WorldType, + type GameState, + type StoryMoment, +} from '../../types'; import { RpgRuntimeShell } from './RpgRuntimeShell'; import type { RpgRuntimeShellProps } from './types'; +const noop = () => {}; +const asyncFalse = async () => false; + vi.mock('../auth/AuthUiContext', () => ({ useAuthUi: () => null, })); @@ -34,7 +42,7 @@ vi.mock('./useRpgRuntimeShellViewModel', () => ({ shouldMountNpcModals: false, visibleGameState: mockVisibleGameState, visibleCurrentStory: { - storyText: '测试故事', + text: '测试故事', options: [], }, sceneTransitionPhase: 'idle', @@ -97,22 +105,23 @@ function createGameState( description: '测试角色', backstory: '测试背景', personality: '冷静', - motivation: '完成测试', - combatStyle: '均衡', - role: '主角', avatar: '', portrait: '', - imageSrc: '', - initialAffinity: 0, - relationshipHooks: [], - tags: [], + assetFolder: '', + assetVariant: '', + attributes: { + strength: 5, + agility: 5, + intelligence: 5, + spirit: 5, + }, backstoryReveal: { publicSummary: '测试', privateChatUnlockAffinity: 60, chapters: [], }, skills: [], - initialItems: [], + adventureOpenings: {}, }, runtimeMode, runtimePersistenceDisabled: runtimePersistenceDisabled ?? false, @@ -176,14 +185,15 @@ function buildProps( runtimePersistenceDisabled?: boolean, ): RpgRuntimeShellProps { const gameState = createGameState(runtimeMode, runtimePersistenceDisabled); + const currentStory: StoryMoment = { + text: '测试故事', + options: [], + }; mockVisibleGameState = gameState; return { session: { gameState, - currentStory: { - storyText: '测试故事', - options: [], - }, + currentStory, isLoading: false, aiError: null, bottomTab: 'adventure', @@ -201,36 +211,66 @@ function buildProps( exitNpcChat: () => false, handleMapTravelToScene: () => false, npcUi: { - isNpcModalOpen: false, - currentNpcEncounter: null, - selectedNpc: null, - isGeneratingNpcResponse: false, - npcResponseError: null, - generatedNpcText: '', - npcResponseOptions: [], - selectedOptionId: null, + tradeModal: null, + giftModal: null, + recruitModal: null, + setTradeMode: noop, + selectTradeNpcItem: noop, + selectTradePlayerItem: noop, + setTradeQuantity: noop, + closeTradeModal: noop, + confirmTrade: noop, + selectGiftItem: noop, + closeGiftModal: noop, + confirmGift: noop, + selectRecruitRelease: noop, + closeRecruitModal: noop, + confirmRecruit: noop, }, characterChatUi: { - isCharacterChatModalOpen: false, - activeCharacter: null, + modal: null, + openChat: noop, + closeChat: noop, + setDraft: noop, + useSuggestion: noop, + refreshSuggestions: noop, + sendDraft: noop, }, inventoryUi: { - isInventoryOpen: false, + useInventoryItem: asyncFalse, + equipInventoryItem: asyncFalse, + unequipItem: asyncFalse, + playerCurrency: 0, + currencyText: '0', + backpackItems: [], + equipmentSlots: [], + forgeRecipes: [], + craftRecipe: asyncFalse, + dismantleItem: asyncFalse, + reforgeItem: asyncFalse, }, battleRewardUi: { - isRewardModalOpen: false, - rewards: [], + reward: null, + dismiss: noop, }, questUi: { - isQuestPanelOpen: false, + acknowledgeQuestCompletion: noop, + claimQuestReward: () => null, }, npcChatQuestOfferUi: { - isOfferModalOpen: false, - pendingQuest: null, + replacePendingOffer: () => false, + abandonPendingOffer: () => false, + acceptPendingOffer: () => null, }, goalUi: { - isGoalPanelOpen: false, - entries: [], + goalStack: { + northStarGoal: null, + activeGoal: null, + immediateStepGoal: null, + supportGoals: [], + }, + pulse: null, + dismissPulse: noop, }, }, entry: { diff --git a/src/hooks/rpg-runtime-story/inventoryActions.ts b/src/hooks/rpg-runtime-story/inventoryActions.ts index fd0b8c41..6fca915d 100644 --- a/src/hooks/rpg-runtime-story/inventoryActions.ts +++ b/src/hooks/rpg-runtime-story/inventoryActions.ts @@ -40,7 +40,7 @@ export function useStoryInventoryActions({ } = runtime; const [serverInventoryView, setServerInventoryView] = useState(null); - const runtimeSessionId = gameState.runtimeSessionId; + const storySessionId = gameState.storySessionId; const runtimeActionVersion = gameState.runtimeActionVersion; const currentScene = gameState.currentScene; const hasPlayerCharacter = Boolean(gameState.playerCharacter); @@ -56,7 +56,7 @@ export function useStoryInventoryActions({ void loadRpgRuntimeInventoryView( { gameState: { - runtimeSessionId, + storySessionId, runtimeActionVersion, }, }, @@ -80,7 +80,7 @@ export function useStoryInventoryActions({ currentScene, hasPlayerCharacter, runtimeActionVersion, - runtimeSessionId, + storySessionId, setAiError, ]); diff --git a/src/hooks/rpg-runtime-story/rpgRuntimeStoryGateway.ts b/src/hooks/rpg-runtime-story/rpgRuntimeStoryGateway.ts index a05804d6..7ef07e19 100644 --- a/src/hooks/rpg-runtime-story/rpgRuntimeStoryGateway.ts +++ b/src/hooks/rpg-runtime-story/rpgRuntimeStoryGateway.ts @@ -3,20 +3,15 @@ import type { HydratedSavedGameSnapshot } from '../../persistence/runtimeSnapsho import { getRpgRuntimeClientVersion, getRpgRuntimeSessionId, - getRpgRuntimeStoryState, + getRpgRuntimeStorySessionId, + getRpgStoryRuntimeProjection, resolveRpgRuntimeStoryAction, + resolveRpgRuntimeStoryProjectionMoment, resolveRpgRuntimeStoryMoment, type RuntimeStoryChoicePayload, - type RuntimeStoryResponse, } from '../../services/rpg-runtime/rpgRuntimeStoryClient'; import type { GameState, StoryMoment, StoryOption } from '../../types'; -function getRuntimeResponseOptions(response: RuntimeStoryResponse) { - return response.viewModel.availableOptions.length > 0 - ? response.viewModel.availableOptions - : response.presentation.options; -} - /** * 前端访问服务端 runtime story 的统一网关。 * 统一处理 option catalog 拉取、继续游戏恢复与正式动作结算。 @@ -27,15 +22,13 @@ export async function loadServerRuntimeOptionCatalog(params: { }) { // 中文注释:状态目录只从服务端持久化 session 读取, // 前端不再上传本地 GameState 快照参与动作合法性解析。 - const response = await getRpgRuntimeStoryState({ - sessionId: getRpgRuntimeSessionId(params.gameState), + const response = await getRpgStoryRuntimeProjection({ + storySessionId: getRpgRuntimeStorySessionId(params.gameState), clientVersion: getRpgRuntimeClientVersion(params.gameState), }); - const options = resolveRpgRuntimeStoryMoment({ - response, - hydratedSnapshot: response.snapshot, - fallbackGameState: params.gameState, - fallbackStoryText: response.presentation.storyText, + const options = resolveRpgRuntimeStoryProjectionMoment({ + projection: response, + gameState: params.gameState, }).options; return options.length > 0 ? options : null; @@ -59,24 +52,26 @@ export async function resumeServerRuntimeStory( // 中文注释:继续游戏后向服务端刷新一次状态, // 让长期离线的本地快照重新对齐服务端当前 runtime view model。 - const response = await getRpgRuntimeStoryState({ - sessionId: getRpgRuntimeSessionId(hydratedSnapshot.gameState), + const response = await getRpgStoryRuntimeProjection({ + storySessionId: getRpgRuntimeStorySessionId(hydratedSnapshot.gameState), }); - const resumedSnapshot = rehydrateSavedSnapshot(response.snapshot); - const runtimeOptions = getRuntimeResponseOptions(response); + const runtimeOptions = response.options; const nextStory = - response.presentation.storyText || runtimeOptions.length > 0 - ? resolveRpgRuntimeStoryMoment({ - response, - hydratedSnapshot: resumedSnapshot, - fallbackGameState: hydratedSnapshot.gameState, - fallbackStoryText: - response.presentation.storyText || - resumedSnapshot.currentStory?.text || - hydratedSnapshot.currentStory?.text || - '', + response.currentNarrativeText || runtimeOptions.length > 0 + ? resolveRpgRuntimeStoryProjectionMoment({ + projection: response, + gameState: hydratedSnapshot.gameState, }) - : resumedSnapshot.currentStory; + : hydratedSnapshot.currentStory; + const resumedSnapshot = { + ...hydratedSnapshot, + gameState: { + ...hydratedSnapshot.gameState, + runtimeSessionId: response.storySession.runtimeSessionId, + storySessionId: response.storySession.storySessionId, + runtimeActionVersion: response.serverVersion, + }, + } satisfies HydratedSavedGameSnapshot; return { hydratedSnapshot: resumedSnapshot, diff --git a/src/hooks/rpg-runtime-story/runtimeStoryCoordinator.test.ts b/src/hooks/rpg-runtime-story/runtimeStoryCoordinator.test.ts index b8b1670f..e29edc68 100644 --- a/src/hooks/rpg-runtime-story/runtimeStoryCoordinator.test.ts +++ b/src/hooks/rpg-runtime-story/runtimeStoryCoordinator.test.ts @@ -1,14 +1,16 @@ import { beforeEach, describe, expect, it, vi } from 'vitest'; const { - getRuntimeStoryStateMock, + getStoryRuntimeProjectionMock, resolveRuntimeStoryActionMock, getRuntimeSessionIdMock, + getRuntimeStorySessionIdMock, getRuntimeClientVersionMock, } = vi.hoisted(() => ({ - getRuntimeStoryStateMock: vi.fn(), + getStoryRuntimeProjectionMock: vi.fn(), resolveRuntimeStoryActionMock: vi.fn(), getRuntimeSessionIdMock: vi.fn(() => 'runtime-main'), + getRuntimeStorySessionIdMock: vi.fn(() => 'storysess-main'), getRuntimeClientVersionMock: vi.fn(() => 0), })); @@ -22,13 +24,15 @@ vi.mock('../../services/rpg-runtime/rpgRuntimeStoryClient', async () => { return { ...actual, - getRpgRuntimeStoryState: getRuntimeStoryStateMock, + getRpgStoryRuntimeProjection: getStoryRuntimeProjectionMock, resolveRpgRuntimeStoryAction: resolveRuntimeStoryActionMock, getRpgRuntimeSessionId: getRuntimeSessionIdMock, + getRpgRuntimeStorySessionId: getRuntimeStorySessionIdMock, getRpgRuntimeClientVersion: getRuntimeClientVersionMock, - getRuntimeStoryState: getRuntimeStoryStateMock, + getStoryRuntimeProjection: getStoryRuntimeProjectionMock, resolveRuntimeStoryAction: resolveRuntimeStoryActionMock, getRuntimeSessionId: getRuntimeSessionIdMock, + getRuntimeStorySessionId: getRuntimeStorySessionIdMock, getRuntimeClientVersion: getRuntimeClientVersionMock, }; }); @@ -52,6 +56,7 @@ function createStory(text: string): StoryMoment { function createGameState(): GameState { return { runtimeSessionId: 'runtime-main', + storySessionId: 'storysess-main', runtimeActionVersion: 7, } as GameState; } @@ -59,6 +64,7 @@ function createGameState(): GameState { function createTravelGameState(): GameState { return { runtimeSessionId: 'runtime-main', + storySessionId: 'storysess-main', runtimeActionVersion: 7, worldType: WorldType.WUXIA, currentScene: 'Story', @@ -172,6 +178,7 @@ function createRuntimeNpcBattleSnapshot( }, runtimeActionVersion: 8, runtimeSessionId: 'runtime-main', + storySessionId: 'storysess-main', currentScene: 'Story', runtimeStats: { playTimeMs: 0, @@ -249,10 +256,12 @@ function createRuntimeNpcBattleSnapshot( describe('runtimeStoryCoordinator', () => { beforeEach(() => { - getRuntimeStoryStateMock.mockReset(); + getStoryRuntimeProjectionMock.mockReset(); resolveRuntimeStoryActionMock.mockReset(); getRuntimeSessionIdMock.mockReset(); getRuntimeSessionIdMock.mockReturnValue('runtime-main'); + getRuntimeStorySessionIdMock.mockReset(); + getRuntimeStorySessionIdMock.mockReturnValue('storysess-main'); getRuntimeClientVersionMock.mockReset(); getRuntimeClientVersionMock.mockReturnValue(7); }); @@ -261,46 +270,53 @@ describe('runtimeStoryCoordinator', () => { const gameState = createGameState(); const currentStory = createStory('当前故事'); - getRuntimeStoryStateMock.mockResolvedValue({ - sessionId: 'runtime-main', + getStoryRuntimeProjectionMock.mockResolvedValue({ + storySession: { + storySessionId: 'storysess-main', + runtimeSessionId: 'runtime-main', + actorUserId: 'user-1', + worldProfileId: 'profile-1', + initialPrompt: '进入营地', + openingSummary: null, + latestNarrativeText: '服务端故事', + latestChoiceFunctionId: null, + status: 'active', + version: 3, + createdAt: '2026-04-08T00:00:00.000Z', + updatedAt: '2026-04-08T00:00:00.000Z', + }, + storyEvents: [], serverVersion: 3, - viewModel: { - player: { - hp: 100, - maxHp: 100, - mana: 20, - maxMana: 20, - }, - encounter: null, - companions: [], - availableOptions: [ - { - functionId: 'npc_chat', - actionText: '继续交谈', - scope: 'npc', - }, - ], - status: { - inBattle: false, - npcInteractionActive: true, - currentNpcBattleMode: null, - currentNpcBattleOutcome: null, + actor: { + hp: 100, + maxHp: 100, + mana: 20, + maxMana: 20, + currency: 0, + currencyText: '0 铜钱', + }, + inventory: { + backpackItems: [], + equipmentSlots: [], + forgeRecipes: [], + }, + options: [ + { + functionId: 'npc_chat', + actionText: '继续交谈', + scope: 'npc', + enabled: true, }, + ], + status: { + inBattle: false, + npcInteractionActive: true, + currentNpcBattleMode: null, + currentNpcBattleOutcome: null, }, - presentation: { - actionText: '', - resultText: '', - storyText: '服务端故事', - options: [], - }, - patches: [], - snapshot: { - version: 2, - savedAt: '2026-04-08T00:00:00.000Z', - bottomTab: 'adventure', - gameState: {}, - currentStory: null, - }, + currentNarrativeText: '服务端故事', + actionResultText: null, + toast: null, }); const options = await loadServerRuntimeOptionCatalog({ @@ -308,8 +324,8 @@ describe('runtimeStoryCoordinator', () => { currentStory, }); - expect(getRuntimeStoryStateMock).toHaveBeenCalledWith({ - sessionId: 'runtime-main', + expect(getStoryRuntimeProjectionMock).toHaveBeenCalledWith({ + storySessionId: 'storysess-main', clientVersion: 7, }); expect(options).toEqual([ @@ -443,73 +459,72 @@ describe('runtimeStoryCoordinator', () => { }, runtimeActionVersion: 7, runtimeSessionId: 'runtime-main', + storySessionId: 'storysess-main', } as unknown as GameState, currentStory: createStory('本地快照故事'), bottomTab: 'inventory' as const, } as HydratedSavedGameSnapshot; - const serverHydratedSnapshot = { - version: 8, - savedAt: '2026-04-08T00:00:00.000Z', - gameState: { - currentScene: 'Story', - worldType: 'wuxia', - playerCharacter: { - id: 'hero', - }, - playerEquipment: { - weapon: null, - armor: null, - relic: null, - }, - runtimeActionVersion: 8, + getStoryRuntimeProjectionMock.mockResolvedValue({ + storySession: { + storySessionId: 'storysess-main', runtimeSessionId: 'runtime-main', - } as unknown as GameState, - currentStory: createStory('服务端快照故事'), - bottomTab: 'character' as const, - } as HydratedSavedGameSnapshot; - - getRuntimeStoryStateMock.mockResolvedValue({ - sessionId: 'runtime-main', + actorUserId: 'user-1', + worldProfileId: 'profile-1', + initialPrompt: '进入营地', + openingSummary: null, + latestNarrativeText: '服务端恢复后的故事', + latestChoiceFunctionId: null, + status: 'active', + version: 8, + createdAt: '2026-04-08T00:00:00.000Z', + updatedAt: '2026-04-08T00:00:00.000Z', + }, + storyEvents: [], serverVersion: 8, - viewModel: { - player: { - hp: 90, - maxHp: 100, - mana: 16, - maxMana: 20, - }, - encounter: null, - companions: [], - availableOptions: [ - { - functionId: 'npc_help', - actionText: '请求援手', - scope: 'npc', - }, - ], - status: { - inBattle: false, - npcInteractionActive: false, - currentNpcBattleMode: null, - currentNpcBattleOutcome: null, - }, + actor: { + hp: 90, + maxHp: 100, + mana: 16, + maxMana: 20, + currency: 0, + currencyText: '0 铜钱', }, - presentation: { - actionText: '', - resultText: '', - storyText: '服务端恢复后的故事', - options: [], + inventory: { + backpackItems: [], + equipmentSlots: [], + forgeRecipes: [], }, - patches: [], - snapshot: serverHydratedSnapshot, + options: [ + { + functionId: 'npc_help', + actionText: '请求援手', + scope: 'npc', + enabled: true, + }, + ], + status: { + inBattle: false, + npcInteractionActive: false, + currentNpcBattleMode: null, + currentNpcBattleOutcome: null, + }, + currentNarrativeText: '服务端恢复后的故事', + actionResultText: null, + toast: null, }); const result = await resumeServerRuntimeStory(localHydratedSnapshot); - expect(getRuntimeStoryStateMock).toHaveBeenCalledWith({ - sessionId: 'runtime-main', + expect(getStoryRuntimeProjectionMock).toHaveBeenCalledWith({ + storySessionId: 'storysess-main', }); - expect(result.hydratedSnapshot).toBe(serverHydratedSnapshot); + expect(result.hydratedSnapshot.gameState).toEqual( + expect.objectContaining({ + runtimeSessionId: 'runtime-main', + storySessionId: 'storysess-main', + runtimeActionVersion: 8, + }), + ); expect(result.nextStory).toEqual( expect.objectContaining({ text: '服务端恢复后的故事', @@ -545,7 +560,7 @@ describe('runtimeStoryCoordinator', () => { const result = await resumeServerRuntimeStory(localHydratedSnapshot); - expect(getRuntimeStoryStateMock).not.toHaveBeenCalled(); + expect(getStoryRuntimeProjectionMock).not.toHaveBeenCalled(); expect(result.hydratedSnapshot).toBe(localHydratedSnapshot); expect(result.nextStory).toBe(localHydratedSnapshot.currentStory); }); @@ -844,78 +859,71 @@ describe('runtimeStoryCoordinator', () => { ).toBe(false); }); - it('rehydrates mid-battle snapshots when resuming a saved runtime story', async () => { + it('refreshes mid-battle story options from projection while keeping local snapshot shape', async () => { const localHydratedSnapshot = createRuntimeNpcBattleSnapshot({ runtimeActionVersion: 7, - }); - const rawServerBattleSnapshot = createRuntimeNpcBattleSnapshot({ - runtimeActionVersion: 8, - playerHp: 39, - sceneHostileNpcs: [ - { - id: 'npc-bandit', - name: '断桥匪首', - hp: 14, - maxHp: 32, - description: '拦路的刀客', - }, - ] as unknown as GameState['sceneHostileNpcs'], + storySessionId: 'storysess-main', }); - getRuntimeStoryStateMock.mockResolvedValue({ - sessionId: 'runtime-main', + getStoryRuntimeProjectionMock.mockResolvedValue({ + storySession: { + storySessionId: 'storysess-main', + runtimeSessionId: 'runtime-main', + actorUserId: 'user-1', + worldProfileId: 'profile-1', + initialPrompt: '进入营地', + openingSummary: null, + latestNarrativeText: '断桥匪首还在步步逼近。', + latestChoiceFunctionId: null, + status: 'active', + version: 8, + createdAt: '2026-04-08T00:00:00.000Z', + updatedAt: '2026-04-08T00:00:00.000Z', + }, + storyEvents: [], serverVersion: 8, - viewModel: { - player: { - hp: 39, - maxHp: 50, - mana: 20, - maxMana: 20, - }, - encounter: { - id: 'npc-bandit', - kind: 'npc', - npcName: '断桥匪首', - hostile: true, - affinity: -12, - recruited: false, - interactionActive: false, - battleMode: 'fight', - }, - companions: [], - availableOptions: [ - { - functionId: 'battle_guard_break', - actionText: '破架重击', - scope: 'combat', - }, - ], - status: { - inBattle: true, - npcInteractionActive: false, - currentNpcBattleMode: 'fight', - currentNpcBattleOutcome: null, - }, + actor: { + hp: 39, + maxHp: 50, + mana: 20, + maxMana: 20, + currency: 0, + currencyText: '0 铜钱', }, - presentation: { - actionText: '', - resultText: '', - storyText: '断桥匪首还在步步逼近。', - options: [], + inventory: { + backpackItems: [], + equipmentSlots: [], + forgeRecipes: [], }, - patches: [], - snapshot: rawServerBattleSnapshot, + options: [ + { + functionId: 'battle_guard_break', + actionText: '破架重击', + scope: 'combat', + enabled: true, + }, + ], + status: { + inBattle: true, + npcInteractionActive: false, + currentNpcBattleMode: 'fight', + currentNpcBattleOutcome: null, + }, + currentNarrativeText: '断桥匪首还在步步逼近。', + actionResultText: null, + toast: null, }); const result = await resumeServerRuntimeStory(localHydratedSnapshot); - expect(getRuntimeStoryStateMock).toHaveBeenCalledWith({ - sessionId: 'runtime-main', + expect(getStoryRuntimeProjectionMock).toHaveBeenCalledWith({ + storySessionId: 'storysess-main', }); + expect(result.hydratedSnapshot.gameState.runtimeActionVersion).toBe(8); expect(result.hydratedSnapshot.gameState.sceneHostileNpcs[0]).toEqual( expect.objectContaining({ id: 'npc-bandit', - hp: 14, + hp: 21, maxHp: 32, encounter: expect.objectContaining({ kind: 'npc', diff --git a/src/hooks/useGameFlow.customWorld.test.tsx b/src/hooks/useGameFlow.customWorld.test.tsx index 5effc078..a450bd66 100644 --- a/src/hooks/useGameFlow.customWorld.test.tsx +++ b/src/hooks/useGameFlow.customWorld.test.tsx @@ -18,6 +18,9 @@ import { useRpgSessionBootstrap } from './rpg-session'; const aiServiceMocks = vi.hoisted(() => ({ streamNpcChatTurn: vi.fn(), })); +const rpgRuntimeStoryClientMocks = vi.hoisted(() => ({ + beginRpgRuntimeStorySession: vi.fn(), +})); vi.mock('../services/aiService', async () => { const actual = @@ -31,6 +34,18 @@ vi.mock('../services/aiService', async () => { }; }); +vi.mock('../services/rpg-runtime/rpgRuntimeStoryClient', async () => { + const actual = await vi.importActual< + typeof import('../services/rpg-runtime/rpgRuntimeStoryClient') + >('../services/rpg-runtime/rpgRuntimeStoryClient'); + + return { + ...actual, + beginRpgRuntimeStorySession: + rpgRuntimeStoryClientMocks.beginRpgRuntimeStorySession, + }; +}); + function buildBackstoryReveal(label: string) { return { publicSummary: `${label}的公开背景`, @@ -401,6 +416,163 @@ function readSnapshot() { }; } +function findRuntimeNpc(profile: ReturnType) { + const npc = profile.storyNpcs.find((candidate) => candidate.id === 'story-act-only'); + if (!npc) { + throw new Error('test npc story-act-only not found'); + } + + return npc; +} + +function buildRuntimeStoryBootstrapSnapshot(params: { + profile: ReturnType; + character: NonNullable[number]>; +}) { + const npc = findRuntimeNpc(params.profile); + const playableSource = params.profile.playableNpcs.find( + (candidate) => candidate.id === params.character.id, + ); + const initialItems = playableSource?.initialItems ?? []; + const currentScenePreset = { + id: 'custom-scene-camp', + name: '回潮暂栖所', + description: '能暂时收拢队伍、整理灯册与潮路线索的落脚点。', + imageSrc: '', + connectedSceneIds: ['custom-scene-landmark-1', 'custom-scene-landmark-2'], + }; + const weapon = initialItems.find( + (item) => item.id === 'item-playable-1', + ); + const relic = initialItems.find( + (item) => item.id === 'item-playable-3', + ); + + return { + sessionId: 'runtime-main', + serverVersion: 1, + snapshot: { + version: 2, + savedAt: '2026-04-29T00:00:00.000Z', + bottomTab: 'adventure', + currentStory: null, + gameState: { + worldType: WorldType.CUSTOM, + customWorldProfile: params.profile, + playerCharacter: params.character, + runtimeSessionId: 'runtime-main', + storySessionId: 'storysess-main', + runtimeActionVersion: 1, + runtimeMode: 'play', + runtimePersistenceDisabled: false, + runtimeStats: { + playTimeMs: 0, + lastPlayTickAt: null, + hostileNpcsDefeated: 0, + questsAccepted: 0, + itemsUsed: 0, + scenesTraveled: 0, + }, + playerProgression: { + level: 1, + currentLevelXp: 0, + totalXp: 0, + xpToNextLevel: 100, + }, + currentScene: 'Story', + storyHistory: [], + storyEngineMemory: { + discoveredFactIds: [], + inferredFactIds: [], + activeThreadIds: [], + resolvedScarIds: [], + recentCarrierIds: [], + openedSceneChapterIds: ['chapter-1'], + currentSceneActState: { + sceneId: 'custom-scene-camp', + chapterId: 'chapter-1', + currentActId: 'act-1', + currentActIndex: 0, + completedActIds: [], + visitedActIds: ['act-1'], + }, + }, + chapterState: null, + campaignState: null, + activeScenarioPackId: 'scenario-pack:tide', + activeCampaignPackId: 'campaign-pack:tide', + characterChats: {}, + lastObserveSignsSceneId: null, + lastObserveSignsReport: null, + animationState: 'idle', + currentEncounter: { + id: 'story-act-only', + kind: 'npc', + npcName: npc.name, + npcDescription: npc.description, + npcAvatar: '', + context: '陆衡拿着异常账本,在开盘前拦住玩家。', + characterId: npc.id, + initialAffinity: npc.initialAffinity, + title: npc.title, + backstory: npc.backstory, + personality: npc.personality, + motivation: npc.motivation, + combatStyle: npc.combatStyle, + relationshipHooks: npc.relationshipHooks, + tags: npc.tags, + backstoryReveal: npc.backstoryReveal, + skills: npc.skills, + initialItems: npc.initialItems, + narrativeProfile: npc.narrativeProfile, + }, + npcInteractionActive: false, + currentScenePreset, + sceneHostileNpcs: [], + playerX: 0, + playerOffsetY: 0, + playerFacing: 'right', + playerActionMode: 'idle', + scrollWorld: false, + inBattle: false, + playerHp: 180, + playerMaxHp: 180, + playerMana: 0, + playerMaxMana: 0, + playerSkillCooldowns: {}, + activeBuildBuffs: [], + activeCombatEffects: [], + playerCurrency: 0, + playerInventory: initialItems, + playerEquipment: { + weapon: weapon ?? null, + armor: { + id: 'test-armor', + category: '防具', + name: '潮雾外衣', + quantity: 1, + rarity: 'common', + tags: ['防具'], + equipmentSlotId: 'armor', + }, + relic: relic ?? null, + }, + npcStates: {}, + quests: [], + roster: [], + companions: [], + currentBattleNpcId: null, + currentNpcBattleMode: null, + currentNpcBattleOutcome: null, + sparReturnEncounter: null, + sparPlayerHpBefore: null, + sparPlayerMaxHpBefore: null, + sparStoryHistoryBefore: null, + }, + }, + }; +} + function GameFlowHarness({ openingOppositeNpcId, }: { @@ -415,6 +587,14 @@ function GameFlowHarness({ [profile], ); const selectedCharacter = playableCharacters[0] ?? null; + if (selectedCharacter) { + rpgRuntimeStoryClientMocks.beginRpgRuntimeStorySession.mockResolvedValue( + buildRuntimeStoryBootstrapSnapshot({ + profile, + character: selectedCharacter, + }), + ); + } const { gameState, setGameState, diff --git a/src/persistence/runtimeSnapshot.test.ts b/src/persistence/runtimeSnapshot.test.ts index 222bb6d4..be13573f 100644 --- a/src/persistence/runtimeSnapshot.test.ts +++ b/src/persistence/runtimeSnapshot.test.ts @@ -127,6 +127,7 @@ describe('runtimeSnapshot', () => { }, runtimeActionVersion: 3, runtimeSessionId: 'runtime-main', + storySessionId: null, } as GameState, currentStory: createStory('服务端恢复故事'), bottomTab: 'inventory', diff --git a/src/persistence/runtimeSnapshot.ts b/src/persistence/runtimeSnapshot.ts index b45c946c..ebe66b3e 100644 --- a/src/persistence/runtimeSnapshot.ts +++ b/src/persistence/runtimeSnapshot.ts @@ -287,6 +287,11 @@ export function normalizeSavedGameState(gameState: GameState) { typeof hydratableState.runtimeSessionId === 'string' ? hydratableState.runtimeSessionId : null, + storySessionId: + typeof hydratableState.storySessionId === 'string' && + hydratableState.storySessionId.trim() + ? hydratableState.storySessionId.trim() + : null, } satisfies HydratedGameState); } @@ -315,6 +320,8 @@ export function isHydratedSnapshotState( typeof gameState.runtimeActionVersion === 'number' && (gameState.runtimeSessionId === null || typeof gameState.runtimeSessionId === 'string') && + (gameState.storySessionId === null || + typeof gameState.storySessionId === 'string') && (!gameState.playerCharacter || Boolean( gameState.playerEquipment && diff --git a/src/persistence/runtimeSnapshotTypes.ts b/src/persistence/runtimeSnapshotTypes.ts index 64813202..39793ced 100644 --- a/src/persistence/runtimeSnapshotTypes.ts +++ b/src/persistence/runtimeSnapshotTypes.ts @@ -10,6 +10,7 @@ export type HydratedGameState = GameState & { playerEquipment: GameState['playerEquipment']; runtimeActionVersion: number; runtimeSessionId: string | null; + storySessionId: string | null; }; export type SnapshotState = { diff --git a/src/services/big-fish-runtime/bigFishLocalRuntime.ts b/src/services/big-fish-runtime/bigFishLocalRuntime.ts deleted file mode 100644 index 8ba019b8..00000000 --- a/src/services/big-fish-runtime/bigFishLocalRuntime.ts +++ /dev/null @@ -1,395 +0,0 @@ -import type { - BigFishGameDraftResponse, - BigFishRuntimeEntityResponse, - BigFishRuntimeSnapshotResponse, - BigFishSessionSnapshotResponse, - SubmitBigFishInputRequest, -} from '../../../packages/shared/src/contracts/bigFish'; -import type { BigFishWorkSummary } from '../../../packages/shared/src/contracts/bigFishWorkSummary'; - -const VIEW_WIDTH = 720; -const VIEW_HEIGHT = 1280; -const WORLD_HALF_WIDTH = 1400; -const WORLD_HALF_HEIGHT = 2400; -const DEFAULT_LEVEL_COUNT = 8; -const DEFAULT_WILD_COUNT = 28; -const LEADER_SPEED = 210; -const FOLLOWER_SPEED = 170; -const WILD_SPEED = 74; -const MERGE_COUNT = 3; - -function clamp(value: number, min: number, max: number) { - return Math.max(min, Math.min(max, value)); -} - -function entityRadius(level: number) { - return 18 + level * 4; -} - -function normalizeVector(x: number, y: number) { - const length = Math.hypot(x, y); - if (length <= 0.001) { - return { x: 0, y: 0 }; - } - return { x: x / length, y: y / length }; -} - -function distance( - first: BigFishRuntimeEntityResponse, - second: BigFishRuntimeEntityResponse, -) { - return Math.hypot( - first.position.x - second.position.x, - first.position.y - second.position.y, - ); -} - -function buildEntity( - entityId: string, - level: number, - x: number, - y: number, -): BigFishRuntimeEntityResponse { - return { - entityId, - level, - position: { x, y }, - radius: entityRadius(level), - offscreenSeconds: 0, - }; -} - -function resolveWinLevel( - draft?: BigFishGameDraftResponse | null, - work?: BigFishWorkSummary | null, -) { - return draft?.runtimeParams.winLevel ?? work?.levelCount ?? DEFAULT_LEVEL_COUNT; -} - -function resolveWildTargetCount(draft?: BigFishGameDraftResponse | null) { - return Math.max(DEFAULT_WILD_COUNT, draft?.runtimeParams.spawnTargetCount ?? 0); -} - -function spawnLevel(playerLevel: number, winLevel: number, index: number) { - if (playerLevel <= 1 && index % 4 < 2) { - return 1; - } - const deltas = [-2, -1, 1, 2]; - const delta = deltas[index % deltas.length] ?? 1; - return clamp(playerLevel + delta, 1, winLevel); -} - -function spawnPosition(center: { x: number; y: number }, index: number) { - const side = index % 4; - const offset = ((index * 97) % 980) - 490; - if (side === 0) { - return { x: center.x - VIEW_WIDTH * 0.72, y: center.y + offset }; - } - if (side === 1) { - return { x: center.x + VIEW_WIDTH * 0.72, y: center.y + offset }; - } - if (side === 2) { - return { x: center.x + offset, y: center.y - VIEW_HEIGHT * 0.64 }; - } - return { x: center.x + offset, y: center.y + VIEW_HEIGHT * 0.64 }; -} - -function buildWildEntity( - tick: number, - index: number, - playerLevel: number, - winLevel: number, - center: { x: number; y: number }, -) { - const level = spawnLevel(playerLevel, winLevel, index); - const position = spawnPosition(center, index); - return buildEntity(`wild-${tick}-${index}`, level, position.x, position.y); -} - -export function startLocalBigFishRuntimeRun({ - session, - work, -}: { - session?: BigFishSessionSnapshotResponse | null; - work?: BigFishWorkSummary | null; -}): BigFishRuntimeSnapshotResponse { - const winLevel = resolveWinLevel(session?.draft, work); - const wildCount = resolveWildTargetCount(session?.draft); - const leader = buildEntity('owned-1', 1, 0, 0); - const wildEntities = [ - buildEntity('wild-open-1', 1, 92, 0), - buildEntity('wild-open-2', 1, -118, 46), - ]; - while (wildEntities.length < wildCount) { - wildEntities.push( - buildWildEntity(0, wildEntities.length, 1, winLevel, leader.position), - ); - } - - return { - runId: `local-big-fish-run-${Date.now()}`, - sessionId: session?.sessionId ?? work?.sourceSessionId ?? 'local-big-fish-session', - status: 'running', - tick: 0, - playerLevel: 1, - winLevel, - leaderEntityId: leader.entityId, - ownedEntities: [leader], - wildEntities, - cameraCenter: { ...leader.position }, - lastInput: { x: 0, y: 0 }, - eventLog: ['开局生成同级可收编目标'], - updatedAt: new Date().toISOString(), - }; -} - -function moveLeader( - leader: BigFishRuntimeEntityResponse, - input: SubmitBigFishInputRequest, -) { - return { - ...leader, - position: { - x: clamp( - leader.position.x + input.x * LEADER_SPEED * 0.1, - -WORLD_HALF_WIDTH, - WORLD_HALF_WIDTH, - ), - y: clamp( - leader.position.y + input.y * LEADER_SPEED * 0.1, - -WORLD_HALF_HEIGHT, - WORLD_HALF_HEIGHT, - ), - }, - }; -} - -function moveFollower( - follower: BigFishRuntimeEntityResponse, - leader: BigFishRuntimeEntityResponse, - index: number, -) { - const slotY = Math.sin(index * 0.7) * 42; - const target = { - x: leader.position.x - 52 - index * 10, - y: leader.position.y + slotY, - }; - const delta = { - x: target.x - follower.position.x, - y: target.y - follower.position.y, - }; - const direction = normalizeVector(delta.x, delta.y); - const step = Math.min(FOLLOWER_SPEED * 0.1, Math.hypot(delta.x, delta.y)); - return { - ...follower, - position: { - x: follower.position.x + direction.x * step, - y: follower.position.y + direction.y * step, - }, - }; -} - -function moveWildEntity(entity: BigFishRuntimeEntityResponse, tick: number) { - const phase = tick * 0.23 + entity.level * 0.91 + entity.entityId.length * 0.13; - return { - ...entity, - position: { - x: clamp( - entity.position.x + Math.cos(phase) * (WILD_SPEED + entity.level * 3) * 0.1, - -WORLD_HALF_WIDTH, - WORLD_HALF_WIDTH, - ), - y: clamp( - entity.position.y + Math.sin(phase * 0.72) * (WILD_SPEED + entity.level * 3) * 0.1, - -WORLD_HALF_HEIGHT, - WORLD_HALF_HEIGHT, - ), - }, - }; -} - -function mergeOwnedEntities( - ownedEntities: BigFishRuntimeEntityResponse[], - tick: number, -) { - let nextOwned = [...ownedEntities]; - const events: string[] = []; - let changed = true; - - while (changed) { - changed = false; - for (let level = 1; level < 32; level += 1) { - const sameLevel = nextOwned - .map((entity, index) => ({ entity, index })) - .filter(({ entity }) => entity.level === level) - .slice(0, MERGE_COUNT); - if (sameLevel.length < MERGE_COUNT) { - continue; - } - - const center = sameLevel.reduce( - (acc, { entity }) => ({ - x: acc.x + entity.position.x / MERGE_COUNT, - y: acc.y + entity.position.y / MERGE_COUNT, - }), - { x: 0, y: 0 }, - ); - const removeSet = new Set(sameLevel.map(({ index }) => index)); - nextOwned = nextOwned.filter((_, index) => !removeSet.has(index)); - nextOwned.push( - buildEntity(`owned-merge-${level + 1}-${tick}`, level + 1, center.x, center.y), - ); - events.push(`3 个 ${level} 级实体合成 ${level + 1} 级`); - changed = true; - break; - } - } - - return { ownedEntities: nextOwned, events }; -} - -function isOffscreen( - entity: BigFishRuntimeEntityResponse, - cameraCenter: { x: number; y: number }, -) { - return ( - entity.position.x + entity.radius < cameraCenter.x - VIEW_WIDTH / 2 || - entity.position.x - entity.radius > cameraCenter.x + VIEW_WIDTH / 2 || - entity.position.y + entity.radius < cameraCenter.y - VIEW_HEIGHT / 2 || - entity.position.y - entity.radius > cameraCenter.y + VIEW_HEIGHT / 2 - ); -} - -function refreshLeader(ownedEntities: BigFishRuntimeEntityResponse[]) { - return [...ownedEntities].sort((left, right) => { - if (right.level !== left.level) { - return right.level - left.level; - } - return left.entityId.localeCompare(right.entityId); - }); -} - -export function advanceLocalBigFishRuntimeRun( - run: BigFishRuntimeSnapshotResponse, - input: SubmitBigFishInputRequest, -): BigFishRuntimeSnapshotResponse { - if (run.status !== 'running') { - return run; - } - - const nextTick = run.tick + 1; - const normalizedInput = normalizeVector(input.x, input.y); - const sortedOwned = refreshLeader(run.ownedEntities); - const currentLeader = sortedOwned[0]; - if (!currentLeader) { - return { ...run, status: 'failed', eventLog: ['己方实体归零,本局失败'] }; - } - - const nextLeader = moveLeader(currentLeader, normalizedInput); - let ownedEntities = [ - nextLeader, - ...sortedOwned.slice(1).map((entity, index) => - moveFollower(entity, nextLeader, index + 1), - ), - ]; - let wildEntities = run.wildEntities.map((entity) => - moveWildEntity(entity, nextTick), - ); - const events = [...run.eventLog]; - const removedWild = new Set(); - const removedOwned = new Set(); - const newlyOwned: BigFishRuntimeEntityResponse[] = []; - - for (const owned of ownedEntities) { - if (removedOwned.has(owned.entityId)) { - continue; - } - for (const wild of wildEntities) { - if (removedWild.has(wild.entityId)) { - continue; - } - if (distance(owned, wild) > owned.radius + wild.radius) { - continue; - } - if (owned.level >= wild.level) { - removedWild.add(wild.entityId); - newlyOwned.push( - buildEntity( - `owned-from-${wild.entityId}-${nextTick}`, - wild.level, - wild.position.x, - wild.position.y, - ), - ); - events.push(`收编 ${wild.level} 级实体`); - } else { - removedOwned.add(owned.entityId); - events.push(`${owned.level} 级己方实体被 ${wild.level} 级野生实体吃掉`); - } - } - } - - ownedEntities = ownedEntities - .filter((entity) => !removedOwned.has(entity.entityId)) - .concat(newlyOwned); - wildEntities = wildEntities.filter((entity) => !removedWild.has(entity.entityId)); - - const mergeResult = mergeOwnedEntities(ownedEntities, nextTick); - ownedEntities = refreshLeader(mergeResult.ownedEntities); - events.push(...mergeResult.events); - - const playerLevel = Math.max(...ownedEntities.map((entity) => entity.level), 0); - const leader = ownedEntities[0] ?? null; - const cameraCenter = leader ? { ...leader.position } : run.cameraCenter; - wildEntities = wildEntities - .map((entity) => { - const shouldCull = - entity.level === playerLevel || - entity.level >= playerLevel + 3 || - entity.level + 3 <= playerLevel; - const offscreenSeconds = - shouldCull && isOffscreen(entity, cameraCenter) - ? entity.offscreenSeconds + 0.1 - : 0; - return { ...entity, offscreenSeconds }; - }) - .filter((entity) => entity.offscreenSeconds < 3); - - while (wildEntities.length < DEFAULT_WILD_COUNT) { - wildEntities.push( - buildWildEntity( - nextTick, - wildEntities.length + nextTick, - Math.max(playerLevel, 1), - run.winLevel, - cameraCenter, - ), - ); - } - - const status = - ownedEntities.length === 0 - ? 'failed' - : playerLevel >= run.winLevel - ? 'won' - : 'running'; - if (status === 'failed') { - events.push('己方实体归零,本局失败'); - } else if (status === 'won') { - events.push('获得最高等级实体,通关'); - } - - return { - ...run, - status, - tick: nextTick, - playerLevel, - leaderEntityId: leader?.entityId ?? null, - ownedEntities, - wildEntities, - cameraCenter, - lastInput: normalizedInput, - eventLog: events.slice(-5), - updatedAt: new Date().toISOString(), - }; -} diff --git a/src/services/big-fish-runtime/bigFishRuntimeClient.ts b/src/services/big-fish-runtime/bigFishRuntimeClient.ts index ac289927..f0365e21 100644 --- a/src/services/big-fish-runtime/bigFishRuntimeClient.ts +++ b/src/services/big-fish-runtime/bigFishRuntimeClient.ts @@ -1,7 +1,9 @@ import type { - BigFishSessionResponse, + BigFishRunResponse, RecordBigFishPlayRequest, + SubmitBigFishInputRequest, } from '../../../packages/shared/src/contracts/bigFish'; +import type { BigFishWorksResponse } from '../../../packages/shared/src/contracts/bigFishWorkSummary'; import { type ApiRetryOptions, requestJson } from '../apiClient'; const BIG_FISH_RUNTIME_WRITE_RETRY: ApiRetryOptions = { @@ -18,7 +20,7 @@ export function recordBigFishPlay( sessionId: string, payload: RecordBigFishPlayRequest, ) { - return requestJson( + return requestJson( `/api/runtime/big-fish/sessions/${encodeURIComponent(sessionId)}/play`, { method: 'POST', @@ -31,3 +33,44 @@ export function recordBigFishPlay( }, ); } + +export function startBigFishRun(sessionId: string) { + return requestJson( + `/api/runtime/big-fish/sessions/${encodeURIComponent(sessionId)}/runs`, + { + method: 'POST', + }, + '启动大鱼吃小鱼玩法失败', + { + retry: BIG_FISH_RUNTIME_WRITE_RETRY, + }, + ); +} + +export function getBigFishRun(runId: string) { + return requestJson( + `/api/runtime/big-fish/runs/${encodeURIComponent(runId)}`, + { + method: 'GET', + }, + '读取大鱼吃小鱼玩法失败', + ); +} + +export function submitBigFishInput( + runId: string, + payload: SubmitBigFishInputRequest, +) { + return requestJson( + `/api/runtime/big-fish/runs/${encodeURIComponent(runId)}/input`, + { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(payload), + }, + '同步大鱼吃小鱼输入失败', + { + retry: BIG_FISH_RUNTIME_WRITE_RETRY, + }, + ); +} diff --git a/src/services/big-fish-runtime/index.ts b/src/services/big-fish-runtime/index.ts index 30187a9e..18c502ab 100644 --- a/src/services/big-fish-runtime/index.ts +++ b/src/services/big-fish-runtime/index.ts @@ -1,5 +1,6 @@ export { - advanceLocalBigFishRuntimeRun, - startLocalBigFishRuntimeRun, -} from './bigFishLocalRuntime'; -export { recordBigFishPlay } from './bigFishRuntimeClient'; + getBigFishRun, + recordBigFishPlay, + startBigFishRun, + submitBigFishInput, +} from './bigFishRuntimeClient'; diff --git a/src/services/customWorldAgentGenerationProgress.ts b/src/services/customWorldAgentGenerationProgress.ts index bb2de528..9acea494 100644 --- a/src/services/customWorldAgentGenerationProgress.ts +++ b/src/services/customWorldAgentGenerationProgress.ts @@ -151,12 +151,20 @@ const AGENT_DRAFT_FOUNDATION_STEP_DEFINITIONS = [ minProgress: 44, expectedDurationMs: 36_000, }, + { + id: 'scene-link', + label: '建立场景连接', + detail: '正在整理关键场景之间的入口、连接和章节线索。', + matchers: ['建立场景连接'], + minProgress: 66, + expectedDurationMs: 8_000, + }, { id: 'playable-detail', label: '补全可扮演角色细节', detail: '正在补全可扮演角色的叙事基础与档案细节。', matchers: ['补全可扮演角色'], - minProgress: 66, + minProgress: 76, expectedDurationMs: 32_000, }, { diff --git a/src/services/rpg-entry/rpgEntryClients.routing.test.ts b/src/services/rpg-entry/rpgEntryClients.routing.test.ts index 2e6d3a43..a68a6b53 100644 --- a/src/services/rpg-entry/rpgEntryClients.routing.test.ts +++ b/src/services/rpg-entry/rpgEntryClients.routing.test.ts @@ -27,11 +27,11 @@ describe('rpgEntry profile browse history routes', () => { requestJsonMock.mockResolvedValue({ entries: [] }); }); - it('reads browse history from the runtime profile route', async () => { + it('reads browse history from the profile route', async () => { await listRpgProfileBrowseHistory(); expect(requestJsonMock).toHaveBeenCalledWith( - '/api/runtime/profile/browse-history', + '/api/profile/browse-history', expect.objectContaining({ method: 'GET' }), '读取浏览历史失败', expect.objectContaining({ @@ -40,7 +40,7 @@ describe('rpgEntry profile browse history routes', () => { ); }); - it('writes browse history through the runtime profile route', async () => { + it('writes browse history through the profile route', async () => { await upsertRpgProfileBrowseHistory({ ownerUserId: 'user-1', profileId: 'profile-1', @@ -53,7 +53,7 @@ describe('rpgEntry profile browse history routes', () => { }); expect(requestJsonMock).toHaveBeenCalledWith( - '/api/runtime/profile/browse-history', + '/api/profile/browse-history', expect.objectContaining({ method: 'POST', headers: { 'Content-Type': 'application/json' }, @@ -68,7 +68,7 @@ describe('rpgEntry profile browse history routes', () => { ); }); - it('syncs browse history through the runtime profile route', async () => { + it('syncs browse history through the profile route', async () => { await syncRpgProfileBrowseHistory([ { ownerUserId: 'user-1', @@ -83,7 +83,7 @@ describe('rpgEntry profile browse history routes', () => { ]); expect(requestJsonMock).toHaveBeenCalledWith( - '/api/runtime/profile/browse-history', + '/api/profile/browse-history', expect.objectContaining({ method: 'POST', headers: { 'Content-Type': 'application/json' }, @@ -93,11 +93,11 @@ describe('rpgEntry profile browse history routes', () => { ); }); - it('clears browse history through the runtime profile route', async () => { + it('clears browse history through the profile route', async () => { await clearRpgProfileBrowseHistory(); expect(requestJsonMock).toHaveBeenCalledWith( - '/api/runtime/profile/browse-history', + '/api/profile/browse-history', expect.objectContaining({ method: 'DELETE' }), '清空浏览历史失败', expect.objectContaining({ @@ -160,11 +160,11 @@ describe('rpgEntry save archive routes', () => { requestJsonMock.mockResolvedValue({ entries: [] }); }); - it('reads save archives from the runtime profile route', async () => { + it('reads save archives from the profile route', async () => { await listRpgProfileSaveArchives(); expect(requestJsonMock).toHaveBeenCalledWith( - '/api/runtime/profile/save-archives', + '/api/profile/save-archives', expect.objectContaining({ method: 'GET' }), '读取存档列表失败', expect.objectContaining({ @@ -173,7 +173,7 @@ describe('rpgEntry save archive routes', () => { ); }); - it('resumes a save archive through the runtime profile route', async () => { + it('resumes a save archive through the profile route', async () => { requestJsonMock.mockResolvedValueOnce({ entry: { worldKey: 'custom:world-1', @@ -192,7 +192,7 @@ describe('rpgEntry save archive routes', () => { await resumeRpgProfileSaveArchive('custom:world-1'); expect(requestJsonMock).toHaveBeenCalledWith( - '/api/runtime/profile/save-archives/custom%3Aworld-1', + '/api/profile/save-archives/custom%3Aworld-1', expect.objectContaining({ method: 'POST' }), '恢复存档失败', expect.objectContaining({ diff --git a/src/services/rpg-entry/rpgProfileClient.test.ts b/src/services/rpg-entry/rpgProfileClient.test.ts index ab000601..79d97338 100644 --- a/src/services/rpg-entry/rpgProfileClient.test.ts +++ b/src/services/rpg-entry/rpgProfileClient.test.ts @@ -23,11 +23,11 @@ describe('rpgProfileClient browse history routes', () => { requestJsonMock.mockResolvedValue({ entries: [] }); }); - it('reads browse history from the runtime profile route', async () => { + it('reads browse history from the profile route', async () => { await listRpgProfileBrowseHistory(); expect(requestJsonMock).toHaveBeenCalledWith( - '/api/runtime/profile/browse-history', + '/api/profile/browse-history', expect.objectContaining({ method: 'GET' }), '读取浏览历史失败', expect.objectContaining({ @@ -36,7 +36,7 @@ describe('rpgProfileClient browse history routes', () => { ); }); - it('writes browse history through the runtime profile route', async () => { + it('writes browse history through the profile route', async () => { await upsertRpgProfileBrowseHistory({ ownerUserId: 'user-1', profileId: 'profile-1', @@ -49,7 +49,7 @@ describe('rpgProfileClient browse history routes', () => { }); expect(requestJsonMock).toHaveBeenCalledWith( - '/api/runtime/profile/browse-history', + '/api/profile/browse-history', expect.objectContaining({ method: 'POST', headers: { 'Content-Type': 'application/json' }, @@ -64,7 +64,7 @@ describe('rpgProfileClient browse history routes', () => { ); }); - it('syncs browse history through the runtime profile route', async () => { + it('syncs browse history through the profile route', async () => { await syncRpgProfileBrowseHistory([ { ownerUserId: 'user-1', @@ -79,7 +79,7 @@ describe('rpgProfileClient browse history routes', () => { ]); expect(requestJsonMock).toHaveBeenCalledWith( - '/api/runtime/profile/browse-history', + '/api/profile/browse-history', expect.objectContaining({ method: 'POST', headers: { 'Content-Type': 'application/json' }, @@ -89,11 +89,11 @@ describe('rpgProfileClient browse history routes', () => { ); }); - it('clears browse history through the runtime profile route', async () => { + it('clears browse history through the profile route', async () => { await clearRpgProfileBrowseHistory(); expect(requestJsonMock).toHaveBeenCalledWith( - '/api/runtime/profile/browse-history', + '/api/profile/browse-history', expect.objectContaining({ method: 'DELETE' }), '清空浏览历史失败', expect.objectContaining({ @@ -112,11 +112,11 @@ describe('rpgProfileClient save archive routes', () => { requestJsonMock.mockResolvedValue({ entries: [] }); }); - it('reads save archives from the runtime profile route', async () => { + it('reads save archives from the profile route', async () => { await listRpgProfileSaveArchives(); expect(requestJsonMock).toHaveBeenCalledWith( - '/api/runtime/profile/save-archives', + '/api/profile/save-archives', expect.objectContaining({ method: 'GET' }), '读取存档列表失败', expect.objectContaining({ @@ -125,7 +125,7 @@ describe('rpgProfileClient save archive routes', () => { ); }); - it('resumes a save archive through the runtime profile route', async () => { + it('resumes a save archive through the profile route', async () => { requestJsonMock.mockResolvedValueOnce({ entry: { worldKey: 'custom:world-1', @@ -144,7 +144,7 @@ describe('rpgProfileClient save archive routes', () => { await resumeRpgProfileSaveArchive('custom:world-1'); expect(requestJsonMock).toHaveBeenCalledWith( - '/api/runtime/profile/save-archives/custom%3Aworld-1', + '/api/profile/save-archives/custom%3Aworld-1', expect.objectContaining({ method: 'POST' }), '恢复存档失败', expect.objectContaining({ diff --git a/src/services/rpg-runtime/index.ts b/src/services/rpg-runtime/index.ts index 32cf5fe1..dae58baa 100644 --- a/src/services/rpg-runtime/index.ts +++ b/src/services/rpg-runtime/index.ts @@ -8,10 +8,15 @@ export { streamRpgNpcRecruitDialogue, } from './rpgRuntimeChatClient'; export { + beginRpgStorySession, getRpgRuntimeActionSnapshot, getRpgRuntimeClientVersion, getRpgRuntimeSessionId, + getRpgRuntimeStorySessionId, getRpgRuntimeStoryState, + continueRpgStorySession, + getRpgStoryRuntimeProjection, + getRpgStorySessionState, isRpgRuntimeServerFunctionId, isRpgRuntimeTaskFunctionId, loadRpgRuntimeInventoryView, @@ -21,7 +26,10 @@ export { type RpgRuntimeStoryClientOptions, type RuntimeStoryChoicePayload, type RuntimeStoryInventoryView, + type RuntimeStoryProjectionResult, type RuntimeStoryResponse, + type StorySessionMutationResult, + type StorySessionStateResult, shouldUseRpgRuntimeServerOptions, } from './rpgRuntimeStoryClient'; export { diff --git a/src/services/rpg-runtime/rpgRuntimeRequest.ts b/src/services/rpg-runtime/rpgRuntimeRequest.ts index d83e45b6..b55abbdd 100644 --- a/src/services/rpg-runtime/rpgRuntimeRequest.ts +++ b/src/services/rpg-runtime/rpgRuntimeRequest.ts @@ -35,9 +35,12 @@ export function requestRpgRuntimeJson( const retry = options.retry ?? (method === 'GET' ? RUNTIME_READ_RETRY : RUNTIME_WRITE_RETRY); + const normalizedPath = path.startsWith('/profile/') + ? `/api${path}` + : `${RUNTIME_API_BASE}${path}`; return requestJson( - `${RUNTIME_API_BASE}${path}`, + normalizedPath, { ...init, signal: options.signal, diff --git a/src/services/rpg-runtime/rpgRuntimeStoryClient.test.ts b/src/services/rpg-runtime/rpgRuntimeStoryClient.test.ts index b628a29e..ff773df5 100644 --- a/src/services/rpg-runtime/rpgRuntimeStoryClient.test.ts +++ b/src/services/rpg-runtime/rpgRuntimeStoryClient.test.ts @@ -15,14 +15,19 @@ vi.mock('../apiClient', async () => { import { AnimationState } from '../../types'; import { + beginRpgStorySession, beginRpgRuntimeStorySession, buildStoryMomentFromRuntimeOptions, + continueRpgStorySession, + getRpgStorySessionState, getRpgRuntimeClientVersion, getRpgRuntimeSessionId, + getRpgRuntimeStorySessionId, getRpgRuntimeStoryState, isRpgRuntimeServerFunctionId, isRpgRuntimeTaskFunctionId, loadRpgRuntimeInventoryView, + resolveRpgRuntimeStoryProjectionMoment, resolveRpgRuntimeStoryAction, resolveRpgRuntimeStoryMoment, shouldUseRpgRuntimeServerOptions, @@ -33,6 +38,134 @@ describe('rpgRuntimeStoryClient', () => { requestJsonMock.mockReset(); }); + it('creates story sessions through the new story session endpoint', async () => { + requestJsonMock.mockResolvedValue({ + storySession: { + storySessionId: 'storysess-main', + runtimeSessionId: 'runtime-main', + actorUserId: 'user-1', + worldProfileId: 'profile-1', + initialPrompt: '进入营地', + openingSummary: '营地开场', + latestNarrativeText: '篝火正在燃烧。', + latestChoiceFunctionId: null, + status: 'active', + version: 1, + createdAt: '2026-04-29T00:00:00.000Z', + updatedAt: '2026-04-29T00:00:00.000Z', + }, + storyEvent: { + eventId: 'storyevt-main', + storySessionId: 'storysess-main', + eventKind: 'session_started', + narrativeText: '篝火正在燃烧。', + choiceFunctionId: null, + createdAt: '2026-04-29T00:00:00.000Z', + }, + }); + + const result = await beginRpgStorySession({ + runtimeSessionId: 'runtime-main', + worldProfileId: 'profile-1', + initialPrompt: '进入营地', + openingSummary: '营地开场', + }); + + expect(result.storySession.storySessionId).toBe('storysess-main'); + expect(requestJsonMock).toHaveBeenCalledWith( + '/api/story/sessions', + expect.objectContaining({ + method: 'POST', + body: JSON.stringify({ + runtimeSessionId: 'runtime-main', + worldProfileId: 'profile-1', + initialPrompt: '进入营地', + openingSummary: '营地开场', + }), + }), + '创建故事会话失败', + expect.any(Object), + ); + }); + + it('continues story sessions through the new story session endpoint', async () => { + requestJsonMock.mockResolvedValue({ + storySession: { + storySessionId: 'storysess-main', + runtimeSessionId: 'runtime-main', + actorUserId: 'user-1', + worldProfileId: 'profile-1', + initialPrompt: '进入营地', + openingSummary: null, + latestNarrativeText: '你继续向前。', + latestChoiceFunctionId: 'story_continue', + status: 'active', + version: 2, + createdAt: '2026-04-29T00:00:00.000Z', + updatedAt: '2026-04-29T00:00:01.000Z', + }, + storyEvent: { + eventId: 'storyevt-next', + storySessionId: 'storysess-main', + eventKind: 'story_continued', + narrativeText: '你继续向前。', + choiceFunctionId: 'story_continue', + createdAt: '2026-04-29T00:00:01.000Z', + }, + }); + + await continueRpgStorySession({ + storySessionId: ' storysess-main ', + narrativeText: '你继续向前。', + choiceFunctionId: 'story_continue', + }); + + expect(requestJsonMock).toHaveBeenCalledWith( + '/api/story/sessions/continue', + expect.objectContaining({ + method: 'POST', + body: JSON.stringify({ + storySessionId: 'storysess-main', + narrativeText: '你继续向前。', + choiceFunctionId: 'story_continue', + }), + }), + '继续故事会话失败', + expect.any(Object), + ); + }); + + it('reads story session state through the new state endpoint', async () => { + requestJsonMock.mockResolvedValue({ + storySession: { + storySessionId: 'storysess-main', + runtimeSessionId: 'runtime-main', + actorUserId: 'user-1', + worldProfileId: 'profile-1', + initialPrompt: '进入营地', + openingSummary: null, + latestNarrativeText: '服务端故事', + latestChoiceFunctionId: null, + status: 'active', + version: 3, + createdAt: '2026-04-29T00:00:00.000Z', + updatedAt: '2026-04-29T00:00:02.000Z', + }, + storyEvents: [], + }); + + await getRpgStorySessionState({ storySessionId: ' storysess-main ' }); + + expect(requestJsonMock).toHaveBeenCalledWith( + '/api/story/sessions/storysess-main/state', + expect.objectContaining({ + method: 'GET', + }), + '读取故事会话状态失败', + expect.any(Object), + ); + }); + it('starts runtime sessions through the backend bootstrap endpoint', async () => { requestJsonMock.mockResolvedValue({ sessionId: 'runtime-server-1', @@ -185,145 +318,180 @@ describe('rpgRuntimeStoryClient', () => { ); }); - it('reads runtime story state by server session id', async () => { + it('reads runtime story state by story session id', async () => { requestJsonMock.mockResolvedValue({ - sessionId: 'runtime-main', + storySession: { + storySessionId: 'storysess-main', + runtimeSessionId: 'runtime-main', + actorUserId: 'user-1', + worldProfileId: 'profile-1', + initialPrompt: '进入营地', + openingSummary: null, + latestNarrativeText: '服务端故事', + latestChoiceFunctionId: null, + status: 'active', + version: 4, + createdAt: '2026-04-08T00:00:00.000Z', + updatedAt: '2026-04-08T00:00:00.000Z', + }, + storyEvents: [], serverVersion: 4, - viewModel: { - player: { - hp: 100, - maxHp: 100, - mana: 20, - maxMana: 20, - }, - encounter: null, - companions: [], - availableOptions: [], - status: { - inBattle: false, - npcInteractionActive: false, - currentNpcBattleMode: null, - currentNpcBattleOutcome: null, - }, + actor: { + hp: 100, + maxHp: 100, + mana: 20, + maxMana: 20, + currency: 0, + currencyText: '0 铜钱', }, - presentation: { - actionText: '', - resultText: '', - storyText: '服务端故事', - options: [], + inventory: { + backpackItems: [], + equipmentSlots: [], + forgeRecipes: [], }, - patches: [], - snapshot: { - version: 2, - savedAt: '2026-04-08T00:00:00.000Z', - bottomTab: 'adventure', - gameState: {}, - currentStory: null, + options: [], + status: { + inBattle: false, + npcInteractionActive: false, + currentNpcBattleMode: null, + currentNpcBattleOutcome: null, }, + currentNarrativeText: '服务端故事', + actionResultText: null, + toast: null, }); await getRpgRuntimeStoryState({ - sessionId: 'runtime-main', + storySessionId: 'storysess-main', clientVersion: 7, }); expect(requestJsonMock).toHaveBeenCalledWith( - '/api/runtime/story/state/runtime-main', + '/api/story/sessions/storysess-main/runtime-projection', expect.objectContaining({ method: 'GET', }), - '读取运行时故事状态失败', + '读取运行时故事投影失败', expect.any(Object), ); }); - it('loads backend inventory view from runtime story state', async () => { - requestJsonMock.mockResolvedValue({ - sessionId: 'runtime-inventory', - serverVersion: 5, - viewModel: { - player: { - hp: 100, - maxHp: 100, - mana: 20, - maxMana: 20, - }, - encounter: null, - companions: [], - inventory: { - playerCurrency: 90, - currencyText: '90 铜钱', - inBattle: false, - backpackItems: [], - equipmentSlots: [], - forgeRecipes: [ - { - id: 'synthesis-refined-ingot', - name: '压炼锭材', - kind: 'synthesis', - description: '把零散残片和基础材料压成稳定可用的金属锭材。', - resultLabel: '精炼锭材', - currencyCost: 18, - currencyText: '18 铜钱', - requirements: [ - { - id: 'material:any', - label: '任意材料', - quantity: 3, - owned: 3, - }, - ], - canCraft: true, - action: { - functionId: 'forge_craft', - actionText: '制作精炼锭材', - payload: { recipeId: 'synthesis-refined-ingot' }, - enabled: true, - }, - }, - ], - }, - availableOptions: [], - status: { - inBattle: false, - npcInteractionActive: false, - currentNpcBattleMode: null, - currentNpcBattleOutcome: null, - }, - }, - presentation: { - actionText: '', - resultText: '', - storyText: '', - options: [], - }, - patches: [], - snapshot: { - version: 2, - savedAt: '2026-04-08T00:00:00.000Z', - bottomTab: 'adventure', + it('rejects missing story session id instead of falling back to runtime id', async () => { + expect(() => + getRpgRuntimeStorySessionId({ + storySessionId: '', + }), + ).toThrow('运行时故事会话不存在,无法读取服务端投影'); + + await expect( + loadRpgRuntimeInventoryView({ gameState: { runtimeSessionId: 'runtime-inventory', + storySessionId: null, runtimeActionVersion: 5, - }, - currentStory: null, + } as never, + }), + ).rejects.toThrow('运行时故事会话不存在,无法读取服务端投影'); + + await expect( + continueRpgStorySession({ + storySessionId: '', + narrativeText: '继续', + }), + ).rejects.toThrow('故事会话不存在,无法继续故事'); + + await expect( + getRpgStorySessionState({ + storySessionId: '', + }), + ).rejects.toThrow('故事会话不存在,无法读取故事会话状态'); + + expect(requestJsonMock).not.toHaveBeenCalled(); + }); + + it('loads backend inventory view from story runtime projection', async () => { + requestJsonMock.mockResolvedValue({ + storySession: { + storySessionId: 'storysess-inventory', + runtimeSessionId: 'runtime-inventory', + actorUserId: 'user-1', + worldProfileId: 'profile-1', + initialPrompt: '进入营地', + openingSummary: null, + latestNarrativeText: '背包状态', + latestChoiceFunctionId: null, + status: 'active', + version: 5, + createdAt: '2026-04-08T00:00:00.000Z', + updatedAt: '2026-04-08T00:00:00.000Z', }, + storyEvents: [], + serverVersion: 5, + actor: { + hp: 100, + maxHp: 100, + mana: 20, + maxMana: 20, + currency: 90, + currencyText: '90 铜钱', + }, + inventory: { + backpackItems: [], + equipmentSlots: [], + forgeRecipes: [ + { + id: 'synthesis-refined-ingot', + name: '压炼锭材', + kind: 'synthesis', + description: '把零散残片和基础材料压成稳定可用的金属锭材。', + resultLabel: '精炼锭材', + currencyCost: 18, + currencyText: '18 铜钱', + requirements: [ + { + id: 'material:any', + label: '任意材料', + quantity: 3, + owned: 3, + }, + ], + canCraft: true, + action: { + functionId: 'forge_craft', + actionText: '制作精炼锭材', + payload: { recipeId: 'synthesis-refined-ingot' }, + enabled: true, + }, + }, + ], + }, + options: [], + status: { + inBattle: false, + npcInteractionActive: false, + currentNpcBattleMode: null, + currentNpcBattleOutcome: null, + }, + currentNarrativeText: '', + actionResultText: null, + toast: null, }); const view = await loadRpgRuntimeInventoryView({ gameState: { - runtimeSessionId: 'runtime-inventory', + storySessionId: 'storysess-inventory', runtimeActionVersion: 5, } as never, }); expect(view.forgeRecipes[0]?.action.functionId).toBe('forge_craft'); + expect(view.playerCurrency).toBe(90); expect(requestJsonMock).toHaveBeenCalledWith( - '/api/runtime/story/state/runtime-inventory', + '/api/story/sessions/storysess-inventory/runtime-projection', expect.objectContaining({ method: 'GET', }), - '读取运行时故事状态失败', + '读取运行时故事投影失败', expect.any(Object), ); }); @@ -415,9 +583,78 @@ describe('rpgRuntimeStoryClient', () => { expect(getRpgRuntimeSessionId({ runtimeSessionId: '' })).toBe( 'runtime-main', ); + expect(getRpgRuntimeStorySessionId({ storySessionId: ' storysess-1 ' })).toBe( + 'storysess-1', + ); expect(getRpgRuntimeClientVersion({ runtimeActionVersion: 3 })).toBe(3); }); + it('builds story moments from story runtime projection options', () => { + const story = resolveRpgRuntimeStoryProjectionMoment({ + projection: { + storySession: { + storySessionId: 'storysess-main', + runtimeSessionId: 'runtime-main', + actorUserId: 'user-1', + worldProfileId: 'profile-1', + initialPrompt: '进入营地', + openingSummary: null, + latestNarrativeText: '兜底故事', + latestChoiceFunctionId: null, + status: 'active', + version: 5, + createdAt: '2026-04-08T00:00:00.000Z', + updatedAt: '2026-04-08T00:00:00.000Z', + }, + storyEvents: [], + serverVersion: 5, + actor: { + hp: 100, + maxHp: 100, + mana: 20, + maxMana: 20, + currency: 0, + currencyText: '0 铜钱', + }, + inventory: { + backpackItems: [], + equipmentSlots: [], + forgeRecipes: [], + }, + options: [ + { + functionId: 'npc_chat', + actionText: '继续交谈', + detailText: '推进当前话题', + scope: 'npc', + payload: { npcId: 'npc-merchant' }, + enabled: false, + reason: '对方暂时不想说话', + }, + ], + status: { + inBattle: false, + npcInteractionActive: true, + currentNpcBattleMode: null, + currentNpcBattleOutcome: null, + }, + currentNarrativeText: '服务端投影故事', + actionResultText: null, + toast: null, + }, + }); + + expect(story.text).toBe('服务端投影故事'); + expect(story.options[0]).toEqual( + expect.objectContaining({ + functionId: 'npc_chat', + actionText: '继续交谈', + disabled: true, + disabledReason: '对方暂时不想说话', + }), + ); + }); + it('preserves runtime option interaction metadata from the server response', () => { const story = buildStoryMomentFromRuntimeOptions({ storyText: '服务端返回的新故事', diff --git a/src/services/rpg-runtime/rpgRuntimeStoryClient.ts b/src/services/rpg-runtime/rpgRuntimeStoryClient.ts index 0fd34c07..ed73b103 100644 --- a/src/services/rpg-runtime/rpgRuntimeStoryClient.ts +++ b/src/services/rpg-runtime/rpgRuntimeStoryClient.ts @@ -14,6 +14,14 @@ import type { RuntimeStoryBootstrapResponse, RuntimeStoryOptionView, } from '../../../packages/shared/src/contracts/rpgRuntimeStoryState'; +import type { + BeginStorySessionRequest, + ContinueStoryRequest, + StorySessionMutationResponse, + StorySessionStateResponse, + StoryRuntimeOptionProjection, + StoryRuntimeProjectionResponse, +} from '../../../packages/shared/src/contracts/story'; import { rehydrateSavedSnapshot } from '../../persistence/runtimeSnapshot'; import type { HydratedSavedGameSnapshot } from '../../persistence/runtimeSnapshotTypes'; import type { GameState, StoryMoment, StoryOption } from '../../types'; @@ -21,6 +29,7 @@ import { AnimationState } from '../../types'; import { type ApiRetryOptions, requestJson } from '../apiClient'; const RUNTIME_STORY_API_BASE = '/api/runtime/story'; +const STORY_SESSIONS_API_BASE = '/api/story/sessions'; const DEFAULT_SESSION_ID = 'runtime-main'; const RUNTIME_STORY_RETRY: ApiRetryOptions = { maxRetries: 1, @@ -49,6 +58,9 @@ export type RuntimeStoryBootstrapResult = RuntimeStoryBootstrapResponse< GameState, StoryMoment >; +export type StorySessionMutationResult = StorySessionMutationResponse; +export type StorySessionStateResult = StorySessionStateResponse; +export type RuntimeStoryProjectionResult = StoryRuntimeProjectionResponse; export type RuntimeStoryInventoryView = RuntimeStoryResponse['viewModel']['inventory']; export type { RuntimeStoryChoicePayload }; @@ -72,6 +84,23 @@ function requestRuntimeStoryJson( ); } +function requestStorySessionJson( + path: string, + init: RequestInit, + fallbackMessage: string, + options: RpgRuntimeStoryClientOptions = {}, +) { + return requestJson( + `${STORY_SESSIONS_API_BASE}${path}`, + { + ...init, + signal: options.signal, + }, + fallbackMessage, + { retry: options.retry ?? RUNTIME_STORY_RETRY }, + ); +} + function createRuntimeStoryOption( option: RuntimeStoryOptionView, _gameState?: Pick, @@ -98,12 +127,84 @@ function createRuntimeStoryOption( }; } +function normalizeProjectionOptionScope( + scope: string, +): RuntimeStoryOptionView['scope'] { + return scope === 'combat' || scope === 'npc' ? scope : 'story'; +} + +function mapRuntimeProjectionOption( + option: StoryRuntimeOptionProjection, +): RuntimeStoryOptionView { + return { + functionId: option.functionId, + actionText: option.actionText, + detailText: option.detailText ?? undefined, + scope: normalizeProjectionOptionScope(option.scope), + payload: option.payload ?? undefined, + disabled: option.enabled ? undefined : true, + reason: option.enabled ? undefined : (option.reason ?? undefined), + }; +} + +function mapRuntimeProjectionInventory( + projection: StoryRuntimeProjectionResponse, +): RuntimeStoryInventoryView { + return { + playerCurrency: projection.actor.currency, + currencyText: projection.actor.currencyText, + inBattle: projection.status.inBattle, + backpackItems: + projection.inventory + .backpackItems as RuntimeStoryInventoryView['backpackItems'], + equipmentSlots: + projection.inventory + .equipmentSlots as RuntimeStoryInventoryView['equipmentSlots'], + forgeRecipes: + projection.inventory + .forgeRecipes as RuntimeStoryInventoryView['forgeRecipes'], + }; +} + +function getRuntimeProjectionStoryText( + projection: Pick< + StoryRuntimeProjectionResponse, + 'currentNarrativeText' | 'storySession' + >, +) { + return ( + projection.currentNarrativeText?.trim() || + projection.storySession.latestNarrativeText.trim() + ); +} + export function getRuntimeSessionId( gameState: Pick, ) { return gameState.runtimeSessionId?.trim() || DEFAULT_SESSION_ID; } +export function getRuntimeStorySessionId( + gameState: Pick, +) { + return normalizeStorySessionId( + gameState.storySessionId, + '运行时故事会话不存在,无法读取服务端投影', + ); +} + +function normalizeStorySessionId( + storySessionId: string | null | undefined, + message: string, +) { + const normalizedStorySessionId = storySessionId?.trim(); + if (!normalizedStorySessionId) { + throw new Error(message); + } + + return normalizedStorySessionId; +} + export function getRuntimeClientVersion( gameState: Pick, ) { @@ -146,6 +247,17 @@ export function buildStoryMomentFromRuntimeOptions(params: { } satisfies StoryMoment; } +export function buildStoryMomentFromRuntimeProjection(params: { + projection: StoryRuntimeProjectionResponse; + gameState?: Pick; +}): StoryMoment { + return buildStoryMomentFromRuntimeOptions({ + storyText: getRuntimeProjectionStoryText(params.projection), + options: params.projection.options.map(mapRuntimeProjectionOption), + gameState: params.gameState, + }); +} + function shouldPreferSnapshotStory(story: StoryMoment | null) { return Boolean( story && @@ -185,48 +297,114 @@ export function resolveRuntimeStoryMoment(params: { }); } -export async function getRuntimeStoryState( +export async function beginStorySession( + params: BeginStorySessionRequest, + options: RpgRuntimeStoryClientOptions = {}, +) { + return requestStorySessionJson( + '', + { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(params), + }, + '创建故事会话失败', + options, + ); +} + +export async function continueStorySession( + params: ContinueStoryRequest, + options: RpgRuntimeStoryClientOptions = {}, +) { + const storySessionId = normalizeStorySessionId( + params.storySessionId, + '故事会话不存在,无法继续故事', + ); + + return requestStorySessionJson( + '/continue', + { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + ...params, + storySessionId, + }), + }, + '继续故事会话失败', + options, + ); +} + +export async function getStorySessionState( + params: { storySessionId: string }, + options: RpgRuntimeStoryClientOptions = {}, +) { + const storySessionId = normalizeStorySessionId( + params.storySessionId, + '故事会话不存在,无法读取故事会话状态', + ); + + return requestStorySessionJson( + `/${encodeURIComponent(storySessionId)}/state`, + { method: 'GET' }, + '读取故事会话状态失败', + options, + ); +} + +export async function getStoryRuntimeProjection( params: { - sessionId: string; + storySessionId: string; clientVersion?: number; }, options: RpgRuntimeStoryClientOptions = {}, ) { - const normalizedSessionId = params.sessionId || DEFAULT_SESSION_ID; - // 中文注释:runtime story 状态读取只按服务端持久化 sessionId 拉取, - // 不再允许前端上传本地 GameState 快照参与解析。 - const response = await requestRuntimeStoryJson( - `/state/${encodeURIComponent(normalizedSessionId)}`, - { method: 'GET' }, - '读取运行时故事状态失败', - options, + const storySessionId = normalizeStorySessionId( + params.storySessionId, + '运行时故事会话不存在,无法读取服务端投影', ); - return { - ...response, - snapshot: rehydrateSavedSnapshot( - response.snapshot as HydratedSavedGameSnapshot, - ), - } satisfies RuntimeStoryResponse; + // 中文注释:当前 BFF route 以 storySessionId 为唯一读取键; + // clientVersion 保留在调用签名里,等待后端增量投影契约稳定后再接查询参数。 + return requestStorySessionJson( + `/${encodeURIComponent(storySessionId)}/runtime-projection`, + { method: 'GET' }, + '读取运行时故事投影失败', + options, + ); +} + +export async function getRuntimeStoryState( + params: { + storySessionId: string; + clientVersion?: number; + }, + options: RpgRuntimeStoryClientOptions = {}, +) { + // 中文注释:读取侧正式切到 story session scoped 投影; + // 这里不允许用 runtimeSessionId 兜底,避免两个会话主键被悄悄混用。 + return getStoryRuntimeProjection(params, options); } export async function loadRuntimeInventoryView( params: { - gameState: Pick; + gameState: Pick; }, options: RpgRuntimeStoryClientOptions = {}, ) { - // 中文注释:背包 / 装备 / 锻造 view 只读取后端已持久化的 runtime session; + // 中文注释:背包 / 装备 / 锻造 view 只读取 story runtime 投影; // 前端不再用本地背包、货币或装备状态重算配方可用性。 const response = await getRuntimeStoryState( { - sessionId: getRuntimeSessionId(params.gameState), + storySessionId: getRuntimeStorySessionId(params.gameState), clientVersion: getRuntimeClientVersion(params.gameState), }, options, ); - return response.viewModel.inventory; + return mapRuntimeProjectionInventory(response); } export async function beginRuntimeStorySession( @@ -303,25 +481,38 @@ export function getRuntimeActionSnapshot(response: RuntimeStoryResponse) { } export const beginRpgRuntimeStorySession = beginRuntimeStorySession; +export const beginRpgStorySession = beginStorySession; +export const continueRpgStorySession = continueStorySession; +export const getRpgStoryRuntimeProjection = getStoryRuntimeProjection; +export const getRpgStorySessionState = getStorySessionState; export const getRpgRuntimeActionSnapshot = getRuntimeActionSnapshot; export const getRpgRuntimeClientVersion = getRuntimeClientVersion; export const getRpgRuntimeSessionId = getRuntimeSessionId; +export const getRpgRuntimeStorySessionId = getRuntimeStorySessionId; export const getRpgRuntimeStoryState = getRuntimeStoryState; export const isRpgRuntimeServerFunctionId = isServerRuntimeFunctionId; export const isRpgRuntimeTaskFunctionId = isTask5RuntimeFunctionId; export const loadRpgRuntimeInventoryView = loadRuntimeInventoryView; export const resolveRpgRuntimeStoryAction = resolveRuntimeStoryAction; +export const resolveRpgRuntimeStoryProjectionMoment = + buildStoryMomentFromRuntimeProjection; export const resolveRpgRuntimeStoryMoment = resolveRuntimeStoryMoment; export const shouldUseRpgRuntimeServerOptions = shouldUseServerRuntimeOptions; export const rpgRuntimeStoryClient = { beginSession: beginRpgRuntimeStorySession, + beginStorySession: beginRpgStorySession, + continueStorySession: continueRpgStorySession, getActionSnapshot: getRpgRuntimeActionSnapshot, getClientVersion: getRpgRuntimeClientVersion, getInventoryView: loadRpgRuntimeInventoryView, getSessionId: getRpgRuntimeSessionId, + getStoryRuntimeProjection: getRpgStoryRuntimeProjection, + getStorySessionId: getRpgRuntimeStorySessionId, + getStorySessionState: getRpgStorySessionState, getState: getRpgRuntimeStoryState, resolveAction: resolveRpgRuntimeStoryAction, + resolveProjectionMoment: resolveRpgRuntimeStoryProjectionMoment, resolveMoment: resolveRpgRuntimeStoryMoment, shouldUseServerOptions: shouldUseRpgRuntimeServerOptions, }; diff --git a/src/types/game.ts b/src/types/game.ts index fdbd1d26..f71d5af2 100644 --- a/src/types/game.ts +++ b/src/types/game.ts @@ -85,6 +85,7 @@ export interface GameState { customWorldProfile: CustomWorldProfile | null; playerCharacter: Character | null; runtimeSessionId?: string | null; + storySessionId?: string | null; runtimeActionVersion?: number; runtimeMode?: GameRuntimeMode; runtimePersistenceDisabled?: boolean;