Skip to main content

gpn_tron/
packets.rs

1use crate::errors::{Error, Result, ServerError};
2
3/// Packets sent by the server to the client.
4#[derive(Clone, Debug)]
5pub enum ServerPacket {
6    /// Inform about new round.
7    Game {
8        width: usize,
9        height: usize,
10        curr_player_id: usize,
11    },
12    /// Inform about player's position.
13    Position {
14        player_id: usize,
15        x: usize,
16        y: usize,
17    },
18    /// At connection.
19    MessageOfTheDay(String),
20    /// Share player information.
21    Player { id: usize, name: String },
22    /// Every time a turn has been done.
23    Tick,
24    /// Inform about who died.
25    Die { user_ids: Vec<usize> },
26    /// Inform of a message by another player.
27    Message { player_id: usize, content: String },
28    /// Inform the client they won.
29    Win {
30        total_wins: usize,
31        total_losses: usize,
32    },
33    /// Inform the client they lost.
34    Lose {
35        total_wins: usize,
36        total_losses: usize,
37    },
38    /// An error or warning reported by the server.
39    Error(ServerError),
40}
41
42impl ServerPacket {
43    pub fn parse(s: impl AsRef<str>) -> Result<Self> {
44        let input = s.as_ref();
45        let mut input = input.split('|');
46        let name = input.next().ok_or(Error::NoName)?;
47        let packet = match name {
48            "game" => {
49                let width: usize = input.next().ok_or(Error::MalformedPacket)?.parse()?;
50                let height: usize = input.next().ok_or(Error::MalformedPacket)?.parse()?;
51                let curr_player_id: usize = input.next().ok_or(Error::MalformedPacket)?.parse()?;
52
53                ServerPacket::Game {
54                    width,
55                    height,
56                    curr_player_id,
57                }
58            }
59            "pos" => {
60                let player_id: usize = input.next().ok_or(Error::MalformedPacket)?.parse()?;
61                let x: usize = input.next().ok_or(Error::MalformedPacket)?.parse()?;
62                let y: usize = input.next().ok_or(Error::MalformedPacket)?.parse()?;
63                ServerPacket::Position { player_id, x, y }
64            }
65            "motd" => {
66                let content = input.collect::<Vec<_>>().join("|");
67                if content.is_empty() {
68                    return Err(Error::MalformedPacket);
69                }
70                ServerPacket::MessageOfTheDay(content)
71            }
72            "error" => {
73                let code = input.next().ok_or(Error::MalformedPacket)?;
74                ServerPacket::Error(code.parse::<ServerError>()?)
75            }
76            "player" => {
77                let id: usize = input.next().ok_or(Error::MalformedPacket)?.parse()?;
78                let name = input.next().ok_or(Error::MalformedPacket)?.to_string();
79                ServerPacket::Player { id, name }
80            }
81            "tick" => ServerPacket::Tick,
82            "die" => {
83                let user_ids = input
84                    .map(|s| s.parse::<usize>())
85                    .collect::<core::result::Result<Vec<_>, _>>()?;
86                if user_ids.is_empty() {
87                    return Err(Error::MalformedPacket);
88                }
89                ServerPacket::Die { user_ids }
90            }
91            "message" => {
92                let player_id: usize = input.next().ok_or(Error::MalformedPacket)?.parse()?;
93                let content = input.collect::<Vec<_>>().join("|");
94                if content.is_empty() {
95                    return Err(Error::MalformedPacket);
96                }
97                ServerPacket::Message { player_id, content }
98            }
99            "win" => {
100                let total_wins: usize = input.next().ok_or(Error::MalformedPacket)?.parse()?;
101                let total_losses: usize = input.next().ok_or(Error::MalformedPacket)?.parse()?;
102                ServerPacket::Win {
103                    total_wins,
104                    total_losses,
105                }
106            }
107            "lose" => {
108                let total_wins: usize = input.next().ok_or(Error::MalformedPacket)?.parse()?;
109                let total_losses: usize = input.next().ok_or(Error::MalformedPacket)?.parse()?;
110                ServerPacket::Lose {
111                    total_wins,
112                    total_losses,
113                }
114            }
115            _ => return Err(Error::MalformedPacket),
116        };
117        Ok(packet)
118    }
119}
120
121/// Packets sent by the client to the server.
122pub enum ClientPacket {
123    /// First packet sent.
124    Join { username: String, password: String },
125    /// Decide where to move.
126    Move(MoveDirection),
127    /// Send a message to chat.
128    Chat(String),
129}
130
131impl ClientPacket {
132    pub fn join(username: impl Into<String>, password: impl Into<String>) -> Self {
133        let username = username.into();
134        let password = password.into();
135        Self::Join { username, password }
136    }
137}
138
139#[derive(Debug, Clone, Copy, Eq, PartialEq)]
140pub enum MoveDirection {
141    Up,
142    Down,
143    Left,
144    Right,
145}
146
147impl std::fmt::Display for MoveDirection {
148    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
149        match self {
150            MoveDirection::Up => write!(f, "up"),
151            MoveDirection::Down => write!(f, "down"),
152            MoveDirection::Left => write!(f, "left"),
153            MoveDirection::Right => write!(f, "right"),
154        }
155    }
156}
157impl std::fmt::Display for ClientPacket {
158    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
159        match self {
160            ClientPacket::Join { username, password } => writeln!(f, "join|{username}|{password}"),
161            ClientPacket::Move(move_direction) => writeln!(f, "move|{move_direction}"),
162            ClientPacket::Chat(msg) => writeln!(f, "chat|{msg}"),
163        }
164    }
165}
166
167impl std::fmt::Display for ServerPacket {
168    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
169        match self {
170            ServerPacket::Game {
171                width,
172                height,
173                curr_player_id,
174            } => writeln!(f, "game|{width}|{height}|{curr_player_id}"),
175            ServerPacket::Position { player_id, x, y } => writeln!(f, "pos|{player_id}|{x}|{y}"),
176            ServerPacket::MessageOfTheDay(content) => writeln!(f, "motd|{content}"),
177            ServerPacket::Player { id, name } => writeln!(f, "player|{id}|{name}"),
178            ServerPacket::Tick => writeln!(f, "tick"),
179            ServerPacket::Die { user_ids } => {
180                writeln!(
181                    f,
182                    "die{}",
183                    user_ids.iter().fold(String::new(), |mut acc, v| {
184                        use std::fmt::Write as _;
185                        _ = write!(&mut acc, "|{v}");
186                        acc
187                    })
188                )
189            }
190            ServerPacket::Message { player_id, content } => {
191                writeln!(f, "message|{player_id}|{content}")
192            }
193            ServerPacket::Win {
194                total_wins,
195                total_losses,
196            } => writeln!(f, "win|{total_wins}|{total_losses}"),
197            ServerPacket::Lose {
198                total_wins,
199                total_losses,
200            } => writeln!(f, "lose|{total_wins}|{total_losses}"),
201            ServerPacket::Error(err) => writeln!(f, "error|{}", err.code()),
202        }
203    }
204}