Skip to main content

nil_core/
event.rs

1// Copyright (C) Call of Nil contributors
2// SPDX-License-Identifier: AGPL-3.0-only
3
4use crate::chat::ChatMessage;
5use crate::continent::coord::Coord;
6use crate::error::{Error, Result};
7use crate::player::PlayerId;
8use crate::report::ReportKind;
9use crate::round::Round;
10use crate::world::config::WorldId;
11use bytes::Bytes;
12use serde::{Deserialize, Serialize};
13use std::fmt;
14use strum::Display;
15use tokio::sync::broadcast::{Receiver, Sender, channel};
16use uuid::Uuid;
17
18pub type Listener = Receiver<(Bytes, EventTarget)>;
19
20#[derive(Clone)]
21pub(crate) struct Emitter {
22  sender: Sender<(Bytes, EventTarget)>,
23}
24
25impl Emitter {
26  pub fn new() -> Self {
27    let (sender, _) = channel(100);
28    Self { sender }
29  }
30
31  pub(crate) fn emit(&self, event: Event, target: EventTarget) -> Result<()> {
32    tracing::trace!(?target, ?event);
33    let bytes = Bytes::try_from(event)?;
34    let _ = self.sender.send((bytes, target));
35    Ok(())
36  }
37
38  pub(crate) fn emit_to(&self, target: PlayerId, event: Event) -> Result<()> {
39    self.emit(event, EventTarget::Player(target))
40  }
41
42  pub(crate) fn broadcast(&self, event: Event) -> Result<()> {
43    self.emit(event, EventTarget::Broadcast)
44  }
45
46  pub(crate) fn subscribe(&self) -> Listener {
47    self.sender.subscribe()
48  }
49}
50
51impl Default for Emitter {
52  fn default() -> Self {
53    Self::new()
54  }
55}
56
57impl fmt::Debug for Emitter {
58  fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
59    f.debug_struct("Emitter")
60      .field("sender", &self.sender.receiver_count())
61      .finish()
62  }
63}
64
65#[derive(Clone, Display, Deserialize, Serialize)]
66#[serde(tag = "kind", rename_all = "kebab-case")]
67#[strum(serialize_all = "kebab-case")]
68#[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
69#[cfg_attr(feature = "typescript", ts(export))]
70#[remain::sorted]
71pub enum Event {
72  /// Uma nova mensagem foi enviada no chat.
73  ChatMessage {
74    id: EventId,
75    world: WorldId,
76    message: ChatMessage,
77  },
78
79  /// Indica que houve uma alteração nos dados da cidade, públicos ou privados.
80  ///
81  /// Este evento é emitido apenas para o dono da cidade.
82  /// Se você acredita que todos os jogadores devem ser notificados,
83  /// considere usar [`Event::PublicCity`].
84  City {
85    id: EventId,
86    world: WorldId,
87    coord: Coord,
88  },
89
90  /// Sinaliza que todos os jogadores ativos devem se desconectar, pois o mundo está prestes a ser descartado.
91  ///
92  /// Este evento **DEVE** ser emitido apenas dentro da implementação de `Drop` da struct `World`.
93  Drop { id: EventId, world: WorldId },
94
95  /// Indica que os preços do mercado mudaram.
96  Market { id: EventId, world: WorldId },
97
98  /// Indica que houve uma alteração nas forças militares do jogador.
99  /// Geralmente significa que novas manobras foram iniciadas,
100  /// pois os exércitos em si são processados no final da rodada.
101  Military {
102    id: EventId,
103    world: WorldId,
104    player: PlayerId,
105  },
106
107  /// Indica que houve uma alteração em alguns dados do jogador, públicos ou privados.
108  Player {
109    id: EventId,
110    world: WorldId,
111    player: PlayerId,
112  },
113
114  /// Indica que houve uma alteração nos dados públicos da cidade.
115  ///
116  /// Diferentemente de [`Event::City`], que é emitido apenas para o dono da cidade,
117  /// este evento é enviado para todos os jogadores do mundo.
118  ///
119  /// Como regra, sempre que a situação exigir a emissão deste evento,
120  /// [`Event::City`] também deve ser emitido, mas o contrário não é verdadeiro!
121  PublicCity {
122    id: EventId,
123    world: WorldId,
124    coord: Coord,
125  },
126
127  /// Um relatório foi gerado.
128  Report {
129    id: EventId,
130    world: WorldId,
131    report: Box<ReportKind>,
132  },
133
134  /// Indica alterações na rodada, como o fim do turno de um jogador ou
135  /// a transição entre rodadas, depois que todos os jogadores concluíram suas ações.
136  ///
137  /// Quando emitido no início do jogo ou no final de uma rodada,
138  /// [`Event::Round`] geralmente torna desnecessária a emissão de outros eventos,
139  /// pois essa situação naturalmente faz com que todas as entidades sejam atualizadas.
140  Round {
141    id: EventId,
142    world: WorldId,
143    round: Round,
144  },
145}
146
147impl fmt::Debug for Event {
148  fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
149    match self {
150      Self::ChatMessage { id, world, message } => {
151        f.debug_struct("ChatMessage")
152          .field("id", id)
153          .field("world", world)
154          .field("message", &message.id())
155          .finish()
156      }
157      Self::City { id, world, coord } => {
158        f.debug_struct("City")
159          .field("id", id)
160          .field("world", world)
161          .field("coord", coord)
162          .finish()
163      }
164      Self::Drop { id, world } => {
165        f.debug_struct("Drop")
166          .field("id", id)
167          .field("world", world)
168          .finish()
169      }
170      Self::Market { id, world } => {
171        f.debug_struct("Market")
172          .field("id", id)
173          .field("world", world)
174          .finish()
175      }
176      Self::Military { id, world, player } => {
177        f.debug_struct("Military")
178          .field("id", id)
179          .field("world", world)
180          .field("player", player)
181          .finish()
182      }
183      Self::Player { id, world, player } => {
184        f.debug_struct("Player")
185          .field("id", id)
186          .field("world", world)
187          .field("player", player)
188          .finish()
189      }
190      Self::PublicCity { id, world, coord } => {
191        f.debug_struct("PublicCity")
192          .field("id", id)
193          .field("world", world)
194          .field("coord", coord)
195          .finish()
196      }
197      Self::Report { id, world, report } => {
198        f.debug_struct("Report")
199          .field("id", id)
200          .field("world", world)
201          .field("report", &report.id())
202          .finish()
203      }
204      Self::Round { id, world, round } => {
205        f.debug_struct("Round")
206          .field("id", id)
207          .field("world", world)
208          .field("round", &round.id())
209          .finish()
210      }
211    }
212  }
213}
214
215impl TryFrom<Bytes> for Event {
216  type Error = Error;
217
218  fn try_from(bytes: Bytes) -> Result<Self> {
219    serde_json::from_slice(&bytes).map_err(|err| {
220      tracing::error!("Failed to deserialize event: {err}");
221      Error::FailedToDeserializeEvent
222    })
223  }
224}
225
226impl TryFrom<Event> for Bytes {
227  type Error = Error;
228
229  fn try_from(event: Event) -> Result<Self> {
230    serde_json::to_vec(&event)
231      .map(Bytes::from)
232      .map_err(|err| {
233        tracing::error!("Failed to serialize event: {err}");
234        Error::FailedToSerializeEvent
235      })
236  }
237}
238
239#[derive(
240  Clone,
241  Copy,
242  Debug,
243  derive_more::Deref,
244  derive_more::Display,
245  PartialEq,
246  Eq,
247  PartialOrd,
248  Ord,
249  Hash,
250  Deserialize,
251  Serialize,
252)]
253#[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
254pub struct EventId(Uuid);
255
256impl EventId {
257  #[must_use]
258  pub fn new() -> Self {
259    Self(Uuid::now_v7())
260  }
261}
262
263impl Default for EventId {
264  fn default() -> Self {
265    Self::new()
266  }
267}
268
269#[derive(Clone, Debug, PartialEq, Eq)]
270pub enum EventTarget {
271  Broadcast,
272  Player(PlayerId),
273}