Rewrite Genarrative play-type integration guidance across .codex and .hermes to define a platform-level SOP: default to form/image workbench, unify single-image asset slots (CreativeImageInputPanel), standardize series-material sheet->cut->transparent->OSS pipeline, and forbid copying legacy chat/agent workflows as the default. Add decision-log entry freezing the SOP and a pitfalls note warning against direct reuse of old play tools. Update CONTEXT.md and docs/README.md, add a new PRD file, and apply related small server-side changes (module-auth, spacetime-client mappers and runtime) to align back-end code with the new contracts and flows.
402 lines
11 KiB
Rust
402 lines
11 KiB
Rust
use serde::{Deserialize, Serialize};
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
|
#[serde(rename_all = "kebab-case")]
|
|
pub enum JumpHopDifficulty {
|
|
Easy,
|
|
Standard,
|
|
Advanced,
|
|
Challenge,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
|
#[serde(rename_all = "kebab-case")]
|
|
pub enum JumpHopStylePreset {
|
|
MinimalBlocks,
|
|
PaperToy,
|
|
NeonGlass,
|
|
ForestStone,
|
|
FutureMetal,
|
|
Custom,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
|
#[serde(rename_all = "kebab-case")]
|
|
pub enum JumpHopGenerationStatus {
|
|
Draft,
|
|
Generating,
|
|
Ready,
|
|
Failed,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
|
#[serde(rename_all = "kebab-case")]
|
|
pub enum JumpHopTileType {
|
|
Start,
|
|
Normal,
|
|
Target,
|
|
Finish,
|
|
Bonus,
|
|
Accent,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
|
#[serde(rename_all = "kebab-case")]
|
|
pub enum JumpHopActionType {
|
|
CompileDraft,
|
|
RegenerateCharacter,
|
|
RegenerateTiles,
|
|
UpdateWorkMeta,
|
|
UpdateDifficulty,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
|
#[serde(rename_all = "kebab-case")]
|
|
pub enum JumpHopRunStatus {
|
|
Playing,
|
|
Failed,
|
|
Cleared,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
|
#[serde(rename_all = "kebab-case")]
|
|
pub enum JumpHopJumpResult {
|
|
Miss,
|
|
Hit,
|
|
Perfect,
|
|
Finish,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct JumpHopWorkspaceCreateRequest {
|
|
pub template_id: String,
|
|
pub work_title: String,
|
|
pub work_description: String,
|
|
pub theme_tags: Vec<String>,
|
|
pub difficulty: JumpHopDifficulty,
|
|
pub style_preset: JumpHopStylePreset,
|
|
pub character_prompt: String,
|
|
pub tile_prompt: String,
|
|
#[serde(default)]
|
|
pub end_mood_prompt: Option<String>,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct JumpHopActionRequest {
|
|
pub action_type: JumpHopActionType,
|
|
#[serde(default)]
|
|
pub work_title: Option<String>,
|
|
#[serde(default)]
|
|
pub work_description: Option<String>,
|
|
#[serde(default)]
|
|
pub theme_tags: Option<Vec<String>>,
|
|
#[serde(default)]
|
|
pub difficulty: Option<JumpHopDifficulty>,
|
|
#[serde(default)]
|
|
pub style_preset: Option<JumpHopStylePreset>,
|
|
#[serde(default)]
|
|
pub character_prompt: Option<String>,
|
|
#[serde(default)]
|
|
pub tile_prompt: Option<String>,
|
|
#[serde(default)]
|
|
pub end_mood_prompt: Option<String>,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct JumpHopCharacterAsset {
|
|
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, Serialize, Deserialize, PartialEq)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct JumpHopTileAsset {
|
|
pub tile_type: JumpHopTileType,
|
|
pub image_src: String,
|
|
pub image_object_key: String,
|
|
pub asset_object_id: String,
|
|
pub source_atlas_cell: String,
|
|
pub visual_width: u32,
|
|
pub visual_height: u32,
|
|
pub top_surface_radius: f32,
|
|
pub landing_radius: f32,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct JumpHopScoring {
|
|
pub charge_to_distance_ratio: f32,
|
|
pub max_charge_ms: u32,
|
|
pub hit_bonus: u32,
|
|
pub perfect_bonus: u32,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct JumpHopPlatform {
|
|
pub platform_id: String,
|
|
pub tile_type: JumpHopTileType,
|
|
pub x: f32,
|
|
pub y: f32,
|
|
pub width: f32,
|
|
pub height: f32,
|
|
pub landing_radius: f32,
|
|
pub perfect_radius: f32,
|
|
pub score_value: u32,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct JumpHopPath {
|
|
pub seed: String,
|
|
pub difficulty: JumpHopDifficulty,
|
|
pub platforms: Vec<JumpHopPlatform>,
|
|
pub finish_index: u32,
|
|
pub camera_preset: String,
|
|
pub scoring: JumpHopScoring,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct JumpHopLastJump {
|
|
pub charge_ms: u32,
|
|
pub jump_distance: f32,
|
|
pub target_platform_index: u32,
|
|
pub landed_x: f32,
|
|
pub landed_y: f32,
|
|
pub result: JumpHopJumpResult,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct JumpHopDraftResponse {
|
|
pub template_id: String,
|
|
pub template_name: String,
|
|
#[serde(default)]
|
|
pub profile_id: Option<String>,
|
|
pub work_title: String,
|
|
pub work_description: String,
|
|
pub theme_tags: Vec<String>,
|
|
pub difficulty: JumpHopDifficulty,
|
|
pub style_preset: JumpHopStylePreset,
|
|
pub character_prompt: String,
|
|
pub tile_prompt: String,
|
|
#[serde(default)]
|
|
pub end_mood_prompt: Option<String>,
|
|
#[serde(default)]
|
|
pub character_asset: Option<JumpHopCharacterAsset>,
|
|
#[serde(default)]
|
|
pub tile_atlas_asset: Option<JumpHopCharacterAsset>,
|
|
#[serde(default)]
|
|
pub tile_assets: Vec<JumpHopTileAsset>,
|
|
#[serde(default)]
|
|
pub path: Option<JumpHopPath>,
|
|
#[serde(default)]
|
|
pub cover_composite: Option<String>,
|
|
pub generation_status: JumpHopGenerationStatus,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct JumpHopSessionSnapshotResponse {
|
|
pub session_id: String,
|
|
pub owner_user_id: String,
|
|
pub status: JumpHopGenerationStatus,
|
|
#[serde(default)]
|
|
pub draft: Option<JumpHopDraftResponse>,
|
|
pub created_at: String,
|
|
pub updated_at: String,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct JumpHopSessionResponse {
|
|
pub session: JumpHopSessionSnapshotResponse,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct JumpHopActionResponse {
|
|
pub action_type: JumpHopActionType,
|
|
pub session: JumpHopSessionSnapshotResponse,
|
|
#[serde(default)]
|
|
pub work: Option<JumpHopWorkProfileResponse>,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct JumpHopWorkSummaryResponse {
|
|
pub runtime_kind: String,
|
|
pub work_id: String,
|
|
pub profile_id: String,
|
|
pub owner_user_id: String,
|
|
#[serde(default)]
|
|
pub source_session_id: Option<String>,
|
|
pub work_title: String,
|
|
pub work_description: String,
|
|
pub theme_tags: Vec<String>,
|
|
pub difficulty: JumpHopDifficulty,
|
|
pub style_preset: JumpHopStylePreset,
|
|
#[serde(default)]
|
|
pub cover_image_src: Option<String>,
|
|
pub publication_status: String,
|
|
pub play_count: u32,
|
|
pub updated_at: String,
|
|
#[serde(default)]
|
|
pub published_at: Option<String>,
|
|
pub publish_ready: bool,
|
|
pub generation_status: JumpHopGenerationStatus,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct JumpHopWorkProfileResponse {
|
|
#[serde(flatten)]
|
|
pub summary: JumpHopWorkSummaryResponse,
|
|
pub draft: JumpHopDraftResponse,
|
|
pub path: JumpHopPath,
|
|
pub character_asset: JumpHopCharacterAsset,
|
|
pub tile_atlas_asset: JumpHopCharacterAsset,
|
|
pub tile_assets: Vec<JumpHopTileAsset>,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct JumpHopWorksResponse {
|
|
pub items: Vec<JumpHopWorkSummaryResponse>,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct JumpHopWorkDetailResponse {
|
|
pub item: JumpHopWorkProfileResponse,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct JumpHopWorkMutationResponse {
|
|
pub item: JumpHopWorkProfileResponse,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct JumpHopGalleryCardResponse {
|
|
pub public_work_code: String,
|
|
pub work_id: String,
|
|
pub profile_id: String,
|
|
pub owner_user_id: String,
|
|
pub author_display_name: String,
|
|
pub work_title: String,
|
|
pub work_description: String,
|
|
#[serde(default)]
|
|
pub cover_image_src: Option<String>,
|
|
pub theme_tags: Vec<String>,
|
|
pub difficulty: JumpHopDifficulty,
|
|
pub style_preset: JumpHopStylePreset,
|
|
pub publication_status: String,
|
|
pub play_count: u32,
|
|
pub updated_at: String,
|
|
#[serde(default)]
|
|
pub published_at: Option<String>,
|
|
pub generation_status: JumpHopGenerationStatus,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct JumpHopGalleryResponse {
|
|
pub items: Vec<JumpHopGalleryCardResponse>,
|
|
pub has_more: bool,
|
|
#[serde(default)]
|
|
pub next_cursor: Option<String>,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct JumpHopGalleryDetailResponse {
|
|
pub item: JumpHopWorkProfileResponse,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct JumpHopRuntimeRunSnapshotResponse {
|
|
pub run_id: String,
|
|
pub profile_id: String,
|
|
pub owner_user_id: String,
|
|
pub status: JumpHopRunStatus,
|
|
pub current_platform_index: u32,
|
|
pub score: u32,
|
|
pub combo: u32,
|
|
pub path: JumpHopPath,
|
|
#[serde(default)]
|
|
pub last_jump: Option<JumpHopLastJump>,
|
|
pub started_at_ms: u64,
|
|
#[serde(default)]
|
|
pub finished_at_ms: Option<u64>,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct JumpHopRunResponse {
|
|
pub run: JumpHopRuntimeRunSnapshotResponse,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct JumpHopStartRunRequest {
|
|
pub profile_id: String,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct JumpHopJumpRequest {
|
|
pub charge_ms: u32,
|
|
pub client_event_id: String,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct JumpHopRestartRunRequest {
|
|
pub client_action_id: String,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct JumpHopJumpResponse {
|
|
pub run: JumpHopRuntimeRunSnapshotResponse,
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
use serde_json::json;
|
|
|
|
#[test]
|
|
fn jump_hop_workspace_request_uses_camel_case() {
|
|
let payload = serde_json::to_value(JumpHopWorkspaceCreateRequest {
|
|
template_id: "jump-hop".to_string(),
|
|
work_title: "跳一跳".to_string(),
|
|
work_description: "俯视角跳跃闯关".to_string(),
|
|
theme_tags: vec!["休闲".to_string()],
|
|
difficulty: JumpHopDifficulty::Easy,
|
|
style_preset: JumpHopStylePreset::MinimalBlocks,
|
|
character_prompt: "角色".to_string(),
|
|
tile_prompt: "地块".to_string(),
|
|
end_mood_prompt: None,
|
|
})
|
|
.expect("payload should serialize");
|
|
|
|
assert_eq!(payload["templateId"], json!("jump-hop"));
|
|
assert_eq!(payload["difficulty"], json!("easy"));
|
|
assert_eq!(payload["stylePreset"], json!("minimal-blocks"));
|
|
}
|
|
}
|