Refactor personal event handling in EventsWorker: Introduce a new CharacterInfo struct to encapsulate character details extracted from event effects. Streamline the application of personal effects by consolidating logic into a single method, improving clarity and maintainability. Enhance notification creation by directly incorporating character information, ensuring accurate event communication.
This commit is contained in:
@@ -101,6 +101,13 @@ pub enum EventEffect {
|
||||
HouseQualityChange { probability: f64, min_change: i32, max_change: i32 },
|
||||
}
|
||||
|
||||
/// Hilfsstruktur für Character-Informationen aus Effekten
|
||||
struct CharacterInfo {
|
||||
character_id: Option<i32>,
|
||||
first_name: Option<String>,
|
||||
last_name: Option<String>,
|
||||
}
|
||||
|
||||
/// Definition eines zufälligen Ereignisses
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct RandomEvent {
|
||||
@@ -530,24 +537,11 @@ impl EventsWorker {
|
||||
event: &RandomEvent,
|
||||
rng: &mut impl Rng,
|
||||
) -> Result<(), DbError> {
|
||||
let mut conn = pool
|
||||
.get()
|
||||
.map_err(|e| DbError::new(format!("DB-Verbindung fehlgeschlagen: {e}")))?;
|
||||
|
||||
// Spezielle Behandlung für plötzlichen Kindstod: Finde ein zufälliges Kind unter 2 Jahren
|
||||
if event.id == "sudden_infant_death" {
|
||||
return Self::trigger_sudden_infant_death(pool, broker, event, rng);
|
||||
}
|
||||
|
||||
conn.prepare("get_random_user", QUERY_GET_RANDOM_USER)?;
|
||||
let rows = conn.execute("get_random_user", &[])?;
|
||||
|
||||
let user_id: Option<i32> = rows
|
||||
.first()
|
||||
.and_then(|r| r.get("id"))
|
||||
.and_then(|v| v.parse::<i32>().ok());
|
||||
|
||||
let user_id = match user_id {
|
||||
let user_id = match Self::get_random_user_id(pool)? {
|
||||
Some(id) => id,
|
||||
None => {
|
||||
eprintln!("[EventsWorker] Kein Spieler gefunden für persönliches Ereignis");
|
||||
@@ -555,136 +549,248 @@ impl EventsWorker {
|
||||
}
|
||||
};
|
||||
|
||||
// Wende Effekte an
|
||||
let mut conn = pool
|
||||
.get()
|
||||
.map_err(|e| DbError::new(format!("DB-Verbindung fehlgeschlagen: {e}")))?;
|
||||
|
||||
let effect_results = Self::apply_personal_effects(&mut conn, pool, broker, user_id, event, rng)?;
|
||||
|
||||
if effect_results.is_empty() {
|
||||
eprintln!(
|
||||
"[EventsWorker] Persönliches Ereignis '{}' für Spieler {} übersprungen (keine Effekte)",
|
||||
event.id, user_id
|
||||
);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let char_info = Self::extract_character_info_from_effects(&effect_results);
|
||||
Self::send_personal_event_notifications(pool, broker, user_id, event, &effect_results, &char_info)?;
|
||||
|
||||
eprintln!(
|
||||
"[EventsWorker] Persönliches Ereignis '{}' für Spieler {} verarbeitet",
|
||||
event.id, user_id
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn get_random_user_id(pool: &ConnectionPool) -> Result<Option<i32>, DbError> {
|
||||
let mut conn = pool
|
||||
.get()
|
||||
.map_err(|e| DbError::new(format!("DB-Verbindung fehlgeschlagen: {e}")))?;
|
||||
|
||||
conn.prepare("get_random_user", QUERY_GET_RANDOM_USER)?;
|
||||
let rows = conn.execute("get_random_user", &[])?;
|
||||
|
||||
Ok(rows
|
||||
.first()
|
||||
.and_then(|r| r.get("id"))
|
||||
.and_then(|v| v.parse::<i32>().ok()))
|
||||
}
|
||||
|
||||
fn apply_personal_effects(
|
||||
conn: &mut DbConnection,
|
||||
pool: &ConnectionPool,
|
||||
broker: &MessageBroker,
|
||||
user_id: i32,
|
||||
event: &RandomEvent,
|
||||
rng: &mut impl Rng,
|
||||
) -> Result<Vec<serde_json::Value>, DbError> {
|
||||
let mut effect_results = Vec::new();
|
||||
|
||||
for effect in &event.effects {
|
||||
let effect_roll = rng.gen_range(0.0..=1.0);
|
||||
match effect {
|
||||
EventEffect::MoneyChange {
|
||||
probability,
|
||||
min_percent,
|
||||
max_percent,
|
||||
} => {
|
||||
if effect_roll < *probability {
|
||||
let percent_change = rng.gen_range(*min_percent..=*max_percent);
|
||||
if let Ok(absolute_change) = Self::apply_money_change(&mut conn, user_id, percent_change) {
|
||||
effect_results.push(json!({
|
||||
"type": "money_change",
|
||||
"percent": percent_change,
|
||||
"absolute": absolute_change
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
EventEffect::StorageCapacityChange {
|
||||
probability,
|
||||
min_percent,
|
||||
max_percent,
|
||||
} => {
|
||||
if effect_roll < *probability {
|
||||
let percent_change = rng.gen_range(*min_percent..=*max_percent);
|
||||
Self::apply_storage_capacity_change(&mut conn, user_id, percent_change)?;
|
||||
effect_results.push(json!({
|
||||
"type": "storage_capacity_change",
|
||||
"percent": percent_change
|
||||
}));
|
||||
}
|
||||
}
|
||||
EventEffect::CharacterHealthChange {
|
||||
probability,
|
||||
min_change,
|
||||
max_change,
|
||||
} => {
|
||||
if effect_roll < *probability
|
||||
&& let Ok((character_id, health_change, died, first_name, last_name)) = Self::apply_character_health_change(
|
||||
&mut conn,
|
||||
pool,
|
||||
broker,
|
||||
user_id,
|
||||
*min_change,
|
||||
*max_change,
|
||||
rng,
|
||||
)
|
||||
{
|
||||
effect_results.push(json!({
|
||||
"type": "character_health_change",
|
||||
"character_id": character_id,
|
||||
"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_first_name": first_name,
|
||||
"character_last_name": last_name
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
EventEffect::CharacterDeath { probability } => {
|
||||
if effect_roll < *probability
|
||||
&& 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_first_name": first_name,
|
||||
"character_last_name": last_name
|
||||
}));
|
||||
}
|
||||
}
|
||||
EventEffect::StorageDamage {
|
||||
probability,
|
||||
stock_type_label,
|
||||
inventory_damage_min_percent,
|
||||
inventory_damage_max_percent,
|
||||
storage_destruction_min_percent,
|
||||
storage_destruction_max_percent,
|
||||
} => {
|
||||
if effect_roll < *probability
|
||||
&& let Ok(damage_info) = Self::apply_personal_storage_damage(
|
||||
&mut conn,
|
||||
PersonalStorageDamageParams {
|
||||
user_id,
|
||||
stock_type_label,
|
||||
inventory_damage_min_percent: *inventory_damage_min_percent,
|
||||
inventory_damage_max_percent: *inventory_damage_max_percent,
|
||||
storage_destruction_min_percent: *storage_destruction_min_percent,
|
||||
storage_destruction_max_percent: *storage_destruction_max_percent,
|
||||
},
|
||||
rng,
|
||||
)
|
||||
{
|
||||
effect_results.push(json!({
|
||||
"type": "storage_damage",
|
||||
"stock_type": stock_type_label,
|
||||
"inventory_damage_percent": damage_info.inventory_damage_percent,
|
||||
"storage_destruction_percent": damage_info.storage_destruction_percent,
|
||||
"affected_stocks": damage_info.affected_stocks,
|
||||
"destroyed_stocks": damage_info.destroyed_stocks,
|
||||
}));
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
eprintln!(
|
||||
"[EventsWorker] Effekt {:?} wird für persönliche Ereignisse noch nicht unterstützt",
|
||||
effect
|
||||
);
|
||||
}
|
||||
if let Some(result) = Self::apply_single_personal_effect(conn, pool, broker, user_id, effect, effect_roll, rng)? {
|
||||
effect_results.extend(result);
|
||||
}
|
||||
}
|
||||
|
||||
// Schreibe Benachrichtigung in die Datenbank mit Event-Details
|
||||
// If any effect contains a character_id, include it at top-level for the notification
|
||||
let top_character_id = effect_results.iter().find_map(|eff| {
|
||||
Ok(effect_results)
|
||||
}
|
||||
|
||||
fn apply_single_personal_effect(
|
||||
conn: &mut DbConnection,
|
||||
pool: &ConnectionPool,
|
||||
broker: &MessageBroker,
|
||||
user_id: i32,
|
||||
effect: &EventEffect,
|
||||
effect_roll: f64,
|
||||
rng: &mut impl Rng,
|
||||
) -> Result<Option<Vec<serde_json::Value>>, DbError> {
|
||||
match effect {
|
||||
EventEffect::MoneyChange { probability, min_percent, max_percent } => {
|
||||
Self::handle_money_change_effect(conn, user_id, effect_roll, *probability, *min_percent, *max_percent, rng)
|
||||
}
|
||||
EventEffect::StorageCapacityChange { probability, min_percent, max_percent } => {
|
||||
Self::handle_storage_capacity_effect(conn, user_id, effect_roll, *probability, *min_percent, *max_percent, rng)
|
||||
}
|
||||
EventEffect::CharacterHealthChange { probability, min_change, max_change } => {
|
||||
Self::handle_character_health_effect(conn, pool, broker, user_id, effect_roll, *probability, *min_change, *max_change, rng)
|
||||
}
|
||||
EventEffect::CharacterDeath { probability } => {
|
||||
Self::handle_character_death_effect(conn, pool, broker, user_id, effect_roll, *probability)
|
||||
}
|
||||
EventEffect::StorageDamage { probability, stock_type_label, inventory_damage_min_percent, inventory_damage_max_percent, storage_destruction_min_percent, storage_destruction_max_percent } => {
|
||||
Self::handle_storage_damage_effect(conn, user_id, effect_roll, *probability, stock_type_label, *inventory_damage_min_percent, *inventory_damage_max_percent, *storage_destruction_min_percent, *storage_destruction_max_percent, rng)
|
||||
}
|
||||
_ => {
|
||||
eprintln!("[EventsWorker] Effekt {:?} wird für persönliche Ereignisse noch nicht unterstützt", effect);
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_money_change_effect(
|
||||
conn: &mut DbConnection,
|
||||
user_id: i32,
|
||||
effect_roll: f64,
|
||||
probability: f64,
|
||||
min_percent: f64,
|
||||
max_percent: f64,
|
||||
rng: &mut impl Rng,
|
||||
) -> Result<Option<Vec<serde_json::Value>>, DbError> {
|
||||
if effect_roll >= probability {
|
||||
return Ok(None);
|
||||
}
|
||||
let percent_change = rng.gen_range(min_percent..=max_percent);
|
||||
if let Ok(absolute_change) = Self::apply_money_change(conn, user_id, percent_change) {
|
||||
Ok(Some(vec![json!({
|
||||
"type": "money_change",
|
||||
"percent": percent_change,
|
||||
"absolute": absolute_change
|
||||
})]))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_storage_capacity_effect(
|
||||
conn: &mut DbConnection,
|
||||
user_id: i32,
|
||||
effect_roll: f64,
|
||||
probability: f64,
|
||||
min_percent: f64,
|
||||
max_percent: f64,
|
||||
rng: &mut impl Rng,
|
||||
) -> Result<Option<Vec<serde_json::Value>>, DbError> {
|
||||
if effect_roll >= probability {
|
||||
return Ok(None);
|
||||
}
|
||||
let percent_change = rng.gen_range(min_percent..=max_percent);
|
||||
Self::apply_storage_capacity_change(conn, user_id, percent_change)?;
|
||||
Ok(Some(vec![json!({
|
||||
"type": "storage_capacity_change",
|
||||
"percent": percent_change
|
||||
})]))
|
||||
}
|
||||
|
||||
fn handle_character_health_effect(
|
||||
conn: &mut DbConnection,
|
||||
pool: &ConnectionPool,
|
||||
broker: &MessageBroker,
|
||||
user_id: i32,
|
||||
effect_roll: f64,
|
||||
probability: f64,
|
||||
min_change: i32,
|
||||
max_change: i32,
|
||||
rng: &mut impl Rng,
|
||||
) -> Result<Option<Vec<serde_json::Value>>, DbError> {
|
||||
if effect_roll >= probability {
|
||||
return Ok(None);
|
||||
}
|
||||
let Ok((character_id, health_change, died, first_name, last_name)) =
|
||||
Self::apply_character_health_change(conn, pool, broker, user_id, min_change, max_change, rng)
|
||||
else {
|
||||
return Ok(None);
|
||||
};
|
||||
|
||||
let mut results = vec![json!({
|
||||
"type": "character_health_change",
|
||||
"character_id": character_id,
|
||||
"change": health_change,
|
||||
"character_first_name": first_name,
|
||||
"character_last_name": last_name
|
||||
})];
|
||||
|
||||
if died {
|
||||
results.push(json!({
|
||||
"type": "character_death",
|
||||
"character_id": character_id,
|
||||
"character_first_name": first_name,
|
||||
"character_last_name": last_name
|
||||
}));
|
||||
}
|
||||
|
||||
Ok(Some(results))
|
||||
}
|
||||
|
||||
fn handle_character_death_effect(
|
||||
conn: &mut DbConnection,
|
||||
pool: &ConnectionPool,
|
||||
broker: &MessageBroker,
|
||||
user_id: i32,
|
||||
effect_roll: f64,
|
||||
probability: f64,
|
||||
) -> Result<Option<Vec<serde_json::Value>>, DbError> {
|
||||
if effect_roll >= probability {
|
||||
return Ok(None);
|
||||
}
|
||||
let Ok((character_id, first_name, last_name)) = Self::apply_character_death(conn, user_id, pool, broker) else {
|
||||
return Ok(None);
|
||||
};
|
||||
Ok(Some(vec![json!({
|
||||
"type": "character_death",
|
||||
"character_id": character_id,
|
||||
"character_first_name": first_name,
|
||||
"character_last_name": last_name
|
||||
})]))
|
||||
}
|
||||
|
||||
fn handle_storage_damage_effect(
|
||||
conn: &mut DbConnection,
|
||||
user_id: i32,
|
||||
effect_roll: f64,
|
||||
probability: f64,
|
||||
stock_type_label: &str,
|
||||
inventory_damage_min_percent: f64,
|
||||
inventory_damage_max_percent: f64,
|
||||
storage_destruction_min_percent: f64,
|
||||
storage_destruction_max_percent: f64,
|
||||
rng: &mut impl Rng,
|
||||
) -> Result<Option<Vec<serde_json::Value>>, DbError> {
|
||||
if effect_roll >= probability {
|
||||
return Ok(None);
|
||||
}
|
||||
let Ok(damage_info) = Self::apply_personal_storage_damage(
|
||||
conn,
|
||||
PersonalStorageDamageParams {
|
||||
user_id,
|
||||
stock_type_label,
|
||||
inventory_damage_min_percent,
|
||||
inventory_damage_max_percent,
|
||||
storage_destruction_min_percent,
|
||||
storage_destruction_max_percent,
|
||||
},
|
||||
rng,
|
||||
) else {
|
||||
return Ok(None);
|
||||
};
|
||||
Ok(Some(vec![json!({
|
||||
"type": "storage_damage",
|
||||
"stock_type": stock_type_label,
|
||||
"inventory_damage_percent": damage_info.inventory_damage_percent,
|
||||
"storage_destruction_percent": damage_info.storage_destruction_percent,
|
||||
"affected_stocks": damage_info.affected_stocks,
|
||||
"destroyed_stocks": damage_info.destroyed_stocks,
|
||||
})]))
|
||||
}
|
||||
|
||||
fn extract_character_info_from_effects(effect_results: &[serde_json::Value]) -> CharacterInfo {
|
||||
let character_id = effect_results.iter().find_map(|eff| {
|
||||
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 (first_name, last_name) = if let Some(cid) = character_id {
|
||||
let eff = effect_results.iter().find(|e| {
|
||||
e.get("character_id")
|
||||
.and_then(|v| v.as_i64())
|
||||
@@ -704,16 +810,18 @@ impl EventsWorker {
|
||||
(None, None)
|
||||
};
|
||||
|
||||
// Keine Benachrichtigung erstellen, wenn kein Effekt angewendet wurde
|
||||
if effect_results.is_empty() {
|
||||
eprintln!(
|
||||
"[EventsWorker] Persönliches Ereignis '{}' für Spieler {} übersprungen (keine Effekte)",
|
||||
event.id, user_id
|
||||
);
|
||||
return Ok(());
|
||||
}
|
||||
CharacterInfo { character_id, first_name, last_name }
|
||||
}
|
||||
|
||||
// Baue die Benachrichtigung gemäß EVENT_JSON_MESSAGES.md Struktur
|
||||
fn send_personal_event_notifications(
|
||||
pool: &ConnectionPool,
|
||||
broker: &MessageBroker,
|
||||
user_id: i32,
|
||||
event: &RandomEvent,
|
||||
effect_results: &[serde_json::Value],
|
||||
char_info: &CharacterInfo,
|
||||
) -> Result<(), DbError> {
|
||||
// DB-Benachrichtigung
|
||||
let mut notification_json = serde_json::json!({
|
||||
"tr": format!("random_event.{}", event.id),
|
||||
"event_id": event.id,
|
||||
@@ -721,27 +829,20 @@ impl EventsWorker {
|
||||
"effects": effect_results
|
||||
});
|
||||
|
||||
// Character-Informationen (falls vorhanden)
|
||||
if let Some(cid) = top_character_id {
|
||||
if let Some(cid) = char_info.character_id {
|
||||
notification_json["character_id"] = serde_json::json!(cid);
|
||||
}
|
||||
if let Some(fn_) = top_first_name {
|
||||
if let Some(ref fn_) = char_info.first_name {
|
||||
notification_json["character_first_name"] = serde_json::json!(fn_);
|
||||
}
|
||||
if let Some(ln_) = top_last_name {
|
||||
if let Some(ref ln_) = char_info.last_name {
|
||||
notification_json["character_last_name"] = serde_json::json!(ln_);
|
||||
}
|
||||
|
||||
Self::notify_user(
|
||||
pool,
|
||||
broker,
|
||||
user_id,
|
||||
¬ification_json.to_string(),
|
||||
top_character_id,
|
||||
)?;
|
||||
Self::notify_user(pool, broker, user_id, ¬ification_json.to_string(), char_info.character_id)?;
|
||||
|
||||
// Sende Benachrichtigung über WebSocket
|
||||
let mut notification = json!({
|
||||
// WebSocket-Benachrichtigung
|
||||
let mut ws_notification = json!({
|
||||
"event": "random_event",
|
||||
"event_id": event.id,
|
||||
"event_type": "personal",
|
||||
@@ -751,16 +852,11 @@ impl EventsWorker {
|
||||
"effects": effect_results
|
||||
});
|
||||
|
||||
if let Some(cid) = top_character_id {
|
||||
notification["character_id"] = json!(cid);
|
||||
if let Some(cid) = char_info.character_id {
|
||||
ws_notification["character_id"] = json!(cid);
|
||||
}
|
||||
|
||||
broker.publish(notification.to_string());
|
||||
eprintln!(
|
||||
"[EventsWorker] Persönliches Ereignis '{}' für Spieler {} verarbeitet",
|
||||
event.id, user_id
|
||||
);
|
||||
|
||||
broker.publish(ws_notification.to_string());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user