use super::*; impl From for CreationEntryTypeAdminUpsertInput { fn from(input: module_runtime::CreationEntryTypeAdminUpsertInput) -> Self { Self { id: input.id, title: input.title, subtitle: input.subtitle, badge: input.badge, image_src: input.image_src, visible: input.visible, open: input.open, sort_order: input.sort_order, } } } impl From for RuntimeSettingGetInput { fn from(input: module_runtime::RuntimeSettingGetInput) -> Self { Self { user_id: input.user_id, } } } impl From for RuntimeSettingUpsertInput { fn from(input: module_runtime::RuntimeSettingUpsertInput) -> Self { Self { user_id: input.user_id, music_volume: input.music_volume, platform_theme: map_runtime_platform_theme(input.platform_theme), updated_at_micros: input.updated_at_micros, } } } impl From for RuntimeBrowseHistoryListInput { fn from(input: module_runtime::RuntimeBrowseHistoryListInput) -> Self { Self { user_id: input.user_id, } } } impl From for RuntimeBrowseHistoryClearInput { fn from(input: module_runtime::RuntimeBrowseHistoryClearInput) -> Self { Self { user_id: input.user_id, } } } impl From for RuntimeBrowseHistorySyncInput { fn from(input: module_runtime::RuntimeBrowseHistorySyncInput) -> Self { Self { user_id: input.user_id, entries: input.entries.into_iter().map(Into::into).collect(), updated_at_micros: input.updated_at_micros, } } } impl From for RuntimeBrowseHistoryWriteInput { fn from(input: module_runtime::RuntimeBrowseHistoryWriteInput) -> Self { Self { owner_user_id: input.owner_user_id, profile_id: input.profile_id, world_name: input.world_name, subtitle: input.subtitle, summary_text: input.summary_text, cover_image_src: input.cover_image_src, theme_mode: input.theme_mode, author_display_name: input.author_display_name, visited_at: input.visited_at, } } } impl From for RuntimeSnapshotGetInput { fn from(input: module_runtime::RuntimeSnapshotGetInput) -> Self { Self { user_id: input.user_id, } } } impl From for RuntimeSnapshotDeleteInput { fn from(input: module_runtime::RuntimeSnapshotDeleteInput) -> Self { Self { user_id: input.user_id, } } } impl From for RuntimeTrackingEventInput { fn from(input: module_runtime::RuntimeTrackingEventInput) -> Self { Self { event_id: input.event_id, event_key: input.event_key, scope_kind: map_runtime_tracking_scope_kind(input.scope_kind), scope_id: input.scope_id, user_id: input.user_id, owner_user_id: input.owner_user_id, profile_id: input.profile_id, module_key: input.module_key, metadata_json: input.metadata_json, occurred_at_micros: input.occurred_at_micros, } } } pub type CreationEntryConfigRecord = shared_contracts::creation_entry_config::CreationEntryConfigResponse; pub(crate) fn map_creation_entry_config_procedure_result( result: CreationEntryConfigProcedureResult, ) -> Result { if !result.ok { return Err(SpacetimeClientError::procedure_failed(result.error_message)); } let snapshot = result .record .ok_or_else(|| SpacetimeClientError::missing_snapshot("创作入口配置快照"))?; Ok(module_runtime::build_creation_entry_config_response( map_creation_entry_config_snapshot(snapshot), )) } pub(crate) fn build_creation_entry_config_record_from_rows( header: CreationEntryConfig, mut creation_types: Vec, ) -> CreationEntryConfigRecord { creation_types.sort_by(|left, right| { left.sort_order .cmp(&right.sort_order) .then_with(|| left.id.cmp(&right.id)) }); module_runtime::build_creation_entry_config_response( module_runtime::CreationEntryConfigSnapshot { config_id: header.config_id, start_card: module_runtime::CreationEntryStartCardSnapshot { title: header.start_title, description: header.start_description, idle_badge: header.start_idle_badge, busy_badge: header.start_busy_badge, }, type_modal: module_runtime::CreationEntryTypeModalSnapshot { title: header.modal_title, description: header.modal_description, }, creation_types: creation_types .into_iter() .map(|item| module_runtime::CreationEntryTypeSnapshot { id: item.id, title: item.title, subtitle: item.subtitle, badge: item.badge, image_src: item.image_src, visible: item.visible, open: item.open, sort_order: item.sort_order, updated_at_micros: item.updated_at.to_micros_since_unix_epoch(), }) .collect(), updated_at_micros: header.updated_at.to_micros_since_unix_epoch(), }, ) } fn map_creation_entry_config_snapshot( snapshot: CreationEntryConfigSnapshot, ) -> module_runtime::CreationEntryConfigSnapshot { module_runtime::CreationEntryConfigSnapshot { config_id: snapshot.config_id, start_card: module_runtime::CreationEntryStartCardSnapshot { title: snapshot.start_card.title, description: snapshot.start_card.description, idle_badge: snapshot.start_card.idle_badge, busy_badge: snapshot.start_card.busy_badge, }, type_modal: module_runtime::CreationEntryTypeModalSnapshot { title: snapshot.type_modal.title, description: snapshot.type_modal.description, }, creation_types: snapshot .creation_types .into_iter() .map(|item| module_runtime::CreationEntryTypeSnapshot { id: item.id, title: item.title, subtitle: item.subtitle, badge: item.badge, image_src: item.image_src, visible: item.visible, open: item.open, sort_order: item.sort_order, updated_at_micros: item.updated_at_micros, }) .collect(), updated_at_micros: snapshot.updated_at_micros, } } pub(crate) fn map_runtime_setting_procedure_result( result: RuntimeSettingProcedureResult, ) -> Result { if !result.ok { return Err(SpacetimeClientError::procedure_failed(result.error_message)); } let snapshot = result .record .ok_or_else(|| SpacetimeClientError::missing_snapshot("runtime settings 快照"))?; Ok(build_runtime_setting_record(map_runtime_setting_snapshot( snapshot, ))) } pub(crate) fn map_runtime_tracking_event_procedure_result( result: RuntimeTrackingEventProcedureResult, ) -> Result<(), SpacetimeClientError> { if !result.ok { return Err(SpacetimeClientError::procedure_failed(result.error_message)); } Ok(()) } pub(crate) fn map_runtime_tracking_event_batch_procedure_result( result: RuntimeTrackingEventBatchProcedureResult, ) -> Result { if !result.ok { return Err(SpacetimeClientError::procedure_failed(result.error_message)); } Ok(result.accepted_count) } pub(crate) fn map_runtime_snapshot_procedure_result( result: RuntimeSnapshotProcedureResult, ) -> Result, SpacetimeClientError> { if !result.ok { return Err(SpacetimeClientError::procedure_failed(result.error_message)); } result .record .map(|snapshot| { build_runtime_snapshot_record(map_runtime_snapshot_snapshot(snapshot)) .map_err(|error| SpacetimeClientError::Runtime(error.to_string())) }) .transpose() } pub(crate) fn map_runtime_snapshot_required_procedure_result( result: RuntimeSnapshotProcedureResult, ) -> Result { map_runtime_snapshot_procedure_result(result)? .ok_or_else(|| SpacetimeClientError::missing_snapshot("runtime snapshot 快照")) } pub(crate) fn map_runtime_snapshot_delete_procedure_result( result: RuntimeSnapshotProcedureResult, ) -> Result { map_runtime_snapshot_procedure_result(result).map(|record| record.is_some()) } pub(crate) fn map_runtime_setting_snapshot( snapshot: RuntimeSettingSnapshot, ) -> module_runtime::RuntimeSettingSnapshot { module_runtime::RuntimeSettingSnapshot { user_id: snapshot.user_id, music_volume: snapshot.music_volume, platform_theme: map_runtime_platform_theme_back(snapshot.platform_theme), created_at_micros: snapshot.created_at_micros, updated_at_micros: snapshot.updated_at_micros, } } pub(crate) fn map_runtime_platform_theme( value: DomainRuntimePlatformTheme, ) -> crate::module_bindings::RuntimePlatformTheme { match value { DomainRuntimePlatformTheme::Light => crate::module_bindings::RuntimePlatformTheme::Light, DomainRuntimePlatformTheme::Dark => crate::module_bindings::RuntimePlatformTheme::Dark, } } pub(crate) fn map_runtime_platform_theme_back( value: crate::module_bindings::RuntimePlatformTheme, ) -> DomainRuntimePlatformTheme { match value { crate::module_bindings::RuntimePlatformTheme::Light => DomainRuntimePlatformTheme::Light, crate::module_bindings::RuntimePlatformTheme::Dark => DomainRuntimePlatformTheme::Dark, } } pub(crate) fn map_runtime_tracking_scope_kind( value: DomainRuntimeTrackingScopeKind, ) -> crate::module_bindings::RuntimeTrackingScopeKind { match value { DomainRuntimeTrackingScopeKind::Site => { crate::module_bindings::RuntimeTrackingScopeKind::Site } DomainRuntimeTrackingScopeKind::Work => { crate::module_bindings::RuntimeTrackingScopeKind::Work } DomainRuntimeTrackingScopeKind::Module => { crate::module_bindings::RuntimeTrackingScopeKind::Module } DomainRuntimeTrackingScopeKind::User => { crate::module_bindings::RuntimeTrackingScopeKind::User } } } pub(crate) fn map_runtime_tracking_scope_kind_back( value: crate::module_bindings::RuntimeTrackingScopeKind, ) -> DomainRuntimeTrackingScopeKind { match value { crate::module_bindings::RuntimeTrackingScopeKind::Site => { DomainRuntimeTrackingScopeKind::Site } crate::module_bindings::RuntimeTrackingScopeKind::Work => { DomainRuntimeTrackingScopeKind::Work } crate::module_bindings::RuntimeTrackingScopeKind::Module => { DomainRuntimeTrackingScopeKind::Module } crate::module_bindings::RuntimeTrackingScopeKind::User => { DomainRuntimeTrackingScopeKind::User } } } pub(crate) fn parse_json_value( value: &str, label: &str, ) -> Result { serde_json::from_str::(value) .map_err(|error| SpacetimeClientError::Runtime(format!("{label} 非法: {error}"))) } pub(crate) fn parse_json_array( value: &str, label: &str, ) -> Result, SpacetimeClientError> { match parse_json_value(value, label)? { serde_json::Value::Array(entries) => Ok(entries), _ => Err(SpacetimeClientError::Runtime(format!( "{label} 必须是 JSON array" ))), } } pub(crate) fn parse_json_string_array( value: &str, label: &str, ) -> Result, SpacetimeClientError> { parse_json_array(value, label)? .into_iter() .map(|entry| match entry { serde_json::Value::String(value) => Ok(value), _ => Err(SpacetimeClientError::Runtime(format!( "{label} 必须是 string array" ))), }) .collect() } pub(crate) fn parse_supported_actions_json( value: &str, ) -> Result, SpacetimeClientError> { parse_json_array(value, "custom world agent supported_actions_json")? .into_iter() .map(|entry| { let object = entry.as_object().ok_or_else(|| { SpacetimeClientError::Runtime( "custom world supported action 必须是 JSON object".to_string(), ) })?; let action = object .get("action") .and_then(serde_json::Value::as_str) .map(str::trim) .filter(|value| !value.is_empty()) .ok_or_else(|| { SpacetimeClientError::Runtime( "custom world supported action.action 缺失".to_string(), ) })?; let enabled = object .get("enabled") .and_then(serde_json::Value::as_bool) .ok_or_else(|| { SpacetimeClientError::Runtime( "custom world supported action.enabled 缺失".to_string(), ) })?; Ok(CustomWorldSupportedActionRecord { action: action.to_string(), enabled, reason: object .get("reason") .and_then(serde_json::Value::as_str) .map(str::trim) .filter(|value| !value.is_empty()) .map(ToOwned::to_owned), }) }) .collect() } #[derive(Clone, Debug, PartialEq)] pub struct BigFishRuntimeParamsRecord { pub level_count: u32, pub merge_count_per_upgrade: u32, pub spawn_target_count: u32, pub leader_move_speed: f32, pub follower_catch_up_speed: f32, pub offscreen_cull_seconds: f32, pub prey_spawn_delta_levels: Vec, pub threat_spawn_delta_levels: Vec, pub win_level: u32, } #[derive(Clone, Debug, PartialEq)] pub struct BigFishGameDraftRecord { pub title: String, pub subtitle: String, pub core_fun: String, pub ecology_theme: String, pub levels: Vec, pub background: BigFishBackgroundBlueprintRecord, pub runtime_params: BigFishRuntimeParamsRecord, } #[derive(Clone, Debug, PartialEq)] pub struct BigFishRuntimeEntityRecord { pub entity_id: String, pub level: u32, pub position: BigFishVector2Record, pub radius: f32, pub offscreen_seconds: f32, } #[derive(Clone, Debug, PartialEq)] pub struct BigFishRuntimeRunRecord { pub run_id: String, pub session_id: String, pub status: String, pub tick: u64, pub player_level: u32, pub win_level: u32, pub leader_entity_id: Option, pub owned_entities: Vec, pub wild_entities: Vec, pub camera_center: BigFishVector2Record, pub last_input: BigFishVector2Record, pub event_log: Vec, pub updated_at: String, }