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};
16
17pub type Listener = Receiver<(Bytes, EventTarget)>;
18
19#[derive(Clone)]
20pub(crate) struct Emitter {
21  sender: Sender<(Bytes, EventTarget)>,
22}
23
24impl Emitter {
25  pub fn new(capacity: usize) -> Self {
26    let (sender, _) = channel(capacity);
27    Self { sender }
28  }
29
30  pub(crate) fn emit(&self, event: Event, target: EventTarget) -> Result<()> {
31    tracing::trace!(?target, ?event);
32    let bytes = Bytes::try_from(event)?;
33    let _ = self.sender.send((bytes, target));
34    Ok(())
35  }
36
37  pub(crate) fn emit_to(&self, target: PlayerId, event: Event) -> Result<()> {
38    self.emit(event, EventTarget::Player(target))
39  }
40
41  pub(crate) fn broadcast(&self, event: Event) -> Result<()> {
42    self.emit(event, EventTarget::Broadcast)
43  }
44
45  pub(crate) fn subscribe(&self) -> Listener {
46    self.sender.subscribe()
47  }
48}
49
50impl Default for Emitter {
51  fn default() -> Self {
52    Self::new(100)
53  }
54}
55
56impl fmt::Debug for Emitter {
57  fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
58    f.debug_struct("Emitter")
59      .field("sender", &self.sender.receiver_count())
60      .finish()
61  }
62}
63
64#[derive(Clone, Display, Deserialize, Serialize)]
65#[serde(tag = "kind", rename_all = "kebab-case")]
66#[strum(serialize_all = "kebab-case")]
67#[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
68#[cfg_attr(feature = "typescript", ts(export))]
69#[remain::sorted]
70pub enum Event {
71  /// A new message has been sent in the chat.
72  ChatMessage {
73    world: WorldId,
74    message: ChatMessage,
75  },
76
77  /// Indicates that there has been a change in the city's data, be it public or not.
78  ///
79  /// This event is only emitted to the city owner.
80  /// If you believe that all players should be notified,
81  /// consider using [`Event::PublicCity`] instead.
82  City { world: WorldId, coord: Coord },
83
84  /// Signals that all active players should disconnect, as the world is about to be dropped.
85  ///
86  /// This event **MUST** only be emitted inside the `Drop` implementation of the `World` struct.
87  Drop { world: WorldId },
88
89  /// Indicates that the player's military has changed.
90  /// It usually means new maneuvers have been initiated,
91  /// since the armies themselves are processed at the end of the round.
92  Military { world: WorldId, player: PlayerId },
93
94  /// Indicates that there has been a change in some of the player's data, be it public or not.
95  Player { world: WorldId, player: PlayerId },
96
97  /// Indicates that there has been a change in public data for the city.
98  ///
99  /// Unlike [`Event::City`], which is emitted only to the city owner,
100  /// this event is sent to all players in the world.
101  ///
102  /// As a rule, whenever the situation requires this event to be emitted,
103  /// [`Event::City`] should also be emitted, but the opposite is not true!
104  PublicCity { world: WorldId, coord: Coord },
105
106  /// A report was generated.
107  Report {
108    world: WorldId,
109    report: Box<ReportKind>,
110  },
111
112  /// Indicates changes in the round, such as the end of a player's turn or
113  /// the transition from one round to another, after all players have completed their actions.
114  ///
115  /// When emitted at the start of the game or at the end of a round,
116  /// [`Event::Round`] typically makes it unnecessary to emit other events,
117  /// as this situation naturally prompts all entities to update themselves.
118  Round { world: WorldId, round: Round },
119}
120
121impl fmt::Debug for Event {
122  fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
123    match self {
124      Self::ChatMessage { world, message } => {
125        f.debug_struct("ChatMessage")
126          .field("world", world)
127          .field("message", &message.id())
128          .finish()
129      }
130      Self::City { world, coord } => {
131        f.debug_struct("City")
132          .field("world", world)
133          .field("coord", coord)
134          .finish()
135      }
136      Self::Drop { world } => {
137        f.debug_struct("Drop")
138          .field("world", world)
139          .finish()
140      }
141      Self::Military { world, player } => {
142        f.debug_struct("Military")
143          .field("world", world)
144          .field("player", player)
145          .finish()
146      }
147      Self::Player { world, player } => {
148        f.debug_struct("Player")
149          .field("world", world)
150          .field("player", player)
151          .finish()
152      }
153      Self::PublicCity { world, coord } => {
154        f.debug_struct("PublicCity")
155          .field("world", world)
156          .field("coord", coord)
157          .finish()
158      }
159      Self::Report { world, report } => {
160        f.debug_struct("Report")
161          .field("world", world)
162          .field("report", &report.id())
163          .finish()
164      }
165      Self::Round { world, round } => {
166        f.debug_struct("Round")
167          .field("world", world)
168          .field("round", &round.id())
169          .finish()
170      }
171    }
172  }
173}
174
175impl TryFrom<Bytes> for Event {
176  type Error = Error;
177
178  fn try_from(bytes: Bytes) -> Result<Self> {
179    serde_json::from_slice(&bytes).map_err(|err| {
180      tracing::error!("Failed to deserialize event: {err}");
181      Error::FailedToDeserializeEvent
182    })
183  }
184}
185
186impl TryFrom<Event> for Bytes {
187  type Error = Error;
188
189  fn try_from(event: Event) -> Result<Self> {
190    serde_json::to_vec(&event)
191      .map(Bytes::from)
192      .map_err(|err| {
193        tracing::error!("Failed to serialize event: {err}");
194        Error::FailedToSerializeEvent
195      })
196  }
197}
198
199#[derive(Clone, Debug, PartialEq, Eq)]
200pub enum EventTarget {
201  Broadcast,
202  Player(PlayerId),
203}