team04_server/messages/
mod.rs

1//! The messages which can be sent to or received from
2//! clients over the websocket connection.
3
4/// (*Tx*) [ATTACK](attack::Attack) message
5pub mod attack;
6/// (*Rx*) [CHARACTER_CHOSEN](character_chosen::CharacterChosen) message
7pub mod character_chosen;
8/// (*enum*) `PLAYER`, `AI`, or `SPECTATOR`
9pub mod client_role;
10/// (*Rx*) [CONNECT_GAME](connect_game::ConnectGame) message
11pub mod connect_game;
12/// (*Tx*) [CONNECTED](connected::Connected) message (for [spectators](connected::ConnectedAsSpectator))
13pub mod connected;
14/// (*Tx*) [END_FIGHT](end_fight::EndFight) message
15pub mod end_fight;
16/// (*Tx*) [ERROR](error::ErrorMessage) message
17pub mod error;
18/// (*Tx*) [GAME_END](game_end::GameEnd) message
19pub mod game_end;
20/// (*Tx*) [GAME_START](game_start::GameStart) message
21pub mod game_start;
22/// (*Tx*) [GAMESTATE](game_state::GameState) message
23pub mod game_state;
24/// (*Tx*) [HELLO_CLIENT](hello_client::HelloClient) message
25pub mod hello_client;
26/// (*Rx*) [LIGHTSABER_CHOSEN](lightsaber_chosen::LightsaberChosen) message
27pub mod lightsaber_chosen;
28/// (*Tx*) [LIGHTSABER_OPTIONS](lightsaber_options::LightsaberOptions) message
29pub mod lightsaber_options;
30/// (*Tx*) [LOBBY_INFO](lobby_info::LobbyInfo) message
31pub mod lobby_info;
32/// (*Tx*) [MOVEMENT](movement::Movement) message
33pub mod movement;
34/// (*Rx*) [PAUSE_REQUEST](pause_request::PauseRequest) message
35pub mod pause_request;
36/// (*Rx*) [PLACEMENT_COMPLETE](placement_complete::PlacementComplete) message
37pub mod placement_complete;
38/// (*enum*) The [PlayerCharacter](player_character::PlayerCharacter)s which can be selected in a lobby before the game starts.
39pub mod player_character;
40/// (*Rx*) [RECONNECT](reconnect::Reconnect) message
41pub mod reconnect;
42/// (*impl*) Defines how certain data types from outside this module should be (de)serialized.
43pub mod serde_impls;
44/// (*Tx*) [TEXT_BROADCAST](text_broadcast::TextBroadcast) message
45pub mod text_broadcast;
46/// (*Rx*) [TEXT_MESSAGE](text_message::TextMessage) message
47pub mod text_message;
48/// (*Rx*) [UNIT_CHOSEN](unit_chosen::UnitChosen) message
49pub mod unit_chosen;
50/// (*Tx*) [UNIT_OPTIONS](unit_options::UnitOptions) message
51pub mod unit_options;
52
53pub(self) use serde::{Deserialize, Serialize};
54pub(self) use std::fmt::{Display, Formatter, Result as FmtResult};
55
56use std::error::Error;
57
58/// Any message that can be received from the client.
59///
60/// Add new messages here, without forgetting to add it to the match expression
61/// in [Self::from_str]. If any fields other than `messageType` are necessary,
62/// create a new struct and add it as a member of the variant. This struct must
63/// implement [Deserialize] and [Debug].
64#[derive(Deserialize, Debug)]
65#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
66#[serde(tag = "messageType")]
67pub enum RxMessage {
68    CharacterChosen(character_chosen::CharacterChosen),
69    LightsaberChosen(lightsaber_chosen::LightsaberChosen),
70    UnitChosen(unit_chosen::UnitChosen),
71    PlacementComplete(placement_complete::PlacementComplete),
72    ConnectGame(connect_game::ConnectGame),
73    PauseRequest(pause_request::PauseRequest),
74    HelloServer,
75    LeaveLobby,
76    Ready,
77    Reconnect(reconnect::Reconnect),
78    TextMessage(text_message::TextMessage),
79}
80
81impl RxMessage {
82    /// Deserializes and validates a message.
83    pub fn from_str(msg: &str) -> Result<Self, RxError> {
84        let parsed_msg = serde_json::from_str(msg)?;
85
86        match &parsed_msg {
87            Self::ConnectGame(msg) => msg.validate()?,
88            Self::CharacterChosen(_)
89            | Self::LightsaberChosen(_)
90            | Self::UnitChosen(_)
91            | Self::PlacementComplete(_)
92            | Self::PauseRequest(_)
93            | Self::HelloServer
94            | Self::LeaveLobby
95            | Self::Ready
96            | Self::Reconnect(_)
97            | Self::TextMessage(_) => (), // We don't need no validation
98        }
99
100        Ok(parsed_msg)
101    }
102}
103
104#[derive(Debug)]
105pub enum RxError {
106    /// The message does not conform to JSON, or it is missing some fields, or it has an unknown
107    /// message type.
108    InvalidMessage(serde_json::Error),
109    ConnectGame(connect_game::ConnectGameError),
110}
111
112impl Error for RxError {}
113
114impl Display for RxError {
115    fn fmt(&self, fmt: &mut Formatter<'_>) -> FmtResult {
116        match self {
117            Self::InvalidMessage(e) => write!(fmt, "Invalid Message: {}", e),
118            Self::ConnectGame(e) => write!(fmt, "CONNECT_GAME: {}", e),
119        }
120    }
121}
122
123impl From<serde_json::Error> for RxError {
124    fn from(e: serde_json::Error) -> RxError {
125        Self::InvalidMessage(e)
126    }
127}
128
129impl From<connect_game::ConnectGameError> for RxError {
130    fn from(e: connect_game::ConnectGameError) -> RxError {
131        Self::ConnectGame(e)
132    }
133}
134
135pub trait MessageTx {
136    fn serialize(&self) -> String;
137}
138
139pub fn serialize<T>(value: &T) -> String
140where
141    T: Serialize,
142{
143    serde_json::ser::to_string(value).expect("unserializable value encountered")
144}
145
146pub fn deserialize<'a, T>(value: &'a str) -> Result<T, serde_json::Error>
147where
148    T: Deserialize<'a>,
149{
150    serde_json::de::from_str(value)
151}