diff --git a/crates/chattyness-db/src/queries.rs b/crates/chattyness-db/src/queries.rs index 1f7eeb6..47b6d7a 100644 --- a/crates/chattyness-db/src/queries.rs +++ b/crates/chattyness-db/src/queries.rs @@ -1,5 +1,6 @@ //! Database query modules. +pub mod avatar_common; pub mod avatars; pub mod channel_members; pub mod channels; diff --git a/crates/chattyness-db/src/queries/avatar_common.rs b/crates/chattyness-db/src/queries/avatar_common.rs new file mode 100644 index 0000000..97fa461 --- /dev/null +++ b/crates/chattyness-db/src/queries/avatar_common.rs @@ -0,0 +1,657 @@ +//! Common avatar query infrastructure. +//! +//! This module provides shared types and traits for server and realm avatars, +//! eliminating duplication between `server_avatars.rs` and `realm_avatars.rs`. + +use std::collections::HashMap; + +use sqlx::PgExecutor; +use uuid::Uuid; + +use crate::models::{AvatarRenderData, EmotionState}; +use chattyness_error::AppError; + +// ============================================================================= +// Avatar Slots - Array-based representation of avatar prop references +// ============================================================================= + +/// Number of positions per layer (0-8). +pub const LAYER_SIZE: usize = 9; + +/// Number of emotion types. +pub const EMOTION_COUNT: usize = 12; + +/// Array-based representation of avatar prop slots. +/// +/// This struct consolidates the 135 individual UUID fields into arrays, +/// making the code more maintainable and enabling iteration. +#[derive(Debug, Clone, Default)] +pub struct AvatarSlots { + /// Skin layer positions 0-8 (behind user, body/face). + pub skin: [Option; LAYER_SIZE], + /// Clothes layer positions 0-8 (with user, worn items). + pub clothes: [Option; LAYER_SIZE], + /// Accessories layer positions 0-8 (in front of user, held/attached items). + pub accessories: [Option; LAYER_SIZE], + /// Emotion layers: 12 emotions × 9 positions each. + /// Index by EmotionState ordinal: neutral=0, happy=1, sad=2, etc. + pub emotions: [[Option; LAYER_SIZE]; EMOTION_COUNT], +} + +impl AvatarSlots { + /// Get the emotion layer for a specific emotion state. + pub fn emotion_layer(&self, emotion: EmotionState) -> &[Option; LAYER_SIZE] { + &self.emotions[emotion.as_index()] + } + + /// Collect all non-null prop UUIDs from all layers. + pub fn collect_all_prop_ids(&self) -> Vec { + let mut ids = Vec::new(); + + // Content layers + for id in self.skin.iter().flatten() { + ids.push(*id); + } + for id in self.clothes.iter().flatten() { + ids.push(*id); + } + for id in self.accessories.iter().flatten() { + ids.push(*id); + } + + // All emotion layers + for emotion_layer in &self.emotions { + for id in emotion_layer.iter().flatten() { + ids.push(*id); + } + } + + ids + } + + /// Collect prop UUIDs for content layers + specific emotion. + pub fn collect_render_prop_ids(&self, emotion: EmotionState) -> Vec { + let mut ids = Vec::new(); + + // Content layers + for id in self.skin.iter().flatten() { + ids.push(*id); + } + for id in self.clothes.iter().flatten() { + ids.push(*id); + } + for id in self.accessories.iter().flatten() { + ids.push(*id); + } + + // Specific emotion layer + for id in self.emotion_layer(emotion).iter().flatten() { + ids.push(*id); + } + + ids + } +} + +/// Extension trait for EmotionState to get array indices. +pub trait EmotionIndex { + fn as_index(&self) -> usize; + fn from_index(index: usize) -> Option + where + Self: Sized; +} + +impl EmotionIndex for EmotionState { + fn as_index(&self) -> usize { + match self { + EmotionState::Neutral => 0, + EmotionState::Happy => 1, + EmotionState::Sad => 2, + EmotionState::Angry => 3, + EmotionState::Surprised => 4, + EmotionState::Thinking => 5, + EmotionState::Laughing => 6, + EmotionState::Crying => 7, + EmotionState::Love => 8, + EmotionState::Confused => 9, + EmotionState::Sleeping => 10, + EmotionState::Wink => 11, + } + } + + fn from_index(index: usize) -> Option { + match index { + 0 => Some(EmotionState::Neutral), + 1 => Some(EmotionState::Happy), + 2 => Some(EmotionState::Sad), + 3 => Some(EmotionState::Angry), + 4 => Some(EmotionState::Surprised), + 5 => Some(EmotionState::Thinking), + 6 => Some(EmotionState::Laughing), + 7 => Some(EmotionState::Crying), + 8 => Some(EmotionState::Love), + 9 => Some(EmotionState::Confused), + 10 => Some(EmotionState::Sleeping), + 11 => Some(EmotionState::Wink), + _ => None, + } + } +} + +// ============================================================================= +// Shared Row Types for Database Queries +// ============================================================================= + +/// Row type for avatar with resolved asset paths. +/// Used by both server and realm avatar queries. +#[derive(Debug, sqlx::FromRow)] +pub struct AvatarWithPathsRow { + pub id: Uuid, + pub slug: Option, // Server avatars have slug, realm might not in some queries + pub name: String, + pub description: Option, + // Skin layer paths + pub skin_0: Option, + pub skin_1: Option, + pub skin_2: Option, + pub skin_3: Option, + pub skin_4: Option, + pub skin_5: Option, + pub skin_6: Option, + pub skin_7: Option, + pub skin_8: Option, + // Clothes layer paths + pub clothes_0: Option, + pub clothes_1: Option, + pub clothes_2: Option, + pub clothes_3: Option, + pub clothes_4: Option, + pub clothes_5: Option, + pub clothes_6: Option, + pub clothes_7: Option, + pub clothes_8: Option, + // Accessories layer paths + pub accessories_0: Option, + pub accessories_1: Option, + pub accessories_2: Option, + pub accessories_3: Option, + pub accessories_4: Option, + pub accessories_5: Option, + pub accessories_6: Option, + pub accessories_7: Option, + pub accessories_8: Option, + // Happy emotion layer paths (e1 - for store display) + pub emotion_0: Option, + pub emotion_1: Option, + pub emotion_2: Option, + pub emotion_3: Option, + pub emotion_4: Option, + pub emotion_5: Option, + pub emotion_6: Option, + pub emotion_7: Option, + pub emotion_8: Option, +} + +impl AvatarWithPathsRow { + /// Extract skin layer as array. + pub fn skin_layer(&self) -> [Option; LAYER_SIZE] { + [ + self.skin_0.clone(), + self.skin_1.clone(), + self.skin_2.clone(), + self.skin_3.clone(), + self.skin_4.clone(), + self.skin_5.clone(), + self.skin_6.clone(), + self.skin_7.clone(), + self.skin_8.clone(), + ] + } + + /// Extract clothes layer as array. + pub fn clothes_layer(&self) -> [Option; LAYER_SIZE] { + [ + self.clothes_0.clone(), + self.clothes_1.clone(), + self.clothes_2.clone(), + self.clothes_3.clone(), + self.clothes_4.clone(), + self.clothes_5.clone(), + self.clothes_6.clone(), + self.clothes_7.clone(), + self.clothes_8.clone(), + ] + } + + /// Extract accessories layer as array. + pub fn accessories_layer(&self) -> [Option; LAYER_SIZE] { + [ + self.accessories_0.clone(), + self.accessories_1.clone(), + self.accessories_2.clone(), + self.accessories_3.clone(), + self.accessories_4.clone(), + self.accessories_5.clone(), + self.accessories_6.clone(), + self.accessories_7.clone(), + self.accessories_8.clone(), + ] + } + + /// Extract emotion layer as array. + pub fn emotion_layer(&self) -> [Option; LAYER_SIZE] { + [ + self.emotion_0.clone(), + self.emotion_1.clone(), + self.emotion_2.clone(), + self.emotion_3.clone(), + self.emotion_4.clone(), + self.emotion_5.clone(), + self.emotion_6.clone(), + self.emotion_7.clone(), + self.emotion_8.clone(), + ] + } +} + +/// Row type for prop asset lookup. +#[derive(Debug, sqlx::FromRow)] +pub struct PropAssetRow { + pub id: Uuid, + pub asset_path: String, +} + +// ============================================================================= +// Generic Query Helpers +// ============================================================================= + +/// Build a prop UUID to asset path lookup map. +pub async fn build_prop_map<'e>( + executor: impl PgExecutor<'e>, + prop_ids: &[Uuid], + props_table: &str, +) -> Result, AppError> { + if prop_ids.is_empty() { + return Ok(HashMap::new()); + } + + let query = format!( + r#" + SELECT id, asset_path + FROM {} + WHERE id = ANY($1) + "#, + props_table + ); + + let rows = sqlx::query_as::<_, PropAssetRow>(&query) + .bind(prop_ids) + .fetch_all(executor) + .await?; + + Ok(rows.into_iter().map(|r| (r.id, r.asset_path)).collect()) +} + +/// Resolve avatar slots to render data using a prop map. +pub fn resolve_slots_to_render_data( + avatar_id: Uuid, + slots: &AvatarSlots, + current_emotion: EmotionState, + prop_map: &HashMap, +) -> AvatarRenderData { + let get_path = |id: Option| -> Option { + id.and_then(|id| prop_map.get(&id).cloned()) + }; + + let emotion_layer = slots.emotion_layer(current_emotion); + + AvatarRenderData { + avatar_id, + current_emotion, + skin_layer: [ + get_path(slots.skin[0]), + get_path(slots.skin[1]), + get_path(slots.skin[2]), + get_path(slots.skin[3]), + get_path(slots.skin[4]), + get_path(slots.skin[5]), + get_path(slots.skin[6]), + get_path(slots.skin[7]), + get_path(slots.skin[8]), + ], + clothes_layer: [ + get_path(slots.clothes[0]), + get_path(slots.clothes[1]), + get_path(slots.clothes[2]), + get_path(slots.clothes[3]), + get_path(slots.clothes[4]), + get_path(slots.clothes[5]), + get_path(slots.clothes[6]), + get_path(slots.clothes[7]), + get_path(slots.clothes[8]), + ], + accessories_layer: [ + get_path(slots.accessories[0]), + get_path(slots.accessories[1]), + get_path(slots.accessories[2]), + get_path(slots.accessories[3]), + get_path(slots.accessories[4]), + get_path(slots.accessories[5]), + get_path(slots.accessories[6]), + get_path(slots.accessories[7]), + get_path(slots.accessories[8]), + ], + emotion_layer: [ + get_path(emotion_layer[0]), + get_path(emotion_layer[1]), + get_path(emotion_layer[2]), + get_path(emotion_layer[3]), + get_path(emotion_layer[4]), + get_path(emotion_layer[5]), + get_path(emotion_layer[6]), + get_path(emotion_layer[7]), + get_path(emotion_layer[8]), + ], + } +} + +// ============================================================================= +// SQL Generation Helpers +// ============================================================================= + +/// Generate the SELECT clause for avatar paths query. +/// Returns the column selections for joining props to get asset paths. +pub fn avatar_paths_select_clause() -> &'static str { + r#" + a.id, + a.name, + a.description, + -- Skin layer + p_skin_0.asset_path AS skin_0, + p_skin_1.asset_path AS skin_1, + p_skin_2.asset_path AS skin_2, + p_skin_3.asset_path AS skin_3, + p_skin_4.asset_path AS skin_4, + p_skin_5.asset_path AS skin_5, + p_skin_6.asset_path AS skin_6, + p_skin_7.asset_path AS skin_7, + p_skin_8.asset_path AS skin_8, + -- Clothes layer + p_clothes_0.asset_path AS clothes_0, + p_clothes_1.asset_path AS clothes_1, + p_clothes_2.asset_path AS clothes_2, + p_clothes_3.asset_path AS clothes_3, + p_clothes_4.asset_path AS clothes_4, + p_clothes_5.asset_path AS clothes_5, + p_clothes_6.asset_path AS clothes_6, + p_clothes_7.asset_path AS clothes_7, + p_clothes_8.asset_path AS clothes_8, + -- Accessories layer + p_acc_0.asset_path AS accessories_0, + p_acc_1.asset_path AS accessories_1, + p_acc_2.asset_path AS accessories_2, + p_acc_3.asset_path AS accessories_3, + p_acc_4.asset_path AS accessories_4, + p_acc_5.asset_path AS accessories_5, + p_acc_6.asset_path AS accessories_6, + p_acc_7.asset_path AS accessories_7, + p_acc_8.asset_path AS accessories_8, + -- Happy emotion layer (e1 - more inviting for store display) + p_emo_0.asset_path AS emotion_0, + p_emo_1.asset_path AS emotion_1, + p_emo_2.asset_path AS emotion_2, + p_emo_3.asset_path AS emotion_3, + p_emo_4.asset_path AS emotion_4, + p_emo_5.asset_path AS emotion_5, + p_emo_6.asset_path AS emotion_6, + p_emo_7.asset_path AS emotion_7, + p_emo_8.asset_path AS emotion_8 + "# +} + +/// Generate the JOIN clause for avatar paths query. +/// `props_table` should be "server.props" or "realm.props". +pub fn avatar_paths_join_clause(props_table: &str) -> String { + format!( + r#" + -- Skin layer joins + LEFT JOIN {props} p_skin_0 ON a.l_skin_0 = p_skin_0.id + LEFT JOIN {props} p_skin_1 ON a.l_skin_1 = p_skin_1.id + LEFT JOIN {props} p_skin_2 ON a.l_skin_2 = p_skin_2.id + LEFT JOIN {props} p_skin_3 ON a.l_skin_3 = p_skin_3.id + LEFT JOIN {props} p_skin_4 ON a.l_skin_4 = p_skin_4.id + LEFT JOIN {props} p_skin_5 ON a.l_skin_5 = p_skin_5.id + LEFT JOIN {props} p_skin_6 ON a.l_skin_6 = p_skin_6.id + LEFT JOIN {props} p_skin_7 ON a.l_skin_7 = p_skin_7.id + LEFT JOIN {props} p_skin_8 ON a.l_skin_8 = p_skin_8.id + -- Clothes layer joins + LEFT JOIN {props} p_clothes_0 ON a.l_clothes_0 = p_clothes_0.id + LEFT JOIN {props} p_clothes_1 ON a.l_clothes_1 = p_clothes_1.id + LEFT JOIN {props} p_clothes_2 ON a.l_clothes_2 = p_clothes_2.id + LEFT JOIN {props} p_clothes_3 ON a.l_clothes_3 = p_clothes_3.id + LEFT JOIN {props} p_clothes_4 ON a.l_clothes_4 = p_clothes_4.id + LEFT JOIN {props} p_clothes_5 ON a.l_clothes_5 = p_clothes_5.id + LEFT JOIN {props} p_clothes_6 ON a.l_clothes_6 = p_clothes_6.id + LEFT JOIN {props} p_clothes_7 ON a.l_clothes_7 = p_clothes_7.id + LEFT JOIN {props} p_clothes_8 ON a.l_clothes_8 = p_clothes_8.id + -- Accessories layer joins + LEFT JOIN {props} p_acc_0 ON a.l_accessories_0 = p_acc_0.id + LEFT JOIN {props} p_acc_1 ON a.l_accessories_1 = p_acc_1.id + LEFT JOIN {props} p_acc_2 ON a.l_accessories_2 = p_acc_2.id + LEFT JOIN {props} p_acc_3 ON a.l_accessories_3 = p_acc_3.id + LEFT JOIN {props} p_acc_4 ON a.l_accessories_4 = p_acc_4.id + LEFT JOIN {props} p_acc_5 ON a.l_accessories_5 = p_acc_5.id + LEFT JOIN {props} p_acc_6 ON a.l_accessories_6 = p_acc_6.id + LEFT JOIN {props} p_acc_7 ON a.l_accessories_7 = p_acc_7.id + LEFT JOIN {props} p_acc_8 ON a.l_accessories_8 = p_acc_8.id + -- Happy emotion layer joins (e1 - more inviting for store display) + LEFT JOIN {props} p_emo_0 ON a.e_happy_0 = p_emo_0.id + LEFT JOIN {props} p_emo_1 ON a.e_happy_1 = p_emo_1.id + LEFT JOIN {props} p_emo_2 ON a.e_happy_2 = p_emo_2.id + LEFT JOIN {props} p_emo_3 ON a.e_happy_3 = p_emo_3.id + LEFT JOIN {props} p_emo_4 ON a.e_happy_4 = p_emo_4.id + LEFT JOIN {props} p_emo_5 ON a.e_happy_5 = p_emo_5.id + LEFT JOIN {props} p_emo_6 ON a.e_happy_6 = p_emo_6.id + LEFT JOIN {props} p_emo_7 ON a.e_happy_7 = p_emo_7.id + LEFT JOIN {props} p_emo_8 ON a.e_happy_8 = p_emo_8.id + "#, + props = props_table + ) +} + +// ============================================================================= +// Macros for Avatar Slot Extraction +// ============================================================================= + +/// Extract AvatarSlots from an avatar struct with the standard field naming. +/// Works with both ServerAvatar and RealmAvatar. +#[macro_export] +macro_rules! extract_avatar_slots { + ($avatar:expr) => {{ + use $crate::queries::avatar_common::AvatarSlots; + + AvatarSlots { + skin: [ + $avatar.l_skin_0, + $avatar.l_skin_1, + $avatar.l_skin_2, + $avatar.l_skin_3, + $avatar.l_skin_4, + $avatar.l_skin_5, + $avatar.l_skin_6, + $avatar.l_skin_7, + $avatar.l_skin_8, + ], + clothes: [ + $avatar.l_clothes_0, + $avatar.l_clothes_1, + $avatar.l_clothes_2, + $avatar.l_clothes_3, + $avatar.l_clothes_4, + $avatar.l_clothes_5, + $avatar.l_clothes_6, + $avatar.l_clothes_7, + $avatar.l_clothes_8, + ], + accessories: [ + $avatar.l_accessories_0, + $avatar.l_accessories_1, + $avatar.l_accessories_2, + $avatar.l_accessories_3, + $avatar.l_accessories_4, + $avatar.l_accessories_5, + $avatar.l_accessories_6, + $avatar.l_accessories_7, + $avatar.l_accessories_8, + ], + emotions: [ + // Neutral (e0) + [ + $avatar.e_neutral_0, + $avatar.e_neutral_1, + $avatar.e_neutral_2, + $avatar.e_neutral_3, + $avatar.e_neutral_4, + $avatar.e_neutral_5, + $avatar.e_neutral_6, + $avatar.e_neutral_7, + $avatar.e_neutral_8, + ], + // Happy (e1) + [ + $avatar.e_happy_0, + $avatar.e_happy_1, + $avatar.e_happy_2, + $avatar.e_happy_3, + $avatar.e_happy_4, + $avatar.e_happy_5, + $avatar.e_happy_6, + $avatar.e_happy_7, + $avatar.e_happy_8, + ], + // Sad (e2) + [ + $avatar.e_sad_0, + $avatar.e_sad_1, + $avatar.e_sad_2, + $avatar.e_sad_3, + $avatar.e_sad_4, + $avatar.e_sad_5, + $avatar.e_sad_6, + $avatar.e_sad_7, + $avatar.e_sad_8, + ], + // Angry (e3) + [ + $avatar.e_angry_0, + $avatar.e_angry_1, + $avatar.e_angry_2, + $avatar.e_angry_3, + $avatar.e_angry_4, + $avatar.e_angry_5, + $avatar.e_angry_6, + $avatar.e_angry_7, + $avatar.e_angry_8, + ], + // Surprised (e4) + [ + $avatar.e_surprised_0, + $avatar.e_surprised_1, + $avatar.e_surprised_2, + $avatar.e_surprised_3, + $avatar.e_surprised_4, + $avatar.e_surprised_5, + $avatar.e_surprised_6, + $avatar.e_surprised_7, + $avatar.e_surprised_8, + ], + // Thinking (e5) + [ + $avatar.e_thinking_0, + $avatar.e_thinking_1, + $avatar.e_thinking_2, + $avatar.e_thinking_3, + $avatar.e_thinking_4, + $avatar.e_thinking_5, + $avatar.e_thinking_6, + $avatar.e_thinking_7, + $avatar.e_thinking_8, + ], + // Laughing (e6) + [ + $avatar.e_laughing_0, + $avatar.e_laughing_1, + $avatar.e_laughing_2, + $avatar.e_laughing_3, + $avatar.e_laughing_4, + $avatar.e_laughing_5, + $avatar.e_laughing_6, + $avatar.e_laughing_7, + $avatar.e_laughing_8, + ], + // Crying (e7) + [ + $avatar.e_crying_0, + $avatar.e_crying_1, + $avatar.e_crying_2, + $avatar.e_crying_3, + $avatar.e_crying_4, + $avatar.e_crying_5, + $avatar.e_crying_6, + $avatar.e_crying_7, + $avatar.e_crying_8, + ], + // Love (e8) + [ + $avatar.e_love_0, + $avatar.e_love_1, + $avatar.e_love_2, + $avatar.e_love_3, + $avatar.e_love_4, + $avatar.e_love_5, + $avatar.e_love_6, + $avatar.e_love_7, + $avatar.e_love_8, + ], + // Confused (e9) + [ + $avatar.e_confused_0, + $avatar.e_confused_1, + $avatar.e_confused_2, + $avatar.e_confused_3, + $avatar.e_confused_4, + $avatar.e_confused_5, + $avatar.e_confused_6, + $avatar.e_confused_7, + $avatar.e_confused_8, + ], + // Sleeping (e10) + [ + $avatar.e_sleeping_0, + $avatar.e_sleeping_1, + $avatar.e_sleeping_2, + $avatar.e_sleeping_3, + $avatar.e_sleeping_4, + $avatar.e_sleeping_5, + $avatar.e_sleeping_6, + $avatar.e_sleeping_7, + $avatar.e_sleeping_8, + ], + // Wink (e11) + [ + $avatar.e_wink_0, + $avatar.e_wink_1, + $avatar.e_wink_2, + $avatar.e_wink_3, + $avatar.e_wink_4, + $avatar.e_wink_5, + $avatar.e_wink_6, + $avatar.e_wink_7, + $avatar.e_wink_8, + ], + ], + } + }}; +} + +pub use extract_avatar_slots; diff --git a/crates/chattyness-db/src/queries/realm_avatars.rs b/crates/chattyness-db/src/queries/realm_avatars.rs index 359d227..95c1a8a 100644 --- a/crates/chattyness-db/src/queries/realm_avatars.rs +++ b/crates/chattyness-db/src/queries/realm_avatars.rs @@ -3,15 +3,22 @@ //! Realm avatars are pre-configured avatar configurations specific to a realm. //! They reference realm.props directly (not inventory items). -use std::collections::HashMap; - use chrono::{Duration, Utc}; use sqlx::PgExecutor; use uuid::Uuid; -use crate::models::{AvatarRenderData, EmotionState, RealmAvatar}; +use crate::extract_avatar_slots; +use crate::models::{AvatarRenderData, EmotionState, RealmAvatar, RealmAvatarWithPaths}; +use crate::queries::avatar_common::{ + avatar_paths_join_clause, avatar_paths_select_clause, build_prop_map, + resolve_slots_to_render_data, +}; use chattyness_error::AppError; +// ============================================================================= +// Basic Queries +// ============================================================================= + /// Get a realm avatar by slug within a realm. pub async fn get_realm_avatar_by_slug<'e>( executor: impl PgExecutor<'e>, @@ -72,7 +79,9 @@ pub async fn list_public_realm_avatars<'e>( Ok(avatars) } -use crate::models::RealmAvatarWithPaths; +// ============================================================================= +// Avatar with Paths Queries +// ============================================================================= /// Row type for realm avatar with paths query. #[derive(Debug, sqlx::FromRow)] @@ -110,7 +119,7 @@ struct RealmAvatarWithPathsRow { accessories_6: Option, accessories_7: Option, accessories_8: Option, - // Happy emotion layer paths (e1 - more inviting for store display) + // Happy emotion layer paths emotion_0: Option, emotion_1: Option, emotion_2: Option, @@ -153,236 +162,52 @@ impl From for RealmAvatarWithPaths { } /// List all active public realm avatars with resolved asset paths. -/// -/// Joins with the props table to resolve prop UUIDs to asset paths, -/// suitable for client-side rendering without additional lookups. pub async fn list_public_realm_avatars_with_paths<'e>( executor: impl PgExecutor<'e>, realm_id: Uuid, ) -> Result, AppError> { - let rows = sqlx::query_as::<_, RealmAvatarWithPathsRow>( + let join_clause = avatar_paths_join_clause("realm.props"); + let query = format!( r#" SELECT - a.id, - a.name, - a.description, - -- Skin layer - p_skin_0.asset_path AS skin_0, - p_skin_1.asset_path AS skin_1, - p_skin_2.asset_path AS skin_2, - p_skin_3.asset_path AS skin_3, - p_skin_4.asset_path AS skin_4, - p_skin_5.asset_path AS skin_5, - p_skin_6.asset_path AS skin_6, - p_skin_7.asset_path AS skin_7, - p_skin_8.asset_path AS skin_8, - -- Clothes layer - p_clothes_0.asset_path AS clothes_0, - p_clothes_1.asset_path AS clothes_1, - p_clothes_2.asset_path AS clothes_2, - p_clothes_3.asset_path AS clothes_3, - p_clothes_4.asset_path AS clothes_4, - p_clothes_5.asset_path AS clothes_5, - p_clothes_6.asset_path AS clothes_6, - p_clothes_7.asset_path AS clothes_7, - p_clothes_8.asset_path AS clothes_8, - -- Accessories layer - p_acc_0.asset_path AS accessories_0, - p_acc_1.asset_path AS accessories_1, - p_acc_2.asset_path AS accessories_2, - p_acc_3.asset_path AS accessories_3, - p_acc_4.asset_path AS accessories_4, - p_acc_5.asset_path AS accessories_5, - p_acc_6.asset_path AS accessories_6, - p_acc_7.asset_path AS accessories_7, - p_acc_8.asset_path AS accessories_8, - -- Happy emotion layer (e1 - more inviting for store display) - p_emo_0.asset_path AS emotion_0, - p_emo_1.asset_path AS emotion_1, - p_emo_2.asset_path AS emotion_2, - p_emo_3.asset_path AS emotion_3, - p_emo_4.asset_path AS emotion_4, - p_emo_5.asset_path AS emotion_5, - p_emo_6.asset_path AS emotion_6, - p_emo_7.asset_path AS emotion_7, - p_emo_8.asset_path AS emotion_8 + {} FROM realm.avatars a - -- Skin layer joins - LEFT JOIN realm.props p_skin_0 ON a.l_skin_0 = p_skin_0.id - LEFT JOIN realm.props p_skin_1 ON a.l_skin_1 = p_skin_1.id - LEFT JOIN realm.props p_skin_2 ON a.l_skin_2 = p_skin_2.id - LEFT JOIN realm.props p_skin_3 ON a.l_skin_3 = p_skin_3.id - LEFT JOIN realm.props p_skin_4 ON a.l_skin_4 = p_skin_4.id - LEFT JOIN realm.props p_skin_5 ON a.l_skin_5 = p_skin_5.id - LEFT JOIN realm.props p_skin_6 ON a.l_skin_6 = p_skin_6.id - LEFT JOIN realm.props p_skin_7 ON a.l_skin_7 = p_skin_7.id - LEFT JOIN realm.props p_skin_8 ON a.l_skin_8 = p_skin_8.id - -- Clothes layer joins - LEFT JOIN realm.props p_clothes_0 ON a.l_clothes_0 = p_clothes_0.id - LEFT JOIN realm.props p_clothes_1 ON a.l_clothes_1 = p_clothes_1.id - LEFT JOIN realm.props p_clothes_2 ON a.l_clothes_2 = p_clothes_2.id - LEFT JOIN realm.props p_clothes_3 ON a.l_clothes_3 = p_clothes_3.id - LEFT JOIN realm.props p_clothes_4 ON a.l_clothes_4 = p_clothes_4.id - LEFT JOIN realm.props p_clothes_5 ON a.l_clothes_5 = p_clothes_5.id - LEFT JOIN realm.props p_clothes_6 ON a.l_clothes_6 = p_clothes_6.id - LEFT JOIN realm.props p_clothes_7 ON a.l_clothes_7 = p_clothes_7.id - LEFT JOIN realm.props p_clothes_8 ON a.l_clothes_8 = p_clothes_8.id - -- Accessories layer joins - LEFT JOIN realm.props p_acc_0 ON a.l_accessories_0 = p_acc_0.id - LEFT JOIN realm.props p_acc_1 ON a.l_accessories_1 = p_acc_1.id - LEFT JOIN realm.props p_acc_2 ON a.l_accessories_2 = p_acc_2.id - LEFT JOIN realm.props p_acc_3 ON a.l_accessories_3 = p_acc_3.id - LEFT JOIN realm.props p_acc_4 ON a.l_accessories_4 = p_acc_4.id - LEFT JOIN realm.props p_acc_5 ON a.l_accessories_5 = p_acc_5.id - LEFT JOIN realm.props p_acc_6 ON a.l_accessories_6 = p_acc_6.id - LEFT JOIN realm.props p_acc_7 ON a.l_accessories_7 = p_acc_7.id - LEFT JOIN realm.props p_acc_8 ON a.l_accessories_8 = p_acc_8.id - -- Happy emotion layer joins (e1 - more inviting for store display) - LEFT JOIN realm.props p_emo_0 ON a.e_happy_0 = p_emo_0.id - LEFT JOIN realm.props p_emo_1 ON a.e_happy_1 = p_emo_1.id - LEFT JOIN realm.props p_emo_2 ON a.e_happy_2 = p_emo_2.id - LEFT JOIN realm.props p_emo_3 ON a.e_happy_3 = p_emo_3.id - LEFT JOIN realm.props p_emo_4 ON a.e_happy_4 = p_emo_4.id - LEFT JOIN realm.props p_emo_5 ON a.e_happy_5 = p_emo_5.id - LEFT JOIN realm.props p_emo_6 ON a.e_happy_6 = p_emo_6.id - LEFT JOIN realm.props p_emo_7 ON a.e_happy_7 = p_emo_7.id - LEFT JOIN realm.props p_emo_8 ON a.e_happy_8 = p_emo_8.id + {} WHERE a.realm_id = $1 AND a.is_active = true AND a.is_public = true ORDER BY a.name ASC "#, - ) - .bind(realm_id) - .fetch_all(executor) - .await?; + avatar_paths_select_clause(), + join_clause + ); + + let rows = sqlx::query_as::<_, RealmAvatarWithPathsRow>(&query) + .bind(realm_id) + .fetch_all(executor) + .await?; Ok(rows.into_iter().map(RealmAvatarWithPaths::from).collect()) } -/// Row type for prop asset lookup. -#[derive(Debug, sqlx::FromRow)] -struct PropAssetRow { - id: Uuid, - asset_path: String, -} +// ============================================================================= +// Render Data Resolution +// ============================================================================= /// Resolve a realm avatar to render data. -/// Joins the avatar's prop UUIDs with realm.props to get asset paths. pub async fn resolve_realm_avatar_to_render_data<'e>( executor: impl PgExecutor<'e>, avatar: &RealmAvatar, current_emotion: EmotionState, ) -> Result { - // Collect all non-null prop UUIDs - let mut prop_ids: Vec = Vec::new(); - - // Content layers - for id in [ - avatar.l_skin_0, avatar.l_skin_1, avatar.l_skin_2, - avatar.l_skin_3, avatar.l_skin_4, avatar.l_skin_5, - avatar.l_skin_6, avatar.l_skin_7, avatar.l_skin_8, - avatar.l_clothes_0, avatar.l_clothes_1, avatar.l_clothes_2, - avatar.l_clothes_3, avatar.l_clothes_4, avatar.l_clothes_5, - avatar.l_clothes_6, avatar.l_clothes_7, avatar.l_clothes_8, - avatar.l_accessories_0, avatar.l_accessories_1, avatar.l_accessories_2, - avatar.l_accessories_3, avatar.l_accessories_4, avatar.l_accessories_5, - avatar.l_accessories_6, avatar.l_accessories_7, avatar.l_accessories_8, - ].iter().flatten() { - prop_ids.push(*id); - } - - // Get emotion layer slots based on current emotion - let emotion_slots: [Option; 9] = match current_emotion { - EmotionState::Neutral => [avatar.e_neutral_0, avatar.e_neutral_1, avatar.e_neutral_2, - avatar.e_neutral_3, avatar.e_neutral_4, avatar.e_neutral_5, - avatar.e_neutral_6, avatar.e_neutral_7, avatar.e_neutral_8], - EmotionState::Happy => [avatar.e_happy_0, avatar.e_happy_1, avatar.e_happy_2, - avatar.e_happy_3, avatar.e_happy_4, avatar.e_happy_5, - avatar.e_happy_6, avatar.e_happy_7, avatar.e_happy_8], - EmotionState::Sad => [avatar.e_sad_0, avatar.e_sad_1, avatar.e_sad_2, - avatar.e_sad_3, avatar.e_sad_4, avatar.e_sad_5, - avatar.e_sad_6, avatar.e_sad_7, avatar.e_sad_8], - EmotionState::Angry => [avatar.e_angry_0, avatar.e_angry_1, avatar.e_angry_2, - avatar.e_angry_3, avatar.e_angry_4, avatar.e_angry_5, - avatar.e_angry_6, avatar.e_angry_7, avatar.e_angry_8], - EmotionState::Surprised => [avatar.e_surprised_0, avatar.e_surprised_1, avatar.e_surprised_2, - avatar.e_surprised_3, avatar.e_surprised_4, avatar.e_surprised_5, - avatar.e_surprised_6, avatar.e_surprised_7, avatar.e_surprised_8], - EmotionState::Thinking => [avatar.e_thinking_0, avatar.e_thinking_1, avatar.e_thinking_2, - avatar.e_thinking_3, avatar.e_thinking_4, avatar.e_thinking_5, - avatar.e_thinking_6, avatar.e_thinking_7, avatar.e_thinking_8], - EmotionState::Laughing => [avatar.e_laughing_0, avatar.e_laughing_1, avatar.e_laughing_2, - avatar.e_laughing_3, avatar.e_laughing_4, avatar.e_laughing_5, - avatar.e_laughing_6, avatar.e_laughing_7, avatar.e_laughing_8], - EmotionState::Crying => [avatar.e_crying_0, avatar.e_crying_1, avatar.e_crying_2, - avatar.e_crying_3, avatar.e_crying_4, avatar.e_crying_5, - avatar.e_crying_6, avatar.e_crying_7, avatar.e_crying_8], - EmotionState::Love => [avatar.e_love_0, avatar.e_love_1, avatar.e_love_2, - avatar.e_love_3, avatar.e_love_4, avatar.e_love_5, - avatar.e_love_6, avatar.e_love_7, avatar.e_love_8], - EmotionState::Confused => [avatar.e_confused_0, avatar.e_confused_1, avatar.e_confused_2, - avatar.e_confused_3, avatar.e_confused_4, avatar.e_confused_5, - avatar.e_confused_6, avatar.e_confused_7, avatar.e_confused_8], - EmotionState::Sleeping => [avatar.e_sleeping_0, avatar.e_sleeping_1, avatar.e_sleeping_2, - avatar.e_sleeping_3, avatar.e_sleeping_4, avatar.e_sleeping_5, - avatar.e_sleeping_6, avatar.e_sleeping_7, avatar.e_sleeping_8], - EmotionState::Wink => [avatar.e_wink_0, avatar.e_wink_1, avatar.e_wink_2, - avatar.e_wink_3, avatar.e_wink_4, avatar.e_wink_5, - avatar.e_wink_6, avatar.e_wink_7, avatar.e_wink_8], - }; - - for id in emotion_slots.iter().flatten() { - prop_ids.push(*id); - } - - // Bulk lookup all prop asset paths from realm.props - let prop_map: HashMap = if prop_ids.is_empty() { - HashMap::new() - } else { - let rows = sqlx::query_as::<_, PropAssetRow>( - r#" - SELECT id, asset_path - FROM realm.props - WHERE id = ANY($1) - "#, - ) - .bind(&prop_ids) - .fetch_all(executor) - .await?; - - rows.into_iter().map(|r| (r.id, r.asset_path)).collect() - }; - - // Helper to look up path - let get_path = |id: Option| -> Option { - id.and_then(|id| prop_map.get(&id).cloned()) - }; - - Ok(AvatarRenderData { - avatar_id: avatar.id, - current_emotion, - skin_layer: [ - get_path(avatar.l_skin_0), get_path(avatar.l_skin_1), get_path(avatar.l_skin_2), - get_path(avatar.l_skin_3), get_path(avatar.l_skin_4), get_path(avatar.l_skin_5), - get_path(avatar.l_skin_6), get_path(avatar.l_skin_7), get_path(avatar.l_skin_8), - ], - clothes_layer: [ - get_path(avatar.l_clothes_0), get_path(avatar.l_clothes_1), get_path(avatar.l_clothes_2), - get_path(avatar.l_clothes_3), get_path(avatar.l_clothes_4), get_path(avatar.l_clothes_5), - get_path(avatar.l_clothes_6), get_path(avatar.l_clothes_7), get_path(avatar.l_clothes_8), - ], - accessories_layer: [ - get_path(avatar.l_accessories_0), get_path(avatar.l_accessories_1), get_path(avatar.l_accessories_2), - get_path(avatar.l_accessories_3), get_path(avatar.l_accessories_4), get_path(avatar.l_accessories_5), - get_path(avatar.l_accessories_6), get_path(avatar.l_accessories_7), get_path(avatar.l_accessories_8), - ], - emotion_layer: [ - get_path(emotion_slots[0]), get_path(emotion_slots[1]), get_path(emotion_slots[2]), - get_path(emotion_slots[3]), get_path(emotion_slots[4]), get_path(emotion_slots[5]), - get_path(emotion_slots[6]), get_path(emotion_slots[7]), get_path(emotion_slots[8]), - ], - }) + let slots = extract_avatar_slots!(avatar); + let prop_ids = slots.collect_render_prop_ids(current_emotion); + let prop_map = build_prop_map(executor, &prop_ids, "realm.props").await?; + Ok(resolve_slots_to_render_data(avatar.id, &slots, current_emotion, &prop_map)) } +// ============================================================================= +// Forced Avatar Management +// ============================================================================= + /// Apply a forced realm avatar to a user. pub async fn apply_forced_realm_avatar<'e>( executor: impl PgExecutor<'e>, @@ -576,155 +401,65 @@ pub async fn create_realm_avatar<'e>( .bind(&req.thumbnail_path) .bind(created_by) // Skin layer - .bind(req.l_skin_0) - .bind(req.l_skin_1) - .bind(req.l_skin_2) - .bind(req.l_skin_3) - .bind(req.l_skin_4) - .bind(req.l_skin_5) - .bind(req.l_skin_6) - .bind(req.l_skin_7) - .bind(req.l_skin_8) + .bind(req.l_skin_0).bind(req.l_skin_1).bind(req.l_skin_2) + .bind(req.l_skin_3).bind(req.l_skin_4).bind(req.l_skin_5) + .bind(req.l_skin_6).bind(req.l_skin_7).bind(req.l_skin_8) // Clothes layer - .bind(req.l_clothes_0) - .bind(req.l_clothes_1) - .bind(req.l_clothes_2) - .bind(req.l_clothes_3) - .bind(req.l_clothes_4) - .bind(req.l_clothes_5) - .bind(req.l_clothes_6) - .bind(req.l_clothes_7) - .bind(req.l_clothes_8) + .bind(req.l_clothes_0).bind(req.l_clothes_1).bind(req.l_clothes_2) + .bind(req.l_clothes_3).bind(req.l_clothes_4).bind(req.l_clothes_5) + .bind(req.l_clothes_6).bind(req.l_clothes_7).bind(req.l_clothes_8) // Accessories layer - .bind(req.l_accessories_0) - .bind(req.l_accessories_1) - .bind(req.l_accessories_2) - .bind(req.l_accessories_3) - .bind(req.l_accessories_4) - .bind(req.l_accessories_5) - .bind(req.l_accessories_6) - .bind(req.l_accessories_7) - .bind(req.l_accessories_8) + .bind(req.l_accessories_0).bind(req.l_accessories_1).bind(req.l_accessories_2) + .bind(req.l_accessories_3).bind(req.l_accessories_4).bind(req.l_accessories_5) + .bind(req.l_accessories_6).bind(req.l_accessories_7).bind(req.l_accessories_8) // Neutral emotion - .bind(req.e_neutral_0) - .bind(req.e_neutral_1) - .bind(req.e_neutral_2) - .bind(req.e_neutral_3) - .bind(req.e_neutral_4) - .bind(req.e_neutral_5) - .bind(req.e_neutral_6) - .bind(req.e_neutral_7) - .bind(req.e_neutral_8) + .bind(req.e_neutral_0).bind(req.e_neutral_1).bind(req.e_neutral_2) + .bind(req.e_neutral_3).bind(req.e_neutral_4).bind(req.e_neutral_5) + .bind(req.e_neutral_6).bind(req.e_neutral_7).bind(req.e_neutral_8) // Happy emotion - .bind(req.e_happy_0) - .bind(req.e_happy_1) - .bind(req.e_happy_2) - .bind(req.e_happy_3) - .bind(req.e_happy_4) - .bind(req.e_happy_5) - .bind(req.e_happy_6) - .bind(req.e_happy_7) - .bind(req.e_happy_8) + .bind(req.e_happy_0).bind(req.e_happy_1).bind(req.e_happy_2) + .bind(req.e_happy_3).bind(req.e_happy_4).bind(req.e_happy_5) + .bind(req.e_happy_6).bind(req.e_happy_7).bind(req.e_happy_8) // Sad emotion - .bind(req.e_sad_0) - .bind(req.e_sad_1) - .bind(req.e_sad_2) - .bind(req.e_sad_3) - .bind(req.e_sad_4) - .bind(req.e_sad_5) - .bind(req.e_sad_6) - .bind(req.e_sad_7) - .bind(req.e_sad_8) + .bind(req.e_sad_0).bind(req.e_sad_1).bind(req.e_sad_2) + .bind(req.e_sad_3).bind(req.e_sad_4).bind(req.e_sad_5) + .bind(req.e_sad_6).bind(req.e_sad_7).bind(req.e_sad_8) // Angry emotion - .bind(req.e_angry_0) - .bind(req.e_angry_1) - .bind(req.e_angry_2) - .bind(req.e_angry_3) - .bind(req.e_angry_4) - .bind(req.e_angry_5) - .bind(req.e_angry_6) - .bind(req.e_angry_7) - .bind(req.e_angry_8) + .bind(req.e_angry_0).bind(req.e_angry_1).bind(req.e_angry_2) + .bind(req.e_angry_3).bind(req.e_angry_4).bind(req.e_angry_5) + .bind(req.e_angry_6).bind(req.e_angry_7).bind(req.e_angry_8) // Surprised emotion - .bind(req.e_surprised_0) - .bind(req.e_surprised_1) - .bind(req.e_surprised_2) - .bind(req.e_surprised_3) - .bind(req.e_surprised_4) - .bind(req.e_surprised_5) - .bind(req.e_surprised_6) - .bind(req.e_surprised_7) - .bind(req.e_surprised_8) + .bind(req.e_surprised_0).bind(req.e_surprised_1).bind(req.e_surprised_2) + .bind(req.e_surprised_3).bind(req.e_surprised_4).bind(req.e_surprised_5) + .bind(req.e_surprised_6).bind(req.e_surprised_7).bind(req.e_surprised_8) // Thinking emotion - .bind(req.e_thinking_0) - .bind(req.e_thinking_1) - .bind(req.e_thinking_2) - .bind(req.e_thinking_3) - .bind(req.e_thinking_4) - .bind(req.e_thinking_5) - .bind(req.e_thinking_6) - .bind(req.e_thinking_7) - .bind(req.e_thinking_8) + .bind(req.e_thinking_0).bind(req.e_thinking_1).bind(req.e_thinking_2) + .bind(req.e_thinking_3).bind(req.e_thinking_4).bind(req.e_thinking_5) + .bind(req.e_thinking_6).bind(req.e_thinking_7).bind(req.e_thinking_8) // Laughing emotion - .bind(req.e_laughing_0) - .bind(req.e_laughing_1) - .bind(req.e_laughing_2) - .bind(req.e_laughing_3) - .bind(req.e_laughing_4) - .bind(req.e_laughing_5) - .bind(req.e_laughing_6) - .bind(req.e_laughing_7) - .bind(req.e_laughing_8) + .bind(req.e_laughing_0).bind(req.e_laughing_1).bind(req.e_laughing_2) + .bind(req.e_laughing_3).bind(req.e_laughing_4).bind(req.e_laughing_5) + .bind(req.e_laughing_6).bind(req.e_laughing_7).bind(req.e_laughing_8) // Crying emotion - .bind(req.e_crying_0) - .bind(req.e_crying_1) - .bind(req.e_crying_2) - .bind(req.e_crying_3) - .bind(req.e_crying_4) - .bind(req.e_crying_5) - .bind(req.e_crying_6) - .bind(req.e_crying_7) - .bind(req.e_crying_8) + .bind(req.e_crying_0).bind(req.e_crying_1).bind(req.e_crying_2) + .bind(req.e_crying_3).bind(req.e_crying_4).bind(req.e_crying_5) + .bind(req.e_crying_6).bind(req.e_crying_7).bind(req.e_crying_8) // Love emotion - .bind(req.e_love_0) - .bind(req.e_love_1) - .bind(req.e_love_2) - .bind(req.e_love_3) - .bind(req.e_love_4) - .bind(req.e_love_5) - .bind(req.e_love_6) - .bind(req.e_love_7) - .bind(req.e_love_8) + .bind(req.e_love_0).bind(req.e_love_1).bind(req.e_love_2) + .bind(req.e_love_3).bind(req.e_love_4).bind(req.e_love_5) + .bind(req.e_love_6).bind(req.e_love_7).bind(req.e_love_8) // Confused emotion - .bind(req.e_confused_0) - .bind(req.e_confused_1) - .bind(req.e_confused_2) - .bind(req.e_confused_3) - .bind(req.e_confused_4) - .bind(req.e_confused_5) - .bind(req.e_confused_6) - .bind(req.e_confused_7) - .bind(req.e_confused_8) + .bind(req.e_confused_0).bind(req.e_confused_1).bind(req.e_confused_2) + .bind(req.e_confused_3).bind(req.e_confused_4).bind(req.e_confused_5) + .bind(req.e_confused_6).bind(req.e_confused_7).bind(req.e_confused_8) // Sleeping emotion - .bind(req.e_sleeping_0) - .bind(req.e_sleeping_1) - .bind(req.e_sleeping_2) - .bind(req.e_sleeping_3) - .bind(req.e_sleeping_4) - .bind(req.e_sleeping_5) - .bind(req.e_sleeping_6) - .bind(req.e_sleeping_7) - .bind(req.e_sleeping_8) + .bind(req.e_sleeping_0).bind(req.e_sleeping_1).bind(req.e_sleeping_2) + .bind(req.e_sleeping_3).bind(req.e_sleeping_4).bind(req.e_sleeping_5) + .bind(req.e_sleeping_6).bind(req.e_sleeping_7).bind(req.e_sleeping_8) // Wink emotion - .bind(req.e_wink_0) - .bind(req.e_wink_1) - .bind(req.e_wink_2) - .bind(req.e_wink_3) - .bind(req.e_wink_4) - .bind(req.e_wink_5) - .bind(req.e_wink_6) - .bind(req.e_wink_7) - .bind(req.e_wink_8) + .bind(req.e_wink_0).bind(req.e_wink_1).bind(req.e_wink_2) + .bind(req.e_wink_3).bind(req.e_wink_4).bind(req.e_wink_5) + .bind(req.e_wink_6).bind(req.e_wink_7).bind(req.e_wink_8) .fetch_one(executor) .await?; @@ -745,141 +480,51 @@ pub async fn update_realm_avatar<'e>( is_public = COALESCE($4, is_public), is_active = COALESCE($5, is_active), thumbnail_path = COALESCE($6, thumbnail_path), - l_skin_0 = COALESCE($7, l_skin_0), - l_skin_1 = COALESCE($8, l_skin_1), - l_skin_2 = COALESCE($9, l_skin_2), - l_skin_3 = COALESCE($10, l_skin_3), - l_skin_4 = COALESCE($11, l_skin_4), - l_skin_5 = COALESCE($12, l_skin_5), - l_skin_6 = COALESCE($13, l_skin_6), - l_skin_7 = COALESCE($14, l_skin_7), - l_skin_8 = COALESCE($15, l_skin_8), - l_clothes_0 = COALESCE($16, l_clothes_0), - l_clothes_1 = COALESCE($17, l_clothes_1), - l_clothes_2 = COALESCE($18, l_clothes_2), - l_clothes_3 = COALESCE($19, l_clothes_3), - l_clothes_4 = COALESCE($20, l_clothes_4), - l_clothes_5 = COALESCE($21, l_clothes_5), - l_clothes_6 = COALESCE($22, l_clothes_6), - l_clothes_7 = COALESCE($23, l_clothes_7), - l_clothes_8 = COALESCE($24, l_clothes_8), - l_accessories_0 = COALESCE($25, l_accessories_0), - l_accessories_1 = COALESCE($26, l_accessories_1), - l_accessories_2 = COALESCE($27, l_accessories_2), - l_accessories_3 = COALESCE($28, l_accessories_3), - l_accessories_4 = COALESCE($29, l_accessories_4), - l_accessories_5 = COALESCE($30, l_accessories_5), - l_accessories_6 = COALESCE($31, l_accessories_6), - l_accessories_7 = COALESCE($32, l_accessories_7), - l_accessories_8 = COALESCE($33, l_accessories_8), - e_neutral_0 = COALESCE($34, e_neutral_0), - e_neutral_1 = COALESCE($35, e_neutral_1), - e_neutral_2 = COALESCE($36, e_neutral_2), - e_neutral_3 = COALESCE($37, e_neutral_3), - e_neutral_4 = COALESCE($38, e_neutral_4), - e_neutral_5 = COALESCE($39, e_neutral_5), - e_neutral_6 = COALESCE($40, e_neutral_6), - e_neutral_7 = COALESCE($41, e_neutral_7), - e_neutral_8 = COALESCE($42, e_neutral_8), - e_happy_0 = COALESCE($43, e_happy_0), - e_happy_1 = COALESCE($44, e_happy_1), - e_happy_2 = COALESCE($45, e_happy_2), - e_happy_3 = COALESCE($46, e_happy_3), - e_happy_4 = COALESCE($47, e_happy_4), - e_happy_5 = COALESCE($48, e_happy_5), - e_happy_6 = COALESCE($49, e_happy_6), - e_happy_7 = COALESCE($50, e_happy_7), - e_happy_8 = COALESCE($51, e_happy_8), - e_sad_0 = COALESCE($52, e_sad_0), - e_sad_1 = COALESCE($53, e_sad_1), - e_sad_2 = COALESCE($54, e_sad_2), - e_sad_3 = COALESCE($55, e_sad_3), - e_sad_4 = COALESCE($56, e_sad_4), - e_sad_5 = COALESCE($57, e_sad_5), - e_sad_6 = COALESCE($58, e_sad_6), - e_sad_7 = COALESCE($59, e_sad_7), - e_sad_8 = COALESCE($60, e_sad_8), - e_angry_0 = COALESCE($61, e_angry_0), - e_angry_1 = COALESCE($62, e_angry_1), - e_angry_2 = COALESCE($63, e_angry_2), - e_angry_3 = COALESCE($64, e_angry_3), - e_angry_4 = COALESCE($65, e_angry_4), - e_angry_5 = COALESCE($66, e_angry_5), - e_angry_6 = COALESCE($67, e_angry_6), - e_angry_7 = COALESCE($68, e_angry_7), - e_angry_8 = COALESCE($69, e_angry_8), - e_surprised_0 = COALESCE($70, e_surprised_0), - e_surprised_1 = COALESCE($71, e_surprised_1), - e_surprised_2 = COALESCE($72, e_surprised_2), - e_surprised_3 = COALESCE($73, e_surprised_3), - e_surprised_4 = COALESCE($74, e_surprised_4), - e_surprised_5 = COALESCE($75, e_surprised_5), - e_surprised_6 = COALESCE($76, e_surprised_6), - e_surprised_7 = COALESCE($77, e_surprised_7), - e_surprised_8 = COALESCE($78, e_surprised_8), - e_thinking_0 = COALESCE($79, e_thinking_0), - e_thinking_1 = COALESCE($80, e_thinking_1), - e_thinking_2 = COALESCE($81, e_thinking_2), - e_thinking_3 = COALESCE($82, e_thinking_3), - e_thinking_4 = COALESCE($83, e_thinking_4), - e_thinking_5 = COALESCE($84, e_thinking_5), - e_thinking_6 = COALESCE($85, e_thinking_6), - e_thinking_7 = COALESCE($86, e_thinking_7), - e_thinking_8 = COALESCE($87, e_thinking_8), - e_laughing_0 = COALESCE($88, e_laughing_0), - e_laughing_1 = COALESCE($89, e_laughing_1), - e_laughing_2 = COALESCE($90, e_laughing_2), - e_laughing_3 = COALESCE($91, e_laughing_3), - e_laughing_4 = COALESCE($92, e_laughing_4), - e_laughing_5 = COALESCE($93, e_laughing_5), - e_laughing_6 = COALESCE($94, e_laughing_6), - e_laughing_7 = COALESCE($95, e_laughing_7), - e_laughing_8 = COALESCE($96, e_laughing_8), - e_crying_0 = COALESCE($97, e_crying_0), - e_crying_1 = COALESCE($98, e_crying_1), - e_crying_2 = COALESCE($99, e_crying_2), - e_crying_3 = COALESCE($100, e_crying_3), - e_crying_4 = COALESCE($101, e_crying_4), - e_crying_5 = COALESCE($102, e_crying_5), - e_crying_6 = COALESCE($103, e_crying_6), - e_crying_7 = COALESCE($104, e_crying_7), - e_crying_8 = COALESCE($105, e_crying_8), - e_love_0 = COALESCE($106, e_love_0), - e_love_1 = COALESCE($107, e_love_1), - e_love_2 = COALESCE($108, e_love_2), - e_love_3 = COALESCE($109, e_love_3), - e_love_4 = COALESCE($110, e_love_4), - e_love_5 = COALESCE($111, e_love_5), - e_love_6 = COALESCE($112, e_love_6), - e_love_7 = COALESCE($113, e_love_7), - e_love_8 = COALESCE($114, e_love_8), - e_confused_0 = COALESCE($115, e_confused_0), - e_confused_1 = COALESCE($116, e_confused_1), - e_confused_2 = COALESCE($117, e_confused_2), - e_confused_3 = COALESCE($118, e_confused_3), - e_confused_4 = COALESCE($119, e_confused_4), - e_confused_5 = COALESCE($120, e_confused_5), - e_confused_6 = COALESCE($121, e_confused_6), - e_confused_7 = COALESCE($122, e_confused_7), - e_confused_8 = COALESCE($123, e_confused_8), - e_sleeping_0 = COALESCE($124, e_sleeping_0), - e_sleeping_1 = COALESCE($125, e_sleeping_1), - e_sleeping_2 = COALESCE($126, e_sleeping_2), - e_sleeping_3 = COALESCE($127, e_sleeping_3), - e_sleeping_4 = COALESCE($128, e_sleeping_4), - e_sleeping_5 = COALESCE($129, e_sleeping_5), - e_sleeping_6 = COALESCE($130, e_sleeping_6), - e_sleeping_7 = COALESCE($131, e_sleeping_7), - e_sleeping_8 = COALESCE($132, e_sleeping_8), - e_wink_0 = COALESCE($133, e_wink_0), - e_wink_1 = COALESCE($134, e_wink_1), - e_wink_2 = COALESCE($135, e_wink_2), - e_wink_3 = COALESCE($136, e_wink_3), - e_wink_4 = COALESCE($137, e_wink_4), - e_wink_5 = COALESCE($138, e_wink_5), - e_wink_6 = COALESCE($139, e_wink_6), - e_wink_7 = COALESCE($140, e_wink_7), - e_wink_8 = COALESCE($141, e_wink_8), + l_skin_0 = COALESCE($7, l_skin_0), l_skin_1 = COALESCE($8, l_skin_1), l_skin_2 = COALESCE($9, l_skin_2), + l_skin_3 = COALESCE($10, l_skin_3), l_skin_4 = COALESCE($11, l_skin_4), l_skin_5 = COALESCE($12, l_skin_5), + l_skin_6 = COALESCE($13, l_skin_6), l_skin_7 = COALESCE($14, l_skin_7), l_skin_8 = COALESCE($15, l_skin_8), + l_clothes_0 = COALESCE($16, l_clothes_0), l_clothes_1 = COALESCE($17, l_clothes_1), l_clothes_2 = COALESCE($18, l_clothes_2), + l_clothes_3 = COALESCE($19, l_clothes_3), l_clothes_4 = COALESCE($20, l_clothes_4), l_clothes_5 = COALESCE($21, l_clothes_5), + l_clothes_6 = COALESCE($22, l_clothes_6), l_clothes_7 = COALESCE($23, l_clothes_7), l_clothes_8 = COALESCE($24, l_clothes_8), + l_accessories_0 = COALESCE($25, l_accessories_0), l_accessories_1 = COALESCE($26, l_accessories_1), l_accessories_2 = COALESCE($27, l_accessories_2), + l_accessories_3 = COALESCE($28, l_accessories_3), l_accessories_4 = COALESCE($29, l_accessories_4), l_accessories_5 = COALESCE($30, l_accessories_5), + l_accessories_6 = COALESCE($31, l_accessories_6), l_accessories_7 = COALESCE($32, l_accessories_7), l_accessories_8 = COALESCE($33, l_accessories_8), + e_neutral_0 = COALESCE($34, e_neutral_0), e_neutral_1 = COALESCE($35, e_neutral_1), e_neutral_2 = COALESCE($36, e_neutral_2), + e_neutral_3 = COALESCE($37, e_neutral_3), e_neutral_4 = COALESCE($38, e_neutral_4), e_neutral_5 = COALESCE($39, e_neutral_5), + e_neutral_6 = COALESCE($40, e_neutral_6), e_neutral_7 = COALESCE($41, e_neutral_7), e_neutral_8 = COALESCE($42, e_neutral_8), + e_happy_0 = COALESCE($43, e_happy_0), e_happy_1 = COALESCE($44, e_happy_1), e_happy_2 = COALESCE($45, e_happy_2), + e_happy_3 = COALESCE($46, e_happy_3), e_happy_4 = COALESCE($47, e_happy_4), e_happy_5 = COALESCE($48, e_happy_5), + e_happy_6 = COALESCE($49, e_happy_6), e_happy_7 = COALESCE($50, e_happy_7), e_happy_8 = COALESCE($51, e_happy_8), + e_sad_0 = COALESCE($52, e_sad_0), e_sad_1 = COALESCE($53, e_sad_1), e_sad_2 = COALESCE($54, e_sad_2), + e_sad_3 = COALESCE($55, e_sad_3), e_sad_4 = COALESCE($56, e_sad_4), e_sad_5 = COALESCE($57, e_sad_5), + e_sad_6 = COALESCE($58, e_sad_6), e_sad_7 = COALESCE($59, e_sad_7), e_sad_8 = COALESCE($60, e_sad_8), + e_angry_0 = COALESCE($61, e_angry_0), e_angry_1 = COALESCE($62, e_angry_1), e_angry_2 = COALESCE($63, e_angry_2), + e_angry_3 = COALESCE($64, e_angry_3), e_angry_4 = COALESCE($65, e_angry_4), e_angry_5 = COALESCE($66, e_angry_5), + e_angry_6 = COALESCE($67, e_angry_6), e_angry_7 = COALESCE($68, e_angry_7), e_angry_8 = COALESCE($69, e_angry_8), + e_surprised_0 = COALESCE($70, e_surprised_0), e_surprised_1 = COALESCE($71, e_surprised_1), e_surprised_2 = COALESCE($72, e_surprised_2), + e_surprised_3 = COALESCE($73, e_surprised_3), e_surprised_4 = COALESCE($74, e_surprised_4), e_surprised_5 = COALESCE($75, e_surprised_5), + e_surprised_6 = COALESCE($76, e_surprised_6), e_surprised_7 = COALESCE($77, e_surprised_7), e_surprised_8 = COALESCE($78, e_surprised_8), + e_thinking_0 = COALESCE($79, e_thinking_0), e_thinking_1 = COALESCE($80, e_thinking_1), e_thinking_2 = COALESCE($81, e_thinking_2), + e_thinking_3 = COALESCE($82, e_thinking_3), e_thinking_4 = COALESCE($83, e_thinking_4), e_thinking_5 = COALESCE($84, e_thinking_5), + e_thinking_6 = COALESCE($85, e_thinking_6), e_thinking_7 = COALESCE($86, e_thinking_7), e_thinking_8 = COALESCE($87, e_thinking_8), + e_laughing_0 = COALESCE($88, e_laughing_0), e_laughing_1 = COALESCE($89, e_laughing_1), e_laughing_2 = COALESCE($90, e_laughing_2), + e_laughing_3 = COALESCE($91, e_laughing_3), e_laughing_4 = COALESCE($92, e_laughing_4), e_laughing_5 = COALESCE($93, e_laughing_5), + e_laughing_6 = COALESCE($94, e_laughing_6), e_laughing_7 = COALESCE($95, e_laughing_7), e_laughing_8 = COALESCE($96, e_laughing_8), + e_crying_0 = COALESCE($97, e_crying_0), e_crying_1 = COALESCE($98, e_crying_1), e_crying_2 = COALESCE($99, e_crying_2), + e_crying_3 = COALESCE($100, e_crying_3), e_crying_4 = COALESCE($101, e_crying_4), e_crying_5 = COALESCE($102, e_crying_5), + e_crying_6 = COALESCE($103, e_crying_6), e_crying_7 = COALESCE($104, e_crying_7), e_crying_8 = COALESCE($105, e_crying_8), + e_love_0 = COALESCE($106, e_love_0), e_love_1 = COALESCE($107, e_love_1), e_love_2 = COALESCE($108, e_love_2), + e_love_3 = COALESCE($109, e_love_3), e_love_4 = COALESCE($110, e_love_4), e_love_5 = COALESCE($111, e_love_5), + e_love_6 = COALESCE($112, e_love_6), e_love_7 = COALESCE($113, e_love_7), e_love_8 = COALESCE($114, e_love_8), + e_confused_0 = COALESCE($115, e_confused_0), e_confused_1 = COALESCE($116, e_confused_1), e_confused_2 = COALESCE($117, e_confused_2), + e_confused_3 = COALESCE($118, e_confused_3), e_confused_4 = COALESCE($119, e_confused_4), e_confused_5 = COALESCE($120, e_confused_5), + e_confused_6 = COALESCE($121, e_confused_6), e_confused_7 = COALESCE($122, e_confused_7), e_confused_8 = COALESCE($123, e_confused_8), + e_sleeping_0 = COALESCE($124, e_sleeping_0), e_sleeping_1 = COALESCE($125, e_sleeping_1), e_sleeping_2 = COALESCE($126, e_sleeping_2), + e_sleeping_3 = COALESCE($127, e_sleeping_3), e_sleeping_4 = COALESCE($128, e_sleeping_4), e_sleeping_5 = COALESCE($129, e_sleeping_5), + e_sleeping_6 = COALESCE($130, e_sleeping_6), e_sleeping_7 = COALESCE($131, e_sleeping_7), e_sleeping_8 = COALESCE($132, e_sleeping_8), + e_wink_0 = COALESCE($133, e_wink_0), e_wink_1 = COALESCE($134, e_wink_1), e_wink_2 = COALESCE($135, e_wink_2), + e_wink_3 = COALESCE($136, e_wink_3), e_wink_4 = COALESCE($137, e_wink_4), e_wink_5 = COALESCE($138, e_wink_5), + e_wink_6 = COALESCE($139, e_wink_6), e_wink_7 = COALESCE($140, e_wink_7), e_wink_8 = COALESCE($141, e_wink_8), updated_at = now() WHERE id = $1 RETURNING * @@ -892,155 +537,65 @@ pub async fn update_realm_avatar<'e>( .bind(req.is_active) .bind(&req.thumbnail_path) // Skin layer - .bind(req.l_skin_0) - .bind(req.l_skin_1) - .bind(req.l_skin_2) - .bind(req.l_skin_3) - .bind(req.l_skin_4) - .bind(req.l_skin_5) - .bind(req.l_skin_6) - .bind(req.l_skin_7) - .bind(req.l_skin_8) + .bind(req.l_skin_0).bind(req.l_skin_1).bind(req.l_skin_2) + .bind(req.l_skin_3).bind(req.l_skin_4).bind(req.l_skin_5) + .bind(req.l_skin_6).bind(req.l_skin_7).bind(req.l_skin_8) // Clothes layer - .bind(req.l_clothes_0) - .bind(req.l_clothes_1) - .bind(req.l_clothes_2) - .bind(req.l_clothes_3) - .bind(req.l_clothes_4) - .bind(req.l_clothes_5) - .bind(req.l_clothes_6) - .bind(req.l_clothes_7) - .bind(req.l_clothes_8) + .bind(req.l_clothes_0).bind(req.l_clothes_1).bind(req.l_clothes_2) + .bind(req.l_clothes_3).bind(req.l_clothes_4).bind(req.l_clothes_5) + .bind(req.l_clothes_6).bind(req.l_clothes_7).bind(req.l_clothes_8) // Accessories layer - .bind(req.l_accessories_0) - .bind(req.l_accessories_1) - .bind(req.l_accessories_2) - .bind(req.l_accessories_3) - .bind(req.l_accessories_4) - .bind(req.l_accessories_5) - .bind(req.l_accessories_6) - .bind(req.l_accessories_7) - .bind(req.l_accessories_8) + .bind(req.l_accessories_0).bind(req.l_accessories_1).bind(req.l_accessories_2) + .bind(req.l_accessories_3).bind(req.l_accessories_4).bind(req.l_accessories_5) + .bind(req.l_accessories_6).bind(req.l_accessories_7).bind(req.l_accessories_8) // Neutral emotion - .bind(req.e_neutral_0) - .bind(req.e_neutral_1) - .bind(req.e_neutral_2) - .bind(req.e_neutral_3) - .bind(req.e_neutral_4) - .bind(req.e_neutral_5) - .bind(req.e_neutral_6) - .bind(req.e_neutral_7) - .bind(req.e_neutral_8) + .bind(req.e_neutral_0).bind(req.e_neutral_1).bind(req.e_neutral_2) + .bind(req.e_neutral_3).bind(req.e_neutral_4).bind(req.e_neutral_5) + .bind(req.e_neutral_6).bind(req.e_neutral_7).bind(req.e_neutral_8) // Happy emotion - .bind(req.e_happy_0) - .bind(req.e_happy_1) - .bind(req.e_happy_2) - .bind(req.e_happy_3) - .bind(req.e_happy_4) - .bind(req.e_happy_5) - .bind(req.e_happy_6) - .bind(req.e_happy_7) - .bind(req.e_happy_8) + .bind(req.e_happy_0).bind(req.e_happy_1).bind(req.e_happy_2) + .bind(req.e_happy_3).bind(req.e_happy_4).bind(req.e_happy_5) + .bind(req.e_happy_6).bind(req.e_happy_7).bind(req.e_happy_8) // Sad emotion - .bind(req.e_sad_0) - .bind(req.e_sad_1) - .bind(req.e_sad_2) - .bind(req.e_sad_3) - .bind(req.e_sad_4) - .bind(req.e_sad_5) - .bind(req.e_sad_6) - .bind(req.e_sad_7) - .bind(req.e_sad_8) + .bind(req.e_sad_0).bind(req.e_sad_1).bind(req.e_sad_2) + .bind(req.e_sad_3).bind(req.e_sad_4).bind(req.e_sad_5) + .bind(req.e_sad_6).bind(req.e_sad_7).bind(req.e_sad_8) // Angry emotion - .bind(req.e_angry_0) - .bind(req.e_angry_1) - .bind(req.e_angry_2) - .bind(req.e_angry_3) - .bind(req.e_angry_4) - .bind(req.e_angry_5) - .bind(req.e_angry_6) - .bind(req.e_angry_7) - .bind(req.e_angry_8) + .bind(req.e_angry_0).bind(req.e_angry_1).bind(req.e_angry_2) + .bind(req.e_angry_3).bind(req.e_angry_4).bind(req.e_angry_5) + .bind(req.e_angry_6).bind(req.e_angry_7).bind(req.e_angry_8) // Surprised emotion - .bind(req.e_surprised_0) - .bind(req.e_surprised_1) - .bind(req.e_surprised_2) - .bind(req.e_surprised_3) - .bind(req.e_surprised_4) - .bind(req.e_surprised_5) - .bind(req.e_surprised_6) - .bind(req.e_surprised_7) - .bind(req.e_surprised_8) + .bind(req.e_surprised_0).bind(req.e_surprised_1).bind(req.e_surprised_2) + .bind(req.e_surprised_3).bind(req.e_surprised_4).bind(req.e_surprised_5) + .bind(req.e_surprised_6).bind(req.e_surprised_7).bind(req.e_surprised_8) // Thinking emotion - .bind(req.e_thinking_0) - .bind(req.e_thinking_1) - .bind(req.e_thinking_2) - .bind(req.e_thinking_3) - .bind(req.e_thinking_4) - .bind(req.e_thinking_5) - .bind(req.e_thinking_6) - .bind(req.e_thinking_7) - .bind(req.e_thinking_8) + .bind(req.e_thinking_0).bind(req.e_thinking_1).bind(req.e_thinking_2) + .bind(req.e_thinking_3).bind(req.e_thinking_4).bind(req.e_thinking_5) + .bind(req.e_thinking_6).bind(req.e_thinking_7).bind(req.e_thinking_8) // Laughing emotion - .bind(req.e_laughing_0) - .bind(req.e_laughing_1) - .bind(req.e_laughing_2) - .bind(req.e_laughing_3) - .bind(req.e_laughing_4) - .bind(req.e_laughing_5) - .bind(req.e_laughing_6) - .bind(req.e_laughing_7) - .bind(req.e_laughing_8) + .bind(req.e_laughing_0).bind(req.e_laughing_1).bind(req.e_laughing_2) + .bind(req.e_laughing_3).bind(req.e_laughing_4).bind(req.e_laughing_5) + .bind(req.e_laughing_6).bind(req.e_laughing_7).bind(req.e_laughing_8) // Crying emotion - .bind(req.e_crying_0) - .bind(req.e_crying_1) - .bind(req.e_crying_2) - .bind(req.e_crying_3) - .bind(req.e_crying_4) - .bind(req.e_crying_5) - .bind(req.e_crying_6) - .bind(req.e_crying_7) - .bind(req.e_crying_8) + .bind(req.e_crying_0).bind(req.e_crying_1).bind(req.e_crying_2) + .bind(req.e_crying_3).bind(req.e_crying_4).bind(req.e_crying_5) + .bind(req.e_crying_6).bind(req.e_crying_7).bind(req.e_crying_8) // Love emotion - .bind(req.e_love_0) - .bind(req.e_love_1) - .bind(req.e_love_2) - .bind(req.e_love_3) - .bind(req.e_love_4) - .bind(req.e_love_5) - .bind(req.e_love_6) - .bind(req.e_love_7) - .bind(req.e_love_8) + .bind(req.e_love_0).bind(req.e_love_1).bind(req.e_love_2) + .bind(req.e_love_3).bind(req.e_love_4).bind(req.e_love_5) + .bind(req.e_love_6).bind(req.e_love_7).bind(req.e_love_8) // Confused emotion - .bind(req.e_confused_0) - .bind(req.e_confused_1) - .bind(req.e_confused_2) - .bind(req.e_confused_3) - .bind(req.e_confused_4) - .bind(req.e_confused_5) - .bind(req.e_confused_6) - .bind(req.e_confused_7) - .bind(req.e_confused_8) + .bind(req.e_confused_0).bind(req.e_confused_1).bind(req.e_confused_2) + .bind(req.e_confused_3).bind(req.e_confused_4).bind(req.e_confused_5) + .bind(req.e_confused_6).bind(req.e_confused_7).bind(req.e_confused_8) // Sleeping emotion - .bind(req.e_sleeping_0) - .bind(req.e_sleeping_1) - .bind(req.e_sleeping_2) - .bind(req.e_sleeping_3) - .bind(req.e_sleeping_4) - .bind(req.e_sleeping_5) - .bind(req.e_sleeping_6) - .bind(req.e_sleeping_7) - .bind(req.e_sleeping_8) + .bind(req.e_sleeping_0).bind(req.e_sleeping_1).bind(req.e_sleeping_2) + .bind(req.e_sleeping_3).bind(req.e_sleeping_4).bind(req.e_sleeping_5) + .bind(req.e_sleeping_6).bind(req.e_sleeping_7).bind(req.e_sleeping_8) // Wink emotion - .bind(req.e_wink_0) - .bind(req.e_wink_1) - .bind(req.e_wink_2) - .bind(req.e_wink_3) - .bind(req.e_wink_4) - .bind(req.e_wink_5) - .bind(req.e_wink_6) - .bind(req.e_wink_7) - .bind(req.e_wink_8) + .bind(req.e_wink_0).bind(req.e_wink_1).bind(req.e_wink_2) + .bind(req.e_wink_3).bind(req.e_wink_4).bind(req.e_wink_5) + .bind(req.e_wink_6).bind(req.e_wink_7).bind(req.e_wink_8) .fetch_one(executor) .await?; diff --git a/crates/chattyness-db/src/queries/server_avatars.rs b/crates/chattyness-db/src/queries/server_avatars.rs index bc35d5d..4544162 100644 --- a/crates/chattyness-db/src/queries/server_avatars.rs +++ b/crates/chattyness-db/src/queries/server_avatars.rs @@ -3,15 +3,22 @@ //! Server avatars are pre-configured avatar configurations available globally //! across all realms. They reference server.props directly (not inventory items). -use std::collections::HashMap; - use chrono::{DateTime, Duration, Utc}; use sqlx::PgExecutor; use uuid::Uuid; -use crate::models::{AvatarRenderData, EmotionState, ServerAvatar}; +use crate::extract_avatar_slots; +use crate::models::{AvatarRenderData, EmotionState, ServerAvatar, ServerAvatarWithPaths}; +use crate::queries::avatar_common::{ + avatar_paths_join_clause, avatar_paths_select_clause, build_prop_map, + resolve_slots_to_render_data, +}; use chattyness_error::AppError; +// ============================================================================= +// Basic Queries +// ============================================================================= + /// Get a server avatar by slug. pub async fn get_server_avatar_by_slug<'e>( executor: impl PgExecutor<'e>, @@ -68,9 +75,11 @@ pub async fn list_public_server_avatars<'e>( Ok(avatars) } -use crate::models::ServerAvatarWithPaths; +// ============================================================================= +// Avatar with Paths Queries +// ============================================================================= -/// Row type for server avatar with paths query. +/// Row type for server avatar with paths query (includes slug). #[derive(Debug, sqlx::FromRow)] struct ServerAvatarWithPathsRow { id: Uuid, @@ -107,7 +116,7 @@ struct ServerAvatarWithPathsRow { accessories_6: Option, accessories_7: Option, accessories_8: Option, - // Happy emotion layer paths (e1 - more inviting for store display) + // Happy emotion layer paths emotion_0: Option, emotion_1: Option, emotion_2: Option, @@ -151,235 +160,52 @@ impl From for ServerAvatarWithPaths { } /// List all active public server avatars with resolved asset paths. -/// -/// Joins with the props table to resolve prop UUIDs to asset paths, -/// suitable for client-side rendering without additional lookups. pub async fn list_public_server_avatars_with_paths<'e>( executor: impl PgExecutor<'e>, ) -> Result, AppError> { - let rows = sqlx::query_as::<_, ServerAvatarWithPathsRow>( + let join_clause = avatar_paths_join_clause("server.props"); + let query = format!( r#" SELECT a.id, a.slug, - a.name, - a.description, - -- Skin layer - p_skin_0.asset_path AS skin_0, - p_skin_1.asset_path AS skin_1, - p_skin_2.asset_path AS skin_2, - p_skin_3.asset_path AS skin_3, - p_skin_4.asset_path AS skin_4, - p_skin_5.asset_path AS skin_5, - p_skin_6.asset_path AS skin_6, - p_skin_7.asset_path AS skin_7, - p_skin_8.asset_path AS skin_8, - -- Clothes layer - p_clothes_0.asset_path AS clothes_0, - p_clothes_1.asset_path AS clothes_1, - p_clothes_2.asset_path AS clothes_2, - p_clothes_3.asset_path AS clothes_3, - p_clothes_4.asset_path AS clothes_4, - p_clothes_5.asset_path AS clothes_5, - p_clothes_6.asset_path AS clothes_6, - p_clothes_7.asset_path AS clothes_7, - p_clothes_8.asset_path AS clothes_8, - -- Accessories layer - p_acc_0.asset_path AS accessories_0, - p_acc_1.asset_path AS accessories_1, - p_acc_2.asset_path AS accessories_2, - p_acc_3.asset_path AS accessories_3, - p_acc_4.asset_path AS accessories_4, - p_acc_5.asset_path AS accessories_5, - p_acc_6.asset_path AS accessories_6, - p_acc_7.asset_path AS accessories_7, - p_acc_8.asset_path AS accessories_8, - -- Happy emotion layer (e1 - more inviting for store display) - p_emo_0.asset_path AS emotion_0, - p_emo_1.asset_path AS emotion_1, - p_emo_2.asset_path AS emotion_2, - p_emo_3.asset_path AS emotion_3, - p_emo_4.asset_path AS emotion_4, - p_emo_5.asset_path AS emotion_5, - p_emo_6.asset_path AS emotion_6, - p_emo_7.asset_path AS emotion_7, - p_emo_8.asset_path AS emotion_8 + {} FROM server.avatars a - -- Skin layer joins - LEFT JOIN server.props p_skin_0 ON a.l_skin_0 = p_skin_0.id - LEFT JOIN server.props p_skin_1 ON a.l_skin_1 = p_skin_1.id - LEFT JOIN server.props p_skin_2 ON a.l_skin_2 = p_skin_2.id - LEFT JOIN server.props p_skin_3 ON a.l_skin_3 = p_skin_3.id - LEFT JOIN server.props p_skin_4 ON a.l_skin_4 = p_skin_4.id - LEFT JOIN server.props p_skin_5 ON a.l_skin_5 = p_skin_5.id - LEFT JOIN server.props p_skin_6 ON a.l_skin_6 = p_skin_6.id - LEFT JOIN server.props p_skin_7 ON a.l_skin_7 = p_skin_7.id - LEFT JOIN server.props p_skin_8 ON a.l_skin_8 = p_skin_8.id - -- Clothes layer joins - LEFT JOIN server.props p_clothes_0 ON a.l_clothes_0 = p_clothes_0.id - LEFT JOIN server.props p_clothes_1 ON a.l_clothes_1 = p_clothes_1.id - LEFT JOIN server.props p_clothes_2 ON a.l_clothes_2 = p_clothes_2.id - LEFT JOIN server.props p_clothes_3 ON a.l_clothes_3 = p_clothes_3.id - LEFT JOIN server.props p_clothes_4 ON a.l_clothes_4 = p_clothes_4.id - LEFT JOIN server.props p_clothes_5 ON a.l_clothes_5 = p_clothes_5.id - LEFT JOIN server.props p_clothes_6 ON a.l_clothes_6 = p_clothes_6.id - LEFT JOIN server.props p_clothes_7 ON a.l_clothes_7 = p_clothes_7.id - LEFT JOIN server.props p_clothes_8 ON a.l_clothes_8 = p_clothes_8.id - -- Accessories layer joins - LEFT JOIN server.props p_acc_0 ON a.l_accessories_0 = p_acc_0.id - LEFT JOIN server.props p_acc_1 ON a.l_accessories_1 = p_acc_1.id - LEFT JOIN server.props p_acc_2 ON a.l_accessories_2 = p_acc_2.id - LEFT JOIN server.props p_acc_3 ON a.l_accessories_3 = p_acc_3.id - LEFT JOIN server.props p_acc_4 ON a.l_accessories_4 = p_acc_4.id - LEFT JOIN server.props p_acc_5 ON a.l_accessories_5 = p_acc_5.id - LEFT JOIN server.props p_acc_6 ON a.l_accessories_6 = p_acc_6.id - LEFT JOIN server.props p_acc_7 ON a.l_accessories_7 = p_acc_7.id - LEFT JOIN server.props p_acc_8 ON a.l_accessories_8 = p_acc_8.id - -- Happy emotion layer joins (e1 - more inviting for store display) - LEFT JOIN server.props p_emo_0 ON a.e_happy_0 = p_emo_0.id - LEFT JOIN server.props p_emo_1 ON a.e_happy_1 = p_emo_1.id - LEFT JOIN server.props p_emo_2 ON a.e_happy_2 = p_emo_2.id - LEFT JOIN server.props p_emo_3 ON a.e_happy_3 = p_emo_3.id - LEFT JOIN server.props p_emo_4 ON a.e_happy_4 = p_emo_4.id - LEFT JOIN server.props p_emo_5 ON a.e_happy_5 = p_emo_5.id - LEFT JOIN server.props p_emo_6 ON a.e_happy_6 = p_emo_6.id - LEFT JOIN server.props p_emo_7 ON a.e_happy_7 = p_emo_7.id - LEFT JOIN server.props p_emo_8 ON a.e_happy_8 = p_emo_8.id + {} WHERE a.is_active = true AND a.is_public = true ORDER BY a.name ASC "#, - ) - .fetch_all(executor) - .await?; + avatar_paths_select_clause(), + join_clause + ); + + let rows = sqlx::query_as::<_, ServerAvatarWithPathsRow>(&query) + .fetch_all(executor) + .await?; Ok(rows.into_iter().map(ServerAvatarWithPaths::from).collect()) } -/// Row type for prop asset lookup. -#[derive(Debug, sqlx::FromRow)] -struct PropAssetRow { - id: Uuid, - asset_path: String, -} +// ============================================================================= +// Render Data Resolution +// ============================================================================= /// Resolve a server avatar to render data. -/// Joins the avatar's prop UUIDs with server.props to get asset paths. pub async fn resolve_server_avatar_to_render_data<'e>( executor: impl PgExecutor<'e>, avatar: &ServerAvatar, current_emotion: EmotionState, ) -> Result { - // Collect all non-null prop UUIDs - let mut prop_ids: Vec = Vec::new(); - - // Content layers - for id in [ - avatar.l_skin_0, avatar.l_skin_1, avatar.l_skin_2, - avatar.l_skin_3, avatar.l_skin_4, avatar.l_skin_5, - avatar.l_skin_6, avatar.l_skin_7, avatar.l_skin_8, - avatar.l_clothes_0, avatar.l_clothes_1, avatar.l_clothes_2, - avatar.l_clothes_3, avatar.l_clothes_4, avatar.l_clothes_5, - avatar.l_clothes_6, avatar.l_clothes_7, avatar.l_clothes_8, - avatar.l_accessories_0, avatar.l_accessories_1, avatar.l_accessories_2, - avatar.l_accessories_3, avatar.l_accessories_4, avatar.l_accessories_5, - avatar.l_accessories_6, avatar.l_accessories_7, avatar.l_accessories_8, - ].iter().flatten() { - prop_ids.push(*id); - } - - // Get emotion layer slots based on current emotion - let emotion_slots: [Option; 9] = match current_emotion { - EmotionState::Neutral => [avatar.e_neutral_0, avatar.e_neutral_1, avatar.e_neutral_2, - avatar.e_neutral_3, avatar.e_neutral_4, avatar.e_neutral_5, - avatar.e_neutral_6, avatar.e_neutral_7, avatar.e_neutral_8], - EmotionState::Happy => [avatar.e_happy_0, avatar.e_happy_1, avatar.e_happy_2, - avatar.e_happy_3, avatar.e_happy_4, avatar.e_happy_5, - avatar.e_happy_6, avatar.e_happy_7, avatar.e_happy_8], - EmotionState::Sad => [avatar.e_sad_0, avatar.e_sad_1, avatar.e_sad_2, - avatar.e_sad_3, avatar.e_sad_4, avatar.e_sad_5, - avatar.e_sad_6, avatar.e_sad_7, avatar.e_sad_8], - EmotionState::Angry => [avatar.e_angry_0, avatar.e_angry_1, avatar.e_angry_2, - avatar.e_angry_3, avatar.e_angry_4, avatar.e_angry_5, - avatar.e_angry_6, avatar.e_angry_7, avatar.e_angry_8], - EmotionState::Surprised => [avatar.e_surprised_0, avatar.e_surprised_1, avatar.e_surprised_2, - avatar.e_surprised_3, avatar.e_surprised_4, avatar.e_surprised_5, - avatar.e_surprised_6, avatar.e_surprised_7, avatar.e_surprised_8], - EmotionState::Thinking => [avatar.e_thinking_0, avatar.e_thinking_1, avatar.e_thinking_2, - avatar.e_thinking_3, avatar.e_thinking_4, avatar.e_thinking_5, - avatar.e_thinking_6, avatar.e_thinking_7, avatar.e_thinking_8], - EmotionState::Laughing => [avatar.e_laughing_0, avatar.e_laughing_1, avatar.e_laughing_2, - avatar.e_laughing_3, avatar.e_laughing_4, avatar.e_laughing_5, - avatar.e_laughing_6, avatar.e_laughing_7, avatar.e_laughing_8], - EmotionState::Crying => [avatar.e_crying_0, avatar.e_crying_1, avatar.e_crying_2, - avatar.e_crying_3, avatar.e_crying_4, avatar.e_crying_5, - avatar.e_crying_6, avatar.e_crying_7, avatar.e_crying_8], - EmotionState::Love => [avatar.e_love_0, avatar.e_love_1, avatar.e_love_2, - avatar.e_love_3, avatar.e_love_4, avatar.e_love_5, - avatar.e_love_6, avatar.e_love_7, avatar.e_love_8], - EmotionState::Confused => [avatar.e_confused_0, avatar.e_confused_1, avatar.e_confused_2, - avatar.e_confused_3, avatar.e_confused_4, avatar.e_confused_5, - avatar.e_confused_6, avatar.e_confused_7, avatar.e_confused_8], - EmotionState::Sleeping => [avatar.e_sleeping_0, avatar.e_sleeping_1, avatar.e_sleeping_2, - avatar.e_sleeping_3, avatar.e_sleeping_4, avatar.e_sleeping_5, - avatar.e_sleeping_6, avatar.e_sleeping_7, avatar.e_sleeping_8], - EmotionState::Wink => [avatar.e_wink_0, avatar.e_wink_1, avatar.e_wink_2, - avatar.e_wink_3, avatar.e_wink_4, avatar.e_wink_5, - avatar.e_wink_6, avatar.e_wink_7, avatar.e_wink_8], - }; - - for id in emotion_slots.iter().flatten() { - prop_ids.push(*id); - } - - // Bulk lookup all prop asset paths - let prop_map: HashMap = if prop_ids.is_empty() { - HashMap::new() - } else { - let rows = sqlx::query_as::<_, PropAssetRow>( - r#" - SELECT id, asset_path - FROM server.props - WHERE id = ANY($1) - "#, - ) - .bind(&prop_ids) - .fetch_all(executor) - .await?; - - rows.into_iter().map(|r| (r.id, r.asset_path)).collect() - }; - - // Helper to look up path - let get_path = |id: Option| -> Option { - id.and_then(|id| prop_map.get(&id).cloned()) - }; - - Ok(AvatarRenderData { - avatar_id: avatar.id, - current_emotion, - skin_layer: [ - get_path(avatar.l_skin_0), get_path(avatar.l_skin_1), get_path(avatar.l_skin_2), - get_path(avatar.l_skin_3), get_path(avatar.l_skin_4), get_path(avatar.l_skin_5), - get_path(avatar.l_skin_6), get_path(avatar.l_skin_7), get_path(avatar.l_skin_8), - ], - clothes_layer: [ - get_path(avatar.l_clothes_0), get_path(avatar.l_clothes_1), get_path(avatar.l_clothes_2), - get_path(avatar.l_clothes_3), get_path(avatar.l_clothes_4), get_path(avatar.l_clothes_5), - get_path(avatar.l_clothes_6), get_path(avatar.l_clothes_7), get_path(avatar.l_clothes_8), - ], - accessories_layer: [ - get_path(avatar.l_accessories_0), get_path(avatar.l_accessories_1), get_path(avatar.l_accessories_2), - get_path(avatar.l_accessories_3), get_path(avatar.l_accessories_4), get_path(avatar.l_accessories_5), - get_path(avatar.l_accessories_6), get_path(avatar.l_accessories_7), get_path(avatar.l_accessories_8), - ], - emotion_layer: [ - get_path(emotion_slots[0]), get_path(emotion_slots[1]), get_path(emotion_slots[2]), - get_path(emotion_slots[3]), get_path(emotion_slots[4]), get_path(emotion_slots[5]), - get_path(emotion_slots[6]), get_path(emotion_slots[7]), get_path(emotion_slots[8]), - ], - }) + let slots = extract_avatar_slots!(avatar); + let prop_ids = slots.collect_render_prop_ids(current_emotion); + let prop_map = build_prop_map(executor, &prop_ids, "server.props").await?; + Ok(resolve_slots_to_render_data(avatar.id, &slots, current_emotion, &prop_map)) } +// ============================================================================= +// Forced Avatar Management +// ============================================================================= + /// Apply a forced server avatar to a user. pub async fn apply_forced_server_avatar<'e>( executor: impl PgExecutor<'e>, @@ -599,155 +425,65 @@ pub async fn create_server_avatar<'e>( .bind(&req.thumbnail_path) .bind(created_by) // Skin layer - .bind(req.l_skin_0) - .bind(req.l_skin_1) - .bind(req.l_skin_2) - .bind(req.l_skin_3) - .bind(req.l_skin_4) - .bind(req.l_skin_5) - .bind(req.l_skin_6) - .bind(req.l_skin_7) - .bind(req.l_skin_8) + .bind(req.l_skin_0).bind(req.l_skin_1).bind(req.l_skin_2) + .bind(req.l_skin_3).bind(req.l_skin_4).bind(req.l_skin_5) + .bind(req.l_skin_6).bind(req.l_skin_7).bind(req.l_skin_8) // Clothes layer - .bind(req.l_clothes_0) - .bind(req.l_clothes_1) - .bind(req.l_clothes_2) - .bind(req.l_clothes_3) - .bind(req.l_clothes_4) - .bind(req.l_clothes_5) - .bind(req.l_clothes_6) - .bind(req.l_clothes_7) - .bind(req.l_clothes_8) + .bind(req.l_clothes_0).bind(req.l_clothes_1).bind(req.l_clothes_2) + .bind(req.l_clothes_3).bind(req.l_clothes_4).bind(req.l_clothes_5) + .bind(req.l_clothes_6).bind(req.l_clothes_7).bind(req.l_clothes_8) // Accessories layer - .bind(req.l_accessories_0) - .bind(req.l_accessories_1) - .bind(req.l_accessories_2) - .bind(req.l_accessories_3) - .bind(req.l_accessories_4) - .bind(req.l_accessories_5) - .bind(req.l_accessories_6) - .bind(req.l_accessories_7) - .bind(req.l_accessories_8) + .bind(req.l_accessories_0).bind(req.l_accessories_1).bind(req.l_accessories_2) + .bind(req.l_accessories_3).bind(req.l_accessories_4).bind(req.l_accessories_5) + .bind(req.l_accessories_6).bind(req.l_accessories_7).bind(req.l_accessories_8) // Neutral emotion - .bind(req.e_neutral_0) - .bind(req.e_neutral_1) - .bind(req.e_neutral_2) - .bind(req.e_neutral_3) - .bind(req.e_neutral_4) - .bind(req.e_neutral_5) - .bind(req.e_neutral_6) - .bind(req.e_neutral_7) - .bind(req.e_neutral_8) + .bind(req.e_neutral_0).bind(req.e_neutral_1).bind(req.e_neutral_2) + .bind(req.e_neutral_3).bind(req.e_neutral_4).bind(req.e_neutral_5) + .bind(req.e_neutral_6).bind(req.e_neutral_7).bind(req.e_neutral_8) // Happy emotion - .bind(req.e_happy_0) - .bind(req.e_happy_1) - .bind(req.e_happy_2) - .bind(req.e_happy_3) - .bind(req.e_happy_4) - .bind(req.e_happy_5) - .bind(req.e_happy_6) - .bind(req.e_happy_7) - .bind(req.e_happy_8) + .bind(req.e_happy_0).bind(req.e_happy_1).bind(req.e_happy_2) + .bind(req.e_happy_3).bind(req.e_happy_4).bind(req.e_happy_5) + .bind(req.e_happy_6).bind(req.e_happy_7).bind(req.e_happy_8) // Sad emotion - .bind(req.e_sad_0) - .bind(req.e_sad_1) - .bind(req.e_sad_2) - .bind(req.e_sad_3) - .bind(req.e_sad_4) - .bind(req.e_sad_5) - .bind(req.e_sad_6) - .bind(req.e_sad_7) - .bind(req.e_sad_8) + .bind(req.e_sad_0).bind(req.e_sad_1).bind(req.e_sad_2) + .bind(req.e_sad_3).bind(req.e_sad_4).bind(req.e_sad_5) + .bind(req.e_sad_6).bind(req.e_sad_7).bind(req.e_sad_8) // Angry emotion - .bind(req.e_angry_0) - .bind(req.e_angry_1) - .bind(req.e_angry_2) - .bind(req.e_angry_3) - .bind(req.e_angry_4) - .bind(req.e_angry_5) - .bind(req.e_angry_6) - .bind(req.e_angry_7) - .bind(req.e_angry_8) + .bind(req.e_angry_0).bind(req.e_angry_1).bind(req.e_angry_2) + .bind(req.e_angry_3).bind(req.e_angry_4).bind(req.e_angry_5) + .bind(req.e_angry_6).bind(req.e_angry_7).bind(req.e_angry_8) // Surprised emotion - .bind(req.e_surprised_0) - .bind(req.e_surprised_1) - .bind(req.e_surprised_2) - .bind(req.e_surprised_3) - .bind(req.e_surprised_4) - .bind(req.e_surprised_5) - .bind(req.e_surprised_6) - .bind(req.e_surprised_7) - .bind(req.e_surprised_8) + .bind(req.e_surprised_0).bind(req.e_surprised_1).bind(req.e_surprised_2) + .bind(req.e_surprised_3).bind(req.e_surprised_4).bind(req.e_surprised_5) + .bind(req.e_surprised_6).bind(req.e_surprised_7).bind(req.e_surprised_8) // Thinking emotion - .bind(req.e_thinking_0) - .bind(req.e_thinking_1) - .bind(req.e_thinking_2) - .bind(req.e_thinking_3) - .bind(req.e_thinking_4) - .bind(req.e_thinking_5) - .bind(req.e_thinking_6) - .bind(req.e_thinking_7) - .bind(req.e_thinking_8) + .bind(req.e_thinking_0).bind(req.e_thinking_1).bind(req.e_thinking_2) + .bind(req.e_thinking_3).bind(req.e_thinking_4).bind(req.e_thinking_5) + .bind(req.e_thinking_6).bind(req.e_thinking_7).bind(req.e_thinking_8) // Laughing emotion - .bind(req.e_laughing_0) - .bind(req.e_laughing_1) - .bind(req.e_laughing_2) - .bind(req.e_laughing_3) - .bind(req.e_laughing_4) - .bind(req.e_laughing_5) - .bind(req.e_laughing_6) - .bind(req.e_laughing_7) - .bind(req.e_laughing_8) + .bind(req.e_laughing_0).bind(req.e_laughing_1).bind(req.e_laughing_2) + .bind(req.e_laughing_3).bind(req.e_laughing_4).bind(req.e_laughing_5) + .bind(req.e_laughing_6).bind(req.e_laughing_7).bind(req.e_laughing_8) // Crying emotion - .bind(req.e_crying_0) - .bind(req.e_crying_1) - .bind(req.e_crying_2) - .bind(req.e_crying_3) - .bind(req.e_crying_4) - .bind(req.e_crying_5) - .bind(req.e_crying_6) - .bind(req.e_crying_7) - .bind(req.e_crying_8) + .bind(req.e_crying_0).bind(req.e_crying_1).bind(req.e_crying_2) + .bind(req.e_crying_3).bind(req.e_crying_4).bind(req.e_crying_5) + .bind(req.e_crying_6).bind(req.e_crying_7).bind(req.e_crying_8) // Love emotion - .bind(req.e_love_0) - .bind(req.e_love_1) - .bind(req.e_love_2) - .bind(req.e_love_3) - .bind(req.e_love_4) - .bind(req.e_love_5) - .bind(req.e_love_6) - .bind(req.e_love_7) - .bind(req.e_love_8) + .bind(req.e_love_0).bind(req.e_love_1).bind(req.e_love_2) + .bind(req.e_love_3).bind(req.e_love_4).bind(req.e_love_5) + .bind(req.e_love_6).bind(req.e_love_7).bind(req.e_love_8) // Confused emotion - .bind(req.e_confused_0) - .bind(req.e_confused_1) - .bind(req.e_confused_2) - .bind(req.e_confused_3) - .bind(req.e_confused_4) - .bind(req.e_confused_5) - .bind(req.e_confused_6) - .bind(req.e_confused_7) - .bind(req.e_confused_8) + .bind(req.e_confused_0).bind(req.e_confused_1).bind(req.e_confused_2) + .bind(req.e_confused_3).bind(req.e_confused_4).bind(req.e_confused_5) + .bind(req.e_confused_6).bind(req.e_confused_7).bind(req.e_confused_8) // Sleeping emotion - .bind(req.e_sleeping_0) - .bind(req.e_sleeping_1) - .bind(req.e_sleeping_2) - .bind(req.e_sleeping_3) - .bind(req.e_sleeping_4) - .bind(req.e_sleeping_5) - .bind(req.e_sleeping_6) - .bind(req.e_sleeping_7) - .bind(req.e_sleeping_8) + .bind(req.e_sleeping_0).bind(req.e_sleeping_1).bind(req.e_sleeping_2) + .bind(req.e_sleeping_3).bind(req.e_sleeping_4).bind(req.e_sleeping_5) + .bind(req.e_sleeping_6).bind(req.e_sleeping_7).bind(req.e_sleeping_8) // Wink emotion - .bind(req.e_wink_0) - .bind(req.e_wink_1) - .bind(req.e_wink_2) - .bind(req.e_wink_3) - .bind(req.e_wink_4) - .bind(req.e_wink_5) - .bind(req.e_wink_6) - .bind(req.e_wink_7) - .bind(req.e_wink_8) + .bind(req.e_wink_0).bind(req.e_wink_1).bind(req.e_wink_2) + .bind(req.e_wink_3).bind(req.e_wink_4).bind(req.e_wink_5) + .bind(req.e_wink_6).bind(req.e_wink_7).bind(req.e_wink_8) .fetch_one(executor) .await?; @@ -768,141 +504,51 @@ pub async fn update_server_avatar<'e>( is_public = COALESCE($4, is_public), is_active = COALESCE($5, is_active), thumbnail_path = COALESCE($6, thumbnail_path), - l_skin_0 = COALESCE($7, l_skin_0), - l_skin_1 = COALESCE($8, l_skin_1), - l_skin_2 = COALESCE($9, l_skin_2), - l_skin_3 = COALESCE($10, l_skin_3), - l_skin_4 = COALESCE($11, l_skin_4), - l_skin_5 = COALESCE($12, l_skin_5), - l_skin_6 = COALESCE($13, l_skin_6), - l_skin_7 = COALESCE($14, l_skin_7), - l_skin_8 = COALESCE($15, l_skin_8), - l_clothes_0 = COALESCE($16, l_clothes_0), - l_clothes_1 = COALESCE($17, l_clothes_1), - l_clothes_2 = COALESCE($18, l_clothes_2), - l_clothes_3 = COALESCE($19, l_clothes_3), - l_clothes_4 = COALESCE($20, l_clothes_4), - l_clothes_5 = COALESCE($21, l_clothes_5), - l_clothes_6 = COALESCE($22, l_clothes_6), - l_clothes_7 = COALESCE($23, l_clothes_7), - l_clothes_8 = COALESCE($24, l_clothes_8), - l_accessories_0 = COALESCE($25, l_accessories_0), - l_accessories_1 = COALESCE($26, l_accessories_1), - l_accessories_2 = COALESCE($27, l_accessories_2), - l_accessories_3 = COALESCE($28, l_accessories_3), - l_accessories_4 = COALESCE($29, l_accessories_4), - l_accessories_5 = COALESCE($30, l_accessories_5), - l_accessories_6 = COALESCE($31, l_accessories_6), - l_accessories_7 = COALESCE($32, l_accessories_7), - l_accessories_8 = COALESCE($33, l_accessories_8), - e_neutral_0 = COALESCE($34, e_neutral_0), - e_neutral_1 = COALESCE($35, e_neutral_1), - e_neutral_2 = COALESCE($36, e_neutral_2), - e_neutral_3 = COALESCE($37, e_neutral_3), - e_neutral_4 = COALESCE($38, e_neutral_4), - e_neutral_5 = COALESCE($39, e_neutral_5), - e_neutral_6 = COALESCE($40, e_neutral_6), - e_neutral_7 = COALESCE($41, e_neutral_7), - e_neutral_8 = COALESCE($42, e_neutral_8), - e_happy_0 = COALESCE($43, e_happy_0), - e_happy_1 = COALESCE($44, e_happy_1), - e_happy_2 = COALESCE($45, e_happy_2), - e_happy_3 = COALESCE($46, e_happy_3), - e_happy_4 = COALESCE($47, e_happy_4), - e_happy_5 = COALESCE($48, e_happy_5), - e_happy_6 = COALESCE($49, e_happy_6), - e_happy_7 = COALESCE($50, e_happy_7), - e_happy_8 = COALESCE($51, e_happy_8), - e_sad_0 = COALESCE($52, e_sad_0), - e_sad_1 = COALESCE($53, e_sad_1), - e_sad_2 = COALESCE($54, e_sad_2), - e_sad_3 = COALESCE($55, e_sad_3), - e_sad_4 = COALESCE($56, e_sad_4), - e_sad_5 = COALESCE($57, e_sad_5), - e_sad_6 = COALESCE($58, e_sad_6), - e_sad_7 = COALESCE($59, e_sad_7), - e_sad_8 = COALESCE($60, e_sad_8), - e_angry_0 = COALESCE($61, e_angry_0), - e_angry_1 = COALESCE($62, e_angry_1), - e_angry_2 = COALESCE($63, e_angry_2), - e_angry_3 = COALESCE($64, e_angry_3), - e_angry_4 = COALESCE($65, e_angry_4), - e_angry_5 = COALESCE($66, e_angry_5), - e_angry_6 = COALESCE($67, e_angry_6), - e_angry_7 = COALESCE($68, e_angry_7), - e_angry_8 = COALESCE($69, e_angry_8), - e_surprised_0 = COALESCE($70, e_surprised_0), - e_surprised_1 = COALESCE($71, e_surprised_1), - e_surprised_2 = COALESCE($72, e_surprised_2), - e_surprised_3 = COALESCE($73, e_surprised_3), - e_surprised_4 = COALESCE($74, e_surprised_4), - e_surprised_5 = COALESCE($75, e_surprised_5), - e_surprised_6 = COALESCE($76, e_surprised_6), - e_surprised_7 = COALESCE($77, e_surprised_7), - e_surprised_8 = COALESCE($78, e_surprised_8), - e_thinking_0 = COALESCE($79, e_thinking_0), - e_thinking_1 = COALESCE($80, e_thinking_1), - e_thinking_2 = COALESCE($81, e_thinking_2), - e_thinking_3 = COALESCE($82, e_thinking_3), - e_thinking_4 = COALESCE($83, e_thinking_4), - e_thinking_5 = COALESCE($84, e_thinking_5), - e_thinking_6 = COALESCE($85, e_thinking_6), - e_thinking_7 = COALESCE($86, e_thinking_7), - e_thinking_8 = COALESCE($87, e_thinking_8), - e_laughing_0 = COALESCE($88, e_laughing_0), - e_laughing_1 = COALESCE($89, e_laughing_1), - e_laughing_2 = COALESCE($90, e_laughing_2), - e_laughing_3 = COALESCE($91, e_laughing_3), - e_laughing_4 = COALESCE($92, e_laughing_4), - e_laughing_5 = COALESCE($93, e_laughing_5), - e_laughing_6 = COALESCE($94, e_laughing_6), - e_laughing_7 = COALESCE($95, e_laughing_7), - e_laughing_8 = COALESCE($96, e_laughing_8), - e_crying_0 = COALESCE($97, e_crying_0), - e_crying_1 = COALESCE($98, e_crying_1), - e_crying_2 = COALESCE($99, e_crying_2), - e_crying_3 = COALESCE($100, e_crying_3), - e_crying_4 = COALESCE($101, e_crying_4), - e_crying_5 = COALESCE($102, e_crying_5), - e_crying_6 = COALESCE($103, e_crying_6), - e_crying_7 = COALESCE($104, e_crying_7), - e_crying_8 = COALESCE($105, e_crying_8), - e_love_0 = COALESCE($106, e_love_0), - e_love_1 = COALESCE($107, e_love_1), - e_love_2 = COALESCE($108, e_love_2), - e_love_3 = COALESCE($109, e_love_3), - e_love_4 = COALESCE($110, e_love_4), - e_love_5 = COALESCE($111, e_love_5), - e_love_6 = COALESCE($112, e_love_6), - e_love_7 = COALESCE($113, e_love_7), - e_love_8 = COALESCE($114, e_love_8), - e_confused_0 = COALESCE($115, e_confused_0), - e_confused_1 = COALESCE($116, e_confused_1), - e_confused_2 = COALESCE($117, e_confused_2), - e_confused_3 = COALESCE($118, e_confused_3), - e_confused_4 = COALESCE($119, e_confused_4), - e_confused_5 = COALESCE($120, e_confused_5), - e_confused_6 = COALESCE($121, e_confused_6), - e_confused_7 = COALESCE($122, e_confused_7), - e_confused_8 = COALESCE($123, e_confused_8), - e_sleeping_0 = COALESCE($124, e_sleeping_0), - e_sleeping_1 = COALESCE($125, e_sleeping_1), - e_sleeping_2 = COALESCE($126, e_sleeping_2), - e_sleeping_3 = COALESCE($127, e_sleeping_3), - e_sleeping_4 = COALESCE($128, e_sleeping_4), - e_sleeping_5 = COALESCE($129, e_sleeping_5), - e_sleeping_6 = COALESCE($130, e_sleeping_6), - e_sleeping_7 = COALESCE($131, e_sleeping_7), - e_sleeping_8 = COALESCE($132, e_sleeping_8), - e_wink_0 = COALESCE($133, e_wink_0), - e_wink_1 = COALESCE($134, e_wink_1), - e_wink_2 = COALESCE($135, e_wink_2), - e_wink_3 = COALESCE($136, e_wink_3), - e_wink_4 = COALESCE($137, e_wink_4), - e_wink_5 = COALESCE($138, e_wink_5), - e_wink_6 = COALESCE($139, e_wink_6), - e_wink_7 = COALESCE($140, e_wink_7), - e_wink_8 = COALESCE($141, e_wink_8), + l_skin_0 = COALESCE($7, l_skin_0), l_skin_1 = COALESCE($8, l_skin_1), l_skin_2 = COALESCE($9, l_skin_2), + l_skin_3 = COALESCE($10, l_skin_3), l_skin_4 = COALESCE($11, l_skin_4), l_skin_5 = COALESCE($12, l_skin_5), + l_skin_6 = COALESCE($13, l_skin_6), l_skin_7 = COALESCE($14, l_skin_7), l_skin_8 = COALESCE($15, l_skin_8), + l_clothes_0 = COALESCE($16, l_clothes_0), l_clothes_1 = COALESCE($17, l_clothes_1), l_clothes_2 = COALESCE($18, l_clothes_2), + l_clothes_3 = COALESCE($19, l_clothes_3), l_clothes_4 = COALESCE($20, l_clothes_4), l_clothes_5 = COALESCE($21, l_clothes_5), + l_clothes_6 = COALESCE($22, l_clothes_6), l_clothes_7 = COALESCE($23, l_clothes_7), l_clothes_8 = COALESCE($24, l_clothes_8), + l_accessories_0 = COALESCE($25, l_accessories_0), l_accessories_1 = COALESCE($26, l_accessories_1), l_accessories_2 = COALESCE($27, l_accessories_2), + l_accessories_3 = COALESCE($28, l_accessories_3), l_accessories_4 = COALESCE($29, l_accessories_4), l_accessories_5 = COALESCE($30, l_accessories_5), + l_accessories_6 = COALESCE($31, l_accessories_6), l_accessories_7 = COALESCE($32, l_accessories_7), l_accessories_8 = COALESCE($33, l_accessories_8), + e_neutral_0 = COALESCE($34, e_neutral_0), e_neutral_1 = COALESCE($35, e_neutral_1), e_neutral_2 = COALESCE($36, e_neutral_2), + e_neutral_3 = COALESCE($37, e_neutral_3), e_neutral_4 = COALESCE($38, e_neutral_4), e_neutral_5 = COALESCE($39, e_neutral_5), + e_neutral_6 = COALESCE($40, e_neutral_6), e_neutral_7 = COALESCE($41, e_neutral_7), e_neutral_8 = COALESCE($42, e_neutral_8), + e_happy_0 = COALESCE($43, e_happy_0), e_happy_1 = COALESCE($44, e_happy_1), e_happy_2 = COALESCE($45, e_happy_2), + e_happy_3 = COALESCE($46, e_happy_3), e_happy_4 = COALESCE($47, e_happy_4), e_happy_5 = COALESCE($48, e_happy_5), + e_happy_6 = COALESCE($49, e_happy_6), e_happy_7 = COALESCE($50, e_happy_7), e_happy_8 = COALESCE($51, e_happy_8), + e_sad_0 = COALESCE($52, e_sad_0), e_sad_1 = COALESCE($53, e_sad_1), e_sad_2 = COALESCE($54, e_sad_2), + e_sad_3 = COALESCE($55, e_sad_3), e_sad_4 = COALESCE($56, e_sad_4), e_sad_5 = COALESCE($57, e_sad_5), + e_sad_6 = COALESCE($58, e_sad_6), e_sad_7 = COALESCE($59, e_sad_7), e_sad_8 = COALESCE($60, e_sad_8), + e_angry_0 = COALESCE($61, e_angry_0), e_angry_1 = COALESCE($62, e_angry_1), e_angry_2 = COALESCE($63, e_angry_2), + e_angry_3 = COALESCE($64, e_angry_3), e_angry_4 = COALESCE($65, e_angry_4), e_angry_5 = COALESCE($66, e_angry_5), + e_angry_6 = COALESCE($67, e_angry_6), e_angry_7 = COALESCE($68, e_angry_7), e_angry_8 = COALESCE($69, e_angry_8), + e_surprised_0 = COALESCE($70, e_surprised_0), e_surprised_1 = COALESCE($71, e_surprised_1), e_surprised_2 = COALESCE($72, e_surprised_2), + e_surprised_3 = COALESCE($73, e_surprised_3), e_surprised_4 = COALESCE($74, e_surprised_4), e_surprised_5 = COALESCE($75, e_surprised_5), + e_surprised_6 = COALESCE($76, e_surprised_6), e_surprised_7 = COALESCE($77, e_surprised_7), e_surprised_8 = COALESCE($78, e_surprised_8), + e_thinking_0 = COALESCE($79, e_thinking_0), e_thinking_1 = COALESCE($80, e_thinking_1), e_thinking_2 = COALESCE($81, e_thinking_2), + e_thinking_3 = COALESCE($82, e_thinking_3), e_thinking_4 = COALESCE($83, e_thinking_4), e_thinking_5 = COALESCE($84, e_thinking_5), + e_thinking_6 = COALESCE($85, e_thinking_6), e_thinking_7 = COALESCE($86, e_thinking_7), e_thinking_8 = COALESCE($87, e_thinking_8), + e_laughing_0 = COALESCE($88, e_laughing_0), e_laughing_1 = COALESCE($89, e_laughing_1), e_laughing_2 = COALESCE($90, e_laughing_2), + e_laughing_3 = COALESCE($91, e_laughing_3), e_laughing_4 = COALESCE($92, e_laughing_4), e_laughing_5 = COALESCE($93, e_laughing_5), + e_laughing_6 = COALESCE($94, e_laughing_6), e_laughing_7 = COALESCE($95, e_laughing_7), e_laughing_8 = COALESCE($96, e_laughing_8), + e_crying_0 = COALESCE($97, e_crying_0), e_crying_1 = COALESCE($98, e_crying_1), e_crying_2 = COALESCE($99, e_crying_2), + e_crying_3 = COALESCE($100, e_crying_3), e_crying_4 = COALESCE($101, e_crying_4), e_crying_5 = COALESCE($102, e_crying_5), + e_crying_6 = COALESCE($103, e_crying_6), e_crying_7 = COALESCE($104, e_crying_7), e_crying_8 = COALESCE($105, e_crying_8), + e_love_0 = COALESCE($106, e_love_0), e_love_1 = COALESCE($107, e_love_1), e_love_2 = COALESCE($108, e_love_2), + e_love_3 = COALESCE($109, e_love_3), e_love_4 = COALESCE($110, e_love_4), e_love_5 = COALESCE($111, e_love_5), + e_love_6 = COALESCE($112, e_love_6), e_love_7 = COALESCE($113, e_love_7), e_love_8 = COALESCE($114, e_love_8), + e_confused_0 = COALESCE($115, e_confused_0), e_confused_1 = COALESCE($116, e_confused_1), e_confused_2 = COALESCE($117, e_confused_2), + e_confused_3 = COALESCE($118, e_confused_3), e_confused_4 = COALESCE($119, e_confused_4), e_confused_5 = COALESCE($120, e_confused_5), + e_confused_6 = COALESCE($121, e_confused_6), e_confused_7 = COALESCE($122, e_confused_7), e_confused_8 = COALESCE($123, e_confused_8), + e_sleeping_0 = COALESCE($124, e_sleeping_0), e_sleeping_1 = COALESCE($125, e_sleeping_1), e_sleeping_2 = COALESCE($126, e_sleeping_2), + e_sleeping_3 = COALESCE($127, e_sleeping_3), e_sleeping_4 = COALESCE($128, e_sleeping_4), e_sleeping_5 = COALESCE($129, e_sleeping_5), + e_sleeping_6 = COALESCE($130, e_sleeping_6), e_sleeping_7 = COALESCE($131, e_sleeping_7), e_sleeping_8 = COALESCE($132, e_sleeping_8), + e_wink_0 = COALESCE($133, e_wink_0), e_wink_1 = COALESCE($134, e_wink_1), e_wink_2 = COALESCE($135, e_wink_2), + e_wink_3 = COALESCE($136, e_wink_3), e_wink_4 = COALESCE($137, e_wink_4), e_wink_5 = COALESCE($138, e_wink_5), + e_wink_6 = COALESCE($139, e_wink_6), e_wink_7 = COALESCE($140, e_wink_7), e_wink_8 = COALESCE($141, e_wink_8), updated_at = now() WHERE id = $1 RETURNING * @@ -915,155 +561,65 @@ pub async fn update_server_avatar<'e>( .bind(req.is_active) .bind(&req.thumbnail_path) // Skin layer - .bind(req.l_skin_0) - .bind(req.l_skin_1) - .bind(req.l_skin_2) - .bind(req.l_skin_3) - .bind(req.l_skin_4) - .bind(req.l_skin_5) - .bind(req.l_skin_6) - .bind(req.l_skin_7) - .bind(req.l_skin_8) + .bind(req.l_skin_0).bind(req.l_skin_1).bind(req.l_skin_2) + .bind(req.l_skin_3).bind(req.l_skin_4).bind(req.l_skin_5) + .bind(req.l_skin_6).bind(req.l_skin_7).bind(req.l_skin_8) // Clothes layer - .bind(req.l_clothes_0) - .bind(req.l_clothes_1) - .bind(req.l_clothes_2) - .bind(req.l_clothes_3) - .bind(req.l_clothes_4) - .bind(req.l_clothes_5) - .bind(req.l_clothes_6) - .bind(req.l_clothes_7) - .bind(req.l_clothes_8) + .bind(req.l_clothes_0).bind(req.l_clothes_1).bind(req.l_clothes_2) + .bind(req.l_clothes_3).bind(req.l_clothes_4).bind(req.l_clothes_5) + .bind(req.l_clothes_6).bind(req.l_clothes_7).bind(req.l_clothes_8) // Accessories layer - .bind(req.l_accessories_0) - .bind(req.l_accessories_1) - .bind(req.l_accessories_2) - .bind(req.l_accessories_3) - .bind(req.l_accessories_4) - .bind(req.l_accessories_5) - .bind(req.l_accessories_6) - .bind(req.l_accessories_7) - .bind(req.l_accessories_8) + .bind(req.l_accessories_0).bind(req.l_accessories_1).bind(req.l_accessories_2) + .bind(req.l_accessories_3).bind(req.l_accessories_4).bind(req.l_accessories_5) + .bind(req.l_accessories_6).bind(req.l_accessories_7).bind(req.l_accessories_8) // Neutral emotion - .bind(req.e_neutral_0) - .bind(req.e_neutral_1) - .bind(req.e_neutral_2) - .bind(req.e_neutral_3) - .bind(req.e_neutral_4) - .bind(req.e_neutral_5) - .bind(req.e_neutral_6) - .bind(req.e_neutral_7) - .bind(req.e_neutral_8) + .bind(req.e_neutral_0).bind(req.e_neutral_1).bind(req.e_neutral_2) + .bind(req.e_neutral_3).bind(req.e_neutral_4).bind(req.e_neutral_5) + .bind(req.e_neutral_6).bind(req.e_neutral_7).bind(req.e_neutral_8) // Happy emotion - .bind(req.e_happy_0) - .bind(req.e_happy_1) - .bind(req.e_happy_2) - .bind(req.e_happy_3) - .bind(req.e_happy_4) - .bind(req.e_happy_5) - .bind(req.e_happy_6) - .bind(req.e_happy_7) - .bind(req.e_happy_8) + .bind(req.e_happy_0).bind(req.e_happy_1).bind(req.e_happy_2) + .bind(req.e_happy_3).bind(req.e_happy_4).bind(req.e_happy_5) + .bind(req.e_happy_6).bind(req.e_happy_7).bind(req.e_happy_8) // Sad emotion - .bind(req.e_sad_0) - .bind(req.e_sad_1) - .bind(req.e_sad_2) - .bind(req.e_sad_3) - .bind(req.e_sad_4) - .bind(req.e_sad_5) - .bind(req.e_sad_6) - .bind(req.e_sad_7) - .bind(req.e_sad_8) + .bind(req.e_sad_0).bind(req.e_sad_1).bind(req.e_sad_2) + .bind(req.e_sad_3).bind(req.e_sad_4).bind(req.e_sad_5) + .bind(req.e_sad_6).bind(req.e_sad_7).bind(req.e_sad_8) // Angry emotion - .bind(req.e_angry_0) - .bind(req.e_angry_1) - .bind(req.e_angry_2) - .bind(req.e_angry_3) - .bind(req.e_angry_4) - .bind(req.e_angry_5) - .bind(req.e_angry_6) - .bind(req.e_angry_7) - .bind(req.e_angry_8) + .bind(req.e_angry_0).bind(req.e_angry_1).bind(req.e_angry_2) + .bind(req.e_angry_3).bind(req.e_angry_4).bind(req.e_angry_5) + .bind(req.e_angry_6).bind(req.e_angry_7).bind(req.e_angry_8) // Surprised emotion - .bind(req.e_surprised_0) - .bind(req.e_surprised_1) - .bind(req.e_surprised_2) - .bind(req.e_surprised_3) - .bind(req.e_surprised_4) - .bind(req.e_surprised_5) - .bind(req.e_surprised_6) - .bind(req.e_surprised_7) - .bind(req.e_surprised_8) + .bind(req.e_surprised_0).bind(req.e_surprised_1).bind(req.e_surprised_2) + .bind(req.e_surprised_3).bind(req.e_surprised_4).bind(req.e_surprised_5) + .bind(req.e_surprised_6).bind(req.e_surprised_7).bind(req.e_surprised_8) // Thinking emotion - .bind(req.e_thinking_0) - .bind(req.e_thinking_1) - .bind(req.e_thinking_2) - .bind(req.e_thinking_3) - .bind(req.e_thinking_4) - .bind(req.e_thinking_5) - .bind(req.e_thinking_6) - .bind(req.e_thinking_7) - .bind(req.e_thinking_8) + .bind(req.e_thinking_0).bind(req.e_thinking_1).bind(req.e_thinking_2) + .bind(req.e_thinking_3).bind(req.e_thinking_4).bind(req.e_thinking_5) + .bind(req.e_thinking_6).bind(req.e_thinking_7).bind(req.e_thinking_8) // Laughing emotion - .bind(req.e_laughing_0) - .bind(req.e_laughing_1) - .bind(req.e_laughing_2) - .bind(req.e_laughing_3) - .bind(req.e_laughing_4) - .bind(req.e_laughing_5) - .bind(req.e_laughing_6) - .bind(req.e_laughing_7) - .bind(req.e_laughing_8) + .bind(req.e_laughing_0).bind(req.e_laughing_1).bind(req.e_laughing_2) + .bind(req.e_laughing_3).bind(req.e_laughing_4).bind(req.e_laughing_5) + .bind(req.e_laughing_6).bind(req.e_laughing_7).bind(req.e_laughing_8) // Crying emotion - .bind(req.e_crying_0) - .bind(req.e_crying_1) - .bind(req.e_crying_2) - .bind(req.e_crying_3) - .bind(req.e_crying_4) - .bind(req.e_crying_5) - .bind(req.e_crying_6) - .bind(req.e_crying_7) - .bind(req.e_crying_8) + .bind(req.e_crying_0).bind(req.e_crying_1).bind(req.e_crying_2) + .bind(req.e_crying_3).bind(req.e_crying_4).bind(req.e_crying_5) + .bind(req.e_crying_6).bind(req.e_crying_7).bind(req.e_crying_8) // Love emotion - .bind(req.e_love_0) - .bind(req.e_love_1) - .bind(req.e_love_2) - .bind(req.e_love_3) - .bind(req.e_love_4) - .bind(req.e_love_5) - .bind(req.e_love_6) - .bind(req.e_love_7) - .bind(req.e_love_8) + .bind(req.e_love_0).bind(req.e_love_1).bind(req.e_love_2) + .bind(req.e_love_3).bind(req.e_love_4).bind(req.e_love_5) + .bind(req.e_love_6).bind(req.e_love_7).bind(req.e_love_8) // Confused emotion - .bind(req.e_confused_0) - .bind(req.e_confused_1) - .bind(req.e_confused_2) - .bind(req.e_confused_3) - .bind(req.e_confused_4) - .bind(req.e_confused_5) - .bind(req.e_confused_6) - .bind(req.e_confused_7) - .bind(req.e_confused_8) + .bind(req.e_confused_0).bind(req.e_confused_1).bind(req.e_confused_2) + .bind(req.e_confused_3).bind(req.e_confused_4).bind(req.e_confused_5) + .bind(req.e_confused_6).bind(req.e_confused_7).bind(req.e_confused_8) // Sleeping emotion - .bind(req.e_sleeping_0) - .bind(req.e_sleeping_1) - .bind(req.e_sleeping_2) - .bind(req.e_sleeping_3) - .bind(req.e_sleeping_4) - .bind(req.e_sleeping_5) - .bind(req.e_sleeping_6) - .bind(req.e_sleeping_7) - .bind(req.e_sleeping_8) + .bind(req.e_sleeping_0).bind(req.e_sleeping_1).bind(req.e_sleeping_2) + .bind(req.e_sleeping_3).bind(req.e_sleeping_4).bind(req.e_sleeping_5) + .bind(req.e_sleeping_6).bind(req.e_sleeping_7).bind(req.e_sleeping_8) // Wink emotion - .bind(req.e_wink_0) - .bind(req.e_wink_1) - .bind(req.e_wink_2) - .bind(req.e_wink_3) - .bind(req.e_wink_4) - .bind(req.e_wink_5) - .bind(req.e_wink_6) - .bind(req.e_wink_7) - .bind(req.e_wink_8) + .bind(req.e_wink_0).bind(req.e_wink_1).bind(req.e_wink_2) + .bind(req.e_wink_3).bind(req.e_wink_4).bind(req.e_wink_5) + .bind(req.e_wink_6).bind(req.e_wink_7).bind(req.e_wink_8) .fetch_one(executor) .await?;