fix some emotion bugs
This commit is contained in:
parent
bd28e201a2
commit
989e20757b
11 changed files with 1203 additions and 190 deletions
|
|
@ -1,9 +1,11 @@
|
|||
//! Avatar-related database queries.
|
||||
|
||||
use sqlx::PgExecutor;
|
||||
use std::collections::HashMap;
|
||||
|
||||
use sqlx::{postgres::PgConnection, PgExecutor, PgPool};
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::models::{ActiveAvatar, AvatarRenderData, EmotionAvailability};
|
||||
use crate::models::{ActiveAvatar, AvatarWithPaths, EmotionAvailability};
|
||||
use chattyness_error::AppError;
|
||||
|
||||
/// Get the active avatar for a user in a realm.
|
||||
|
|
@ -112,96 +114,6 @@ struct EmotionLayerRow {
|
|||
p8: Option<String>,
|
||||
}
|
||||
|
||||
/// Get render data for a user's avatar in a realm.
|
||||
///
|
||||
/// Returns the asset paths for all equipped props in the avatar's current state.
|
||||
/// This is a simplified version that only returns the center position (position 4)
|
||||
/// props for skin, clothes, accessories, and current emotion layers.
|
||||
pub async fn get_avatar_render_data<'e>(
|
||||
executor: impl PgExecutor<'e>,
|
||||
user_id: Uuid,
|
||||
realm_id: Uuid,
|
||||
) -> Result<AvatarRenderData, AppError> {
|
||||
// Simplified query: just get position 4 (center) props for each layer
|
||||
// This covers the common case of simple face avatars
|
||||
let render_data = sqlx::query_as::<_, SimplifiedAvatarRow>(
|
||||
r#"
|
||||
SELECT
|
||||
a.id as avatar_id,
|
||||
aa.current_emotion,
|
||||
-- Skin layer center
|
||||
skin.prop_asset_path as skin_center,
|
||||
-- Clothes layer center
|
||||
clothes.prop_asset_path as clothes_center,
|
||||
-- Accessories layer center
|
||||
acc.prop_asset_path as accessories_center,
|
||||
-- Current emotion layer center (based on current_emotion)
|
||||
CASE aa.current_emotion
|
||||
WHEN 0 THEN (SELECT prop_asset_path FROM props.inventory WHERE id = a.e_neutral_4)
|
||||
WHEN 1 THEN (SELECT prop_asset_path FROM props.inventory WHERE id = a.e_happy_4)
|
||||
WHEN 2 THEN (SELECT prop_asset_path FROM props.inventory WHERE id = a.e_sad_4)
|
||||
WHEN 3 THEN (SELECT prop_asset_path FROM props.inventory WHERE id = a.e_angry_4)
|
||||
WHEN 4 THEN (SELECT prop_asset_path FROM props.inventory WHERE id = a.e_surprised_4)
|
||||
WHEN 5 THEN (SELECT prop_asset_path FROM props.inventory WHERE id = a.e_thinking_4)
|
||||
WHEN 6 THEN (SELECT prop_asset_path FROM props.inventory WHERE id = a.e_laughing_4)
|
||||
WHEN 7 THEN (SELECT prop_asset_path FROM props.inventory WHERE id = a.e_crying_4)
|
||||
WHEN 8 THEN (SELECT prop_asset_path FROM props.inventory WHERE id = a.e_love_4)
|
||||
WHEN 9 THEN (SELECT prop_asset_path FROM props.inventory WHERE id = a.e_confused_4)
|
||||
END as emotion_center
|
||||
FROM props.active_avatars aa
|
||||
JOIN props.avatars a ON aa.avatar_id = a.id
|
||||
LEFT JOIN props.inventory skin ON a.l_skin_4 = skin.id
|
||||
LEFT JOIN props.inventory clothes ON a.l_clothes_4 = clothes.id
|
||||
LEFT JOIN props.inventory acc ON a.l_accessories_4 = acc.id
|
||||
WHERE aa.user_id = $1 AND aa.realm_id = $2
|
||||
"#,
|
||||
)
|
||||
.bind(user_id)
|
||||
.bind(realm_id)
|
||||
.fetch_optional(executor)
|
||||
.await?;
|
||||
|
||||
match render_data {
|
||||
Some(row) => Ok(row.into()),
|
||||
None => Ok(AvatarRenderData::default()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Simplified avatar row for center-only rendering.
|
||||
#[derive(Debug, sqlx::FromRow)]
|
||||
struct SimplifiedAvatarRow {
|
||||
avatar_id: Uuid,
|
||||
current_emotion: i16,
|
||||
skin_center: Option<String>,
|
||||
clothes_center: Option<String>,
|
||||
accessories_center: Option<String>,
|
||||
emotion_center: Option<String>,
|
||||
}
|
||||
|
||||
impl From<SimplifiedAvatarRow> for AvatarRenderData {
|
||||
fn from(row: SimplifiedAvatarRow) -> Self {
|
||||
// For now, only populate position 4 (center)
|
||||
let mut skin_layer: [Option<String>; 9] = Default::default();
|
||||
let mut clothes_layer: [Option<String>; 9] = Default::default();
|
||||
let mut accessories_layer: [Option<String>; 9] = Default::default();
|
||||
let mut emotion_layer: [Option<String>; 9] = Default::default();
|
||||
|
||||
skin_layer[4] = row.skin_center;
|
||||
clothes_layer[4] = row.clothes_center;
|
||||
accessories_layer[4] = row.accessories_center;
|
||||
emotion_layer[4] = row.emotion_center;
|
||||
|
||||
Self {
|
||||
avatar_id: row.avatar_id,
|
||||
current_emotion: row.current_emotion,
|
||||
skin_layer,
|
||||
clothes_layer,
|
||||
accessories_layer,
|
||||
emotion_layer,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Get emotion availability for a user's avatar in a realm.
|
||||
///
|
||||
/// Returns which emotions have assets configured (any of positions 0-8 non-null)
|
||||
|
|
@ -365,3 +277,688 @@ impl From<EmotionAvailabilityRow> for EmotionAvailability {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the full avatar with all inventory UUIDs resolved to asset paths.
|
||||
///
|
||||
/// This function uses two queries:
|
||||
/// 1. Fetch the avatar row with all 135 UUID slots
|
||||
/// 2. Bulk resolve all UUIDs to asset paths with a single inventory query
|
||||
///
|
||||
/// The result enables client-side emotion availability computation and rendering.
|
||||
pub async fn get_avatar_with_paths(
|
||||
pool: &PgPool,
|
||||
user_id: Uuid,
|
||||
realm_id: Uuid,
|
||||
) -> Result<Option<AvatarWithPaths>, AppError> {
|
||||
// Query 1: Get the avatar row with current_emotion from active_avatars
|
||||
let avatar_row = sqlx::query_as::<_, AvatarWithEmotion>(
|
||||
r#"
|
||||
SELECT
|
||||
a.*,
|
||||
aa.current_emotion
|
||||
FROM props.active_avatars aa
|
||||
JOIN props.avatars a ON aa.avatar_id = a.id
|
||||
WHERE aa.user_id = $1 AND aa.realm_id = $2
|
||||
"#,
|
||||
)
|
||||
.bind(user_id)
|
||||
.bind(realm_id)
|
||||
.fetch_optional(pool)
|
||||
.await?;
|
||||
|
||||
let Some(avatar) = avatar_row else {
|
||||
return Ok(None);
|
||||
};
|
||||
|
||||
// Collect all non-null UUIDs from the avatar slots
|
||||
let mut uuids: Vec<Uuid> = Vec::new();
|
||||
|
||||
// Content layers
|
||||
collect_uuids(&mut uuids, &[
|
||||
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,
|
||||
]);
|
||||
collect_uuids(&mut uuids, &[
|
||||
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,
|
||||
]);
|
||||
collect_uuids(&mut uuids, &[
|
||||
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,
|
||||
]);
|
||||
|
||||
// Emotion layers (12 emotions × 9 positions)
|
||||
collect_uuids(&mut uuids, &[
|
||||
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,
|
||||
]);
|
||||
collect_uuids(&mut uuids, &[
|
||||
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,
|
||||
]);
|
||||
collect_uuids(&mut uuids, &[
|
||||
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,
|
||||
]);
|
||||
collect_uuids(&mut uuids, &[
|
||||
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,
|
||||
]);
|
||||
collect_uuids(&mut uuids, &[
|
||||
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,
|
||||
]);
|
||||
collect_uuids(&mut uuids, &[
|
||||
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,
|
||||
]);
|
||||
collect_uuids(&mut uuids, &[
|
||||
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,
|
||||
]);
|
||||
collect_uuids(&mut uuids, &[
|
||||
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,
|
||||
]);
|
||||
collect_uuids(&mut uuids, &[
|
||||
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,
|
||||
]);
|
||||
collect_uuids(&mut uuids, &[
|
||||
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,
|
||||
]);
|
||||
collect_uuids(&mut uuids, &[
|
||||
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,
|
||||
]);
|
||||
collect_uuids(&mut uuids, &[
|
||||
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,
|
||||
]);
|
||||
|
||||
// Query 2: Bulk resolve all UUIDs to paths
|
||||
let paths: HashMap<Uuid, String> = if uuids.is_empty() {
|
||||
HashMap::new()
|
||||
} else {
|
||||
sqlx::query_as::<_, (Uuid, String)>(
|
||||
"SELECT id, prop_asset_path FROM props.inventory WHERE id = ANY($1)",
|
||||
)
|
||||
.bind(&uuids)
|
||||
.fetch_all(pool)
|
||||
.await?
|
||||
.into_iter()
|
||||
.collect()
|
||||
};
|
||||
|
||||
// Build the AvatarWithPaths
|
||||
let resolve = |uuid: Option<Uuid>| -> Option<String> {
|
||||
uuid.and_then(|id| paths.get(&id).cloned())
|
||||
};
|
||||
|
||||
// Check if any UUID in the array is non-null (emotion is available)
|
||||
let has_any = |slots: &[Option<Uuid>]| -> bool {
|
||||
slots.iter().any(|u| u.is_some())
|
||||
};
|
||||
|
||||
// Compute emotions_available from UUID presence (not path resolution)
|
||||
let emotions_available = [
|
||||
has_any(&[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]),
|
||||
has_any(&[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]),
|
||||
has_any(&[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]),
|
||||
has_any(&[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]),
|
||||
has_any(&[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]),
|
||||
has_any(&[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]),
|
||||
has_any(&[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]),
|
||||
has_any(&[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]),
|
||||
has_any(&[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]),
|
||||
has_any(&[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]),
|
||||
has_any(&[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]),
|
||||
has_any(&[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]),
|
||||
];
|
||||
|
||||
Ok(Some(AvatarWithPaths {
|
||||
avatar_id: avatar.id,
|
||||
current_emotion: avatar.current_emotion,
|
||||
skin_layer: [
|
||||
resolve(avatar.l_skin_0), resolve(avatar.l_skin_1), resolve(avatar.l_skin_2),
|
||||
resolve(avatar.l_skin_3), resolve(avatar.l_skin_4), resolve(avatar.l_skin_5),
|
||||
resolve(avatar.l_skin_6), resolve(avatar.l_skin_7), resolve(avatar.l_skin_8),
|
||||
],
|
||||
clothes_layer: [
|
||||
resolve(avatar.l_clothes_0), resolve(avatar.l_clothes_1), resolve(avatar.l_clothes_2),
|
||||
resolve(avatar.l_clothes_3), resolve(avatar.l_clothes_4), resolve(avatar.l_clothes_5),
|
||||
resolve(avatar.l_clothes_6), resolve(avatar.l_clothes_7), resolve(avatar.l_clothes_8),
|
||||
],
|
||||
accessories_layer: [
|
||||
resolve(avatar.l_accessories_0), resolve(avatar.l_accessories_1), resolve(avatar.l_accessories_2),
|
||||
resolve(avatar.l_accessories_3), resolve(avatar.l_accessories_4), resolve(avatar.l_accessories_5),
|
||||
resolve(avatar.l_accessories_6), resolve(avatar.l_accessories_7), resolve(avatar.l_accessories_8),
|
||||
],
|
||||
emotions: [
|
||||
// Neutral (0)
|
||||
[
|
||||
resolve(avatar.e_neutral_0), resolve(avatar.e_neutral_1), resolve(avatar.e_neutral_2),
|
||||
resolve(avatar.e_neutral_3), resolve(avatar.e_neutral_4), resolve(avatar.e_neutral_5),
|
||||
resolve(avatar.e_neutral_6), resolve(avatar.e_neutral_7), resolve(avatar.e_neutral_8),
|
||||
],
|
||||
// Happy (1)
|
||||
[
|
||||
resolve(avatar.e_happy_0), resolve(avatar.e_happy_1), resolve(avatar.e_happy_2),
|
||||
resolve(avatar.e_happy_3), resolve(avatar.e_happy_4), resolve(avatar.e_happy_5),
|
||||
resolve(avatar.e_happy_6), resolve(avatar.e_happy_7), resolve(avatar.e_happy_8),
|
||||
],
|
||||
// Sad (2)
|
||||
[
|
||||
resolve(avatar.e_sad_0), resolve(avatar.e_sad_1), resolve(avatar.e_sad_2),
|
||||
resolve(avatar.e_sad_3), resolve(avatar.e_sad_4), resolve(avatar.e_sad_5),
|
||||
resolve(avatar.e_sad_6), resolve(avatar.e_sad_7), resolve(avatar.e_sad_8),
|
||||
],
|
||||
// Angry (3)
|
||||
[
|
||||
resolve(avatar.e_angry_0), resolve(avatar.e_angry_1), resolve(avatar.e_angry_2),
|
||||
resolve(avatar.e_angry_3), resolve(avatar.e_angry_4), resolve(avatar.e_angry_5),
|
||||
resolve(avatar.e_angry_6), resolve(avatar.e_angry_7), resolve(avatar.e_angry_8),
|
||||
],
|
||||
// Surprised (4)
|
||||
[
|
||||
resolve(avatar.e_surprised_0), resolve(avatar.e_surprised_1), resolve(avatar.e_surprised_2),
|
||||
resolve(avatar.e_surprised_3), resolve(avatar.e_surprised_4), resolve(avatar.e_surprised_5),
|
||||
resolve(avatar.e_surprised_6), resolve(avatar.e_surprised_7), resolve(avatar.e_surprised_8),
|
||||
],
|
||||
// Thinking (5)
|
||||
[
|
||||
resolve(avatar.e_thinking_0), resolve(avatar.e_thinking_1), resolve(avatar.e_thinking_2),
|
||||
resolve(avatar.e_thinking_3), resolve(avatar.e_thinking_4), resolve(avatar.e_thinking_5),
|
||||
resolve(avatar.e_thinking_6), resolve(avatar.e_thinking_7), resolve(avatar.e_thinking_8),
|
||||
],
|
||||
// Laughing (6)
|
||||
[
|
||||
resolve(avatar.e_laughing_0), resolve(avatar.e_laughing_1), resolve(avatar.e_laughing_2),
|
||||
resolve(avatar.e_laughing_3), resolve(avatar.e_laughing_4), resolve(avatar.e_laughing_5),
|
||||
resolve(avatar.e_laughing_6), resolve(avatar.e_laughing_7), resolve(avatar.e_laughing_8),
|
||||
],
|
||||
// Crying (7)
|
||||
[
|
||||
resolve(avatar.e_crying_0), resolve(avatar.e_crying_1), resolve(avatar.e_crying_2),
|
||||
resolve(avatar.e_crying_3), resolve(avatar.e_crying_4), resolve(avatar.e_crying_5),
|
||||
resolve(avatar.e_crying_6), resolve(avatar.e_crying_7), resolve(avatar.e_crying_8),
|
||||
],
|
||||
// Love (8)
|
||||
[
|
||||
resolve(avatar.e_love_0), resolve(avatar.e_love_1), resolve(avatar.e_love_2),
|
||||
resolve(avatar.e_love_3), resolve(avatar.e_love_4), resolve(avatar.e_love_5),
|
||||
resolve(avatar.e_love_6), resolve(avatar.e_love_7), resolve(avatar.e_love_8),
|
||||
],
|
||||
// Confused (9)
|
||||
[
|
||||
resolve(avatar.e_confused_0), resolve(avatar.e_confused_1), resolve(avatar.e_confused_2),
|
||||
resolve(avatar.e_confused_3), resolve(avatar.e_confused_4), resolve(avatar.e_confused_5),
|
||||
resolve(avatar.e_confused_6), resolve(avatar.e_confused_7), resolve(avatar.e_confused_8),
|
||||
],
|
||||
// Sleeping (10)
|
||||
[
|
||||
resolve(avatar.e_sleeping_0), resolve(avatar.e_sleeping_1), resolve(avatar.e_sleeping_2),
|
||||
resolve(avatar.e_sleeping_3), resolve(avatar.e_sleeping_4), resolve(avatar.e_sleeping_5),
|
||||
resolve(avatar.e_sleeping_6), resolve(avatar.e_sleeping_7), resolve(avatar.e_sleeping_8),
|
||||
],
|
||||
// Wink (11)
|
||||
[
|
||||
resolve(avatar.e_wink_0), resolve(avatar.e_wink_1), resolve(avatar.e_wink_2),
|
||||
resolve(avatar.e_wink_3), resolve(avatar.e_wink_4), resolve(avatar.e_wink_5),
|
||||
resolve(avatar.e_wink_6), resolve(avatar.e_wink_7), resolve(avatar.e_wink_8),
|
||||
],
|
||||
],
|
||||
emotions_available,
|
||||
}))
|
||||
}
|
||||
|
||||
/// Get full avatar with all inventory UUIDs resolved to asset paths (connection variant).
|
||||
///
|
||||
/// This variant accepts a mutable connection reference for use with RLS-enabled connections.
|
||||
pub async fn get_avatar_with_paths_conn(
|
||||
conn: &mut PgConnection,
|
||||
user_id: Uuid,
|
||||
realm_id: Uuid,
|
||||
) -> Result<Option<AvatarWithPaths>, AppError> {
|
||||
// Query 1: Get the avatar row with current_emotion from active_avatars
|
||||
let avatar_row = sqlx::query_as::<_, AvatarWithEmotion>(
|
||||
r#"
|
||||
SELECT
|
||||
a.*,
|
||||
aa.current_emotion
|
||||
FROM props.active_avatars aa
|
||||
JOIN props.avatars a ON aa.avatar_id = a.id
|
||||
WHERE aa.user_id = $1 AND aa.realm_id = $2
|
||||
"#,
|
||||
)
|
||||
.bind(user_id)
|
||||
.bind(realm_id)
|
||||
.fetch_optional(&mut *conn)
|
||||
.await?;
|
||||
|
||||
let Some(avatar) = avatar_row else {
|
||||
return Ok(None);
|
||||
};
|
||||
|
||||
// Collect all non-null UUIDs from the avatar slots
|
||||
let mut uuids: Vec<Uuid> = Vec::new();
|
||||
|
||||
// Content layers
|
||||
collect_uuids(&mut uuids, &[
|
||||
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,
|
||||
]);
|
||||
collect_uuids(&mut uuids, &[
|
||||
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,
|
||||
]);
|
||||
collect_uuids(&mut uuids, &[
|
||||
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,
|
||||
]);
|
||||
|
||||
// Emotion layers (12 emotions × 9 positions)
|
||||
collect_uuids(&mut uuids, &[
|
||||
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,
|
||||
]);
|
||||
collect_uuids(&mut uuids, &[
|
||||
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,
|
||||
]);
|
||||
collect_uuids(&mut uuids, &[
|
||||
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,
|
||||
]);
|
||||
collect_uuids(&mut uuids, &[
|
||||
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,
|
||||
]);
|
||||
collect_uuids(&mut uuids, &[
|
||||
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,
|
||||
]);
|
||||
collect_uuids(&mut uuids, &[
|
||||
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,
|
||||
]);
|
||||
collect_uuids(&mut uuids, &[
|
||||
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,
|
||||
]);
|
||||
collect_uuids(&mut uuids, &[
|
||||
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,
|
||||
]);
|
||||
collect_uuids(&mut uuids, &[
|
||||
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,
|
||||
]);
|
||||
collect_uuids(&mut uuids, &[
|
||||
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,
|
||||
]);
|
||||
collect_uuids(&mut uuids, &[
|
||||
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,
|
||||
]);
|
||||
collect_uuids(&mut uuids, &[
|
||||
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,
|
||||
]);
|
||||
|
||||
// Query 2: Bulk resolve all UUIDs to paths
|
||||
let paths: HashMap<Uuid, String> = if uuids.is_empty() {
|
||||
HashMap::new()
|
||||
} else {
|
||||
sqlx::query_as::<_, (Uuid, String)>(
|
||||
"SELECT id, prop_asset_path FROM props.inventory WHERE id = ANY($1)",
|
||||
)
|
||||
.bind(&uuids)
|
||||
.fetch_all(&mut *conn)
|
||||
.await?
|
||||
.into_iter()
|
||||
.collect()
|
||||
};
|
||||
|
||||
// Build the AvatarWithPaths
|
||||
let resolve = |uuid: Option<Uuid>| -> Option<String> {
|
||||
uuid.and_then(|id| paths.get(&id).cloned())
|
||||
};
|
||||
|
||||
// Check if any UUID in the array is non-null (emotion is available)
|
||||
let has_any = |slots: &[Option<Uuid>]| -> bool {
|
||||
slots.iter().any(|u| u.is_some())
|
||||
};
|
||||
|
||||
// Compute emotions_available from UUID presence (not path resolution)
|
||||
let emotions_available = [
|
||||
has_any(&[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]),
|
||||
has_any(&[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]),
|
||||
has_any(&[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]),
|
||||
has_any(&[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]),
|
||||
has_any(&[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]),
|
||||
has_any(&[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]),
|
||||
has_any(&[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]),
|
||||
has_any(&[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]),
|
||||
has_any(&[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]),
|
||||
has_any(&[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]),
|
||||
has_any(&[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]),
|
||||
has_any(&[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]),
|
||||
];
|
||||
|
||||
Ok(Some(AvatarWithPaths {
|
||||
avatar_id: avatar.id,
|
||||
current_emotion: avatar.current_emotion,
|
||||
skin_layer: [
|
||||
resolve(avatar.l_skin_0), resolve(avatar.l_skin_1), resolve(avatar.l_skin_2),
|
||||
resolve(avatar.l_skin_3), resolve(avatar.l_skin_4), resolve(avatar.l_skin_5),
|
||||
resolve(avatar.l_skin_6), resolve(avatar.l_skin_7), resolve(avatar.l_skin_8),
|
||||
],
|
||||
clothes_layer: [
|
||||
resolve(avatar.l_clothes_0), resolve(avatar.l_clothes_1), resolve(avatar.l_clothes_2),
|
||||
resolve(avatar.l_clothes_3), resolve(avatar.l_clothes_4), resolve(avatar.l_clothes_5),
|
||||
resolve(avatar.l_clothes_6), resolve(avatar.l_clothes_7), resolve(avatar.l_clothes_8),
|
||||
],
|
||||
accessories_layer: [
|
||||
resolve(avatar.l_accessories_0), resolve(avatar.l_accessories_1), resolve(avatar.l_accessories_2),
|
||||
resolve(avatar.l_accessories_3), resolve(avatar.l_accessories_4), resolve(avatar.l_accessories_5),
|
||||
resolve(avatar.l_accessories_6), resolve(avatar.l_accessories_7), resolve(avatar.l_accessories_8),
|
||||
],
|
||||
emotions: [
|
||||
// Neutral (0)
|
||||
[
|
||||
resolve(avatar.e_neutral_0), resolve(avatar.e_neutral_1), resolve(avatar.e_neutral_2),
|
||||
resolve(avatar.e_neutral_3), resolve(avatar.e_neutral_4), resolve(avatar.e_neutral_5),
|
||||
resolve(avatar.e_neutral_6), resolve(avatar.e_neutral_7), resolve(avatar.e_neutral_8),
|
||||
],
|
||||
// Happy (1)
|
||||
[
|
||||
resolve(avatar.e_happy_0), resolve(avatar.e_happy_1), resolve(avatar.e_happy_2),
|
||||
resolve(avatar.e_happy_3), resolve(avatar.e_happy_4), resolve(avatar.e_happy_5),
|
||||
resolve(avatar.e_happy_6), resolve(avatar.e_happy_7), resolve(avatar.e_happy_8),
|
||||
],
|
||||
// Sad (2)
|
||||
[
|
||||
resolve(avatar.e_sad_0), resolve(avatar.e_sad_1), resolve(avatar.e_sad_2),
|
||||
resolve(avatar.e_sad_3), resolve(avatar.e_sad_4), resolve(avatar.e_sad_5),
|
||||
resolve(avatar.e_sad_6), resolve(avatar.e_sad_7), resolve(avatar.e_sad_8),
|
||||
],
|
||||
// Angry (3)
|
||||
[
|
||||
resolve(avatar.e_angry_0), resolve(avatar.e_angry_1), resolve(avatar.e_angry_2),
|
||||
resolve(avatar.e_angry_3), resolve(avatar.e_angry_4), resolve(avatar.e_angry_5),
|
||||
resolve(avatar.e_angry_6), resolve(avatar.e_angry_7), resolve(avatar.e_angry_8),
|
||||
],
|
||||
// Surprised (4)
|
||||
[
|
||||
resolve(avatar.e_surprised_0), resolve(avatar.e_surprised_1), resolve(avatar.e_surprised_2),
|
||||
resolve(avatar.e_surprised_3), resolve(avatar.e_surprised_4), resolve(avatar.e_surprised_5),
|
||||
resolve(avatar.e_surprised_6), resolve(avatar.e_surprised_7), resolve(avatar.e_surprised_8),
|
||||
],
|
||||
// Thinking (5)
|
||||
[
|
||||
resolve(avatar.e_thinking_0), resolve(avatar.e_thinking_1), resolve(avatar.e_thinking_2),
|
||||
resolve(avatar.e_thinking_3), resolve(avatar.e_thinking_4), resolve(avatar.e_thinking_5),
|
||||
resolve(avatar.e_thinking_6), resolve(avatar.e_thinking_7), resolve(avatar.e_thinking_8),
|
||||
],
|
||||
// Laughing (6)
|
||||
[
|
||||
resolve(avatar.e_laughing_0), resolve(avatar.e_laughing_1), resolve(avatar.e_laughing_2),
|
||||
resolve(avatar.e_laughing_3), resolve(avatar.e_laughing_4), resolve(avatar.e_laughing_5),
|
||||
resolve(avatar.e_laughing_6), resolve(avatar.e_laughing_7), resolve(avatar.e_laughing_8),
|
||||
],
|
||||
// Crying (7)
|
||||
[
|
||||
resolve(avatar.e_crying_0), resolve(avatar.e_crying_1), resolve(avatar.e_crying_2),
|
||||
resolve(avatar.e_crying_3), resolve(avatar.e_crying_4), resolve(avatar.e_crying_5),
|
||||
resolve(avatar.e_crying_6), resolve(avatar.e_crying_7), resolve(avatar.e_crying_8),
|
||||
],
|
||||
// Love (8)
|
||||
[
|
||||
resolve(avatar.e_love_0), resolve(avatar.e_love_1), resolve(avatar.e_love_2),
|
||||
resolve(avatar.e_love_3), resolve(avatar.e_love_4), resolve(avatar.e_love_5),
|
||||
resolve(avatar.e_love_6), resolve(avatar.e_love_7), resolve(avatar.e_love_8),
|
||||
],
|
||||
// Confused (9)
|
||||
[
|
||||
resolve(avatar.e_confused_0), resolve(avatar.e_confused_1), resolve(avatar.e_confused_2),
|
||||
resolve(avatar.e_confused_3), resolve(avatar.e_confused_4), resolve(avatar.e_confused_5),
|
||||
resolve(avatar.e_confused_6), resolve(avatar.e_confused_7), resolve(avatar.e_confused_8),
|
||||
],
|
||||
// Sleeping (10)
|
||||
[
|
||||
resolve(avatar.e_sleeping_0), resolve(avatar.e_sleeping_1), resolve(avatar.e_sleeping_2),
|
||||
resolve(avatar.e_sleeping_3), resolve(avatar.e_sleeping_4), resolve(avatar.e_sleeping_5),
|
||||
resolve(avatar.e_sleeping_6), resolve(avatar.e_sleeping_7), resolve(avatar.e_sleeping_8),
|
||||
],
|
||||
// Wink (11)
|
||||
[
|
||||
resolve(avatar.e_wink_0), resolve(avatar.e_wink_1), resolve(avatar.e_wink_2),
|
||||
resolve(avatar.e_wink_3), resolve(avatar.e_wink_4), resolve(avatar.e_wink_5),
|
||||
resolve(avatar.e_wink_6), resolve(avatar.e_wink_7), resolve(avatar.e_wink_8),
|
||||
],
|
||||
],
|
||||
emotions_available,
|
||||
}))
|
||||
}
|
||||
|
||||
/// Helper to collect non-null UUIDs into a Vec.
|
||||
fn collect_uuids(dest: &mut Vec<Uuid>, sources: &[Option<Uuid>]) {
|
||||
for uuid in sources {
|
||||
if let Some(id) = uuid {
|
||||
dest.push(*id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Avatar row with current_emotion from active_avatars join.
|
||||
#[derive(Debug, sqlx::FromRow)]
|
||||
struct AvatarWithEmotion {
|
||||
pub id: Uuid,
|
||||
pub current_emotion: i16,
|
||||
// Content layers
|
||||
pub l_skin_0: Option<Uuid>,
|
||||
pub l_skin_1: Option<Uuid>,
|
||||
pub l_skin_2: Option<Uuid>,
|
||||
pub l_skin_3: Option<Uuid>,
|
||||
pub l_skin_4: Option<Uuid>,
|
||||
pub l_skin_5: Option<Uuid>,
|
||||
pub l_skin_6: Option<Uuid>,
|
||||
pub l_skin_7: Option<Uuid>,
|
||||
pub l_skin_8: Option<Uuid>,
|
||||
pub l_clothes_0: Option<Uuid>,
|
||||
pub l_clothes_1: Option<Uuid>,
|
||||
pub l_clothes_2: Option<Uuid>,
|
||||
pub l_clothes_3: Option<Uuid>,
|
||||
pub l_clothes_4: Option<Uuid>,
|
||||
pub l_clothes_5: Option<Uuid>,
|
||||
pub l_clothes_6: Option<Uuid>,
|
||||
pub l_clothes_7: Option<Uuid>,
|
||||
pub l_clothes_8: Option<Uuid>,
|
||||
pub l_accessories_0: Option<Uuid>,
|
||||
pub l_accessories_1: Option<Uuid>,
|
||||
pub l_accessories_2: Option<Uuid>,
|
||||
pub l_accessories_3: Option<Uuid>,
|
||||
pub l_accessories_4: Option<Uuid>,
|
||||
pub l_accessories_5: Option<Uuid>,
|
||||
pub l_accessories_6: Option<Uuid>,
|
||||
pub l_accessories_7: Option<Uuid>,
|
||||
pub l_accessories_8: Option<Uuid>,
|
||||
// Emotion layers
|
||||
pub e_neutral_0: Option<Uuid>,
|
||||
pub e_neutral_1: Option<Uuid>,
|
||||
pub e_neutral_2: Option<Uuid>,
|
||||
pub e_neutral_3: Option<Uuid>,
|
||||
pub e_neutral_4: Option<Uuid>,
|
||||
pub e_neutral_5: Option<Uuid>,
|
||||
pub e_neutral_6: Option<Uuid>,
|
||||
pub e_neutral_7: Option<Uuid>,
|
||||
pub e_neutral_8: Option<Uuid>,
|
||||
pub e_happy_0: Option<Uuid>,
|
||||
pub e_happy_1: Option<Uuid>,
|
||||
pub e_happy_2: Option<Uuid>,
|
||||
pub e_happy_3: Option<Uuid>,
|
||||
pub e_happy_4: Option<Uuid>,
|
||||
pub e_happy_5: Option<Uuid>,
|
||||
pub e_happy_6: Option<Uuid>,
|
||||
pub e_happy_7: Option<Uuid>,
|
||||
pub e_happy_8: Option<Uuid>,
|
||||
pub e_sad_0: Option<Uuid>,
|
||||
pub e_sad_1: Option<Uuid>,
|
||||
pub e_sad_2: Option<Uuid>,
|
||||
pub e_sad_3: Option<Uuid>,
|
||||
pub e_sad_4: Option<Uuid>,
|
||||
pub e_sad_5: Option<Uuid>,
|
||||
pub e_sad_6: Option<Uuid>,
|
||||
pub e_sad_7: Option<Uuid>,
|
||||
pub e_sad_8: Option<Uuid>,
|
||||
pub e_angry_0: Option<Uuid>,
|
||||
pub e_angry_1: Option<Uuid>,
|
||||
pub e_angry_2: Option<Uuid>,
|
||||
pub e_angry_3: Option<Uuid>,
|
||||
pub e_angry_4: Option<Uuid>,
|
||||
pub e_angry_5: Option<Uuid>,
|
||||
pub e_angry_6: Option<Uuid>,
|
||||
pub e_angry_7: Option<Uuid>,
|
||||
pub e_angry_8: Option<Uuid>,
|
||||
pub e_surprised_0: Option<Uuid>,
|
||||
pub e_surprised_1: Option<Uuid>,
|
||||
pub e_surprised_2: Option<Uuid>,
|
||||
pub e_surprised_3: Option<Uuid>,
|
||||
pub e_surprised_4: Option<Uuid>,
|
||||
pub e_surprised_5: Option<Uuid>,
|
||||
pub e_surprised_6: Option<Uuid>,
|
||||
pub e_surprised_7: Option<Uuid>,
|
||||
pub e_surprised_8: Option<Uuid>,
|
||||
pub e_thinking_0: Option<Uuid>,
|
||||
pub e_thinking_1: Option<Uuid>,
|
||||
pub e_thinking_2: Option<Uuid>,
|
||||
pub e_thinking_3: Option<Uuid>,
|
||||
pub e_thinking_4: Option<Uuid>,
|
||||
pub e_thinking_5: Option<Uuid>,
|
||||
pub e_thinking_6: Option<Uuid>,
|
||||
pub e_thinking_7: Option<Uuid>,
|
||||
pub e_thinking_8: Option<Uuid>,
|
||||
pub e_laughing_0: Option<Uuid>,
|
||||
pub e_laughing_1: Option<Uuid>,
|
||||
pub e_laughing_2: Option<Uuid>,
|
||||
pub e_laughing_3: Option<Uuid>,
|
||||
pub e_laughing_4: Option<Uuid>,
|
||||
pub e_laughing_5: Option<Uuid>,
|
||||
pub e_laughing_6: Option<Uuid>,
|
||||
pub e_laughing_7: Option<Uuid>,
|
||||
pub e_laughing_8: Option<Uuid>,
|
||||
pub e_crying_0: Option<Uuid>,
|
||||
pub e_crying_1: Option<Uuid>,
|
||||
pub e_crying_2: Option<Uuid>,
|
||||
pub e_crying_3: Option<Uuid>,
|
||||
pub e_crying_4: Option<Uuid>,
|
||||
pub e_crying_5: Option<Uuid>,
|
||||
pub e_crying_6: Option<Uuid>,
|
||||
pub e_crying_7: Option<Uuid>,
|
||||
pub e_crying_8: Option<Uuid>,
|
||||
pub e_love_0: Option<Uuid>,
|
||||
pub e_love_1: Option<Uuid>,
|
||||
pub e_love_2: Option<Uuid>,
|
||||
pub e_love_3: Option<Uuid>,
|
||||
pub e_love_4: Option<Uuid>,
|
||||
pub e_love_5: Option<Uuid>,
|
||||
pub e_love_6: Option<Uuid>,
|
||||
pub e_love_7: Option<Uuid>,
|
||||
pub e_love_8: Option<Uuid>,
|
||||
pub e_confused_0: Option<Uuid>,
|
||||
pub e_confused_1: Option<Uuid>,
|
||||
pub e_confused_2: Option<Uuid>,
|
||||
pub e_confused_3: Option<Uuid>,
|
||||
pub e_confused_4: Option<Uuid>,
|
||||
pub e_confused_5: Option<Uuid>,
|
||||
pub e_confused_6: Option<Uuid>,
|
||||
pub e_confused_7: Option<Uuid>,
|
||||
pub e_confused_8: Option<Uuid>,
|
||||
pub e_sleeping_0: Option<Uuid>,
|
||||
pub e_sleeping_1: Option<Uuid>,
|
||||
pub e_sleeping_2: Option<Uuid>,
|
||||
pub e_sleeping_3: Option<Uuid>,
|
||||
pub e_sleeping_4: Option<Uuid>,
|
||||
pub e_sleeping_5: Option<Uuid>,
|
||||
pub e_sleeping_6: Option<Uuid>,
|
||||
pub e_sleeping_7: Option<Uuid>,
|
||||
pub e_sleeping_8: Option<Uuid>,
|
||||
pub e_wink_0: Option<Uuid>,
|
||||
pub e_wink_1: Option<Uuid>,
|
||||
pub e_wink_2: Option<Uuid>,
|
||||
pub e_wink_3: Option<Uuid>,
|
||||
pub e_wink_4: Option<Uuid>,
|
||||
pub e_wink_5: Option<Uuid>,
|
||||
pub e_wink_6: Option<Uuid>,
|
||||
pub e_wink_7: Option<Uuid>,
|
||||
pub e_wink_8: Option<Uuid>,
|
||||
}
|
||||
|
||||
/// Set the current emotion for a user (simplified - no path lookup).
|
||||
///
|
||||
/// This is used when the user's client has the full avatar cached locally
|
||||
/// and can render the emotion from its local cache.
|
||||
pub async fn set_emotion_simple<'e>(
|
||||
executor: impl PgExecutor<'e>,
|
||||
user_id: Uuid,
|
||||
realm_id: Uuid,
|
||||
emotion: i16,
|
||||
) -> Result<(), AppError> {
|
||||
if emotion < 0 || emotion > 11 {
|
||||
return Err(AppError::Validation("Emotion must be 0-11".to_string()));
|
||||
}
|
||||
|
||||
let result = sqlx::query(
|
||||
r#"
|
||||
UPDATE props.active_avatars
|
||||
SET current_emotion = $3, updated_at = now()
|
||||
WHERE user_id = $1 AND realm_id = $2
|
||||
"#,
|
||||
)
|
||||
.bind(user_id)
|
||||
.bind(realm_id)
|
||||
.bind(emotion)
|
||||
.execute(executor)
|
||||
.await?;
|
||||
|
||||
if result.rows_affected() == 0 {
|
||||
return Err(AppError::NotFound(
|
||||
"No active avatar for this user in this realm".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue