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  /// A new message has been sent in the chat.
73  ChatMessage {
74    id: EventId,
75    world: WorldId,
76    message: ChatMessage,
77  },
78
79  /// Indicates that there has been a change in the city's data, be it public or not.
80  ///
81  /// This event is only emitted to the city owner.
82  /// If you believe that all players should be notified,
83  /// consider using [`Event::PublicCity`] instead.
84  City {
85    id: EventId,
86    world: WorldId,
87    coord: Coord,
88  },
89
90  /// Signals that all active players should disconnect, as the world is about to be dropped.
91  ///
92  /// This event **MUST** only be emitted inside the `Drop` implementation of the `World` struct.
93  Drop { id: EventId, world: WorldId },
94
95  /// Indicates that the market prices have changed.
96  Market { id: EventId, world: WorldId },
97
98  /// Indicates that the player's military has changed.
99  /// It usually means new maneuvers have been initiated,
100  /// since the armies themselves are processed at the end of the round.
101  Military {
102    id: EventId,
103    world: WorldId,
104    player: PlayerId,
105  },
106
107  /// Indicates that there has been a change in some of the player's data, be it public or not.
108  Player {
109    id: EventId,
110    world: WorldId,
111    player: PlayerId,
112  },
113
114  /// Indicates that there has been a change in public data for the city.
115  ///
116  /// Unlike [`Event::City`], which is emitted only to the city owner,
117  /// this event is sent to all players in the world.
118  ///
119  /// As a rule, whenever the situation requires this event to be emitted,
120  /// [`Event::City`] should also be emitted, but the opposite is not true!
121  PublicCity {
122    id: EventId,
123    world: WorldId,
124    coord: Coord,
125  },
126
127  /// A report was generated.
128  Report {
129    id: EventId,
130    world: WorldId,
131    report: Box<ReportKind>,
132  },
133
134  /// Indicates changes in the round, such as the end of a player's turn or
135  /// the transition from one round to another, after all players have completed their actions.
136  ///
137  /// When emitted at the start of the game or at the end of a round,
138  /// [`Event::Round`] typically makes it unnecessary to emit other events,
139  /// as this situation naturally prompts all entities to update themselves.
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}