1pub use libtw2_gamenet_ddnet::msg::game::Game as DdnetGameMsg;
2pub use libtw2_gamenet_teeworlds_0_7::msg::game::Game as Tw07GameMsg;
3use libtw2_gamenet_teeworlds_0_7::msg::Game;
4use libtw2_packer::{with_packer, Unpacker};
5use libtw2_warn::Warn;
6use std::fmt;
7
8#[derive(Copy, Clone, Debug, PartialEq, Eq)]
9pub enum NetVersion {
10 V06,
11 V07,
12 Unknown,
13}
14
15pub enum Error<'a> {
16 NonClientGameMsg06(DdnetGameMsg<'a>),
17 NonClientGameMsg07(Tw07GameMsg<'a>),
18 NetMsgParseError(libtw2_gamenet_ddnet::error::Error),
19}
20
21impl fmt::Display for Error<'_> {
22 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
23 use Error::*;
24 match self {
25 NonClientGameMsg06(msg) => write!(f, "NonClientGameMsg06 {msg:?}"),
26 NonClientGameMsg07(msg) => write!(f, "NonClientGameMsg07 {msg:?}"),
27 NetMsgParseError(err) => write!(f, "NetMsgParseError {err:?}"),
28 }
29 }
30}
31
32#[derive(Debug)]
33pub enum Chat {
34 None,
35 All,
36 Team,
37 Whisper,
38}
39
40impl From<libtw2_gamenet_teeworlds_0_7::enums::Chat> for Chat {
41 fn from(chat: libtw2_gamenet_teeworlds_0_7::enums::Chat) -> Chat {
42 use Chat::*;
43 match chat {
44 libtw2_gamenet_teeworlds_0_7::enums::Chat::None => None,
45 libtw2_gamenet_teeworlds_0_7::enums::Chat::All => All,
46 libtw2_gamenet_teeworlds_0_7::enums::Chat::Team => Team,
47 libtw2_gamenet_teeworlds_0_7::enums::Chat::Whisper => Whisper,
48 }
49 }
50}
51
52pub struct ClSay<'a> {
53 pub mode: Chat,
54 pub target: i32,
55 pub message: &'a [u8],
56}
57
58impl<'a> From<libtw2_gamenet_ddnet::msg::game::ClSay<'a>> for ClSay<'a> {
59 fn from(cl_say: libtw2_gamenet_ddnet::msg::game::ClSay<'a>) -> ClSay<'a> {
60 ClSay {
61 mode: if cl_say.team { Chat::Team } else { Chat::All },
62 target: -1,
63 message: cl_say.message,
64 }
65 }
66}
67
68impl<'a> From<libtw2_gamenet_teeworlds_0_7::msg::game::ClSay<'a>> for ClSay<'a> {
69 fn from(cl_say: libtw2_gamenet_teeworlds_0_7::msg::game::ClSay<'a>) -> ClSay<'a> {
70 ClSay {
71 mode: cl_say.mode.into(),
72 target: cl_say.target,
73 message: cl_say.message,
74 }
75 }
76}
77
78#[derive(Debug)]
79pub enum Team {
80 Spectators = -1,
81 Red,
82 Blue,
83}
84
85impl From<libtw2_gamenet_ddnet::enums::Team> for Team {
86 fn from(team: libtw2_gamenet_ddnet::enums::Team) -> Team {
87 use Team::*;
88 match team {
89 libtw2_gamenet_ddnet::enums::Team::Spectators => Spectators,
90 libtw2_gamenet_ddnet::enums::Team::Red => Red,
91 libtw2_gamenet_ddnet::enums::Team::Blue => Blue,
92 libtw2_gamenet_ddnet::enums::Team::All => Spectators, libtw2_gamenet_ddnet::enums::Team::WhisperSend => Spectators, libtw2_gamenet_ddnet::enums::Team::WhisperRecv => Spectators, }
96 }
97}
98
99impl From<libtw2_gamenet_teeworlds_0_7::enums::Team> for Team {
100 fn from(team: libtw2_gamenet_teeworlds_0_7::enums::Team) -> Team {
101 use Team::*;
102 match team {
103 libtw2_gamenet_teeworlds_0_7::enums::Team::Spectators => Spectators,
104 libtw2_gamenet_teeworlds_0_7::enums::Team::Red => Red,
105 libtw2_gamenet_teeworlds_0_7::enums::Team::Blue => Blue,
106 }
107 }
108}
109
110#[derive(Debug)]
111pub enum Spec {
112 Freeview,
113 Player,
114 Flagred,
115 Flagblue,
116}
117
118impl From<libtw2_gamenet_teeworlds_0_7::enums::Spec> for Spec {
119 fn from(spec: libtw2_gamenet_teeworlds_0_7::enums::Spec) -> Spec {
120 use Spec::*;
121 match spec {
122 libtw2_gamenet_teeworlds_0_7::enums::Spec::Freeview => Freeview,
123 libtw2_gamenet_teeworlds_0_7::enums::Spec::Player => Player,
124 libtw2_gamenet_teeworlds_0_7::enums::Spec::Flagred => Flagred,
125 libtw2_gamenet_teeworlds_0_7::enums::Spec::Flagblue => Flagblue,
126 }
127 }
128}
129
130pub struct ClSetSpectatorMode {
131 pub spec_mode: Spec,
132 pub spectator_id: i32,
133}
134
135impl From<libtw2_gamenet_ddnet::msg::game::ClSetSpectatorMode> for ClSetSpectatorMode {
136 fn from(
137 cl_set_spectator_mode: libtw2_gamenet_ddnet::msg::game::ClSetSpectatorMode,
138 ) -> ClSetSpectatorMode {
139 ClSetSpectatorMode {
140 spec_mode: if cl_set_spectator_mode.spectator_id == -1 {
141 Spec::Freeview
142 } else {
143 Spec::Player
144 },
145 spectator_id: cl_set_spectator_mode.spectator_id,
146 }
147 }
148}
149
150impl From<libtw2_gamenet_teeworlds_0_7::msg::game::ClSetSpectatorMode> for ClSetSpectatorMode {
151 fn from(
152 cl_set_spectator_mode: libtw2_gamenet_teeworlds_0_7::msg::game::ClSetSpectatorMode,
153 ) -> ClSetSpectatorMode {
154 ClSetSpectatorMode {
155 spec_mode: cl_set_spectator_mode.spec_mode.into(),
156 spectator_id: cl_set_spectator_mode.spectator_id,
157 }
158 }
159}
160
161#[repr(i32)]
162#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Eq, Hash, Ord)]
163pub enum Emoticon {
164 Oop,
165 Exclamation,
166 Hearts,
167 Drop,
168 Dotdot,
169 Music,
170 Sorry,
171 Ghost,
172 Sushi,
173 Splattee,
174 Deviltee,
175 Zomg,
176 Zzz,
177 Wtf,
178 Eyes,
179 Question,
180}
181
182impl From<libtw2_gamenet_ddnet::enums::Emoticon> for Emoticon {
183 fn from(emoticon: libtw2_gamenet_ddnet::enums::Emoticon) -> Emoticon {
184 use Emoticon::*;
185 match emoticon {
186 libtw2_gamenet_ddnet::enums::Emoticon::Oop => Oop,
187 libtw2_gamenet_ddnet::enums::Emoticon::Exclamation => Exclamation,
188 libtw2_gamenet_ddnet::enums::Emoticon::Hearts => Hearts,
189 libtw2_gamenet_ddnet::enums::Emoticon::Drop => Drop,
190 libtw2_gamenet_ddnet::enums::Emoticon::Dotdot => Dotdot,
191 libtw2_gamenet_ddnet::enums::Emoticon::Music => Music,
192 libtw2_gamenet_ddnet::enums::Emoticon::Sorry => Sorry,
193 libtw2_gamenet_ddnet::enums::Emoticon::Ghost => Ghost,
194 libtw2_gamenet_ddnet::enums::Emoticon::Sushi => Sushi,
195 libtw2_gamenet_ddnet::enums::Emoticon::Splattee => Splattee,
196 libtw2_gamenet_ddnet::enums::Emoticon::Deviltee => Deviltee,
197 libtw2_gamenet_ddnet::enums::Emoticon::Zomg => Zomg,
198 libtw2_gamenet_ddnet::enums::Emoticon::Zzz => Zzz,
199 libtw2_gamenet_ddnet::enums::Emoticon::Wtf => Wtf,
200 libtw2_gamenet_ddnet::enums::Emoticon::Eyes => Eyes,
201 libtw2_gamenet_ddnet::enums::Emoticon::Question => Question,
202 }
203 }
204}
205
206impl From<libtw2_gamenet_teeworlds_0_7::enums::Emoticon> for Emoticon {
207 fn from(emoticon: libtw2_gamenet_teeworlds_0_7::enums::Emoticon) -> Emoticon {
208 use Emoticon::*;
209 match emoticon {
210 libtw2_gamenet_teeworlds_0_7::enums::Emoticon::Oop => Oop,
211 libtw2_gamenet_teeworlds_0_7::enums::Emoticon::Exclamation => Exclamation,
212 libtw2_gamenet_teeworlds_0_7::enums::Emoticon::Hearts => Hearts,
213 libtw2_gamenet_teeworlds_0_7::enums::Emoticon::Drop => Drop,
214 libtw2_gamenet_teeworlds_0_7::enums::Emoticon::Dotdot => Dotdot,
215 libtw2_gamenet_teeworlds_0_7::enums::Emoticon::Music => Music,
216 libtw2_gamenet_teeworlds_0_7::enums::Emoticon::Sorry => Sorry,
217 libtw2_gamenet_teeworlds_0_7::enums::Emoticon::Ghost => Ghost,
218 libtw2_gamenet_teeworlds_0_7::enums::Emoticon::Sushi => Sushi,
219 libtw2_gamenet_teeworlds_0_7::enums::Emoticon::Splattee => Splattee,
220 libtw2_gamenet_teeworlds_0_7::enums::Emoticon::Deviltee => Deviltee,
221 libtw2_gamenet_teeworlds_0_7::enums::Emoticon::Zomg => Zomg,
222 libtw2_gamenet_teeworlds_0_7::enums::Emoticon::Zzz => Zzz,
223 libtw2_gamenet_teeworlds_0_7::enums::Emoticon::Wtf => Wtf,
224 libtw2_gamenet_teeworlds_0_7::enums::Emoticon::Eyes => Eyes,
225 libtw2_gamenet_teeworlds_0_7::enums::Emoticon::Question => Question,
226 }
227 }
228}
229
230pub struct ClCallVote<'a> {
231 pub type_: &'a [u8],
232 pub value: &'a [u8],
233 pub reason: &'a [u8],
234 pub force: bool,
235}
236
237impl<'a> From<libtw2_gamenet_ddnet::msg::game::ClCallVote<'a>> for ClCallVote<'a> {
238 fn from(cl_call_vote: libtw2_gamenet_ddnet::msg::game::ClCallVote<'a>) -> ClCallVote<'a> {
239 ClCallVote {
240 type_: cl_call_vote.type_,
241 value: cl_call_vote.value,
242 reason: cl_call_vote.reason,
243 force: false,
244 }
245 }
246}
247
248impl<'a> From<libtw2_gamenet_teeworlds_0_7::msg::game::ClCallVote<'a>> for ClCallVote<'a> {
249 fn from(
250 cl_call_vote: libtw2_gamenet_teeworlds_0_7::msg::game::ClCallVote<'a>,
251 ) -> ClCallVote<'a> {
252 ClCallVote {
253 type_: cl_call_vote.type_,
254 value: cl_call_vote.value,
255 reason: cl_call_vote.reason,
256 force: cl_call_vote.force,
257 }
258 }
259}
260
261pub struct Skin06<'a> {
262 pub skin: &'a [u8],
263 pub use_custom_color: bool,
264 pub color_body: i32,
265 pub color_feet: i32,
266}
267
268pub struct Skin07<'a> {
269 pub skin_part_names: [&'a [u8]; 6],
270 pub use_custom_colors: [bool; 6],
271 pub skin_part_colors: [i32; 6],
272}
273
274pub enum Skin<'a> {
275 V6(Skin06<'a>),
276 V7(Skin07<'a>),
277}
278
279impl fmt::Display for Skin<'_> {
280 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
281 match self {
282 Skin::V6(v6) => {
283 let Skin06 {
284 skin,
285 use_custom_color,
286 color_body,
287 color_feet,
288 } = v6;
289 let skin = String::from_utf8_lossy(skin);
290 write!(f, "{{ skin={skin:?} use_custom_color={use_custom_color} color_body={color_body} color_feet={color_feet} }}")
291 }
292 Skin::V7(v7) => {
293 let Skin07 {
294 skin_part_names,
295 use_custom_colors,
296 skin_part_colors,
297 } = v7;
298 write!(f, "{{")?;
299 for i in 0..6 {
300 let name = String::from_utf8_lossy(skin_part_names[i]);
301 let custom = use_custom_colors[i];
302 let color = skin_part_colors[i];
303 write!(f, "(part={name} use_custom_color={custom} color={color})")?;
304 if i != 5 {
305 write!(f, " ")?;
306 }
307 }
308 write!(f, "}}")
309 }
310 }
311 }
312}
313
314pub struct ClPlayerInfo<'a> {
315 pub name: &'a [u8],
316 pub clan: &'a [u8],
317 pub country: i32,
318 pub skin: Skin<'a>,
319}
320
321impl<'a> From<libtw2_gamenet_ddnet::msg::game::ClStartInfo<'a>> for ClPlayerInfo<'a> {
322 fn from(cl_start_info: libtw2_gamenet_ddnet::msg::game::ClStartInfo<'a>) -> ClPlayerInfo<'a> {
323 ClPlayerInfo {
324 name: cl_start_info.name,
325 clan: cl_start_info.clan,
326 country: cl_start_info.country,
327 skin: Skin::V6(Skin06 {
328 skin: cl_start_info.skin,
329 use_custom_color: cl_start_info.use_custom_color,
330 color_body: cl_start_info.color_body,
331 color_feet: cl_start_info.color_feet,
332 }),
333 }
334 }
335}
336
337impl<'a> From<libtw2_gamenet_ddnet::msg::game::ClChangeInfo<'a>> for ClPlayerInfo<'a> {
338 fn from(cl_change_info: libtw2_gamenet_ddnet::msg::game::ClChangeInfo<'a>) -> ClPlayerInfo<'a> {
339 ClPlayerInfo {
340 name: cl_change_info.name,
341 clan: cl_change_info.clan,
342 country: cl_change_info.country,
343 skin: Skin::V6(Skin06 {
344 skin: cl_change_info.skin,
345 use_custom_color: cl_change_info.use_custom_color,
346 color_body: cl_change_info.color_body,
347 color_feet: cl_change_info.color_feet,
348 }),
349 }
350 }
351}
352
353impl<'a> From<libtw2_gamenet_teeworlds_0_7::msg::game::ClStartInfo<'a>> for ClPlayerInfo<'a> {
354 fn from(
355 cl_start_info: libtw2_gamenet_teeworlds_0_7::msg::game::ClStartInfo<'a>,
356 ) -> ClPlayerInfo<'a> {
357 ClPlayerInfo {
358 name: cl_start_info.name,
359 clan: cl_start_info.clan,
360 country: cl_start_info.country,
361 skin: Skin::V7(Skin07 {
362 skin_part_names: cl_start_info.skin_part_names,
363 use_custom_colors: cl_start_info.use_custom_colors,
364 skin_part_colors: cl_start_info.skin_part_colors,
365 }),
366 }
367 }
368}
369
370pub struct ClShowDistance {
371 pub x: i32,
372 pub y: i32,
373}
374
375impl From<libtw2_gamenet_ddnet::msg::game::ClShowDistance> for ClShowDistance {
376 fn from(cl_show_distance: libtw2_gamenet_ddnet::msg::game::ClShowDistance) -> ClShowDistance {
377 ClShowDistance {
378 x: cl_show_distance.x,
379 y: cl_show_distance.y,
380 }
381 }
382}
383
384pub struct ClCameraInfo {
385 pub zoom: i32,
386 pub deadzone: i32,
387 pub follow_factor: i32,
388}
389
390impl From<libtw2_gamenet_ddnet::msg::game::ClCameraInfo> for ClCameraInfo {
391 fn from(cl_camera_info: libtw2_gamenet_ddnet::msg::game::ClCameraInfo) -> ClCameraInfo {
392 ClCameraInfo {
393 zoom: cl_camera_info.zoom,
394 deadzone: cl_camera_info.deadzone,
395 follow_factor: cl_camera_info.follow_factor,
396 }
397 }
398}
399
400pub struct ClCommand<'a> {
401 pub name: &'a [u8],
402 pub arguments: &'a [u8],
403}
404
405impl<'a> From<libtw2_gamenet_teeworlds_0_7::msg::game::ClCommand<'a>> for ClCommand<'a> {
406 fn from(cl_command: libtw2_gamenet_teeworlds_0_7::msg::game::ClCommand) -> ClCommand {
407 ClCommand {
408 name: cl_command.name,
409 arguments: cl_command.arguments,
410 }
411 }
412}
413
414pub enum ClNetMessage<'a> {
415 ClSay(ClSay<'a>),
416 ClSetTeam(Team),
417 ClSetSpectatorMode(ClSetSpectatorMode),
418 ClStartInfo(ClPlayerInfo<'a>),
419 ClChangeInfo(ClPlayerInfo<'a>),
420 ClKill,
421 ClEmoticon(Emoticon),
422 ClVote(i32),
423 ClCallVote(ClCallVote<'a>),
424 ClIsDdnet(i32),
426 ClShowOthers(i32),
427 ClShowDistance(ClShowDistance),
428 ClCameraInfo(ClCameraInfo),
429 ClEnableSpectatorCount(bool),
430 ClCommand(ClCommand<'a>),
431}
432
433impl<'a> ClNetMessage<'a> {
434 pub fn serialize_ddnet06(&self) -> Vec<u8> {
435 let ddnet_msg = self.to_ddnet06_msg();
436 const DDNET06_MAX_PACKETSIZE: usize = 1400;
437
438 let mut buf = Vec::with_capacity(DDNET06_MAX_PACKETSIZE);
439 with_packer(&mut buf, |p| ddnet_msg.encode(p))
441 .expect("ddnet 0.6 netmsg exceeds max packet size");
442 buf
443 }
444
445 fn to_ddnet06_msg(&self) -> DdnetGameMsg<'a> {
446 use libtw2_gamenet_ddnet::{enums as ddnet_enums, msg::game as ddnet_game};
447
448 match self {
449 ClNetMessage::ClSay(say) => {
450 let team = match say.mode {
451 Chat::All => false,
452 Chat::Team => true,
453 _ => false,
454 };
455 ddnet_game::Game::ClSay(ddnet_game::ClSay {
456 team,
457 message: say.message,
458 })
459 }
460 ClNetMessage::ClSetTeam(team) => ddnet_game::Game::ClSetTeam(ddnet_game::ClSetTeam {
461 team: match team {
462 Team::Spectators => ddnet_enums::Team::Spectators,
463 Team::Red => ddnet_enums::Team::Red,
464 Team::Blue => ddnet_enums::Team::Blue,
465 },
466 }),
467 ClNetMessage::ClSetSpectatorMode(mode) => {
468 let spectator_id = match mode.spec_mode {
469 Spec::Freeview => -1,
470 _ => mode.spectator_id,
471 };
472 ddnet_game::Game::ClSetSpectatorMode(ddnet_game::ClSetSpectatorMode {
473 spectator_id,
474 })
475 }
476 ClNetMessage::ClStartInfo(info) => {
477 let skin = match info.skin {
478 Skin::V6(ref skin) => skin,
479 Skin::V7(_) => &Skin06 {
480 skin: b"default",
481 use_custom_color: false,
482 color_body: 0,
483 color_feet: 0,
484 },
485 };
486 ddnet_game::Game::ClStartInfo(ddnet_game::ClStartInfo {
487 name: info.name,
488 clan: info.clan,
489 country: info.country,
490 skin: skin.skin,
491 use_custom_color: skin.use_custom_color,
492 color_body: skin.color_body,
493 color_feet: skin.color_feet,
494 })
495 }
496 ClNetMessage::ClChangeInfo(info) => {
497 let skin = match info.skin {
498 Skin::V6(ref skin) => skin,
499 Skin::V7(_) => &Skin06 {
500 skin: b"default",
501 use_custom_color: false,
502 color_body: 0,
503 color_feet: 0,
504 },
505 };
506 ddnet_game::Game::ClChangeInfo(ddnet_game::ClChangeInfo {
507 name: info.name,
508 clan: info.clan,
509 country: info.country,
510 skin: skin.skin,
511 use_custom_color: skin.use_custom_color,
512 color_body: skin.color_body,
513 color_feet: skin.color_feet,
514 })
515 }
516 ClNetMessage::ClKill => ddnet_game::Game::ClKill(ddnet_game::ClKill),
517 ClNetMessage::ClEmoticon(emoticon) => {
518 ddnet_game::Game::ClEmoticon(ddnet_game::ClEmoticon {
519 emoticon: match emoticon {
520 Emoticon::Oop => ddnet_enums::Emoticon::Oop,
521 Emoticon::Exclamation => ddnet_enums::Emoticon::Exclamation,
522 Emoticon::Hearts => ddnet_enums::Emoticon::Hearts,
523 Emoticon::Drop => ddnet_enums::Emoticon::Drop,
524 Emoticon::Dotdot => ddnet_enums::Emoticon::Dotdot,
525 Emoticon::Music => ddnet_enums::Emoticon::Music,
526 Emoticon::Sorry => ddnet_enums::Emoticon::Sorry,
527 Emoticon::Ghost => ddnet_enums::Emoticon::Ghost,
528 Emoticon::Sushi => ddnet_enums::Emoticon::Sushi,
529 Emoticon::Splattee => ddnet_enums::Emoticon::Splattee,
530 Emoticon::Deviltee => ddnet_enums::Emoticon::Deviltee,
531 Emoticon::Zomg => ddnet_enums::Emoticon::Zomg,
532 Emoticon::Zzz => ddnet_enums::Emoticon::Zzz,
533 Emoticon::Wtf => ddnet_enums::Emoticon::Wtf,
534 Emoticon::Eyes => ddnet_enums::Emoticon::Eyes,
535 Emoticon::Question => ddnet_enums::Emoticon::Question,
536 },
537 })
538 }
539 ClNetMessage::ClVote(vote) => {
540 ddnet_game::Game::ClVote(ddnet_game::ClVote { vote: *vote })
541 }
542 ClNetMessage::ClCallVote(call) => {
543 ddnet_game::Game::ClCallVote(ddnet_game::ClCallVote {
544 type_: call.type_,
545 value: call.value,
546 reason: call.reason,
547 })
548 }
549 ClNetMessage::ClIsDdnet(version) => {
550 ddnet_game::Game::ClIsDdnetLegacy(ddnet_game::ClIsDdnetLegacy {
551 ddnet_version: *version,
552 })
553 }
554 ClNetMessage::ClShowOthers(show) => {
555 ddnet_game::Game::ClShowOthers(ddnet_game::ClShowOthers { show: *show })
556 }
557 ClNetMessage::ClShowDistance(dist) => {
558 ddnet_game::Game::ClShowDistance(ddnet_game::ClShowDistance {
559 x: dist.x,
560 y: dist.y,
561 })
562 }
563 ClNetMessage::ClCameraInfo(info) => {
564 ddnet_game::Game::ClCameraInfo(ddnet_game::ClCameraInfo {
565 zoom: info.zoom,
566 deadzone: info.deadzone,
567 follow_factor: info.follow_factor,
568 })
569 }
570 ClNetMessage::ClEnableSpectatorCount(enable) => {
571 ddnet_game::Game::ClEnableSpectatorCount(ddnet_game::ClEnableSpectatorCount {
572 enable: *enable,
573 })
574 }
575 ClNetMessage::ClCommand(_) => DdnetGameMsg::ClSay(ddnet_game::ClSay {
577 team: false,
578 message: b"dropped 0.7 command",
579 }),
580 }
581 }
582}
583
584pub struct Stdout;
586impl<W: fmt::Debug> Warn<W> for Stdout {
587 fn warn(&mut self, warning: W) {
588 println!("WARN: {warning:?}");
589 }
590}
591
592#[allow(clippy::result_large_err)]
593fn parse_ddnet(buf: &[u8]) -> Result<ClNetMessage<'_>, Error<'_>> {
594 let mut b = Unpacker::new(buf);
595 match DdnetGameMsg::decode(&mut Stdout, &mut b) {
597 Ok(msg) => match msg {
598 DdnetGameMsg::SvMotd(_)
599 | DdnetGameMsg::SvBroadcast(_)
600 | DdnetGameMsg::SvChat(_)
601 | DdnetGameMsg::SvKillMsg(_)
602 | DdnetGameMsg::SvKillMsgTeam(_)
603 | DdnetGameMsg::SvSoundGlobal(_)
604 | DdnetGameMsg::SvTuneParams(_)
605 | DdnetGameMsg::SvReadyToEnter(_)
606 | DdnetGameMsg::SvWeaponPickup(_)
607 | DdnetGameMsg::SvEmoticon(_)
608 | DdnetGameMsg::SvVoteClearOptions(_)
609 | DdnetGameMsg::SvVoteOptionListAdd(_)
610 | DdnetGameMsg::SvVoteOptionAdd(_)
611 | DdnetGameMsg::SvVoteOptionRemove(_)
612 | DdnetGameMsg::SvVoteSet(_)
613 | DdnetGameMsg::SvVoteStatus(_)
614 | DdnetGameMsg::SvDdraceTime(_)
615 | DdnetGameMsg::SvRecord(_)
616 | DdnetGameMsg::Unused(_)
617 | DdnetGameMsg::Unused2(_)
618 | DdnetGameMsg::SvTeamsState(_)
619 | DdnetGameMsg::SvMyOwnMessage(_)
620 | DdnetGameMsg::SvDdraceTimeLegacy(_)
621 | DdnetGameMsg::SvRecordLegacy(_)
622 | DdnetGameMsg::SvTeamsStateLegacy(_)
623 | DdnetGameMsg::SvYourVote(_)
624 | DdnetGameMsg::SvRaceFinish(_)
625 | DdnetGameMsg::SvCommandInfo(_)
626 | DdnetGameMsg::SvCommandInfoRemove(_)
627 | DdnetGameMsg::SvVoteOptionGroupStart(_)
628 | DdnetGameMsg::SvVoteOptionGroupEnd(_)
629 | DdnetGameMsg::SvCommandInfoGroupStart(_)
630 | DdnetGameMsg::SvCommandInfoGroupEnd(_)
631 | DdnetGameMsg::SvChangeInfoCooldown(_)
632 | DdnetGameMsg::SvMapSoundGlobal(_)
633 | DdnetGameMsg::SvPreInput(_)
634 | DdnetGameMsg::SvSaveCode(_)
635 | DdnetGameMsg::SvServerAlert(_)
636 | DdnetGameMsg::SvModeratorAlert(_) => Err(Error::NonClientGameMsg06(msg)),
637 DdnetGameMsg::ClSay(msg) => Ok(ClNetMessage::ClSay(msg.into())),
638 DdnetGameMsg::ClSetTeam(msg) => Ok(ClNetMessage::ClSetTeam(msg.team.into())),
639 DdnetGameMsg::ClSetSpectatorMode(msg) => {
640 Ok(ClNetMessage::ClSetSpectatorMode(msg.into()))
641 }
642 DdnetGameMsg::ClStartInfo(msg) => Ok(ClNetMessage::ClStartInfo(msg.into())),
643 DdnetGameMsg::ClChangeInfo(msg) => Ok(ClNetMessage::ClChangeInfo(msg.into())),
644 DdnetGameMsg::ClKill(_) => Ok(ClNetMessage::ClKill),
645 DdnetGameMsg::ClEmoticon(msg) => Ok(ClNetMessage::ClEmoticon(msg.emoticon.into())),
646 DdnetGameMsg::ClVote(msg) => Ok(ClNetMessage::ClVote(msg.vote)),
647 DdnetGameMsg::ClCallVote(msg) => Ok(ClNetMessage::ClCallVote(msg.into())),
648 DdnetGameMsg::ClShowOthersLegacy(msg) => {
649 Ok(ClNetMessage::ClShowOthers(msg.show as i32))
650 }
651 DdnetGameMsg::ClShowDistance(msg) => Ok(ClNetMessage::ClShowDistance(msg.into())),
652 DdnetGameMsg::ClShowOthers(msg) => Ok(ClNetMessage::ClShowOthers(msg.show)),
653 DdnetGameMsg::ClIsDdnetLegacy(_msg) => Ok(ClNetMessage::ClIsDdnet(0)),
654 DdnetGameMsg::ClCameraInfo(msg) => Ok(ClNetMessage::ClCameraInfo(msg.into())),
655 DdnetGameMsg::ClEnableSpectatorCount(msg) => {
656 Ok(ClNetMessage::ClEnableSpectatorCount(msg.enable))
657 }
658 },
659 Err(err) => Err(Error::NetMsgParseError(err)),
660 }
661}
662
663#[allow(clippy::result_large_err)]
664fn parse_teeworlds_07(buf: &[u8]) -> Result<ClNetMessage<'_>, Error<'_>> {
665 let mut b = Unpacker::new(buf);
666 match Tw07GameMsg::decode(&mut Stdout, &mut b) {
668 Ok(msg) => match msg {
669 Game::SvMotd(_)
670 | Game::SvBroadcast(_)
671 | Game::SvChat(_)
672 | Game::SvTeam(_)
673 | Game::SvKillMsg(_)
674 | Game::SvTuneParams(_)
675 | Game::SvExtraProjectile(_)
676 | Game::SvReadyToEnter(_)
677 | Game::SvWeaponPickup(_)
678 | Game::SvEmoticon(_)
679 | Game::SvVoteClearOptions(_)
680 | Game::SvVoteOptionListAdd(_)
681 | Game::SvVoteOptionAdd(_)
682 | Game::SvVoteOptionRemove(_)
683 | Game::SvVoteSet(_)
684 | Game::SvVoteStatus(_)
685 | Game::SvServerSettings(_)
686 | Game::SvClientInfo(_)
687 | Game::SvGameInfo(_)
688 | Game::SvClientDrop(_)
689 | Game::SvGameMsg(_)
690 | Game::SvRaceFinish(_)
691 | Game::SvCheckpoint(_)
692 | Game::SvCommandInfo(_)
693 | Game::SvCommandInfoRemove(_)
694 | Game::SvSkinChange(_) => Err(Error::NonClientGameMsg07(msg)),
695 Game::DeClientEnter(_) => Err(Error::NonClientGameMsg07(msg)), Game::DeClientLeave(_) => Err(Error::NonClientGameMsg07(msg)), Game::ClSay(msg) => Ok(ClNetMessage::ClSay(msg.into())),
698 Game::ClSetTeam(msg) => Ok(ClNetMessage::ClSetTeam(msg.team.into())),
699 Game::ClSetSpectatorMode(msg) => Ok(ClNetMessage::ClSetSpectatorMode(msg.into())),
700 Game::ClStartInfo(msg) => Ok(ClNetMessage::ClStartInfo(msg.into())),
701 Game::ClKill(_) => Ok(ClNetMessage::ClKill),
702 Game::ClReadyChange(_) => Err(Error::NonClientGameMsg07(msg)), Game::ClEmoticon(msg) => Ok(ClNetMessage::ClEmoticon(msg.emoticon.into())),
704 Game::ClVote(msg) => Ok(ClNetMessage::ClVote(msg.vote)),
705 Game::ClCallVote(msg) => Ok(ClNetMessage::ClCallVote(msg.into())),
706 Game::ClSkinChange(_) => Err(Error::NonClientGameMsg07(msg)), Game::ClCommand(msg) => Ok(ClNetMessage::ClCommand(msg.into())),
708 },
709 Err(err) => Err(Error::NetMsgParseError(err)),
710 }
711}
712
713#[allow(clippy::result_large_err)]
714pub fn parse_net_msg<'a>(
715 buf: &'a [u8],
716 net_version: &mut NetVersion,
717) -> Result<ClNetMessage<'a>, Error<'a>> {
718 match *net_version {
719 NetVersion::V06 => parse_ddnet(buf),
720 NetVersion::V07 => parse_teeworlds_07(buf),
721 NetVersion::Unknown => match parse_ddnet(buf) {
722 Ok(msg) => {
723 *net_version = NetVersion::V06;
724 Ok(msg)
725 }
726 Err(err) => {
727 if let Ok(msg) = parse_teeworlds_07(buf) {
728 *net_version = NetVersion::V07;
729 Ok(msg)
730 } else {
731 Err(err)
732 }
733 }
734 },
735 }
736}