diff --git a/docs/technical/BIG_FISH_DIRECTION_TOUCH_CONTROL_2026-04-24.md b/docs/technical/BIG_FISH_DIRECTION_TOUCH_CONTROL_2026-04-24.md index 3b4d3699..e92f43a9 100644 --- a/docs/technical/BIG_FISH_DIRECTION_TOUCH_CONTROL_2026-04-24.md +++ b/docs/technical/BIG_FISH_DIRECTION_TOUCH_CONTROL_2026-04-24.md @@ -2,26 +2,34 @@ ## 背景 -当前大鱼运行时使用左下固定虚拟摇杆,玩家必须点到摇杆区域才能移动。移动端实际体验应改为屏幕任意位置触控:第一次触点只建立方向原点,不直接产生移动;后续触点相对原点形成方向向量,角色按恒定速度朝该方向行动。 +当前大鱼运行时使用左下固定虚拟摇杆,玩家必须点到摇杆区域才能移动。移动端实际体验应改为屏幕任意位置触控:按住屏幕时不再用“触点相对按下原点”的距离判断方向,而是按固定采样间隔比较“上一次触点位置”和“当前触点位置”的位移方向,角色按恒定速度朝采样方向行动。 ## 交互规则 -1. 玩家在玩法舞台内按下时,记录第一个触点坐标为本次操作原点。 +1. 玩家在玩法舞台内按下时,记录当前触点坐标为采样起点。 2. 按下瞬间提交 `{ x: 0, y: 0 }`,保证一开始玩家不动。 -3. 手指/鼠标移动后,用“当前触点 - 原点”的向量计算方向。 -4. 输入只表达方向,不表达速度;超过死区后归一化为单位方向向量。 -5. 松开或取消触控后,清空操作原点并提交 `{ x: 0, y: 0 }`。 -6. 前端继续定时提交当前方向,即使没有玩家输入也提交零向量,让后端或本地直达局持续推进世界 tick。 +3. 按住期间每 `0.1` 秒采样一次当前触点坐标,并用“当前触点 - 上一次采样触点”的位移计算方向。 +4. 输入只表达方向,不表达速度;超过采样死区后归一化为单位方向向量,未超过死区则沿用上一段有效方向。 +5. 每次采样完成后,把当前触点写为下一次采样的“上一次位置”。 +6. 松开或取消触控后,清空采样状态并提交 `{ x: 0, y: 0 }`。 +7. 前端继续定时提交当前方向,即使没有玩家输入也提交零向量,让后端或本地直达局持续推进世界 tick。 ## 本地直达局边界 -- `/big-fish` 的本地占位局必须在玩家未操作时继续移动野生对象。 +- 大鱼吃小鱼的最终游玩部分统一放在前端本地 runtime,不再调用后端 start/input/get run 接口。 +- 后端只保留 Agent 创作、草稿编译、资产生成、发布和作品列表;不负责移动、碰撞、刷鱼、合成、屏外清理或胜负模拟。 +- `/big-fish` 的本地直达局和平台内测试玩法必须共用前端本地 runtime,并在玩家未操作时继续移动野生对象。 - 玩家速度保持恒定,只由方向决定移动方向。 - 野生对象使用确定性游动规则,避免直达入口看起来像静态截图。 ## 验收口径 1. 在舞台任意位置按下时玩家不立即移动。 -2. 按住并拖动后,玩家朝拖动方向恒速移动。 +2. 按住并拖动后,玩家方向来自最近 `0.1` 秒位移,而不是来自按下原点。 3. 松开后玩家停止。 4. 不操作时野生对象仍会持续游动。 + +## 资产生成补充口径 + +- 大鱼实体主图、`idle_float` 和 `move_swim` 动作图都按 RPG 角色资产口径处理:单体完整入镜、中心构图、轮廓清晰、透明背景、无 UI/文字/水印。 +- 场地背景只生成环境,不生成规则说明、UI 或巨大主体遮挡;画面元素要少,中央活动区域要大,边缘只保留少量出生区提示。 diff --git a/packages/shared/src/contracts/bigFish.ts b/packages/shared/src/contracts/bigFish.ts index a3248f14..4bedd2d1 100644 --- a/packages/shared/src/contracts/bigFish.ts +++ b/packages/shared/src/contracts/bigFish.ts @@ -185,7 +185,3 @@ export type BigFishRuntimeSnapshotResponse = { eventLog: string[]; updatedAt: string; }; - -export type BigFishRunResponse = { - run: BigFishRuntimeSnapshotResponse; -}; diff --git a/server-rs/crates/api-server/src/admin.rs b/server-rs/crates/api-server/src/admin.rs index 69489b8c..327687e0 100644 --- a/server-rs/crates/api-server/src/admin.rs +++ b/server-rs/crates/api-server/src/admin.rs @@ -65,7 +65,6 @@ const DATABASE_OVERVIEW_TABLES: &[&str] = &[ "big_fish_creation_session", "big_fish_agent_message", "big_fish_asset_slot", - "big_fish_runtime_run", "puzzle_work_profile", "puzzle_agent_session", "puzzle_agent_message", diff --git a/server-rs/crates/api-server/src/app.rs b/server-rs/crates/api-server/src/app.rs index 9446bcd4..3c00073b 100644 --- a/server-rs/crates/api-server/src/app.rs +++ b/server-rs/crates/api-server/src/app.rs @@ -33,9 +33,9 @@ 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, get_big_fish_run, - get_big_fish_session, get_big_fish_works, list_big_fish_gallery, start_big_fish_run, - stream_big_fish_message, submit_big_fish_input, submit_big_fish_message, + create_big_fish_session, delete_big_fish_work, execute_big_fish_action, + get_big_fish_session, get_big_fish_works, list_big_fish_gallery, stream_big_fish_message, + submit_big_fish_message, }, character_animation_assets::{ generate_character_animation, get_character_animation_job, get_character_workflow_cache, @@ -574,27 +574,6 @@ 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( diff --git a/server-rs/crates/api-server/src/big_fish.rs b/server-rs/crates/api-server/src/big_fish.rs index 3e4c53e6..8ab50e46 100644 --- a/server-rs/crates/api-server/src/big_fish.rs +++ b/server-rs/crates/api-server/src/big_fish.rs @@ -23,10 +23,8 @@ use shared_contracts::big_fish::{ BigFishActionResponse, BigFishAgentMessageResponse, BigFishAnchorItemResponse, BigFishAnchorPackResponse, BigFishAssetCoverageResponse, BigFishAssetSlotResponse, BigFishBackgroundBlueprintResponse, BigFishGameDraftResponse, BigFishLevelBlueprintResponse, - BigFishRunResponse, BigFishRuntimeEntityResponse, BigFishRuntimeParamsResponse, - BigFishRuntimeSnapshotResponse, BigFishSessionResponse, BigFishSessionSnapshotResponse, - BigFishVector2Response, CreateBigFishSessionRequest, ExecuteBigFishActionRequest, - SendBigFishMessageRequest, SubmitBigFishInputRequest, + BigFishRuntimeParamsResponse, BigFishSessionResponse, BigFishSessionSnapshotResponse, + CreateBigFishSessionRequest, ExecuteBigFishActionRequest, SendBigFishMessageRequest, }; use shared_contracts::big_fish_works::{BigFishWorkSummaryResponse, BigFishWorksResponse}; use shared_kernel::{build_prefixed_uuid_id, format_timestamp_micros}; @@ -34,10 +32,8 @@ use spacetime_client::{ BigFishAgentMessageRecord, BigFishAnchorItemRecord, BigFishAnchorPackRecord, BigFishAssetCoverageRecord, BigFishAssetGenerateRecordInput, BigFishAssetSlotRecord, BigFishBackgroundBlueprintRecord, BigFishGameDraftRecord, BigFishLevelBlueprintRecord, - BigFishMessageSubmitRecordInput, BigFishRunInputSubmitRecordInput, BigFishRunStartRecordInput, - BigFishRuntimeEntityRecord, BigFishRuntimeParamsRecord, BigFishRuntimeRecord, - BigFishSessionCreateRecordInput, BigFishSessionRecord, BigFishVector2Record, - BigFishWorkSummaryRecord, SpacetimeClientError, + BigFishMessageSubmitRecordInput, BigFishRuntimeParamsRecord, BigFishSessionCreateRecordInput, + BigFishSessionRecord, BigFishWorkSummaryRecord, SpacetimeClientError, }; use tokio::time::sleep; @@ -577,99 +573,6 @@ pub async fn execute_big_fish_action( )) } -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_runtime_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_runtime_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")?; - - let run = state - .spacetime_client() - .submit_big_fish_input(BigFishRunInputSubmitRecordInput { - run_id, - owner_user_id: authenticated.claims().user_id().to_string(), - input_x: payload.x, - input_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_runtime_response(run), - }, - )) -} - fn map_big_fish_session_response(session: BigFishSessionRecord) -> BigFishSessionSnapshotResponse { BigFishSessionSnapshotResponse { session_id: session.session_id, @@ -910,32 +813,6 @@ fn map_big_fish_agent_message_response( } } -fn map_big_fish_runtime_response(run: BigFishRuntimeRecord) -> 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_entity_response) - .collect(), - wild_entities: run - .wild_entities - .into_iter() - .map(map_big_fish_entity_response) - .collect(), - camera_center: map_big_fish_vector_response(run.camera_center), - last_input: map_big_fish_vector_response(run.last_input), - event_log: run.event_log, - updated_at: run.updated_at, - } -} - fn map_big_fish_work_summary_response( item: BigFishWorkSummaryRecord, ) -> BigFishWorkSummaryResponse { @@ -957,25 +834,6 @@ fn map_big_fish_work_summary_response( } } -fn map_big_fish_entity_response( - entity: BigFishRuntimeEntityRecord, -) -> BigFishRuntimeEntityResponse { - BigFishRuntimeEntityResponse { - entity_id: entity.entity_id, - level: entity.level, - position: map_big_fish_vector_response(entity.position), - radius: entity.radius, - offscreen_seconds: entity.offscreen_seconds, - } -} - -fn map_big_fish_vector_response(vector: BigFishVector2Record) -> BigFishVector2Response { - BigFishVector2Response { - x: vector.x, - y: vector.y, - } -} - fn build_big_fish_welcome_text(seed_text: &str) -> String { if seed_text.trim().is_empty() { return "我会先帮你确定大鱼吃小鱼的核心锚点。可以从主题生态、成长阶梯或风险节奏开始。" @@ -1013,7 +871,8 @@ struct BigFishFormalAssetContext { const BIG_FISH_TEXT_TO_IMAGE_MODEL: &str = "wan2.2-t2i-flash"; const BIG_FISH_ENTITY_KIND: &str = "big_fish_session"; -const BIG_FISH_DEFAULT_NEGATIVE_PROMPT: &str = "文字,水印,logo,UI界面,对话框,边框,多余肢体,畸形鱼体,低清晰度,模糊,压缩噪点,现代摄影棚,写实照片背景"; +const BIG_FISH_DEFAULT_NEGATIVE_PROMPT: &str = "文字,水印,logo,UI界面,对话框,边框,多余肢体,畸形鱼体,低清晰度,模糊,压缩噪点,现代摄影棚,写实照片背景,复杂背景"; +const BIG_FISH_TRANSPARENT_ASSET_NEGATIVE_PROMPT: &str = "文字,水印,logo,UI界面,对话框,边框,多余肢体,畸形鱼体,低清晰度,模糊,压缩噪点,现代摄影棚,写实照片背景,场景背景,水草背景,气泡背景,多只主体,阴影地面"; async fn generate_big_fish_formal_asset( state: &AppState, @@ -1087,7 +946,7 @@ fn build_big_fish_formal_asset_context( Ok(BigFishFormalAssetContext { entity_id: session.session_id.clone(), prompt: build_big_fish_level_main_image_prompt(draft, level), - negative_prompt: BIG_FISH_DEFAULT_NEGATIVE_PROMPT.to_string(), + negative_prompt: BIG_FISH_TRANSPARENT_ASSET_NEGATIVE_PROMPT.to_string(), size: "1024*1024".to_string(), asset_object_kind: "big_fish_level_main_image".to_string(), binding_slot: format!("level_main_image:{level_part}"), @@ -1114,7 +973,7 @@ fn build_big_fish_formal_asset_context( Ok(BigFishFormalAssetContext { entity_id: session.session_id.clone(), prompt: build_big_fish_level_motion_prompt(draft, level, motion_key), - negative_prompt: BIG_FISH_DEFAULT_NEGATIVE_PROMPT.to_string(), + negative_prompt: BIG_FISH_TRANSPARENT_ASSET_NEGATIVE_PROMPT.to_string(), size: "1024*1024".to_string(), asset_object_kind: "big_fish_level_motion".to_string(), binding_slot: format!("level_motion:{level_part}:{motion_key}"), @@ -1190,8 +1049,8 @@ fn build_big_fish_level_main_image_prompt( ), format!("轮廓方向:{}。", level.silhouette_direction), format!("视觉提示词种子:{}。", level.visual_prompt_seed), - "画面要求:单体游戏生物完整入镜,轮廓清晰,适合作为大鱼吃小鱼等级角色主图,2D 高完成度游戏插画,深海发光质感,中央构图。".to_string(), - "不要出现 UI、文字、logo、水印、对话框或边框;背景保持干净的深海渐变或透明感,不要出现多只主体。".to_string(), + "画面要求:按 RPG 角色资产口径生成,单体鱼形游戏生物完整入镜,轮廓清晰,中心构图,2D 高完成度游戏插画,深海发光质感。".to_string(), + "背景要求:透明背景 PNG 风格,不出现任何场景、水草、气泡、阴影地面、UI、文字、logo、水印、对话框或边框;不要出现多只主体。".to_string(), ] .join("") } @@ -1217,8 +1076,8 @@ fn build_big_fish_level_motion_prompt( ), format!("动作提示词种子:{}。", level.motion_prompt_seed), format!("动作要求:{motion_text}"), - "画面要求:单体生物完整入镜,轮廓清晰,动作方向明确,2D 高完成度游戏插画,适合作为 Big Fish 动作槽位的静态 keyframe。".to_string(), - "不要出现 UI、文字、logo、水印、对话框或边框;不要生成序列帧拼图,不要出现多只主体。".to_string(), + "画面要求:按 RPG 角色动画资产口径生成,单体鱼形生物完整入镜,轮廓清晰,动作方向明确,2D 高完成度游戏插画,适合作为 Big Fish 动作槽位的静态 keyframe。".to_string(), + "背景要求:透明背景 PNG 风格,不出现任何场景、水草、气泡、阴影地面、UI、文字、logo、水印、对话框或边框;不要生成序列帧拼图,不要出现多只主体。".to_string(), ] .join("") } @@ -1238,8 +1097,8 @@ fn build_big_fish_stage_background_prompt(draft: &BigFishGameDraftRecord) -> Str format!("安全操作区:{}。", background.safe_play_area_hint), format!("出生边缘:{}。", background.spawn_edge_hint), format!("背景提示词种子:{}。", background.background_prompt_seed), - "画面要求:竖屏 9:16,中央 70% 保持清爽可读,边缘有深海生态层次和微弱生物光,适合作为大鱼吃小鱼运行态背景。".to_string(), - "不要出现 UI、文字、logo、水印、对话框、边框或巨大主体遮挡;不要把中央操作区画得过暗或过复杂。".to_string(), + "画面要求:竖屏 9:16,大场地,全屏运行态背景,中央 80% 保持开阔清爽,边缘只保留少量出生区环境提示。".to_string(), + "元素要求:整体元素少,不出现大型主体、密集装饰、鱼群主角、UI、文字、logo、水印、对话框或边框;不要把中央操作区画得过暗或过复杂。".to_string(), ] .join("") } @@ -1769,8 +1628,7 @@ fn big_fish_sse_error_event_message(message: String) -> Event { fn map_big_fish_client_error(error: SpacetimeClientError) -> AppError { let status = match &error { SpacetimeClientError::Procedure(message) - if message.contains("big_fish_creation_session 不存在") - || message.contains("big_fish_runtime_run 不存在") => + if message.contains("big_fish_creation_session 不存在") => { StatusCode::NOT_FOUND } diff --git a/server-rs/crates/module-big-fish/src/lib.rs b/server-rs/crates/module-big-fish/src/lib.rs index 1dbb594f..adeec338 100644 --- a/server-rs/crates/module-big-fish/src/lib.rs +++ b/server-rs/crates/module-big-fish/src/lib.rs @@ -9,17 +9,12 @@ pub const BIG_FISH_SESSION_ID_PREFIX: &str = "big-fish-session-"; pub const BIG_FISH_MESSAGE_ID_PREFIX: &str = "big-fish-message-"; pub const BIG_FISH_OPERATION_ID_PREFIX: &str = "big-fish-operation-"; pub const BIG_FISH_ASSET_SLOT_ID_PREFIX: &str = "big-fish-asset-"; -pub const BIG_FISH_RUN_ID_PREFIX: &str = "big-fish-run-"; pub const BIG_FISH_DEFAULT_LEVEL_COUNT: u32 = 8; pub const BIG_FISH_MIN_LEVEL_COUNT: u32 = 6; pub const BIG_FISH_MAX_LEVEL_COUNT: u32 = 12; pub const BIG_FISH_MERGE_COUNT_PER_UPGRADE: u32 = 3; pub const BIG_FISH_OFFSCREEN_CULL_SECONDS: f32 = 3.0; pub const BIG_FISH_TARGET_WILD_COUNT: usize = 12; -pub const BIG_FISH_VIEW_WIDTH: f32 = 720.0; -pub const BIG_FISH_VIEW_HEIGHT: f32 = 1280.0; -pub const BIG_FISH_WORLD_HALF_WIDTH: f32 = 900.0; -pub const BIG_FISH_WORLD_HALF_HEIGHT: f32 = 1600.0; #[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] #[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] @@ -72,14 +67,6 @@ pub enum BigFishAssetStatus { Ready, } -#[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, Eq, Serialize, Deserialize)] pub struct BigFishAnchorItem { @@ -209,41 +196,6 @@ pub struct BigFishSessionSnapshot { pub updated_at_micros: i64, } -#[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 BigFishRuntimeEntity { - 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, -} - #[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct BigFishSessionProcedureResult { @@ -293,14 +245,6 @@ pub struct BigFishWorksProcedureResult { pub error_message: Option, } -#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] -pub struct BigFishRunProcedureResult { - pub ok: bool, - pub run: Option, - pub error_message: Option, -} - #[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] pub struct BigFishSessionCreateInput { @@ -372,43 +316,15 @@ pub struct BigFishPublishInput { pub published_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, Serialize, Deserialize)] -pub struct BigFishRunInputSubmitInput { - pub run_id: String, - pub owner_user_id: String, - pub input_x: f32, - pub input_y: f32, - pub submitted_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, -} - #[derive(Clone, Debug, PartialEq, Eq)] pub enum BigFishFieldError { MissingSessionId, MissingOwnerUserId, MissingMessageId, MissingMessageText, - MissingRunId, MissingDraft, InvalidLevel, InvalidAssetKind, - InvalidRunState, } impl BigFishCreationStage { @@ -474,16 +390,6 @@ impl BigFishAssetStatus { } } -impl BigFishRunStatus { - pub fn as_str(self) -> &'static str { - match self { - Self::Running => "running", - Self::Won => "won", - Self::Failed => "failed", - } - } -} - pub fn empty_anchor_pack() -> BigFishAnchorPack { BigFishAnchorPack { gameplay_promise: BigFishAnchorItem { @@ -565,12 +471,14 @@ pub fn compile_default_draft(anchor_pack: &BigFishAnchorPack) -> BigFishGameDraf background: BigFishBackgroundBlueprint { theme: theme.clone(), color_mood: "深蓝、青绿、带少量暖色生物光".to_string(), - foreground_hints: "轻微漂浮颗粒和边缘水草,不遮挡中央操作区".to_string(), - midground_composition: "中央留出清晰活动区域,边缘有出生缓冲层".to_string(), - background_depth: "纵深水域与远处体型剪影".to_string(), - safe_play_area_hint: "9:16 竖屏中央 70% 为主要活动区".to_string(), - spawn_edge_hint: "四周边缘作为野生实体出生区".to_string(), - background_prompt_seed: format!("{theme},竖屏 9:16,全屏游戏背景,无文字,无 UI 框"), + foreground_hints: "只保留少量漂浮颗粒和边缘水草,不遮挡中央操作区".to_string(), + midground_composition: "中央留出大面积清晰活动区域,边缘只做出生缓冲层".to_string(), + background_depth: "简洁纵深水域与极少量远处剪影".to_string(), + safe_play_area_hint: "9:16 竖屏中央 80% 为主要活动区".to_string(), + spawn_edge_hint: "四周边缘以少量暗礁或水草提示野生实体出生区".to_string(), + background_prompt_seed: format!( + "{theme},竖屏 9:16,全屏大场地游戏背景,元素少,中央开阔,无文字,无 UI 框" + ), }, runtime_params: BigFishRuntimeParams { level_count, @@ -673,77 +581,6 @@ pub fn build_generated_asset_slot( }) } -pub fn build_initial_runtime_snapshot( - run_id: String, - session_id: String, - draft: &BigFishGameDraft, - now_micros: i64, -) -> BigFishRuntimeSnapshot { - let mut snapshot = BigFishRuntimeSnapshot { - run_id, - session_id, - status: BigFishRunStatus::Running, - tick: 0, - player_level: 1, - win_level: draft.runtime_params.win_level, - leader_entity_id: Some("owned-1".to_string()), - owned_entities: vec![BigFishRuntimeEntity { - entity_id: "owned-1".to_string(), - level: 1, - position: BigFishVector2 { x: 0.0, y: 0.0 }, - radius: entity_radius(1), - offscreen_seconds: 0.0, - }], - wild_entities: vec![ - BigFishRuntimeEntity { - entity_id: "wild-open-1".to_string(), - level: 1, - position: BigFishVector2 { x: 72.0, y: 0.0 }, - radius: entity_radius(1), - offscreen_seconds: 0.0, - }, - BigFishRuntimeEntity { - entity_id: "wild-open-2".to_string(), - level: 1, - position: BigFishVector2 { x: -88.0, y: 30.0 }, - radius: entity_radius(1), - offscreen_seconds: 0.0, - }, - ], - camera_center: BigFishVector2 { x: 0.0, y: 0.0 }, - last_input: BigFishVector2 { x: 0.0, y: 0.0 }, - event_log: vec!["开局生成 2 个同级可收编目标".to_string()], - updated_at_micros: now_micros, - }; - maintain_wild_pool(&mut snapshot, &draft.runtime_params); - snapshot -} - -pub fn advance_runtime_snapshot( - mut snapshot: BigFishRuntimeSnapshot, - params: &BigFishRuntimeParams, - input_x: f32, - input_y: f32, - now_micros: i64, -) -> BigFishRuntimeSnapshot { - if snapshot.status != BigFishRunStatus::Running { - return snapshot; - } - - let step_seconds = resolve_step_seconds(&snapshot, now_micros); - snapshot.tick = snapshot.tick.saturating_add(1); - snapshot.last_input = normalize_input(input_x, input_y); - move_owned_entities(&mut snapshot, params, step_seconds); - resolve_collisions(&mut snapshot, params); - apply_chain_merges(&mut snapshot, params); - refresh_player_leader(&mut snapshot); - apply_win_or_fail(&mut snapshot, params); - update_wild_culling(&mut snapshot, params, step_seconds); - maintain_wild_pool(&mut snapshot, params); - snapshot.updated_at_micros = now_micros; - snapshot -} - pub fn validate_session_get_input(input: &BigFishSessionGetInput) -> Result<(), BigFishFieldError> { validate_session_owner(&input.session_id, &input.owner_user_id) } @@ -817,36 +654,6 @@ pub fn validate_publish_input(input: &BigFishPublishInput) -> Result<(), BigFish validate_session_owner(&input.session_id, &input.owner_user_id) } -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_run_input_submit_input( - input: &BigFishRunInputSubmitInput, -) -> 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 serialize_anchor_pack(anchor_pack: &BigFishAnchorPack) -> Result { serde_json::to_string(anchor_pack) } @@ -873,18 +680,6 @@ pub fn deserialize_asset_coverage(value: &str) -> Result Result { - serde_json::to_string(snapshot) -} - -pub fn deserialize_runtime_snapshot( - value: &str, -) -> Result { - serde_json::from_str(value) -} - fn fallback_anchor_value(anchor: &BigFishAnchorItem, fallback: &str) -> String { normalize_required_string(&anchor.value).unwrap_or_else(|| fallback.to_string()) } @@ -911,8 +706,12 @@ fn build_level_blueprint(level: u32, level_count: u32, theme: &str) -> BigFishLe 1.0 + level as f32 * 0.22 ), size_ratio: 1.0 + (level.saturating_sub(1) as f32 * 0.22), - visual_prompt_seed: format!("{theme} 第 {level} 级实体主图,透明背景,清晰轮廓"), - motion_prompt_seed: format!("{theme} 第 {level} 级实体 idle_float 与 move_swim 动作"), + visual_prompt_seed: format!( + "{theme} 第 {level} 级鱼形实体主图,RPG 角色资产口径,透明背景,单体完整入镜,清晰轮廓" + ), + motion_prompt_seed: format!( + "{theme} 第 {level} 级鱼形实体 idle_float 与 move_swim 动作,RPG 角色动画资产口径,透明背景" + ), merge_source_level: if level == 1 { None } else { Some(level - 1) }, prey_window, threat_window, @@ -945,7 +744,7 @@ fn build_asset_prompt_snapshot( .ok_or(BigFishFieldError::InvalidLevel)?; let motion_key = motion_key.ok_or(BigFishFieldError::InvalidAssetKind)?; Ok(format!( - "{},动作位:{}", + "{},动作位:{},透明背景,单体完整入镜", blueprint.motion_prompt_seed, motion_key )) } @@ -1004,311 +803,6 @@ fn validate_level(level: Option, draft: &BigFishGameDraft) -> Result<(), Bi } } -fn normalize_input(x: f32, y: f32) -> BigFishVector2 { - let length = (x * x + y * y).sqrt(); - if length <= 1.0 { - return BigFishVector2 { x, y }; - } - BigFishVector2 { - x: x / length, - y: y / length, - } -} - -/// 运行态仍由 `POST input` 触发推进,因此“屏外 3 秒”这类规则必须按真实秒数累计, -/// 否则会随着输入频率变化而漂移。 -fn resolve_step_seconds(snapshot: &BigFishRuntimeSnapshot, now_micros: i64) -> f32 { - ((now_micros - snapshot.updated_at_micros).max(0) as f32) / 1_000_000.0 -} - -fn move_owned_entities( - snapshot: &mut BigFishRuntimeSnapshot, - params: &BigFishRuntimeParams, - step_seconds: f32, -) { - let input = snapshot.last_input.clone(); - if let Some(leader) = snapshot.owned_entities.first_mut() { - leader.position.x = clamp_world( - leader.position.x + input.x * params.leader_move_speed * step_seconds, - true, - ); - leader.position.y = clamp_world( - leader.position.y + input.y * params.leader_move_speed * step_seconds, - false, - ); - snapshot.camera_center = leader.position.clone(); - } - - let leader_position = snapshot.camera_center.clone(); - for (index, follower) in snapshot.owned_entities.iter_mut().enumerate().skip(1) { - let slot_offset = ((index as f32) * 0.7).sin() * 36.0; - let target = BigFishVector2 { - x: leader_position.x - 42.0 - index as f32 * 8.0, - y: leader_position.y + slot_offset, - }; - let delta_x = target.x - follower.position.x; - let delta_y = target.y - follower.position.y; - let distance = (delta_x * delta_x + delta_y * delta_y).sqrt(); - if distance <= f32::EPSILON { - continue; - } - let catch_up_ratio = - (params.follower_catch_up_speed * step_seconds / distance).clamp(0.0, 1.0); - follower.position.x += delta_x * catch_up_ratio; - follower.position.y += delta_y * catch_up_ratio; - } -} - -fn resolve_collisions(snapshot: &mut BigFishRuntimeSnapshot, _params: &BigFishRuntimeParams) { - let mut owned_to_remove = Vec::new(); - let mut wild_to_remove = Vec::new(); - let mut newly_owned = Vec::new(); - - for (owned_index, owned) in snapshot.owned_entities.iter().enumerate() { - for (wild_index, wild) in snapshot.wild_entities.iter().enumerate() { - if wild_to_remove.contains(&wild_index) || owned_to_remove.contains(&owned_index) { - continue; - } - if distance(&owned.position, &wild.position) > owned.radius + wild.radius { - continue; - } - - if owned.level >= wild.level { - wild_to_remove.push(wild_index); - newly_owned.push(BigFishRuntimeEntity { - entity_id: format!("owned-from-{}-{}", wild.entity_id, snapshot.tick), - level: wild.level, - position: wild.position.clone(), - radius: entity_radius(wild.level), - offscreen_seconds: 0.0, - }); - snapshot - .event_log - .push(format!("收编 {} 级实体", wild.level)); - } else { - owned_to_remove.push(owned_index); - snapshot.event_log.push(format!( - "{} 级己方实体被 {} 级野生实体吃掉", - owned.level, wild.level - )); - } - } - } - - remove_indices(&mut snapshot.wild_entities, &wild_to_remove); - remove_indices(&mut snapshot.owned_entities, &owned_to_remove); - snapshot.owned_entities.extend(newly_owned); -} - -fn apply_chain_merges(snapshot: &mut BigFishRuntimeSnapshot, params: &BigFishRuntimeParams) { - loop { - let mut merged = false; - for level in 1..params.win_level { - let indices = snapshot - .owned_entities - .iter() - .enumerate() - .filter_map(|(index, entity)| (entity.level == level).then_some(index)) - .take(params.merge_count_per_upgrade as usize) - .collect::>(); - if indices.len() < params.merge_count_per_upgrade as usize { - continue; - } - - let center = average_position(&indices, &snapshot.owned_entities); - remove_indices(&mut snapshot.owned_entities, &indices); - snapshot.owned_entities.push(BigFishRuntimeEntity { - entity_id: format!("owned-merge-{}-{}", level + 1, snapshot.tick), - level: level + 1, - position: center, - radius: entity_radius(level + 1), - offscreen_seconds: 0.0, - }); - snapshot - .event_log - .push(format!("3 个 {} 级实体合成 {} 级", level, level + 1)); - merged = true; - break; - } - - if !merged { - break; - } - } -} - -fn refresh_player_leader(snapshot: &mut BigFishRuntimeSnapshot) { - snapshot.owned_entities.sort_by(|left, right| { - right - .level - .cmp(&left.level) - .then_with(|| { - distance(&left.position, &snapshot.camera_center) - .partial_cmp(&distance(&right.position, &snapshot.camera_center)) - .unwrap_or(std::cmp::Ordering::Equal) - }) - .then_with(|| left.entity_id.cmp(&right.entity_id)) - }); - snapshot.leader_entity_id = snapshot - .owned_entities - .first() - .map(|entity| entity.entity_id.clone()); - snapshot.player_level = snapshot - .owned_entities - .iter() - .map(|entity| entity.level) - .max() - .unwrap_or(0); - if let Some(leader) = snapshot.owned_entities.first() { - snapshot.camera_center = leader.position.clone(); - } -} - -fn apply_win_or_fail(snapshot: &mut BigFishRuntimeSnapshot, params: &BigFishRuntimeParams) { - if snapshot.owned_entities.is_empty() { - snapshot.status = BigFishRunStatus::Failed; - snapshot - .event_log - .push("己方实体归零,本局失败".to_string()); - return; - } - if snapshot.player_level >= params.win_level { - snapshot.status = BigFishRunStatus::Won; - snapshot - .event_log - .push("获得最高等级实体,通关".to_string()); - } -} - -fn update_wild_culling( - snapshot: &mut BigFishRuntimeSnapshot, - params: &BigFishRuntimeParams, - step_seconds: f32, -) { - let player_level = snapshot.player_level; - for wild in &mut snapshot.wild_entities { - let should_cull_level = wild.level == player_level - || wild.level >= player_level.saturating_add(3) - || wild.level.saturating_add(3) <= player_level; - if !should_cull_level { - wild.offscreen_seconds = 0.0; - continue; - } - - if is_offscreen(&wild.position, &snapshot.camera_center, wild.radius) { - wild.offscreen_seconds += step_seconds; - } else { - wild.offscreen_seconds = 0.0; - } - } - snapshot - .wild_entities - .retain(|wild| wild.offscreen_seconds < params.offscreen_cull_seconds); -} - -fn maintain_wild_pool(snapshot: &mut BigFishRuntimeSnapshot, params: &BigFishRuntimeParams) { - if snapshot.status != BigFishRunStatus::Running { - return; - } - let mut next_index = snapshot.wild_entities.len() + snapshot.tick as usize; - while snapshot.wild_entities.len() < params.spawn_target_count as usize { - let level = next_spawn_level(snapshot.player_level.max(1), params.win_level, next_index); - snapshot.wild_entities.push(BigFishRuntimeEntity { - entity_id: format!("wild-{}-{}", snapshot.tick, next_index), - level, - position: spawn_position(&snapshot.camera_center, next_index), - radius: entity_radius(level), - offscreen_seconds: 0.0, - }); - next_index += 1; - } -} - -fn next_spawn_level(player_level: u32, win_level: u32, index: usize) -> u32 { - if player_level == 1 && index % 4 < 2 { - return 1; - } - let deltas = [-2_i32, -1, 1, 2]; - let delta = deltas[index % deltas.len()]; - (player_level as i32 + delta).clamp(1, win_level as i32) as u32 -} - -fn spawn_position(center: &BigFishVector2, index: usize) -> BigFishVector2 { - let side = index % 4; - let offset = ((index as f32 * 37.0) % 420.0) - 210.0; - match side { - 0 => BigFishVector2 { - x: center.x - BIG_FISH_VIEW_WIDTH * 0.62, - y: center.y + offset, - }, - 1 => BigFishVector2 { - x: center.x + BIG_FISH_VIEW_WIDTH * 0.62, - y: center.y + offset, - }, - 2 => BigFishVector2 { - x: center.x + offset, - y: center.y - BIG_FISH_VIEW_HEIGHT * 0.58, - }, - _ => BigFishVector2 { - x: center.x + offset, - y: center.y + BIG_FISH_VIEW_HEIGHT * 0.58, - }, - } -} - -fn remove_indices(items: &mut Vec, indices: &[usize]) { - let mut sorted = indices.to_vec(); - sorted.sort_unstable(); - sorted.dedup(); - for index in sorted.into_iter().rev() { - if index < items.len() { - items.remove(index); - } - } -} - -fn average_position(indices: &[usize], entities: &[BigFishRuntimeEntity]) -> BigFishVector2 { - let mut x = 0.0; - let mut y = 0.0; - for index in indices { - x += entities[*index].position.x; - y += entities[*index].position.y; - } - let count = indices.len().max(1) as f32; - BigFishVector2 { - x: x / count, - y: y / count, - } -} - -fn distance(left: &BigFishVector2, right: &BigFishVector2) -> f32 { - let dx = left.x - right.x; - let dy = left.y - right.y; - (dx * dx + dy * dy).sqrt() -} - -fn is_offscreen(position: &BigFishVector2, camera: &BigFishVector2, radius: f32) -> bool { - let half_w = BIG_FISH_VIEW_WIDTH / 2.0; - let half_h = BIG_FISH_VIEW_HEIGHT / 2.0; - position.x + radius < camera.x - half_w - || position.x - radius > camera.x + half_w - || position.y + radius < camera.y - half_h - || position.y - radius > camera.y + half_h -} - -fn clamp_world(value: f32, horizontal: bool) -> f32 { - let limit = if horizontal { - BIG_FISH_WORLD_HALF_WIDTH - } else { - BIG_FISH_WORLD_HALF_HEIGHT - }; - value.clamp(-limit, limit) -} - -fn entity_radius(level: u32) -> f32 { - 18.0 + level as f32 * 4.0 -} - impl fmt::Display for BigFishFieldError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { @@ -1316,11 +810,9 @@ impl fmt::Display for BigFishFieldError { Self::MissingOwnerUserId => f.write_str("big_fish.owner_user_id 不能为空"), Self::MissingMessageId => f.write_str("big_fish.message_id 不能为空"), Self::MissingMessageText => f.write_str("big_fish.message_text 不能为空"), - Self::MissingRunId => f.write_str("big_fish.run_id 不能为空"), 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::InvalidRunState => f.write_str("big_fish.run 当前状态不允许推进"), } } } @@ -1370,123 +862,4 @@ mod tests { assert!(coverage.blockers.iter().any(|item| item.contains("背景图"))); } - #[test] - fn same_level_wild_entity_can_be_collected_at_start() { - let draft = compile_default_draft(&infer_anchor_pack("深海", None)); - let mut snapshot = - build_initial_runtime_snapshot("run-1".to_string(), "session-1".to_string(), &draft, 1); - snapshot.wild_entities[0].position = BigFishVector2 { x: 1.0, y: 0.0 }; - - let next = advance_runtime_snapshot(snapshot, &draft.runtime_params, 0.0, 0.0, 2); - - assert!(next.owned_entities.len() >= 2); - assert!( - next.event_log - .iter() - .any(|event| event.contains("收编 1 级实体")) - ); - } - - #[test] - fn three_owned_entities_merge_into_next_level() { - let draft = compile_default_draft(&infer_anchor_pack("深海", None)); - let mut snapshot = build_initial_runtime_snapshot( - "run-merge".to_string(), - "session-merge".to_string(), - &draft, - 1, - ); - snapshot.wild_entities.clear(); - snapshot.owned_entities.push(BigFishRuntimeEntity { - entity_id: "owned-2".to_string(), - level: 1, - position: BigFishVector2 { x: 4.0, y: 0.0 }, - radius: entity_radius(1), - offscreen_seconds: 0.0, - }); - snapshot.owned_entities.push(BigFishRuntimeEntity { - entity_id: "owned-3".to_string(), - level: 1, - position: BigFishVector2 { x: 8.0, y: 0.0 }, - radius: entity_radius(1), - offscreen_seconds: 0.0, - }); - - let next = advance_runtime_snapshot(snapshot, &draft.runtime_params, 0.0, 0.0, 2); - - assert!(next.owned_entities.iter().any(|entity| entity.level == 2)); - } - - #[test] - fn final_level_immediately_wins() { - let draft = compile_default_draft(&infer_anchor_pack("深海", None)); - let mut snapshot = build_initial_runtime_snapshot( - "run-win".to_string(), - "session-win".to_string(), - &draft, - 1, - ); - snapshot.owned_entities[0].level = draft.runtime_params.win_level; - - let next = advance_runtime_snapshot(snapshot, &draft.runtime_params, 0.0, 0.0, 2); - - assert_eq!(next.status, BigFishRunStatus::Won); - } - - #[test] - fn offscreen_same_level_wild_entity_is_removed_after_three_seconds() { - let draft = compile_default_draft(&infer_anchor_pack("深海", None)); - let mut snapshot = build_initial_runtime_snapshot( - "run-cull".to_string(), - "session-cull".to_string(), - &draft, - 1, - ); - snapshot.wild_entities.clear(); - snapshot.wild_entities.push(BigFishRuntimeEntity { - entity_id: "wild-cull".to_string(), - level: 1, - position: BigFishVector2 { x: 1000.0, y: 0.0 }, - radius: entity_radius(1), - offscreen_seconds: 2.8, - }); - snapshot.updated_at_micros = 1_000_000; - - let next = advance_runtime_snapshot(snapshot, &draft.runtime_params, 0.0, 0.0, 1_250_000); - - assert!( - !next - .wild_entities - .iter() - .any(|entity| entity.entity_id == "wild-cull") - ); - } - - #[test] - fn offscreen_same_level_wild_entity_is_kept_before_three_seconds_elapsed() { - let draft = compile_default_draft(&infer_anchor_pack("深海", None)); - let mut snapshot = build_initial_runtime_snapshot( - "run-cull-safe".to_string(), - "session-cull-safe".to_string(), - &draft, - 1, - ); - snapshot.wild_entities.clear(); - snapshot.wild_entities.push(BigFishRuntimeEntity { - entity_id: "wild-cull-safe".to_string(), - level: 1, - position: BigFishVector2 { x: 1000.0, y: 0.0 }, - radius: entity_radius(1), - offscreen_seconds: 2.7, - }); - snapshot.updated_at_micros = 1_000_000; - - let next = advance_runtime_snapshot(snapshot, &draft.runtime_params, 0.0, 0.0, 1_200_000); - - assert!( - next.wild_entities - .iter() - .any(|entity| entity.entity_id == "wild-cull-safe") - ); - } } diff --git a/server-rs/crates/shared-contracts/src/big_fish.rs b/server-rs/crates/shared-contracts/src/big_fish.rs index 282c163e..49a69dca 100644 --- a/server-rs/crates/shared-contracts/src/big_fish.rs +++ b/server-rs/crates/shared-contracts/src/big_fish.rs @@ -26,13 +26,6 @@ pub struct ExecuteBigFishActionRequest { pub motion_key: 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 { @@ -169,47 +162,6 @@ 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 168d9c48..01311d6b 100644 --- a/server-rs/crates/spacetime-client/src/big_fish.rs +++ b/server-rs/crates/spacetime-client/src/big_fish.rs @@ -251,76 +251,4 @@ 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(|error| SpacetimeClientError::Procedure(error.to_string())) - .and_then(map_big_fish_run_procedure_result); - send_once(&sender, mapped); - }); - }) - .await - } - - pub async fn submit_big_fish_input( - &self, - input: BigFishRunInputSubmitRecordInput, - ) -> Result { - let procedure_input = BigFishRunInputSubmitInput { - run_id: input.run_id, - owner_user_id: input.owner_user_id, - input_x: input.input_x, - input_y: input.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(|error| SpacetimeClientError::Procedure(error.to_string())) - .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(|error| SpacetimeClientError::Procedure(error.to_string())) - .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 a2379f1f..cdcf785e 100644 --- a/server-rs/crates/spacetime-client/src/lib.rs +++ b/server-rs/crates/spacetime-client/src/lib.rs @@ -10,10 +10,8 @@ pub use mapper::{ BigFishAnchorPackRecord, BigFishAssetCoverageRecord, BigFishAssetGenerateRecordInput, BigFishAssetSlotRecord, BigFishBackgroundBlueprintRecord, BigFishGameDraftRecord, BigFishLevelBlueprintRecord, BigFishMessageFinalizeRecordInput, - BigFishMessageSubmitRecordInput, BigFishRunInputSubmitRecordInput, BigFishRunStartRecordInput, - BigFishRuntimeEntityRecord, BigFishRuntimeParamsRecord, BigFishRuntimeRecord, - BigFishSessionCreateRecordInput, BigFishSessionRecord, BigFishVector2Record, - BigFishWorkSummaryRecord, CustomWorldAgentActionExecuteRecord, + BigFishMessageSubmitRecordInput, BigFishRuntimeParamsRecord, BigFishSessionCreateRecordInput, + BigFishSessionRecord, BigFishWorkSummaryRecord, CustomWorldAgentActionExecuteRecord, CustomWorldAgentActionExecuteRecordInput, CustomWorldAgentCheckpointRecord, CustomWorldAgentMessageFinalizeRecordInput, CustomWorldAgentMessageRecord, CustomWorldAgentMessageSubmitRecordInput, CustomWorldAgentOperationProgressRecordInput, diff --git a/server-rs/crates/spacetime-client/src/mapper.rs b/server-rs/crates/spacetime-client/src/mapper.rs index fc57f93a..8c423c72 100644 --- a/server-rs/crates/spacetime-client/src/mapper.rs +++ b/server-rs/crates/spacetime-client/src/mapper.rs @@ -1262,26 +1262,6 @@ pub(crate) fn map_big_fish_works_procedure_result( }) } -pub(crate) fn map_big_fish_run_procedure_result( - result: BigFishRunProcedureResult, -) -> Result { - if !result.ok { - return Err(SpacetimeClientError::Procedure( - result - .error_message - .unwrap_or_else(|| "SpacetimeDB procedure 返回未知错误".to_string()), - )); - } - - let run = result.run.ok_or_else(|| { - SpacetimeClientError::Procedure( - "SpacetimeDB procedure 未返回 big fish runtime 快照".to_string(), - ) - })?; - - Ok(map_big_fish_runtime_snapshot(run)) -} - pub(crate) fn map_story_session_procedure_result( result: StorySessionProcedureResult, ) -> Result { @@ -2468,53 +2448,6 @@ pub(crate) fn map_big_fish_agent_message_snapshot( } } -pub(crate) fn map_big_fish_runtime_snapshot( - snapshot: BigFishRuntimeSnapshot, -) -> BigFishRuntimeRecord { - BigFishRuntimeRecord { - run_id: snapshot.run_id, - session_id: snapshot.session_id, - status: format_big_fish_run_status(snapshot.status).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) - .collect(), - wild_entities: snapshot - .wild_entities - .into_iter() - .map(map_big_fish_runtime_entity) - .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), - } -} - -pub(crate) fn map_big_fish_runtime_entity( - snapshot: BigFishRuntimeEntity, -) -> 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, - } -} - -pub(crate) fn map_big_fish_vector2(snapshot: 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, @@ -3220,14 +3153,6 @@ pub(crate) fn format_big_fish_asset_status(value: BigFishAssetStatus) -> &'stati } } -pub(crate) fn format_big_fish_run_status(value: BigFishRunStatus) -> &'static str { - match value { - BigFishRunStatus::Running => "running", - BigFishRunStatus::Won => "won", - BigFishRunStatus::Failed => "failed", - } -} - pub(crate) fn format_custom_world_theme_mode(value: DomainCustomWorldThemeMode) -> &'static str { match value { DomainCustomWorldThemeMode::Martial => "martial", @@ -4455,23 +4380,6 @@ pub struct BigFishAssetGenerateRecordInput { pub generated_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 BigFishRunInputSubmitRecordInput { - pub run_id: String, - pub owner_user_id: String, - pub input_x: f32, - pub input_y: f32, - pub submitted_at_micros: i64, -} - #[derive(Clone, Debug, PartialEq, Eq)] pub struct BigFishAnchorItemRecord { pub key: String, @@ -4604,38 +4512,6 @@ pub struct BigFishWorkSummaryRecord { pub background_ready: bool, } -#[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 BigFishRuntimeRecord { - 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)] pub struct ResolveNpcBattleInteractionInput { pub npc_interaction: DomainResolveNpcInteractionInput, 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 new file mode 100644 index 00000000..b5885022 --- /dev/null +++ b/server-rs/crates/spacetime-client/src/module_bindings/authorize_database_migration_operator_procedure.rs @@ -0,0 +1,62 @@ +// 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::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 { + pub input: DatabaseMigrationAuthorizeOperatorInput, +} + +impl __sdk::InModule for AuthorizeDatabaseMigrationOperatorArgs { + type Module = super::RemoteModule; +} + +#[allow(non_camel_case_types)] +/// Extension trait for access to the procedure `authorize_database_migration_operator`. +/// +/// 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_then( + &self, + input: DatabaseMigrationAuthorizeOperatorInput, + + __callback: impl FnOnce( + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, + ); +} + +impl authorize_database_migration_operator for super::RemoteProcedures { + fn authorize_database_migration_operator_then( + &self, + input: DatabaseMigrationAuthorizeOperatorInput, + + __callback: impl FnOnce( + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, + ) { + 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/big_fish_run_input_submit_input_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/big_fish_run_input_submit_input_type.rs deleted file mode 100644 index f3175d40..00000000 --- a/server-rs/crates/spacetime-client/src/module_bindings/big_fish_run_input_submit_input_type.rs +++ /dev/null @@ -1,19 +0,0 @@ -// 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 BigFishRunInputSubmitInput { - pub run_id: String, - pub owner_user_id: String, - pub input_x: f32, - pub input_y: f32, - pub submitted_at_micros: i64, -} - -impl __sdk::InModule for BigFishRunInputSubmitInput { - 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 deleted file mode 100644 index 01852b9c..00000000 --- a/server-rs/crates/spacetime-client/src/module_bindings/big_fish_runtime_run_type.rs +++ /dev/null @@ -1,82 +0,0 @@ -// 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_runtime_snapshot_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/big_fish_runtime_snapshot_type.rs deleted file mode 100644 index 32f2a80c..00000000 --- a/server-rs/crates/spacetime-client/src/module_bindings/big_fish_runtime_snapshot_type.rs +++ /dev/null @@ -1,31 +0,0 @@ -// 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; -use super::big_fish_runtime_entity_type::BigFishRuntimeEntity; -use super::big_fish_vector_2_type::BigFishVector2; - -#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] -#[sats(crate = __lib)] -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 __sdk::InModule for BigFishRuntimeSnapshot { - 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/database_migration_authorize_operator_input_type.rs similarity index 64% rename from server-rs/crates/spacetime-client/src/module_bindings/big_fish_run_status_type.rs rename to server-rs/crates/spacetime-client/src/module_bindings/database_migration_authorize_operator_input_type.rs index 6bb94f36..309de81f 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/big_fish_run_status_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/database_migration_authorize_operator_input_type.rs @@ -6,15 +6,12 @@ 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, +pub struct DatabaseMigrationAuthorizeOperatorInput { + pub bootstrap_secret: String, + pub operator_identity_hex: String, + pub note: String, } -impl __sdk::InModule for BigFishRunStatus { +impl __sdk::InModule for DatabaseMigrationAuthorizeOperatorInput { 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/database_migration_export_input_type.rs similarity index 74% rename from server-rs/crates/spacetime-client/src/module_bindings/big_fish_run_get_input_type.rs rename to server-rs/crates/spacetime-client/src/module_bindings/database_migration_export_input_type.rs index 8e0d0d8d..39381901 100644 --- 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/database_migration_export_input_type.rs @@ -6,11 +6,10 @@ 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, +pub struct DatabaseMigrationExportInput { + pub include_tables: Vec, } -impl __sdk::InModule for BigFishRunGetInput { +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 new file mode 100644 index 00000000..6bdbcb91 --- /dev/null +++ b/server-rs/crates/spacetime-client/src/module_bindings/database_migration_import_input_type.rs @@ -0,0 +1,18 @@ +// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE +// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD. + +#![allow(unused, clippy::all)] +use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws}; + +#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] +#[sats(crate = __lib)] +pub struct DatabaseMigrationImportInput { + pub migration_json: String, + 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/big_fish_run_procedure_result_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/database_migration_operator_procedure_result_type.rs similarity index 68% rename from server-rs/crates/spacetime-client/src/module_bindings/big_fish_run_procedure_result_type.rs rename to server-rs/crates/spacetime-client/src/module_bindings/database_migration_operator_procedure_result_type.rs index 86d73fc2..0c7e3e65 100644 --- 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/database_migration_operator_procedure_result_type.rs @@ -4,16 +4,14 @@ #![allow(unused, clippy::all)] use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws}; -use super::big_fish_runtime_snapshot_type::BigFishRuntimeSnapshot; - #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] -pub struct BigFishRunProcedureResult { +pub struct DatabaseMigrationOperatorProcedureResult { pub ok: bool, - pub run: Option, + pub operator_identity_hex: Option, pub error_message: Option, } -impl __sdk::InModule for BigFishRunProcedureResult { +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 new file mode 100644 index 00000000..7f8b0b6e --- /dev/null +++ b/server-rs/crates/spacetime-client/src/module_bindings/database_migration_operator_type.rs @@ -0,0 +1,59 @@ +// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE +// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD. + +#![allow(unused, clippy::all)] +use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws}; + +#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] +#[sats(crate = __lib)] +pub struct DatabaseMigrationOperator { + pub operator_identity: __sdk::Identity, + pub created_at: __sdk::Timestamp, + pub created_by: __sdk::Identity, + 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. +pub struct DatabaseMigrationOperatorCols { + pub operator_identity: __sdk::__query_builder::Col, + pub created_at: __sdk::__query_builder::Col, + pub created_by: __sdk::__query_builder::Col, + pub note: __sdk::__query_builder::Col, +} + +impl __sdk::__query_builder::HasCols for DatabaseMigrationOperator { + type Cols = DatabaseMigrationOperatorCols; + fn cols(table_name: &'static str) -> Self::Cols { + DatabaseMigrationOperatorCols { + operator_identity: __sdk::__query_builder::Col::new(table_name, "operator_identity"), + 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"), + } + } +} + +/// Indexed column accessor struct for the table `DatabaseMigrationOperator`. +/// +/// Provides typed access to indexed columns for query building. +pub struct DatabaseMigrationOperatorIxCols { + pub operator_identity: + __sdk::__query_builder::IxCol, +} + +impl __sdk::__query_builder::HasIxCols for DatabaseMigrationOperator { + type IxCols = DatabaseMigrationOperatorIxCols; + 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/big_fish_runtime_entity_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/database_migration_procedure_result_type.rs similarity index 52% rename from server-rs/crates/spacetime-client/src/module_bindings/big_fish_runtime_entity_type.rs rename to server-rs/crates/spacetime-client/src/module_bindings/database_migration_procedure_result_type.rs index ee7c160c..a3869c5d 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/big_fish_runtime_entity_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/database_migration_procedure_result_type.rs @@ -4,18 +4,18 @@ #![allow(unused, clippy::all)] use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws}; -use super::big_fish_vector_2_type::BigFishVector2; +use super::database_migration_table_stat_type::DatabaseMigrationTableStat; #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] -pub struct BigFishRuntimeEntity { - pub entity_id: String, - pub level: u32, - pub position: BigFishVector2, - pub radius: f32, - pub offscreen_seconds: f32, +pub struct DatabaseMigrationProcedureResult { + pub ok: bool, + pub schema_version: u32, + pub migration_json: Option, + pub table_stats: Vec, + pub error_message: Option, } -impl __sdk::InModule for BigFishRuntimeEntity { +impl __sdk::InModule for DatabaseMigrationProcedureResult { type Module = super::RemoteModule; } diff --git a/server-rs/crates/spacetime-client/src/module_bindings/big_fish_vector_2_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/database_migration_revoke_operator_input_type.rs similarity index 72% rename from server-rs/crates/spacetime-client/src/module_bindings/big_fish_vector_2_type.rs rename to server-rs/crates/spacetime-client/src/module_bindings/database_migration_revoke_operator_input_type.rs index 745063ad..e550a7a4 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/big_fish_vector_2_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/database_migration_revoke_operator_input_type.rs @@ -6,11 +6,10 @@ 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 BigFishVector2 { - pub x: f32, - pub y: f32, +pub struct DatabaseMigrationRevokeOperatorInput { + pub operator_identity_hex: String, } -impl __sdk::InModule for BigFishVector2 { +impl __sdk::InModule for DatabaseMigrationRevokeOperatorInput { 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/database_migration_table_stat_type.rs similarity index 64% rename from server-rs/crates/spacetime-client/src/module_bindings/big_fish_run_start_input_type.rs rename to server-rs/crates/spacetime-client/src/module_bindings/database_migration_table_stat_type.rs index 944fa6da..1257661d 100644 --- 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/database_migration_table_stat_type.rs @@ -6,13 +6,13 @@ 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, +pub struct DatabaseMigrationTableStat { + pub table_name: String, + pub exported_row_count: u64, + pub imported_row_count: u64, + pub skipped_row_count: u64, } -impl __sdk::InModule for BigFishRunStartInput { +impl __sdk::InModule for DatabaseMigrationTableStat { type Module = super::RemoteModule; } 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 new file mode 100644 index 00000000..3dfe18f8 --- /dev/null +++ b/server-rs/crates/spacetime-client/src/module_bindings/export_database_migration_to_file_procedure.rs @@ -0,0 +1,59 @@ +// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE +// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD. + +#![allow(unused, clippy::all)] +use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws}; + +use super::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 { + pub input: DatabaseMigrationExportInput, +} + +impl __sdk::InModule for ExportDatabaseMigrationToFileArgs { + type Module = super::RemoteModule; +} + +#[allow(non_camel_case_types)] +/// Extension trait for access to the procedure `export_database_migration_to_file`. +/// +/// 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_then( + &self, + input: DatabaseMigrationExportInput, + + __callback: impl FnOnce( + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, + ); +} + +impl export_database_migration_to_file for super::RemoteProcedures { + fn export_database_migration_to_file_then( + &self, + input: DatabaseMigrationExportInput, + + __callback: impl FnOnce( + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, + ) { + 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/get_big_fish_run_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/get_big_fish_run_procedure.rs deleted file mode 100644 index 867a6759..00000000 --- a/server-rs/crates/spacetime-client/src/module_bindings/get_big_fish_run_procedure.rs +++ /dev/null @@ -1,59 +0,0 @@ -// 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/import_database_migration_from_file_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/import_database_migration_from_file_procedure.rs new file mode 100644 index 00000000..7b2322ee --- /dev/null +++ b/server-rs/crates/spacetime-client/src/module_bindings/import_database_migration_from_file_procedure.rs @@ -0,0 +1,59 @@ +// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE +// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD. + +#![allow(unused, clippy::all)] +use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws}; + +use super::database_migration_import_input_type::DatabaseMigrationImportInput; +use super::database_migration_procedure_result_type::DatabaseMigrationProcedureResult; + +#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] +#[sats(crate = __lib)] +struct ImportDatabaseMigrationFromFileArgs { + pub input: DatabaseMigrationImportInput, +} + +impl __sdk::InModule for ImportDatabaseMigrationFromFileArgs { + type Module = super::RemoteModule; +} + +#[allow(non_camel_case_types)] +/// Extension trait for access to the procedure `import_database_migration_from_file`. +/// +/// 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_then( + &self, + input: DatabaseMigrationImportInput, + + __callback: impl FnOnce( + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, + ); +} + +impl import_database_migration_from_file for super::RemoteProcedures { + fn import_database_migration_from_file_then( + &self, + input: DatabaseMigrationImportInput, + + __callback: impl FnOnce( + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, + ) { + 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 new file mode 100644 index 00000000..2fc31804 --- /dev/null +++ b/server-rs/crates/spacetime-client/src/module_bindings/import_database_migration_incremental_from_file_procedure.rs @@ -0,0 +1,59 @@ +// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE +// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD. + +#![allow(unused, clippy::all)] +use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws}; + +use super::database_migration_import_input_type::DatabaseMigrationImportInput; +use super::database_migration_procedure_result_type::DatabaseMigrationProcedureResult; + +#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] +#[sats(crate = __lib)] +struct ImportDatabaseMigrationIncrementalFromFileArgs { + pub input: DatabaseMigrationImportInput, +} + +impl __sdk::InModule for ImportDatabaseMigrationIncrementalFromFileArgs { + type Module = super::RemoteModule; +} + +#[allow(non_camel_case_types)] +/// Extension trait for access to the procedure `import_database_migration_incremental_from_file`. +/// +/// 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_then( + &self, + input: DatabaseMigrationImportInput, + + __callback: impl FnOnce( + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, + ); +} + +impl import_database_migration_incremental_from_file for super::RemoteProcedures { + fn import_database_migration_incremental_from_file_then( + &self, + input: DatabaseMigrationImportInput, + + __callback: impl FnOnce( + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, + ) { + 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/mod.rs b/server-rs/crates/spacetime-client/src/module_bindings/mod.rs index 62c0f774..adf910a1 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/mod.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/mod.rs @@ -58,6 +58,7 @@ 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_input_type; pub mod battle_state_procedure_result_type; @@ -89,20 +90,11 @@ 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_publish_input_type; -pub mod big_fish_run_get_input_type; -pub mod big_fish_run_input_submit_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_entity_type; pub mod big_fish_runtime_params_type; -pub mod big_fish_runtime_run_type; -pub mod big_fish_runtime_snapshot_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; pub mod big_fish_session_snapshot_type; -pub mod big_fish_vector_2_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; @@ -188,6 +180,14 @@ pub mod custom_world_theme_mode_type; pub mod custom_world_work_summary_snapshot_type; pub mod custom_world_works_list_input_type; 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_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; @@ -197,6 +197,7 @@ 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; @@ -204,7 +205,6 @@ 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; @@ -230,6 +230,8 @@ 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; @@ -356,6 +358,7 @@ 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; @@ -425,7 +428,6 @@ 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_big_fish_run_procedure; pub mod start_puzzle_run_procedure; pub mod story_continue_input_type; pub mod story_event_kind_type; @@ -438,7 +440,6 @@ 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 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; @@ -519,6 +520,7 @@ pub use auth_store_snapshot_procedure_result_type::AuthStoreSnapshotProcedureRes 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_input_type::BattleStateInput; pub use battle_state_procedure_result_type::BattleStateProcedureResult; @@ -550,20 +552,11 @@ 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_publish_input_type::BigFishPublishInput; -pub use big_fish_run_get_input_type::BigFishRunGetInput; -pub use big_fish_run_input_submit_input_type::BigFishRunInputSubmitInput; -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_entity_type::BigFishRuntimeEntity; pub use big_fish_runtime_params_type::BigFishRuntimeParams; -pub use big_fish_runtime_run_type::BigFishRuntimeRun; -pub use big_fish_runtime_snapshot_type::BigFishRuntimeSnapshot; 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; pub use big_fish_session_snapshot_type::BigFishSessionSnapshot; -pub use big_fish_vector_2_type::BigFishVector2; 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; @@ -649,6 +642,14 @@ 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; 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_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; @@ -658,6 +659,7 @@ 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; @@ -665,7 +667,6 @@ pub use finalize_puzzle_agent_message_turn_procedure::finalize_puzzle_agent_mess 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; @@ -691,6 +692,8 @@ 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; @@ -817,6 +820,7 @@ 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; @@ -886,7 +890,6 @@ 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_big_fish_run_procedure::start_big_fish_run; pub use start_puzzle_run_procedure::start_puzzle_run; pub use story_continue_input_type::StoryContinueInput; pub use story_event_kind_type::StoryEventKind; @@ -899,7 +902,6 @@ 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 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; 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 new file mode 100644 index 00000000..feb5086e --- /dev/null +++ b/server-rs/crates/spacetime-client/src/module_bindings/revoke_database_migration_operator_procedure.rs @@ -0,0 +1,59 @@ +// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE +// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD. + +#![allow(unused, clippy::all)] +use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws}; + +use super::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 { + pub input: DatabaseMigrationRevokeOperatorInput, +} + +impl __sdk::InModule for RevokeDatabaseMigrationOperatorArgs { + type Module = super::RemoteModule; +} + +#[allow(non_camel_case_types)] +/// Extension trait for access to the procedure `revoke_database_migration_operator`. +/// +/// 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_then( + &self, + input: DatabaseMigrationRevokeOperatorInput, + + __callback: impl FnOnce( + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, + ); +} + +impl revoke_database_migration_operator for super::RemoteProcedures { + fn revoke_database_migration_operator_then( + &self, + input: DatabaseMigrationRevokeOperatorInput, + + __callback: impl FnOnce( + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, + ) { + 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/start_big_fish_run_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/start_big_fish_run_procedure.rs deleted file mode 100644 index 7f3713ab..00000000 --- a/server-rs/crates/spacetime-client/src/module_bindings/start_big_fish_run_procedure.rs +++ /dev/null @@ -1,59 +0,0 @@ -// 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/submit_big_fish_input_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/submit_big_fish_input_procedure.rs deleted file mode 100644 index 21e8f82d..00000000 --- a/server-rs/crates/spacetime-client/src/module_bindings/submit_big_fish_input_procedure.rs +++ /dev/null @@ -1,59 +0,0 @@ -// 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_input_submit_input_type::BigFishRunInputSubmitInput; -use super::big_fish_run_procedure_result_type::BigFishRunProcedureResult; - -#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] -#[sats(crate = __lib)] -struct SubmitBigFishInputArgs { - pub input: BigFishRunInputSubmitInput, -} - -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: BigFishRunInputSubmitInput) { - self.submit_big_fish_input_then(input, |_, _| {}); - } - - fn submit_big_fish_input_then( - &self, - input: BigFishRunInputSubmitInput, - - __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, - ); -} - -impl submit_big_fish_input for super::RemoteProcedures { - fn submit_big_fish_input_then( - &self, - input: BigFishRunInputSubmitInput, - - __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-module/src/big_fish/mod.rs b/server-rs/crates/spacetime-module/src/big_fish/mod.rs index 968034c3..8478f897 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,7 @@ mod assets; -mod runtime; mod session; mod tables; pub use assets::*; -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 deleted file mode 100644 index d794b653..00000000 --- a/server-rs/crates/spacetime-module/src/big_fish/runtime.rs +++ /dev/null @@ -1,198 +0,0 @@ -use crate::big_fish::tables::{big_fish_creation_session, big_fish_runtime_run}; -use crate::*; - -#[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: Some(run), - error_message: None, - }, - Err(message) => BigFishRunProcedureResult { - ok: false, - run: None, - error_message: Some(message), - }, - } -} - -#[spacetimedb::procedure] -pub fn submit_big_fish_input( - ctx: &mut ProcedureContext, - input: BigFishRunInputSubmitInput, -) -> BigFishRunProcedureResult { - match ctx.try_with_tx(|tx| submit_big_fish_input_tx(tx, input.clone())) { - Ok(run) => BigFishRunProcedureResult { - ok: true, - run: Some(run), - error_message: None, - }, - Err(message) => BigFishRunProcedureResult { - ok: false, - run: 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: Some(run), - error_message: None, - }, - Err(message) => BigFishRunProcedureResult { - ok: false, - run: None, - error_message: Some(message), - }, - } -} - -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())?; - if session.owner_user_id != input.owner_user_id - && session.stage != BigFishCreationStage::Published - { - return Err("big_fish_creation_session 不存在".to_string()); - } - let draft = session - .draft_json - .as_deref() - .ok_or_else(|| "big_fish.draft 尚未编译".to_string()) - .and_then(|value| deserialize_draft(value).map_err(|error| error.to_string()))?; - let snapshot = build_initial_runtime_snapshot( - input.run_id.clone(), - input.session_id.clone(), - &draft, - input.started_at_micros, - ); - let now = Timestamp::from_micros_since_unix_epoch(input.started_at_micros); - ctx.db.big_fish_runtime_run().insert(BigFishRuntimeRun { - run_id: input.run_id, - session_id: input.session_id, - owner_user_id: input.owner_user_id, - status: snapshot.status, - snapshot_json: serialize_runtime_snapshot(&snapshot).map_err(|error| error.to_string())?, - last_input_x: 0.0, - last_input_y: 0.0, - tick: snapshot.tick, - created_at: now, - updated_at: now, - }); - - Ok(snapshot) -} - -fn submit_big_fish_input_tx( - ctx: &ReducerContext, - input: BigFishRunInputSubmitInput, -) -> Result { - validate_run_input_submit_input(&input).map_err(|error| error.to_string())?; - let run = ctx - .db - .big_fish_runtime_run() - .run_id() - .find(&input.run_id) - .filter(|row| row.owner_user_id == input.owner_user_id) - .ok_or_else(|| "big_fish_runtime_run 不存在".to_string())?; - let session = ctx - .db - .big_fish_creation_session() - .session_id() - .find(&run.session_id) - .ok_or_else(|| "big_fish_creation_session 不存在".to_string())?; - if session.owner_user_id != input.owner_user_id - && session.stage != BigFishCreationStage::Published - { - return Err("big_fish_creation_session 不存在".to_string()); - } - let draft = session - .draft_json - .as_deref() - .ok_or_else(|| "big_fish.draft 尚未编译".to_string()) - .and_then(|value| deserialize_draft(value).map_err(|error| error.to_string()))?; - let current_snapshot = - deserialize_runtime_snapshot(&run.snapshot_json).map_err(|error| error.to_string())?; - let next_snapshot = advance_runtime_snapshot( - current_snapshot, - &draft.runtime_params, - input.input_x, - input.input_y, - input.submitted_at_micros, - ); - replace_big_fish_run( - ctx, - &run, - BigFishRuntimeRun { - run_id: run.run_id.clone(), - session_id: run.session_id.clone(), - owner_user_id: run.owner_user_id.clone(), - status: next_snapshot.status, - snapshot_json: serialize_runtime_snapshot(&next_snapshot) - .map_err(|error| error.to_string())?, - last_input_x: input.input_x, - last_input_y: input.input_y, - tick: next_snapshot.tick, - created_at: run.created_at, - updated_at: Timestamp::from_micros_since_unix_epoch(input.submitted_at_micros), - }, - ); - - Ok(next_snapshot) -} - -fn get_big_fish_run_tx( - ctx: &ReducerContext, - input: BigFishRunGetInput, -) -> Result { - validate_run_get_input(&input).map_err(|error| error.to_string())?; - let run = ctx - .db - .big_fish_runtime_run() - .run_id() - .find(&input.run_id) - .filter(|row| row.owner_user_id == input.owner_user_id) - .ok_or_else(|| "big_fish_runtime_run 不存在".to_string())?; - - deserialize_runtime_snapshot(&run.snapshot_json).map_err(|error| error.to_string()) -} - -fn replace_big_fish_run( - ctx: &ReducerContext, - current: &BigFishRuntimeRun, - next: BigFishRuntimeRun, -) { - ctx.db - .big_fish_runtime_run() - .run_id() - .delete(¤t.run_id); - ctx.db.big_fish_runtime_run().insert(next); -} 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 28d5bdaa..01459d39 100644 --- a/server-rs/crates/spacetime-module/src/big_fish/session.rs +++ b/server-rs/crates/spacetime-module/src/big_fish/session.rs @@ -295,7 +295,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 消息与素材槽;最终游玩模拟已经迁到前端,不再写后端运行快照。 ctx.db .big_fish_creation_session() .session_id() @@ -321,18 +321,6 @@ 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 && row.owner_user_id == input.owner_user_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 00b7f169..1e280ef6 100644 --- a/server-rs/crates/spacetime-module/src/big_fish/tables.rs +++ b/server-rs/crates/spacetime-module/src/big_fish/tables.rs @@ -51,22 +51,3 @@ 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/migration.rs b/server-rs/crates/spacetime-module/src/migration.rs index 3a128daf..37a52649 100644 --- a/server-rs/crates/spacetime-module/src/migration.rs +++ b/server-rs/crates/spacetime-module/src/migration.rs @@ -140,8 +140,7 @@ macro_rules! migration_tables { puzzle_runtime_run, big_fish_creation_session, big_fish_agent_message, - big_fish_asset_slot, - big_fish_runtime_run + big_fish_asset_slot } }; } diff --git a/src/components/big-fish-runtime/BigFishRuntimeShell.test.tsx b/src/components/big-fish-runtime/BigFishRuntimeShell.test.tsx index 51a771db..c4775a94 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 { fireEvent, render, screen } from '@testing-library/react'; -import { describe, expect, test, vi } from 'vitest'; +import { act, fireEvent, render, screen } from '@testing-library/react'; +import { afterEach, describe, expect, test, vi } from 'vitest'; import type { BigFishRuntimeSnapshotResponse } from '../../../packages/shared/src/contracts/bigFish'; import { BigFishRuntimeShell } from './BigFishRuntimeShell'; @@ -38,7 +38,21 @@ function createRun( }; } +function dispatchPointerEvent( + target: HTMLElement, + type: string, + options: { pointerId: number; clientX: number; clientY: number }, +) { + const event = new Event(type, { bubbles: true, cancelable: true }); + Object.assign(event, options); + target.dispatchEvent(event); +} + describe('BigFishRuntimeShell', () => { + afterEach(() => { + vi.useRealTimers(); + }); + test('renders restart and exit actions after a failed run', () => { const onBack = vi.fn(); const onRestart = vi.fn(); @@ -96,4 +110,51 @@ describe('BigFishRuntimeShell', () => { expect(screen.queryByRole('dialog', { name: '玩法规则' })).toBeNull(); }); + + test('keeps moving in the last sampled direction after drag ends', () => { + vi.useFakeTimers(); + const onSubmitInput = vi.fn(); + + const { container } = render( + {}} + onSubmitInput={onSubmitInput} + />, + ); + const stage = container.querySelector('.touch-none'); + if (!(stage instanceof HTMLElement)) { + throw new Error('Missing big fish stage'); + } + + act(() => { + dispatchPointerEvent(stage, 'pointerdown', { + pointerId: 1, + clientX: 100, + clientY: 100, + }); + }); + act(() => { + dispatchPointerEvent(stage, 'pointermove', { + pointerId: 1, + clientX: 140, + clientY: 100, + }); + }); + act(() => { + vi.advanceTimersByTime(100); + }); + act(() => { + dispatchPointerEvent(stage, 'pointerup', { + pointerId: 1, + clientX: 140, + clientY: 100, + }); + }); + act(() => { + vi.advanceTimersByTime(220); + }); + + expect(onSubmitInput).toHaveBeenLastCalledWith({ x: 1, y: 0 }); + }); }); diff --git a/src/components/big-fish-runtime/BigFishRuntimeShell.tsx b/src/components/big-fish-runtime/BigFishRuntimeShell.tsx index f4c6b353..f169ace3 100644 --- a/src/components/big-fish-runtime/BigFishRuntimeShell.tsx +++ b/src/components/big-fish-runtime/BigFishRuntimeShell.tsx @@ -16,6 +16,8 @@ type TouchOrigin = { y: number; }; +type TouchSample = TouchOrigin; + type BigFishRuntimeShellProps = { run: BigFishRuntimeSnapshotResponse | null; assetSlots?: BigFishAssetSlotResponse[]; @@ -42,14 +44,13 @@ function normalizeVector(x: number, y: number) { }; } -function resolveDirectionFromOrigin( - origin: TouchOrigin, - clientX: number, - clientY: number, -) { - const deadZone = 12; - const deltaX = clientX - origin.x; - const deltaY = clientY - origin.y; +function resolveDirectionFromSample(previous: TouchSample, current: TouchSample) { + const deadZone = 4; + const deltaX = current.x - previous.x; + const deltaY = current.y - previous.y; + if (!Number.isFinite(deltaX) || !Number.isFinite(deltaY)) { + return { x: 0, y: 0 }; + } if (Math.hypot(deltaX, deltaY) < deadZone) { return { x: 0, y: 0 }; } @@ -147,7 +148,7 @@ function BigFishRuleModal({ >
- 拖动屏幕控制方向,角色会按固定速度移动。 + 拖动屏幕改变方向,角色会按固定速度移动。
低级或同级野生实体会被收编。
@@ -178,15 +179,15 @@ function BigFishEntityDot({ return (
run.playerLevel - ? 'border-rose-100/80 shadow-rose-950/28' - : 'border-emerald-100/80 shadow-emerald-950/24' + ? 'drop-shadow-[0_8px_12px_rgba(127,29,29,0.36)]' + : 'drop-shadow-[0_8px_12px_rgba(6,78,59,0.3)]' : owned ? isLeader ? 'border-cyan-100 bg-cyan-300 shadow-cyan-950/30' @@ -202,11 +203,10 @@ function BigFishEntityDot({ -
) : null} @@ -227,6 +227,8 @@ export function BigFishRuntimeShell({ }: BigFishRuntimeShellProps) { const stageRef = useRef(null); const [touchOrigin, setTouchOrigin] = useState(null); + const currentTouchRef = useRef(null); + const lastTouchSampleRef = useRef(null); const [isRuleModalOpen, setIsRuleModalOpen] = useState(false); const [stick, setStick] = useState({ x: 0, y: 0 }); const stickRef = useRef(stick); @@ -251,6 +253,31 @@ export function BigFishRuntimeShell({ }; }, [onSubmitInput, run?.status]); + useEffect(() => { + if (run?.status !== 'running' || !touchOrigin) { + return undefined; + } + + const timer = window.setInterval(() => { + const current = currentTouchRef.current; + const previous = lastTouchSampleRef.current; + if (!current || !previous || current.pointerId !== previous.pointerId) { + return; + } + + const sampledDirection = resolveDirectionFromSample(previous, current); + lastTouchSampleRef.current = { ...current }; + if (sampledDirection.x === 0 && sampledDirection.y === 0) { + return; + } + submitDirection(sampledDirection); + }, 100); + + return () => { + window.clearInterval(timer); + }; + }, [run?.status, touchOrigin]); + const submitDirection = (direction: SubmitBigFishInputRequest) => { setStick(direction); onSubmitInput(direction); @@ -260,22 +287,35 @@ export function BigFishRuntimeShell({ if (event.target instanceof HTMLElement && event.target.closest('button')) { return; } - event.currentTarget.setPointerCapture(event.pointerId); + if (!Number.isFinite(event.clientX) || !Number.isFinite(event.clientY)) { + return; + } + event.currentTarget.setPointerCapture?.(event.pointerId); setTouchOrigin({ pointerId: event.pointerId, x: event.clientX, y: event.clientY, }); - submitDirection({ x: 0, y: 0 }); + currentTouchRef.current = { + pointerId: event.pointerId, + x: event.clientX, + y: event.clientY, + }; + lastTouchSampleRef.current = { ...currentTouchRef.current }; }; const updateTouchControl = (event: PointerEvent) => { if (!touchOrigin || touchOrigin.pointerId !== event.pointerId) { return; } - submitDirection( - resolveDirectionFromOrigin(touchOrigin, event.clientX, event.clientY), - ); + if (!Number.isFinite(event.clientX) || !Number.isFinite(event.clientY)) { + return; + } + currentTouchRef.current = { + pointerId: event.pointerId, + x: event.clientX, + y: event.clientY, + }; }; const endTouchControl = (event: PointerEvent) => { @@ -283,7 +323,8 @@ export function BigFishRuntimeShell({ return; } setTouchOrigin(null); - submitDirection({ x: 0, y: 0 }); + currentTouchRef.current = null; + lastTouchSampleRef.current = null; }; if (!run) { diff --git a/src/components/platform-entry/PlatformEntryFlowShellImpl.tsx b/src/components/platform-entry/PlatformEntryFlowShellImpl.tsx index 925c7d16..e6cd9574 100644 --- a/src/components/platform-entry/PlatformEntryFlowShellImpl.tsx +++ b/src/components/platform-entry/PlatformEntryFlowShellImpl.tsx @@ -46,8 +46,8 @@ import { streamBigFishCreationMessage, } from '../../services/big-fish-creation'; import { - startBigFishRuntimeRun, - submitBigFishRuntimeInput, + advanceLocalBigFishRuntimeRun, + startLocalBigFishRuntimeRun, } from '../../services/big-fish-runtime'; import { listBigFishGallery } from '../../services/big-fish-gallery'; import { @@ -415,7 +415,6 @@ export function PlatformEntryFlowShellImpl({ const [isBigFishLoadingLibrary, setIsBigFishLoadingLibrary] = useState(false); const [bigFishGenerationState, setBigFishGenerationState] = useState(null); - const bigFishInputInFlightRef = useRef(false); const [puzzleOperation, setPuzzleOperation] = useState(null); const [puzzleWorks, setPuzzleWorks] = useState([]); @@ -1105,59 +1104,25 @@ export function PlatformEntryFlowShellImpl({ } }, [bigFishRun, bigFishSession, selectionStage, setSelectionStage]); - const startBigFishRun = useCallback(async () => { - if (!bigFishSession || isBigFishBusy) { + const startBigFishRun = useCallback(() => { + if (!bigFishSession) { return; } - setIsBigFishBusy(true); setBigFishError(null); + setBigFishRun(startLocalBigFishRuntimeRun({ session: bigFishSession })); + setSelectionStage('big-fish-runtime'); + }, [bigFishSession, setSelectionStage]); - try { - const { run } = await startBigFishRuntimeRun(bigFishSession.sessionId); - setBigFishRun(run); - setSelectionStage('big-fish-runtime'); - } catch (error) { - setBigFishError( - resolveBigFishErrorMessage(error, '启动大鱼吃小鱼测试玩法失败。'), - ); - } finally { - setIsBigFishBusy(false); - } - }, [ - bigFishSession, - isBigFishBusy, - resolveBigFishErrorMessage, - setSelectionStage, - ]); - - const restartBigFishRun = useCallback(async () => { - const sessionId = bigFishSession?.sessionId ?? bigFishRun?.sessionId; - if (!sessionId || isBigFishBusy) { + const restartBigFishRun = useCallback(() => { + if (!bigFishSession && !bigFishRun) { return; } - setIsBigFishBusy(true); setBigFishError(null); - - try { - const { run } = await startBigFishRuntimeRun(sessionId); - setBigFishRun(run); - setSelectionStage('big-fish-runtime'); - } catch (error) { - setBigFishError( - resolveBigFishErrorMessage(error, '重新开始大鱼吃小鱼玩法失败。'), - ); - } finally { - setIsBigFishBusy(false); - } - }, [ - bigFishRun?.sessionId, - bigFishSession?.sessionId, - isBigFishBusy, - resolveBigFishErrorMessage, - setSelectionStage, - ]); + setBigFishRun(startLocalBigFishRuntimeRun({ session: bigFishSession })); + setSelectionStage('big-fish-runtime'); + }, [bigFishRun, bigFishSession, setSelectionStage]); const startPuzzleRunFromProfile = useCallback( async (profileId: string) => { @@ -1236,29 +1201,15 @@ export function PlatformEntryFlowShellImpl({ const submitBigFishInput = useCallback( (payload: SubmitBigFishInputRequest) => { - if ( - !bigFishRun || - bigFishRun.status !== 'running' || - bigFishInputInFlightRef.current - ) { + if (!bigFishRun || bigFishRun.status !== 'running') { return; } - bigFishInputInFlightRef.current = true; - void submitBigFishRuntimeInput(bigFishRun.runId, payload) - .then(({ run }) => { - setBigFishRun(run); - }) - .catch((error) => { - setBigFishError( - resolveBigFishErrorMessage(error, '同步大鱼吃小鱼输入失败。'), - ); - }) - .finally(() => { - bigFishInputInFlightRef.current = false; - }); + setBigFishRun((currentRun) => + currentRun ? advanceLocalBigFishRuntimeRun(currentRun, payload) : currentRun, + ); }, - [bigFishRun, resolveBigFishErrorMessage], + [bigFishRun], ); const swapPuzzlePiecesInRun = useCallback( @@ -1636,30 +1587,19 @@ export function PlatformEntryFlowShellImpl({ ); const startBigFishRunFromWork = useCallback( - async (item: BigFishWorkSummary) => { + (item: BigFishWorkSummary) => { const sessionId = item.sourceSessionId?.trim(); if (!sessionId) { setBigFishError('当前作品缺少会话信息,暂时无法进入玩法。'); return; } - setIsBigFishBusy(true); setBigFishError(null); - - try { - const { run } = await startBigFishRuntimeRun(sessionId); - bigFishFlow.setSession(null); - setBigFishRun(run); - setSelectionStage('big-fish-runtime'); - } catch (error) { - setBigFishError( - resolveBigFishErrorMessage(error, '启动大鱼吃小鱼玩法失败。'), - ); - } finally { - setIsBigFishBusy(false); - } + bigFishFlow.setSession(null); + setBigFishRun(startLocalBigFishRuntimeRun({ work: item })); + setSelectionStage('big-fish-runtime'); }, - [bigFishFlow, resolveBigFishErrorMessage, setSelectionStage], + [bigFishFlow, setSelectionStage], ); const handlePublicCodeSearch = useCallback( diff --git a/src/components/rpg-entry/RpgEntryFlowShell.agent.interaction.test.tsx b/src/components/rpg-entry/RpgEntryFlowShell.agent.interaction.test.tsx index a0c89cbf..e2ed05a1 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 { startBigFishRuntimeRun } from '../../services/big-fish-runtime'; +import { startLocalBigFishRuntimeRun } from '../../services/big-fish-runtime'; import { listBigFishWorks } from '../../services/big-fish-works'; import { createPuzzleAgentSession, @@ -153,8 +153,8 @@ vi.mock('../../services/big-fish-gallery', () => ({ })); vi.mock('../../services/big-fish-runtime', () => ({ - startBigFishRuntimeRun: vi.fn(), - submitBigFishRuntimeInput: vi.fn(), + advanceLocalBigFishRuntimeRun: vi.fn((run) => run), + startLocalBigFishRuntimeRun: vi.fn(), })); vi.mock('../../services/puzzle-agent', () => ({ @@ -1033,30 +1033,28 @@ beforeEach(() => { vi.mocked(listBigFishGallery).mockResolvedValue({ items: [], }); - vi.mocked(startBigFishRuntimeRun).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(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(listPuzzleWorks).mockResolvedValue({ items: [], @@ -1990,9 +1988,11 @@ test('public code search opens a published big fish work by BF code', async () = await user.click(screen.getByRole('button', { name: '搜索' })); await waitFor(() => { - expect(startBigFishRuntimeRun).toHaveBeenCalledWith( - 'big-fish-session-public-1', - ); + expect(startLocalBigFishRuntimeRun).toHaveBeenCalledWith({ + work: expect.objectContaining({ + sourceSessionId: 'big-fish-session-public-1', + }), + }); }); expect(await screen.findByText('Lv.1/8 · 进行中')).toBeTruthy(); expect(getBigFishCreationSession).not.toHaveBeenCalledWith( diff --git a/src/services/big-fish-runtime/bigFishLocalRuntime.ts b/src/services/big-fish-runtime/bigFishLocalRuntime.ts new file mode 100644 index 00000000..8ba019b8 --- /dev/null +++ b/src/services/big-fish-runtime/bigFishLocalRuntime.ts @@ -0,0 +1,395 @@ +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 deleted file mode 100644 index cbacd889..00000000 --- a/src/services/big-fish-runtime/bigFishRuntimeClient.ts +++ /dev/null @@ -1,68 +0,0 @@ -import type { - BigFishRunResponse, - SubmitBigFishInputRequest, -} from '../../../packages/shared/src/contracts/bigFish'; -import { type ApiRetryOptions, requestJson } from '../apiClient'; - -const BIG_FISH_RUNTIME_API_BASE = '/api/runtime/big-fish'; -const BIG_FISH_RUNTIME_READ_RETRY: ApiRetryOptions = { - maxRetries: 1, - baseDelayMs: 120, - maxDelayMs: 360, -}; -const BIG_FISH_RUNTIME_WRITE_RETRY: ApiRetryOptions = { - maxRetries: 1, - baseDelayMs: 120, - maxDelayMs: 360, - retryUnsafeMethods: true, -}; - -export async function startBigFishRuntimeRun(sessionId: string) { - return requestJson( - `${BIG_FISH_RUNTIME_API_BASE}/sessions/${encodeURIComponent(sessionId)}/runs`, - { - method: 'POST', - }, - '启动大鱼吃小鱼测试玩法失败', - { - retry: BIG_FISH_RUNTIME_WRITE_RETRY, - }, - ); -} - -export async function getBigFishRuntimeRun(runId: string) { - return requestJson( - `${BIG_FISH_RUNTIME_API_BASE}/runs/${encodeURIComponent(runId)}`, - { - method: 'GET', - }, - '读取大鱼吃小鱼运行快照失败', - { - retry: BIG_FISH_RUNTIME_READ_RETRY, - }, - ); -} - -export async function submitBigFishRuntimeInput( - runId: string, - payload: SubmitBigFishInputRequest, -) { - return requestJson( - `${BIG_FISH_RUNTIME_API_BASE}/runs/${encodeURIComponent(runId)}/input`, - { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify(payload), - }, - '提交大鱼吃小鱼移动输入失败', - { - retry: BIG_FISH_RUNTIME_WRITE_RETRY, - }, - ); -} - -export const bigFishRuntimeClient = { - startRun: startBigFishRuntimeRun, - getRun: getBigFishRuntimeRun, - submitInput: submitBigFishRuntimeInput, -}; diff --git a/src/services/big-fish-runtime/index.ts b/src/services/big-fish-runtime/index.ts index 8da02d2d..bf43b28c 100644 --- a/src/services/big-fish-runtime/index.ts +++ b/src/services/big-fish-runtime/index.ts @@ -1,6 +1,4 @@ export { - bigFishRuntimeClient, - getBigFishRuntimeRun, - startBigFishRuntimeRun, - submitBigFishRuntimeInput, -} from './bigFishRuntimeClient'; + advanceLocalBigFishRuntimeRun, + startLocalBigFishRuntimeRun, +} from './bigFishLocalRuntime';