1use crate::errors::{Error, Result, ServerError};
2
3#[derive(Clone, Debug)]
5pub enum ServerPacket {
6 Game {
8 width: usize,
9 height: usize,
10 curr_player_id: usize,
11 },
12 Position {
14 player_id: usize,
15 x: usize,
16 y: usize,
17 },
18 MessageOfTheDay(String),
20 Player { id: usize, name: String },
22 Tick,
24 Die { user_ids: Vec<usize> },
26 Message { player_id: usize, content: String },
28 Win {
30 total_wins: usize,
31 total_losses: usize,
32 },
33 Lose {
35 total_wins: usize,
36 total_losses: usize,
37 },
38 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
121pub enum ClientPacket {
123 Join { username: String, password: String },
125 Move(MoveDirection),
127 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}