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.

This commit is contained in:
Torsten Schulz (local)
2026-01-07 11:33:23 +01:00
parent 282f6542fe
commit 01d45d5f65
2 changed files with 114 additions and 32 deletions

View File

@@ -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, &notification_json.to_string())?;
Self::notify_user(
pool,
broker,
user_id,
&notification_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, &notification_json.to_string())?;
// Falls das Kind inzwischen gelöscht wurde, halten wir Name + ID im Payload fest.
Self::notify_user(
pool,
broker,
user_id,
&notification_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, &notification_json.to_string()) {
if let Err(err) =
Self::notify_user(pool, broker, uid, &notification_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<String>, Option<String>), 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::<i32>().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<i32, DbError> {
) -> Result<(i32, Option<String>, Option<String>), 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::<i32>().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::<i32>().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::<i32>().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::<i32>().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<i32>,
) -> 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);

View File

@@ -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;