Files
Genarrative/server-rs/crates/module-visual-novel/src/domain.rs
2026-05-08 11:44:42 +08:00

391 lines
12 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//! 视觉小说模板的纯领域模型。
//!
//! 本 crate 只负责草稿校验、运行时 step 解析、状态推进、历史重生成边界
//! 和平台统一存档状态构造HTTP、SpacetimeDB、LLM、OSS 均由外层 adapter 处理。
use serde::{Deserialize, Serialize};
#[cfg(feature = "spacetime-types")]
use spacetimedb::SpacetimeType;
use std::collections::BTreeMap;
pub const VISUAL_NOVEL_PROFILE_ID_PREFIX: &str = "vn-profile-";
pub const VISUAL_NOVEL_RUN_ID_PREFIX: &str = "vn-run-";
pub const VISUAL_NOVEL_HISTORY_ID_PREFIX: &str = "vn-history-";
pub const VISUAL_NOVEL_DEFAULT_MAX_HISTORY_ENTRIES: u32 = 80;
pub const VISUAL_NOVEL_DEFAULT_MAX_ASSISTANT_STEP_COUNT_PER_TURN: u32 = 8;
pub const VISUAL_NOVEL_MIN_INITIAL_CHOICE_COUNT: usize = 2;
pub const VISUAL_NOVEL_MAX_INITIAL_CHOICE_COUNT: usize = 4;
#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum VisualNovelSourceMode {
Idea,
Document,
Blank,
}
#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum VisualNovelCharacterRole {
Protagonist,
Main,
Supporting,
Antagonist,
Background,
}
#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum VisualNovelAssetSource {
PlatformAsset,
Generated,
External,
}
#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum VisualNovelSceneAvailability {
Opening,
Always,
PhaseLocked,
}
#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum VisualNovelAttributePanelMode {
Off,
PlatformWhitelist,
TemplateConfig,
}
#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum VisualNovelValidationSeverity {
Error,
Warning,
}
#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum VisualNovelRunMode {
Test,
Play,
}
#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum VisualNovelRunStatus {
Active,
Completed,
Failed,
}
#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum VisualNovelRuntimeActionKind {
Choice,
FreeText,
Continue,
}
#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum VisualNovelTransitionKind {
Fade,
Cut,
Flash,
None,
}
#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum VisualNovelHistorySource {
Player,
Assistant,
System,
}
#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))]
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(untagged)]
pub enum VisualNovelFlagValue {
String(String),
Number(f64),
Bool(bool),
}
#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))]
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct VisualNovelValidationIssue {
pub issue_id: String,
pub code: String,
pub severity: VisualNovelValidationSeverity,
pub path: String,
pub message: String,
}
#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))]
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct VisualNovelChoiceDraft {
pub choice_id: String,
pub text: String,
pub action_hint: Option<String>,
}
#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))]
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct VisualNovelCharacterImageAsset {
pub asset_id: String,
pub image_src: String,
pub expression: Option<String>,
pub source: VisualNovelAssetSource,
}
#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))]
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct VisualNovelWorldDraft {
pub title: String,
pub summary: String,
pub background: String,
pub premise: String,
pub literary_style: String,
pub player_role: String,
pub default_tone: String,
}
#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))]
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct VisualNovelCharacterDraft {
pub character_id: String,
pub name: String,
pub gender: Option<String>,
pub role: VisualNovelCharacterRole,
pub appearance: String,
pub personality: String,
pub tone: String,
pub background: String,
pub relationship_to_player: String,
pub image_assets: Vec<VisualNovelCharacterImageAsset>,
pub default_expression: Option<String>,
pub is_player_visible: bool,
}
#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))]
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct VisualNovelSceneDraft {
pub scene_id: String,
pub name: String,
pub description: String,
pub background_image_src: Option<String>,
pub music_src: Option<String>,
pub ambient_sound_src: Option<String>,
pub availability: VisualNovelSceneAvailability,
pub phase_ids: Vec<String>,
}
#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))]
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct VisualNovelStoryPhaseDraft {
pub phase_id: String,
pub title: String,
pub goal: String,
pub summary: String,
pub entry_condition: String,
pub exit_condition: String,
pub scene_ids: Vec<String>,
pub character_ids: Vec<String>,
pub suggested_choices: Vec<String>,
}
#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))]
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct VisualNovelOpeningDraft {
pub scene_id: Option<String>,
pub narration: String,
pub speaker_character_id: Option<String>,
pub first_dialogue: Option<String>,
pub initial_choices: Vec<VisualNovelChoiceDraft>,
}
#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))]
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct VisualNovelRuntimeConfigDraft {
pub text_mode_enabled: bool,
pub default_text_mode: bool,
pub max_history_entries: u32,
pub max_assistant_step_count_per_turn: u32,
pub allow_free_text_action: bool,
pub allow_history_regeneration: bool,
pub attribute_panel_mode: VisualNovelAttributePanelMode,
pub save_archive_enabled: bool,
}
#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))]
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct VisualNovelResultDraft {
pub profile_id: Option<String>,
pub work_title: String,
pub work_description: String,
pub work_tags: Vec<String>,
pub cover_image_src: Option<String>,
pub source_mode: VisualNovelSourceMode,
pub source_asset_ids: Vec<String>,
pub world: VisualNovelWorldDraft,
pub characters: Vec<VisualNovelCharacterDraft>,
pub scenes: Vec<VisualNovelSceneDraft>,
pub story_phases: Vec<VisualNovelStoryPhaseDraft>,
pub opening: VisualNovelOpeningDraft,
pub runtime_config: VisualNovelRuntimeConfigDraft,
pub publish_ready: bool,
pub validation_issues: Vec<VisualNovelValidationIssue>,
pub updated_at: String,
}
#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))]
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct VisualNovelWorkProfile {
pub profile_id: String,
pub work_title: String,
pub work_description: String,
pub work_tags: Vec<String>,
pub cover_image_src: Option<String>,
pub source_mode: VisualNovelSourceMode,
pub draft: VisualNovelResultDraft,
}
#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))]
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(
tag = "type",
rename_all = "snake_case",
rename_all_fields = "camelCase"
)]
pub enum VisualNovelRuntimeStep {
SceneChange {
scene_id: String,
background_image_src: Option<String>,
music_src: Option<String>,
},
Narration {
text: String,
},
Dialogue {
character_id: String,
character_name: String,
expression: Option<String>,
text: String,
},
Transition {
transition_kind: VisualNovelTransitionKind,
text: Option<String>,
},
Choice {
choices: Vec<VisualNovelChoiceDraft>,
},
Flag {
key: String,
value: VisualNovelFlagValue,
},
Metric {
key: String,
delta: f64,
},
}
#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))]
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct VisualNovelHistoryEntry {
pub entry_id: String,
pub run_id: String,
pub turn_index: u32,
pub source: VisualNovelHistorySource,
pub action_text: Option<String>,
pub steps: Vec<VisualNovelRuntimeStep>,
pub snapshot_before_hash: Option<String>,
pub snapshot_after_hash: Option<String>,
pub created_at: String,
}
#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))]
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct VisualNovelRunSnapshot {
pub run_id: String,
pub owner_user_id: String,
pub profile_id: String,
pub mode: VisualNovelRunMode,
pub status: VisualNovelRunStatus,
pub current_scene_id: Option<String>,
pub current_phase_id: Option<String>,
pub visible_character_ids: Vec<String>,
pub flags: BTreeMap<String, VisualNovelFlagValue>,
pub metrics: BTreeMap<String, f64>,
pub history: Vec<VisualNovelHistoryEntry>,
pub available_choices: Vec<VisualNovelChoiceDraft>,
pub text_mode_enabled: bool,
pub created_at: String,
pub updated_at: String,
}
#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))]
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct VisualNovelRuntimeAction {
pub action_kind: VisualNovelRuntimeActionKind,
pub choice_id: Option<String>,
pub text: Option<String>,
pub client_event_id: String,
}
#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))]
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct VisualNovelSaveArchiveState {
pub runtime_kind: String,
pub profile_id: String,
pub run_id: String,
pub current_scene_id: Option<String>,
pub current_phase_id: Option<String>,
pub history_cursor: u32,
pub snapshot_hash: Option<String>,
}
impl Default for VisualNovelRuntimeConfigDraft {
fn default() -> Self {
Self {
text_mode_enabled: true,
default_text_mode: false,
max_history_entries: VISUAL_NOVEL_DEFAULT_MAX_HISTORY_ENTRIES,
max_assistant_step_count_per_turn:
VISUAL_NOVEL_DEFAULT_MAX_ASSISTANT_STEP_COUNT_PER_TURN,
allow_free_text_action: true,
allow_history_regeneration: true,
attribute_panel_mode: VisualNovelAttributePanelMode::Off,
save_archive_enabled: true,
}
}
}