From 01d45d5f658437cecc1b6d77d56c79dc91baa7b6 Mon Sep 17 00:00:00 2001 From: "Torsten Schulz (local)" Date: Wed, 7 Jan 2026 11:33:23 +0100 Subject: [PATCH] Enhance character event notifications in EventsWorker: Update character health change and death handling to include first and last names in notifications. Modify SQL queries to retrieve character names, ensuring accurate user feedback. Refactor notification structure for better organization and clarity. --- src/worker/events.rs | 137 +++++++++++++++++++++++++++++++++---------- src/worker/sql.rs | 9 ++- 2 files changed, 114 insertions(+), 32 deletions(-) diff --git a/src/worker/events.rs b/src/worker/events.rs index 8abf30d..20d1e52 100644 --- a/src/worker/events.rs +++ b/src/worker/events.rs @@ -595,7 +595,7 @@ impl EventsWorker { max_change, } => { if effect_roll < *probability - && let Ok((character_id, health_change, died)) = Self::apply_character_health_change( + && let Ok((character_id, health_change, died, first_name, last_name)) = Self::apply_character_health_change( &mut conn, pool, broker, @@ -608,23 +608,30 @@ impl EventsWorker { effect_results.push(json!({ "type": "character_health_change", "character_id": character_id, - "change": health_change + "change": health_change, + "character_first_name": first_name, + "character_last_name": last_name })); if died { effect_results.push(json!({ "type": "character_death", - "character_id": character_id + "character_id": character_id, + "character_first_name": first_name, + "character_last_name": last_name })); } } } EventEffect::CharacterDeath { probability } => { if effect_roll < *probability - && let Ok(character_id) = Self::apply_character_death(&mut conn, user_id, pool, broker) + && let Ok((character_id, first_name, last_name)) = + Self::apply_character_death(&mut conn, user_id, pool, broker) { effect_results.push(json!({ "type": "character_death", - "character_id": character_id + "character_id": character_id, + "character_first_name": first_name, + "character_last_name": last_name })); } } @@ -675,18 +682,56 @@ impl EventsWorker { eff.get("character_id").and_then(|v| v.as_i64()).map(|n| n as i32) }); + // Optional: Namen-Snapshot aus Effekten extrahieren (auch wenn der Charakter danach stirbt/gelöscht wird) + let (top_first_name, top_last_name) = if let Some(cid) = top_character_id { + let eff = effect_results.iter().find(|e| { + e.get("character_id") + .and_then(|v| v.as_i64()) + .map(|n| n as i32) + == Some(cid) + }); + let fn_ = eff + .and_then(|e| e.get("character_first_name")) + .and_then(|v| v.as_str()) + .map(|s| s.to_string()); + let ln_ = eff + .and_then(|e| e.get("character_last_name")) + .and_then(|v| v.as_str()) + .map(|s| s.to_string()); + (fn_, ln_) + } else { + (None, None) + }; + + // Frontend: `tr` ist der i18n-Key, Details liegen in `value` (Interpolation/Detailansicht) let mut notification_json = serde_json::json!({ "tr": format!("random_event.{}", event.id), - "event_id": event.id, - "event_type": "personal", - "effects": effect_results + "value": { + "event_id": event.id, + "event_type": "personal", + "title": event.title, + "description": event.description, + "effects": effect_results + } }); if let Some(cid) = top_character_id { - notification_json["character_id"] = serde_json::json!(cid); + notification_json["value"]["character_id"] = serde_json::json!(cid); + } + if let Some(fn_) = top_first_name { + notification_json["value"]["character_first_name"] = serde_json::json!(fn_); + } + if let Some(ln_) = top_last_name { + notification_json["value"]["character_last_name"] = serde_json::json!(ln_); } - Self::notify_user(pool, broker, user_id, ¬ification_json.to_string())?; + Self::notify_user( + pool, + broker, + user_id, + ¬ification_json.to_string(), + top_character_id, + )?; // Sende Benachrichtigung über WebSocket let mut notification = json!({ @@ -753,6 +798,9 @@ impl EventsWorker { } }; + let first_name = rows.first().and_then(|r| r.get("first_name")).cloned(); + let last_name = rows.first().and_then(|r| r.get("last_name")).cloned(); + // Wende Effekte an (in diesem Fall nur CharacterDeath) let mut effect_results = Vec::new(); for effect in &event.effects { @@ -764,7 +812,9 @@ impl EventsWorker { { effect_results.push(json!({ "type": "character_death", - "character_id": character_id + "character_id": character_id, + "character_first_name": first_name, + "character_last_name": last_name })); } } @@ -780,12 +830,25 @@ impl EventsWorker { // Schreibe Benachrichtigung in die Datenbank mit Event-Details let notification_json = serde_json::json!({ "tr": format!("random_event.{}", event.id), - "event_id": event.id, - "event_type": "personal", - "character_id": character_id, - "effects": effect_results + "value": { + "event_id": event.id, + "event_type": "personal", + "title": event.title, + "description": event.description, + "character_id": character_id, + "character_first_name": first_name, + "character_last_name": last_name, + "effects": effect_results + } }); - Self::notify_user(pool, broker, user_id, ¬ification_json.to_string())?; + // Falls das Kind inzwischen gelöscht wurde, halten wir Name + ID im Payload fest. + Self::notify_user( + pool, + broker, + user_id, + ¬ification_json.to_string(), + Some(character_id), + )?; // Sende Benachrichtigung über WebSocket let notification = json!({ @@ -1041,12 +1104,18 @@ impl EventsWorker { // Schreibe Benachrichtigung in die Datenbank mit Event-Details let notification_json = serde_json::json!({ "tr": format!("random_event.{}", event.id), - "event_id": event.id, - "event_type": "regional", - "region_id": region_id, - "effects": effect_results + "value": { + "event_id": event.id, + "event_type": "regional", + "title": event.title, + "description": event.description, + "region_id": region_id, + "effects": effect_results + } }); - if let Err(err) = Self::notify_user(pool, broker, uid, ¬ification_json.to_string()) { + if let Err(err) = + Self::notify_user(pool, broker, uid, ¬ification_json.to_string(), None) + { eprintln!("[EventsWorker] Fehler beim Schreiben der Benachrichtigung für User {}: {}", uid, err); } @@ -1350,7 +1419,7 @@ impl EventsWorker { min_change: i32, max_change: i32, rng: &mut impl Rng, - ) -> Result<(i32, i32, bool), DbError> { + ) -> Result<(i32, i32, bool, Option, Option), DbError> { // Hole einen zufälligen Charakter des Spielers conn.prepare("get_random_character", QUERY_GET_RANDOM_CHARACTER)?; let rows = conn.execute("get_random_character", &[&user_id])?; @@ -1374,6 +1443,10 @@ impl EventsWorker { .and_then(|v| v.parse::().ok()) .unwrap_or(100); + // Namen-Snapshot (wichtig, falls der Charakter durch das Event stirbt und gelöscht wird) + let first_name = rows.first().and_then(|r| r.get("first_name")).cloned(); + let last_name = rows.first().and_then(|r| r.get("last_name")).cloned(); + let health_change = rng.gen_range(min_change..=max_change); let new_health = (current_health + health_change).clamp(0, 100); @@ -1389,7 +1462,7 @@ impl EventsWorker { let _ = Self::handle_character_death(pool, broker, character_id); } - Ok((character_id, health_change, died)) + Ok((character_id, health_change, died, first_name, last_name)) } fn apply_character_death( @@ -1397,7 +1470,7 @@ impl EventsWorker { user_id: i32, pool: &ConnectionPool, broker: &MessageBroker, - ) -> Result { + ) -> Result<(i32, Option, Option), DbError> { // Hole einen zufälligen Charakter des Spielers conn.prepare("get_random_character_death", QUERY_GET_RANDOM_CHARACTER)?; let rows = conn.execute("get_random_character_death", &[&user_id])?; @@ -1415,11 +1488,14 @@ impl EventsWorker { } }; + let first_name = rows.first().and_then(|r| r.get("first_name")).cloned(); + let last_name = rows.first().and_then(|r| r.get("last_name")).cloned(); + // Verwende die existierende Logik zum Löschen von Charakteren // (ähnlich wie in CharacterCreationWorker) Self::handle_character_death(pool, broker, character_id)?; - Ok(character_id) + Ok((character_id, first_name, last_name)) } fn apply_regional_character_health_change( @@ -1535,7 +1611,7 @@ impl EventsWorker { .get("employer_user_id") .and_then(|v| v.parse::().ok()) { - Self::notify_user(pool, broker, user_id, "director_death")?; + Self::notify_user(pool, broker, user_id, "director_death", None)?; } } @@ -1547,7 +1623,7 @@ impl EventsWorker { .get("related_user_id") .and_then(|v| v.parse::().ok()) { - Self::notify_user(pool, broker, related_user_id, "relationship_death")?; + Self::notify_user(pool, broker, related_user_id, "relationship_death", None)?; } } @@ -1575,13 +1651,13 @@ impl EventsWorker { .get("father_user_id") .and_then(|v| v.parse::().ok()) { - Self::notify_user(pool, broker, father_user_id, "child_death")?; + Self::notify_user(pool, broker, father_user_id, "child_death", None)?; } if let Some(mother_user_id) = row .get("mother_user_id") .and_then(|v| v.parse::().ok()) { - Self::notify_user(pool, broker, mother_user_id, "child_death")?; + Self::notify_user(pool, broker, mother_user_id, "child_death", None)?; } } @@ -1744,9 +1820,10 @@ impl EventsWorker { broker: &MessageBroker, user_id: i32, event_type: &str, + character_id: Option, ) -> Result<(), DbError> { // DB-Notification (zentralisiert). Historisch wird hier als `tr` der event_type-String gespeichert. - insert_notification(pool, user_id, event_type, None)?; + insert_notification(pool, user_id, event_type, character_id)?; // Frontend-Update (zentralisiert) publish_update_status(broker, user_id); diff --git a/src/worker/sql.rs b/src/worker/sql.rs index 8f6b2ba..f36a7ae 100644 --- a/src/worker/sql.rs +++ b/src/worker/sql.rs @@ -14,7 +14,12 @@ SELECT id FROM falukant_data.falukant_user ORDER BY RANDOM() LIMIT 1; "#; pub const QUERY_GET_RANDOM_INFANT: &str = r#" -SELECT c.id AS character_id, c.user_id, CURRENT_DATE - c.birthdate::date AS age_days +SELECT + c.id AS character_id, + c.user_id, + c.first_name, + c.last_name, + CURRENT_DATE - c.birthdate::date AS age_days FROM falukant_data."character" c WHERE c.user_id IS NOT NULL AND c.health > 0 AND CURRENT_DATE - c.birthdate::date <= 730 ORDER BY RANDOM() LIMIT 1; @@ -693,7 +698,7 @@ WHERE region_id = $1; "#; pub const QUERY_GET_RANDOM_CHARACTER: &str = r#" -SELECT id, health +SELECT id, health, first_name, last_name FROM falukant_data."character" WHERE user_id = $1 AND health > 0 ORDER BY RANDOM() LIMIT 1;