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::ClCommand(cl_command) => {
145                            let ClCommand { name, arguments } = cl_command;
146                            let name = String::from_utf8_lossy(name);
147                            let arguments = String::from_utf8_lossy(arguments);
148                            write!(f, "NetMsg cid={cid} type=command name={name:?} arguments={arguments:?};")
149                        }
150                    },
151                    Err(_err) => {
152                        // error parsing the net_msg, just output raw msg
153                        let msg = hex::encode(msg);
154                        write!(f, "NetMsg cid={cid}, raw_msg={msg}")
155                    }
156                }
157            }
158            Chunk::Join { cid } => {
159                write!(f, "Join cid={cid};")
160            }
161            Chunk::Drop(drop) => {
162                let Drop { cid, reason } = drop;
163                let reason = String::from_utf8_lossy(reason);
164                write!(f, "Drop cid={cid} reason={reason:?};")
165            }
166            Chunk::ConsoleCommand(console_command) => {
167                let ConsoleCommand {
168                    cid,
169                    flags,
170                    cmd,
171                    ref args,
172                } = console_command;
173                let cmd = String::from_utf8_lossy(cmd);
174                let mut had_previous = false;
175                write!(
176                    f,
177                    "ConsoleCommand cid={cid} flags={flags} cmd={cmd:?} args=["
178                )?;
179                for arg in args {
180                    if had_previous {
181                        write!(f, " ")?;
182                    } else {
183                        had_previous = true;
184                    }
185                    let arg = String::from_utf8_lossy(arg);
186                    write!(f, "{arg:?}")?;
187                }
188                write!(f, "];")
189            }
190            Chunk::UnknownEx(unknown_ex) => {
191                let UnknownEx { uuid, data } = unknown_ex;
192                let data = hex::encode(data);
193                write!(f, "UnknownEx uuid={uuid} data={data};")
194            }
195            Chunk::Test => write!(f, "Test;"),
196            Chunk::DdnetVersionOld(ddnet_version_old) => {
197                let DdnetVersionOld { cid, version } = ddnet_version_old;
198                write!(f, "DdnetVersionOld cid={cid} version={version};")
199            }
200            Chunk::DdnetVersion(ddnet_version) => {
201                let DdnetVersion {
202                    cid,
203                    connection_id,
204                    version,
205                    version_str,
206                } = ddnet_version;
207                let version_str = String::from_utf8_lossy(version_str);
208                write!(f, "DdnetVersion cid={cid} connection_id={connection_id} version={version} version_str={version_str:?};")
209            }
210            Chunk::AuthInit(auth) => {
211                let Auth {
212                    cid,
213                    level,
214                    auth_name,
215                } = auth;
216                let auth_name = String::from_utf8_lossy(auth_name);
217                write!(
218                    f,
219                    "AuthInit cid={cid} level={level} auth_name={auth_name:?};"
220                )
221            }
222            Chunk::AuthLogin(auth) => {
223                let Auth {
224                    cid,
225                    level,
226                    auth_name,
227                } = auth;
228                let auth_name = String::from_utf8_lossy(auth_name);
229                write!(
230                    f,
231                    "AuthLogin cid={cid} level={level} auth_name={auth_name:?};"
232                )
233            }
234            Chunk::AuthLogout { cid } => {
235                write!(f, "AuthLogout cid={cid};")
236            }
237            Chunk::JoinVer6 { cid } => {
238                write!(f, "JoinVer6 cid={cid};")
239            }
240            Chunk::JoinVer7 { cid } => {
241                write!(f, "JoinVer7 cid={cid};")
242            }
243            Chunk::RejoinVer6 { cid } => {
244                write!(f, "RejoinVer6 cid={cid};")
245            }
246            Chunk::TeamSaveSuccess(team_save) => {
247                let TeamSave {
248                    team,
249                    save_id,
250                    save,
251                } = team_save;
252                let save = String::from_utf8_lossy(save);
253                write!(
254                    f,
255                    "TeamSaveSuccess team={team} save_id={save_id} save={save:?};"
256                )
257            }
258            Chunk::TeamSaveFailure { team } => {
259                write!(f, "TeamSaveFailure team={team};")
260            }
261            Chunk::TeamLoadSuccess(team_save) => {
262                let TeamSave {
263                    team,
264                    save_id,
265                    save,
266                } = team_save;
267                let save = String::from_utf8_lossy(save);
268                write!(
269                    f,
270                    "TeamLoadSuccess team={team} save_id={save_id} save={save:?};"
271                )
272            }
273            Chunk::TeamLoadFailure { team } => {
274                write!(f, "TeamLoadFailure team={team};")
275            }
276            Chunk::PlayerTeam { cid, team } => {
277                write!(f, "PlayerTeam cid={cid} team={team};")
278            }
279            Chunk::TeamPractice { team, practice } => {
280                write!(f, "TeamPractice team={team} enabled={practice};")
281            }
282            Chunk::PlayerReady { cid } => {
283                write!(f, "PlayerReady cid={cid};")
284            }
285            Chunk::PlayerSwap { cid1, cid2 } => {
286                write!(f, "PlayerSwap cid1={cid1} cid2={cid2};")
287            }
288            Chunk::Antibot(antibot) => {
289                let Antibot { data } = antibot;
290                let data = hex::encode(data);
291                write!(f, "Antibot data={data};")
292            }
293            Chunk::PlayerName(player_name) => {
294                let PlayerName { cid, name } = player_name;
295                let name = String::from_utf8_lossy(name);
296                write!(f, "PlayerName cid={cid} name={name:?};")
297            }
298            Chunk::PlayerFinish { cid, time } => {
299                let time = Instant::zero() + Duration::from_ticks(*time);
300                write!(f, r#"PlayerFinish cid={cid} time="{time}";"#)
301            }
302            Chunk::TeamFinish { team, time } => {
303                let time = Instant::zero() + Duration::from_ticks(*time);
304                write!(f, "TeamFinish team={team} time={time};")
305            }
306        }
307    }
308}