feat: add puzzle clear template runtime
This commit is contained in:
@@ -19,6 +19,7 @@ pub mod match3d_runtime;
|
||||
pub mod match3d_works;
|
||||
pub mod public_work;
|
||||
pub mod puzzle_agent;
|
||||
pub mod puzzle_clear;
|
||||
pub mod puzzle_creative_template;
|
||||
pub mod puzzle_gallery;
|
||||
pub mod puzzle_runtime;
|
||||
|
||||
313
server-rs/crates/shared-contracts/src/puzzle_clear.rs
Normal file
313
server-rs/crates/shared-contracts/src/puzzle_clear.rs
Normal file
@@ -0,0 +1,313 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum PuzzleClearGenerationStatus {
|
||||
Draft,
|
||||
Generating,
|
||||
Ready,
|
||||
Failed,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
pub enum PuzzleClearActionType {
|
||||
CompileDraft,
|
||||
RegenerateAtlas,
|
||||
UpdateWorkMeta,
|
||||
UpdateBoardBackground,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum PuzzleClearRunStatus {
|
||||
Playing,
|
||||
LevelFailed,
|
||||
LevelCleared,
|
||||
Finished,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct PuzzleClearImageAsset {
|
||||
pub asset_id: String,
|
||||
pub image_src: String,
|
||||
pub image_object_key: String,
|
||||
pub asset_object_id: String,
|
||||
pub generation_provider: String,
|
||||
pub prompt: String,
|
||||
pub width: u32,
|
||||
pub height: u32,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct PuzzleClearPatternGroup {
|
||||
pub group_id: String,
|
||||
pub shape: String,
|
||||
pub width: u32,
|
||||
pub height: u32,
|
||||
pub atlas_x: u32,
|
||||
pub atlas_y: u32,
|
||||
pub atlas_width: u32,
|
||||
pub atlas_height: u32,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct PuzzleClearCardAsset {
|
||||
pub card_id: String,
|
||||
pub group_id: String,
|
||||
pub shape: String,
|
||||
pub orientation: String,
|
||||
pub part_x: u32,
|
||||
pub part_y: u32,
|
||||
pub image_src: String,
|
||||
pub image_object_key: String,
|
||||
pub asset_object_id: String,
|
||||
pub source_atlas_cell: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct PuzzleClearWorkspaceCreateRequest {
|
||||
pub template_id: String,
|
||||
pub work_title: String,
|
||||
pub work_description: String,
|
||||
pub theme_prompt: String,
|
||||
#[serde(default)]
|
||||
pub board_background_prompt: String,
|
||||
pub generate_board_background: bool,
|
||||
pub board_background_asset: Option<PuzzleClearImageAsset>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct PuzzleClearActionRequest {
|
||||
pub action_type: PuzzleClearActionType,
|
||||
pub profile_id: Option<String>,
|
||||
pub work_title: Option<String>,
|
||||
pub work_description: Option<String>,
|
||||
pub theme_prompt: Option<String>,
|
||||
#[serde(default)]
|
||||
pub board_background_prompt: Option<String>,
|
||||
pub generate_board_background: Option<bool>,
|
||||
pub board_background_asset: Option<PuzzleClearImageAsset>,
|
||||
pub atlas_asset: Option<PuzzleClearImageAsset>,
|
||||
pub pattern_groups: Option<Vec<PuzzleClearPatternGroup>>,
|
||||
pub card_assets: Option<Vec<PuzzleClearCardAsset>>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct PuzzleClearDraftResponse {
|
||||
pub template_id: String,
|
||||
pub template_name: String,
|
||||
pub profile_id: Option<String>,
|
||||
pub work_title: String,
|
||||
pub work_description: String,
|
||||
pub theme_prompt: String,
|
||||
#[serde(default)]
|
||||
pub board_background_prompt: String,
|
||||
pub generate_board_background: bool,
|
||||
pub board_background_asset: Option<PuzzleClearImageAsset>,
|
||||
pub card_back_image_src: Option<String>,
|
||||
pub atlas_asset: Option<PuzzleClearImageAsset>,
|
||||
pub pattern_groups: Vec<PuzzleClearPatternGroup>,
|
||||
pub card_assets: Vec<PuzzleClearCardAsset>,
|
||||
pub generation_status: PuzzleClearGenerationStatus,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct PuzzleClearSessionSnapshotResponse {
|
||||
pub session_id: String,
|
||||
pub owner_user_id: String,
|
||||
pub status: PuzzleClearGenerationStatus,
|
||||
pub draft: Option<PuzzleClearDraftResponse>,
|
||||
pub created_at: String,
|
||||
pub updated_at: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct PuzzleClearSessionResponse {
|
||||
pub session: PuzzleClearSessionSnapshotResponse,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct PuzzleClearActionResponse {
|
||||
pub action_type: PuzzleClearActionType,
|
||||
pub session: PuzzleClearSessionSnapshotResponse,
|
||||
pub work: Option<PuzzleClearWorkProfileResponse>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct PuzzleClearWorkSummaryResponse {
|
||||
pub runtime_kind: String,
|
||||
pub work_id: String,
|
||||
pub profile_id: String,
|
||||
pub owner_user_id: String,
|
||||
pub source_session_id: Option<String>,
|
||||
pub work_title: String,
|
||||
pub work_description: String,
|
||||
pub theme_prompt: String,
|
||||
pub cover_image_src: Option<String>,
|
||||
pub publication_status: String,
|
||||
pub play_count: u32,
|
||||
pub updated_at: String,
|
||||
pub published_at: Option<String>,
|
||||
pub publish_ready: bool,
|
||||
pub generation_status: PuzzleClearGenerationStatus,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct PuzzleClearWorkProfileResponse {
|
||||
pub summary: PuzzleClearWorkSummaryResponse,
|
||||
pub draft: PuzzleClearDraftResponse,
|
||||
pub board_background_asset: Option<PuzzleClearImageAsset>,
|
||||
pub atlas_asset: PuzzleClearImageAsset,
|
||||
pub pattern_groups: Vec<PuzzleClearPatternGroup>,
|
||||
pub card_assets: Vec<PuzzleClearCardAsset>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct PuzzleClearWorksResponse {
|
||||
pub items: Vec<PuzzleClearWorkSummaryResponse>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct PuzzleClearWorkDetailResponse {
|
||||
pub item: PuzzleClearWorkProfileResponse,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct PuzzleClearWorkMutationResponse {
|
||||
pub item: PuzzleClearWorkProfileResponse,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct PuzzleClearBoardCell {
|
||||
pub row: u32,
|
||||
pub col: u32,
|
||||
pub card: Option<PuzzleClearCardAsset>,
|
||||
pub locked_group_id: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct PuzzleClearBoardSnapshot {
|
||||
pub rows: u32,
|
||||
pub cols: u32,
|
||||
pub cells: Vec<PuzzleClearBoardCell>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct PuzzleClearRuntimeSnapshotResponse {
|
||||
pub run_id: String,
|
||||
pub profile_id: String,
|
||||
pub owner_user_id: String,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub runtime_mode: Option<String>,
|
||||
pub status: PuzzleClearRunStatus,
|
||||
pub level_index: u32,
|
||||
pub clears_done: u32,
|
||||
pub target_clears: u32,
|
||||
pub level_duration_seconds: u32,
|
||||
pub level_started_at_ms: u64,
|
||||
pub board: PuzzleClearBoardSnapshot,
|
||||
pub ready_columns: Vec<Vec<PuzzleClearCardAsset>>,
|
||||
pub started_at_ms: u64,
|
||||
pub finished_at_ms: Option<u64>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct PuzzleClearRunResponse {
|
||||
pub run: PuzzleClearRuntimeSnapshotResponse,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct PuzzleClearStartRunRequest {
|
||||
pub profile_id: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct PuzzleClearSwapRequest {
|
||||
pub from_row: u32,
|
||||
pub from_col: u32,
|
||||
pub to_row: u32,
|
||||
pub to_col: u32,
|
||||
pub client_action_id: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct PuzzleClearRetryLevelRequest {
|
||||
pub client_action_id: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct PuzzleClearNextLevelRequest {
|
||||
pub client_action_id: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct PuzzleClearTimeUpRequest {
|
||||
pub client_action_id: String,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use serde_json::json;
|
||||
|
||||
#[test]
|
||||
fn workspace_create_request_uses_camel_case() {
|
||||
let payload = PuzzleClearWorkspaceCreateRequest {
|
||||
template_id: "puzzle-clear".to_string(),
|
||||
work_title: "花园拼消消".to_string(),
|
||||
work_description: "轻松消除".to_string(),
|
||||
theme_prompt: "春日花园".to_string(),
|
||||
board_background_prompt: "樱花庭院".to_string(),
|
||||
generate_board_background: true,
|
||||
board_background_asset: None,
|
||||
};
|
||||
|
||||
let value = serde_json::to_value(payload).expect("request should serialize");
|
||||
|
||||
assert_eq!(value["templateId"], json!("puzzle-clear"));
|
||||
assert_eq!(value["themePrompt"], json!("春日花园"));
|
||||
assert_eq!(value["boardBackgroundPrompt"], json!("樱花庭院"));
|
||||
assert_eq!(value["generateBoardBackground"], json!(true));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn runtime_swap_request_uses_camel_case() {
|
||||
let payload = PuzzleClearSwapRequest {
|
||||
from_row: 1,
|
||||
from_col: 2,
|
||||
to_row: 1,
|
||||
to_col: 3,
|
||||
client_action_id: "swap-1".to_string(),
|
||||
};
|
||||
|
||||
let value = serde_json::to_value(payload).expect("request should serialize");
|
||||
|
||||
assert_eq!(value["fromRow"], json!(1));
|
||||
assert_eq!(value["toCol"], json!(3));
|
||||
assert_eq!(value["clientActionId"], json!("swap-1"));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user