Skip to main content

twgame_core/
display.rs

1use crate::net_msg::{
2    ClCallVote, ClCommand, ClNetMessage, ClPlayerInfo, ClSay, ClSetSpectatorMode, ClShowDistance,
3    NetVersion,
4};
5use crate::Input;
6use std::fmt;
7use teehistorian::chunks::{
8    Antibot, Auth, ConsoleCommand, DdnetVersion, DdnetVersionOld, Drop, InputDiff, InputNew,
9    NetMessage, PlayerDiff, PlayerName, PlayerNew, TeamSave, UnknownEx,
10};
11use teehistorian::Chunk;
12use twsnap::time::{Duration, Instant};
13
14// Using a wrapper struct, because we can pretty print NetMsgs here too
15// They aren't available in the teehistorian crate.
16pub struct DisplayChunk<'a>(pub &'a Chunk<'a>);
17
18impl fmt::Display for DisplayChunk<'_> {
19    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
20        match &self.0 {
21            Chunk::PlayerDiff(player_diff) => {
22                let PlayerDiff { cid, dx, dy } = player_diff;
23                write!(f, "PlayerDiff cid={cid} diff=({dx}, {dy});")
24            }
25            Chunk::Eos => write!(f, "Eos;"),
26            Chunk::TickSkip { dt } => {
27                write!(f, "TickSkip dt={dt};")
28            }
29            Chunk::PlayerNew(player_new) => {
30                let PlayerNew { cid, x, y } = player_new;
31                write!(f, "PlayerNew cid={cid} pos=({x}, {y});")
32            }
33            Chunk::PlayerOld { cid } => {
34                write!(f, "PlayerOld cid={cid};")
35            }
36            Chunk::InputDiff(input_diff) => {
37                let InputDiff { cid, dinput } = input_diff;
38                let Input {
39                    direction,
40                    target_x,
41                    target_y,
42                    jump,
43                    fire,
44                    hook,
45                    player_flags,
46                    wanted_weapon,
47                    next_weapon,
48                    prev_weapon,
49                } = Input::from(*dinput);
50                write!(f, "InputDiff cid={cid} direction={direction} target=({target_x}, {target_y}) jump={jump} fire={fire} hook={hook} player_flags={player_flags} wanted_weapon={wanted_weapon} next_weapon={next_weapon} prev_weapon={prev_weapon};")
51            }
52            Chunk::InputNew(input_new) => {
53                let InputNew { cid, input } = input_new;
54                let Input {
55                    direction,
56                    target_x,
57                    target_y,
58                    jump,
59                    fire,
60                    hook,
61                    player_flags,
62                    wanted_weapon,
63                    next_weapon,
64                    prev_weapon,
65                } = Input::from(*input);
66                write!(f, "InputNew cid={cid} direction={direction} target=({target_x}, {target_y}) jump={jump} fire={fire} hook={hook} player_flags={player_flags} wanted_weapon={wanted_weapon} next_weapon={next_weapon} prev_weapon={prev_weapon};")
67            }
68            Chunk::NetMessage(net_msg) => {
69                let NetMessage { cid, msg } = net_msg;
70                match crate::net_msg::parse_net_msg(msg, &mut NetVersion::Unknown) {
71                    Ok(msg) => match msg {
72                        ClNetMessage::ClSay(cl_say) => {
73                            let ClSay {
74                                mode,
75                                target,
76                                message,
77                            } = cl_say;
78                            let message = String::from_utf8_lossy(message);
79                            write!(f, "NetMsg cid={cid} type=say mode={mode:?} target={target}, message={message:?};")
80                        }
81                        ClNetMessage::ClSetTeam(team) => {
82                            write!(f, "NetMsg cid={cid} type=set_team team={team:?};")
83                        }
84                        ClNetMessage::ClSetSpectatorMode(set_spectator_mode) => {
85                            let ClSetSpectatorMode {
86                                spec_mode,
87                                spectator_id,
88                            } = set_spectator_mode;
89                            write!(f, "NetMsg cid={cid} type=set_spectator_mode spec_mode={spec_mode:?} spectator_id={spectator_id};")
90                        }
91                        ClNetMessage::ClStartInfo(start_info) => {
92                            let ClPlayerInfo {
93                                name,
94                                clan,
95                                country,
96                                skin,
97                            } = start_info;
98                            let name = String::from_utf8_lossy(name);
99                            let clan = String::from_utf8_lossy(clan);
100                            write!(f, "NetMsg cid={cid} type=start_info name={name:?} clan={clan:?} country={country} skin={skin};")
101                        }
102                        ClNetMessage::ClChangeInfo(change_info) => {
103                            let ClPlayerInfo {
104                                name,
105                                clan,
106                                country,
107                                skin,
108                            } = change_info;
109                            let name = String::from_utf8_lossy(name);
110                            let clan = String::from_utf8_lossy(clan);
111                            write!(f, "NetMsg cid={cid} type=change_info name={name:?} clan={clan:?} country={country} skin={skin};")
112                        }
113                        ClNetMessage::ClKill => {
114                            write!(f, "NetMsg cid={cid} type=kill;")
115                        }
116                        ClNetMessage::ClEmoticon(emoticon) => {
117                            write!(f, "NetMsg cid={cid} type=emoticon emoticon={emoticon:?};")
118                        }
119                        ClNetMessage::ClVote(vote) => {
120                            write!(f, "NetMsg cid={cid} type=vote vote={vote};")
121                        }
122                        ClNetMessage::ClCallVote(call_vote) => {
123                            let ClCallVote {
124                                type_,
125                                value,
126                                reason,
127                                force,
128                            } = call_vote;
129                            let type_ = String::from_utf8_lossy(type_);
130                            let value = String::from_utf8_lossy(value);
131                            let reason = String::from_utf8_lossy(reason);
132                            write!(f, "NetMsg cid={cid} type=call_vote_type={type_} vote value={value:?} reason={reason:?} force={force};")
133                        }
134                        ClNetMessage::ClIsDdnet(version) => {
135                            write!(f, "NetMsg cid={cid} type=id_ddnet version={version};")
136                        }
137                        ClNetMessage::ClShowOthers(enabled) => {
138                            write!(f, "NetMsg cid={cid} type=show_others enabled={enabled};")
139                        }
140                        ClNetMessage::ClShowDistance(show_distance) => {
141                            let ClShowDistance { x, y } = show_distance;
142                            write!(f, "NetMsg cid={cid} type=show_distance x={x} y={y};")
143                        }
144                        ClNetMessage::ClCameraInfo(camera_info) => {
145                            let crate::net_msg::ClCameraInfo {
146                                zoom,
147                                deadzone,
148                                follow_factor,
149                            } = camera_info;
150                            write!(
151                                f,
152                                "NetMsg cid={cid} type=camera_info zoom={zoom} deadzone={deadzone} follow_factor={follow_factor};"
153                            )
154                        }
155                        ClNetMessage::ClEnableSpectatorCount(enable) => {
156                            write!(
157                                f,
158                                "NetMsg cid={cid} type=enable_spectator_count enable={enable};"
159                            )
160                        }
161                        ClNetMessage::ClCommand(cl_command) => {
162                            let ClCommand { name, arguments } = cl_command;
163                            let name = String::from_utf8_lossy(name);
164                            let arguments = String::from_utf8_lossy(arguments);
165                            write!(f, "NetMsg cid={cid} type=command name={name:?} arguments={arguments:?};")
166                        }
167                    },
168                    Err(_err) => {
169                        // error parsing the net_msg, just output raw msg
170                        let msg = hex::encode(msg);
171                        write!(f, "NetMsg cid={cid}, raw_msg={msg}")
172                    }
173                }
174            }
175            Chunk::Join { cid } => {
176                write!(f, "Join cid={cid};")
177            }
178            Chunk::Drop(drop) => {
179                let Drop { cid, reason } = drop;
180                let reason = String::from_utf8_lossy(reason);
181                write!(f, "Drop cid={cid} reason={reason:?};")
182            }
183            Chunk::ConsoleCommand(console_command) => {
184                let ConsoleCommand {
185                    cid,
186                    flags,
187                    cmd,
188                    ref args,
189                } = console_command;
190                let cmd = String::from_utf8_lossy(cmd);
191                let mut had_previous = false;
192                write!(
193                    f,
194                    "ConsoleCommand cid={cid} flags={flags} cmd={cmd:?} args=["
195                )?;
196                for arg in args {
197                    if had_previous {
198                        write!(f, " ")?;
199                    } else {
200                        had_previous = true;
201                    }
202                    let arg = String::from_utf8_lossy(arg);
203                    write!(f, "{arg:?}")?;
204                }
205                write!(f, "];")
206            }
207            Chunk::UnknownEx(unknown_ex) => {
208                let UnknownEx { uuid, data } = unknown_ex;
209                let data = hex::encode(data);
210                write!(f, "UnknownEx uuid={uuid} data={data};")
211            }
212            Chunk::Test => write!(f, "Test;"),
213            Chunk::DdnetVersionOld(ddnet_version_old) => {
214                let DdnetVersionOld { cid, version } = ddnet_version_old;
215                write!(f, "DdnetVersionOld cid={cid} version={version};")
216            }
217            Chunk::DdnetVersion(ddnet_version) => {
218                let DdnetVersion {
219                    cid,
220                    connection_id,
221                    version,
222                    version_str,
223                } = ddnet_version;
224                let version_str = String::from_utf8_lossy(version_str);
225                write!(f, "DdnetVersion cid={cid} connection_id={connection_id} version={version} version_str={version_str:?};")
226            }
227            Chunk::AuthInit(auth) => {
228                let Auth {
229                    cid,
230                    level,
231                    auth_name,
232                } = auth;
233                let auth_name = String::from_utf8_lossy(auth_name);
234                write!(
235                    f,
236                    "AuthInit cid={cid} level={level} auth_name={auth_name:?};"
237                )
238            }
239            Chunk::AuthLogin(auth) => {
240                let Auth {
241                    cid,
242                    level,
243                    auth_name,
244                } = auth;
245                let auth_name = String::from_utf8_lossy(auth_name);
246                write!(
247                    f,
248                    "AuthLogin cid={cid} level={level} auth_name={auth_name:?};"
249                )
250            }
251            Chunk::AuthLogout { cid } => {
252                write!(f, "AuthLogout cid={cid};")
253            }
254            Chunk::JoinVer6 { cid } => {
255                write!(f, "JoinVer6 cid={cid};")
256            }
257            Chunk::JoinVer7 { cid } => {
258                write!(f, "JoinVer7 cid={cid};")
259            }
260            Chunk::RejoinVer6 { cid } => {
261                write!(f, "RejoinVer6 cid={cid};")
262            }
263            Chunk::TeamSaveSuccess(team_save) => {
264                let TeamSave {
265                    team,
266                    save_id,
267                    save,
268                } = team_save;
269                let save = String::from_utf8_lossy(save);
270                write!(
271                    f,
272                    "TeamSaveSuccess team={team} save_id={save_id} save={save:?};"
273                )
274            }
275            Chunk::TeamSaveFailure { team } => {
276                write!(f, "TeamSaveFailure team={team};")
277            }
278            Chunk::TeamLoadSuccess(team_save) => {
279                let TeamSave {
280                    team,
281                    save_id,
282                    save,
283                } = team_save;
284                let save = String::from_utf8_lossy(save);
285                write!(
286                    f,
287                    "TeamLoadSuccess team={team} save_id={save_id} save={save:?};"
288                )
289            }
290            Chunk::TeamLoadFailure { team } => {
291                write!(f, "TeamLoadFailure team={team};")
292            }
293            Chunk::PlayerTeam { cid, team } => {
294                write!(f, "PlayerTeam cid={cid} team={team};")
295            }
296            Chunk::TeamPractice { team, practice } => {
297                write!(f, "TeamPractice team={team} enabled={practice};")
298            }
299            Chunk::PlayerReady { cid } => {
300                write!(f, "PlayerReady cid={cid};")
301            }
302            Chunk::PlayerSwap { cid1, cid2 } => {
303                write!(f, "PlayerSwap cid1={cid1} cid2={cid2};")
304            }
305            Chunk::Antibot(antibot) => {
306                let Antibot { data } = antibot;
307                let data = hex::encode(data);
308                write!(f, "Antibot data={data};")
309            }
310            Chunk::PlayerName(player_name) => {
311                let PlayerName { cid, name } = player_name;
312                let name = String::from_utf8_lossy(name);
313                write!(f, "PlayerName cid={cid} name={name:?};")
314            }
315            Chunk::PlayerFinish { cid, time } => {
316                let time = Instant::zero() + Duration::from_ticks(*time);
317                write!(f, r#"PlayerFinish cid={cid} time="{time}";"#)
318            }
319            Chunk::TeamFinish { team, time } => {
320                let time = Instant::zero() + Duration::from_ticks(*time);
321                write!(f, "TeamFinish team={team} time={time};")
322            }
323        }
324    }
325}