644 lines
23 KiB
Rust
644 lines
23 KiB
Rust
use super::*;
|
|
|
|
impl From<module_runtime::CreationEntryTypeAdminUpsertInput> 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,
|
|
category_id: input.category_id,
|
|
category_label: input.category_label,
|
|
category_sort_order: input.category_sort_order,
|
|
unified_creation_spec_json: input.unified_creation_spec_json,
|
|
}
|
|
}
|
|
}
|
|
|
|
/// 将业务层 banner JSON 保存输入转换为 SpacetimeDB 生成绑定类型。
|
|
impl From<module_runtime::CreationEntryEventBannersAdminUpsertInput>
|
|
for CreationEntryEventBannersAdminUpsertInput
|
|
{
|
|
fn from(input: module_runtime::CreationEntryEventBannersAdminUpsertInput) -> Self {
|
|
Self {
|
|
event_banners_json: input.event_banners_json,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<module_runtime::AdminWorkVisibilityListInput> for AdminWorkVisibilityListInput {
|
|
fn from(input: module_runtime::AdminWorkVisibilityListInput) -> Self {
|
|
Self {
|
|
admin_user_id: input.admin_user_id,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<module_runtime::AdminWorkVisibilityUpdateInput> for AdminWorkVisibilityUpdateInput {
|
|
fn from(input: module_runtime::AdminWorkVisibilityUpdateInput) -> Self {
|
|
Self {
|
|
admin_user_id: input.admin_user_id,
|
|
source_type: input.source_type,
|
|
profile_id: input.profile_id,
|
|
visible: input.visible,
|
|
}
|
|
}
|
|
}
|
|
|
|
pub(crate) fn build_admin_work_visibility_list_input(
|
|
admin_user_id: String,
|
|
) -> Result<module_runtime::AdminWorkVisibilityListInput, String> {
|
|
let admin_user_id = admin_user_id.trim().to_string();
|
|
if admin_user_id.is_empty() {
|
|
return Err("adminUserId 不能为空".to_string());
|
|
}
|
|
Ok(module_runtime::AdminWorkVisibilityListInput { admin_user_id })
|
|
}
|
|
|
|
pub(crate) fn build_admin_work_visibility_update_input(
|
|
admin_user_id: String,
|
|
source_type: String,
|
|
profile_id: String,
|
|
visible: bool,
|
|
) -> Result<module_runtime::AdminWorkVisibilityUpdateInput, String> {
|
|
let admin_user_id = admin_user_id.trim().to_string();
|
|
if admin_user_id.is_empty() {
|
|
return Err("adminUserId 不能为空".to_string());
|
|
}
|
|
let source_type = source_type.trim().to_string();
|
|
if source_type.is_empty() {
|
|
return Err("sourceType 不能为空".to_string());
|
|
}
|
|
let profile_id = profile_id.trim().to_string();
|
|
if profile_id.is_empty() {
|
|
return Err("profileId 不能为空".to_string());
|
|
}
|
|
Ok(module_runtime::AdminWorkVisibilityUpdateInput {
|
|
admin_user_id,
|
|
source_type,
|
|
profile_id,
|
|
visible,
|
|
})
|
|
}
|
|
|
|
impl From<module_runtime::RuntimeSettingGetInput> for RuntimeSettingGetInput {
|
|
fn from(input: module_runtime::RuntimeSettingGetInput) -> Self {
|
|
Self {
|
|
user_id: input.user_id,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<module_runtime::RuntimeSettingUpsertInput> 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<module_runtime::RuntimeBrowseHistoryListInput> for RuntimeBrowseHistoryListInput {
|
|
fn from(input: module_runtime::RuntimeBrowseHistoryListInput) -> Self {
|
|
Self {
|
|
user_id: input.user_id,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<module_runtime::RuntimeBrowseHistoryClearInput> for RuntimeBrowseHistoryClearInput {
|
|
fn from(input: module_runtime::RuntimeBrowseHistoryClearInput) -> Self {
|
|
Self {
|
|
user_id: input.user_id,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<module_runtime::RuntimeBrowseHistorySyncInput> 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<module_runtime::RuntimeBrowseHistoryWriteInput> 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<module_runtime::RuntimeSnapshotGetInput> for RuntimeSnapshotGetInput {
|
|
fn from(input: module_runtime::RuntimeSnapshotGetInput) -> Self {
|
|
Self {
|
|
user_id: input.user_id,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<module_runtime::RuntimeSnapshotDeleteInput> for RuntimeSnapshotDeleteInput {
|
|
fn from(input: module_runtime::RuntimeSnapshotDeleteInput) -> Self {
|
|
Self {
|
|
user_id: input.user_id,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<module_runtime::RuntimeTrackingEventInput> 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 type AdminWorkVisibilityRecord = shared_contracts::admin::AdminWorkVisibilityEntryPayload;
|
|
|
|
pub(crate) fn map_creation_entry_config_procedure_result(
|
|
result: CreationEntryConfigProcedureResult,
|
|
) -> Result<CreationEntryConfigRecord, SpacetimeClientError> {
|
|
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 map_admin_work_visibility_list_procedure_result(
|
|
result: AdminWorkVisibilityListProcedureResult,
|
|
) -> Result<Vec<AdminWorkVisibilityRecord>, SpacetimeClientError> {
|
|
if !result.ok {
|
|
return Err(SpacetimeClientError::procedure_failed(result.error_message));
|
|
}
|
|
Ok(result
|
|
.entries
|
|
.into_iter()
|
|
.map(map_admin_work_visibility_snapshot)
|
|
.collect())
|
|
}
|
|
|
|
pub(crate) fn map_admin_work_visibility_procedure_result(
|
|
result: AdminWorkVisibilityProcedureResult,
|
|
) -> Result<AdminWorkVisibilityRecord, SpacetimeClientError> {
|
|
if !result.ok {
|
|
return Err(SpacetimeClientError::procedure_failed(result.error_message));
|
|
}
|
|
result
|
|
.record
|
|
.map(map_admin_work_visibility_snapshot)
|
|
.ok_or_else(|| SpacetimeClientError::missing_snapshot("后台作品可见性快照"))
|
|
}
|
|
|
|
fn map_admin_work_visibility_snapshot(
|
|
snapshot: AdminWorkVisibilitySnapshot,
|
|
) -> AdminWorkVisibilityRecord {
|
|
AdminWorkVisibilityRecord {
|
|
source_type: snapshot.source_type,
|
|
work_id: snapshot.work_id,
|
|
profile_id: snapshot.profile_id,
|
|
source_session_id: snapshot.source_session_id,
|
|
public_work_code: snapshot.public_work_code,
|
|
owner_user_id: snapshot.owner_user_id,
|
|
author_display_name: snapshot.author_display_name,
|
|
title: snapshot.title,
|
|
subtitle: snapshot.subtitle,
|
|
cover_image_src: snapshot.cover_image_src,
|
|
visible: snapshot.visible,
|
|
published_at_micros: snapshot.published_at_micros,
|
|
updated_at_micros: snapshot.updated_at_micros,
|
|
}
|
|
}
|
|
|
|
/// 从本地订阅表行组装创作入口配置响应,兼容旧单条 banner 字段。
|
|
pub(crate) fn build_creation_entry_config_record_from_rows(
|
|
header: CreationEntryConfig,
|
|
mut creation_types: Vec<CreationEntryTypeConfig>,
|
|
) -> 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,
|
|
},
|
|
event_banner: module_runtime::CreationEntryEventBannerSnapshot {
|
|
title: creation_entry_text_or_default(
|
|
header.event_title,
|
|
module_runtime::DEFAULT_CREATION_ENTRY_EVENT_TITLE,
|
|
),
|
|
description: creation_entry_text_or_default(
|
|
header.event_description,
|
|
module_runtime::DEFAULT_CREATION_ENTRY_EVENT_DESCRIPTION,
|
|
),
|
|
cover_image_src: creation_entry_text_or_default(
|
|
header.event_cover_image_src,
|
|
module_runtime::DEFAULT_CREATION_ENTRY_EVENT_COVER_IMAGE_SRC,
|
|
),
|
|
prize_pool_mud_points: header.event_prize_pool_mud_points,
|
|
starts_at_text: creation_entry_text_or_default(
|
|
header.event_starts_at_text,
|
|
module_runtime::DEFAULT_CREATION_ENTRY_EVENT_STARTS_AT_TEXT,
|
|
),
|
|
ends_at_text: creation_entry_text_or_default(
|
|
header.event_ends_at_text,
|
|
module_runtime::DEFAULT_CREATION_ENTRY_EVENT_ENDS_AT_TEXT,
|
|
),
|
|
render_mode: "structured".to_string(),
|
|
html_code: None,
|
|
},
|
|
event_banners_json: header.event_banners_json,
|
|
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,
|
|
category_id: creation_entry_text_or_default(
|
|
item.category_id,
|
|
module_runtime::DEFAULT_CREATION_ENTRY_CATEGORY_ID,
|
|
),
|
|
category_label: creation_entry_text_or_default(
|
|
item.category_label,
|
|
module_runtime::DEFAULT_CREATION_ENTRY_CATEGORY_LABEL,
|
|
),
|
|
category_sort_order: item.category_sort_order,
|
|
updated_at_micros: item.updated_at.to_micros_since_unix_epoch(),
|
|
unified_creation_spec_json: item.unified_creation_spec_json,
|
|
})
|
|
.collect(),
|
|
updated_at_micros: header.updated_at.to_micros_since_unix_epoch(),
|
|
},
|
|
)
|
|
}
|
|
|
|
/// 将 SpacetimeDB procedure 快照映射为 module-runtime 领域快照。
|
|
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,
|
|
},
|
|
event_banner: module_runtime::CreationEntryEventBannerSnapshot {
|
|
title: snapshot.event_banner.title,
|
|
description: snapshot.event_banner.description,
|
|
cover_image_src: snapshot.event_banner.cover_image_src,
|
|
prize_pool_mud_points: snapshot.event_banner.prize_pool_mud_points,
|
|
starts_at_text: snapshot.event_banner.starts_at_text,
|
|
ends_at_text: snapshot.event_banner.ends_at_text,
|
|
render_mode: snapshot.event_banner.render_mode,
|
|
html_code: snapshot.event_banner.html_code,
|
|
},
|
|
event_banners_json: snapshot.event_banners_json,
|
|
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,
|
|
category_id: item.category_id,
|
|
category_label: item.category_label,
|
|
category_sort_order: item.category_sort_order,
|
|
updated_at_micros: item.updated_at_micros,
|
|
unified_creation_spec_json: item.unified_creation_spec_json,
|
|
})
|
|
.collect(),
|
|
updated_at_micros: snapshot.updated_at_micros,
|
|
}
|
|
}
|
|
|
|
fn creation_entry_text_or_default(value: Option<String>, default_value: &str) -> String {
|
|
value
|
|
.map(|value| value.trim().to_string())
|
|
.filter(|value| !value.is_empty())
|
|
.unwrap_or_else(|| default_value.to_string())
|
|
}
|
|
|
|
pub(crate) fn map_runtime_setting_procedure_result(
|
|
result: RuntimeSettingProcedureResult,
|
|
) -> Result<RuntimeSettingsRecord, SpacetimeClientError> {
|
|
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<u32, SpacetimeClientError> {
|
|
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<Option<RuntimeSnapshotRecord>, 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<RuntimeSnapshotRecord, SpacetimeClientError> {
|
|
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<bool, SpacetimeClientError> {
|
|
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::Value, SpacetimeClientError> {
|
|
serde_json::from_str::<serde_json::Value>(value)
|
|
.map_err(|error| SpacetimeClientError::Runtime(format!("{label} 非法: {error}")))
|
|
}
|
|
|
|
pub(crate) fn parse_json_array(
|
|
value: &str,
|
|
label: &str,
|
|
) -> Result<Vec<serde_json::Value>, 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<Vec<String>, 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<Vec<CustomWorldSupportedActionRecord>, 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<u32>,
|
|
pub threat_spawn_delta_levels: Vec<u32>,
|
|
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<BigFishLevelBlueprintRecord>,
|
|
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<String>,
|
|
pub owned_entities: Vec<BigFishRuntimeEntityRecord>,
|
|
pub wild_entities: Vec<BigFishRuntimeEntityRecord>,
|
|
pub camera_center: BigFishVector2Record,
|
|
pub last_input: BigFishVector2Record,
|
|
pub event_log: Vec<String>,
|
|
pub updated_at: String,
|
|
}
|