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;
6use crate::error::{Error, Result};
7use crate::player::PlayerId;
8use crate::report::ReportId;
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::info!(?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, Debug, 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  ChatUpdated {
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::PublicCityUpdated`] instead.
82  CityUpdated { 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  MilitaryUpdated { 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  PlayerUpdated { world: WorldId, player: PlayerId },
96
97  /// Indicates that there has been a change in public data for the city.
98  ///
99  /// As a rule, whenever the situation requires this event to be emitted,
100  /// `Event::CityUpdated` should also be emitted, but the opposite is not true!
101  ///
102  /// Unlike [`Event::CityUpdated`], which is emitted only to the city owner,
103  /// this event is sent to all players in the world.
104  PublicCityUpdated { world: WorldId, coord: Coord },
105
106  /// A report was generated.
107  Report { world: WorldId, report: ReportId },
108
109  /// Indicates changes in the round, such as the end of a player's turn or
110  /// the transition from one round to another, after all players have completed their actions.
111  ///
112  /// When emitted at the start of the game or at the end of a round,
113  /// [`Event::RoundUpdated`] typically makes it unnecessary to emit other events,
114  /// as this situation naturally prompts all entities to update themselves.
115  RoundUpdated { world: WorldId, round: Round },
116}
117
118impl TryFrom<Event> for Bytes {
119  type Error = Error;
120
121  fn try_from(event: Event) -> Result<Self> {
122    serde_json::to_vec(&event)
123      .map(Bytes::from)
124      .map_err(|err| {
125        tracing::error!("Failed to serialize event: {err}");
126        Error::FailedToSerializeEvent
127      })
128  }
129}
130
131impl TryFrom<Bytes> for Event {
132  type Error = Error;
133
134  fn try_from(bytes: Bytes) -> Result<Self> {
135    serde_json::from_slice(&bytes).map_err(|err| {
136      tracing::error!("Failed to deserialize event: {err}");
137      Error::FailedToDeserializeEvent
138    })
139  }
140}
141
142#[derive(Clone, Debug, PartialEq, Eq)]
143pub enum EventTarget {
144  Broadcast,
145  Player(PlayerId),
146}