twgame-core 0.9.0

Game trait, helper types and helper functions for twgame
Documentation
use crate::net_msg::{
    ClCallVote, ClCommand, ClNetMessage, ClPlayerInfo, ClSay, ClSetSpectatorMode, ClShowDistance,
    NetVersion,
};
use crate::Input;
use std::fmt;
use teehistorian::chunks::{
    Antibot, Auth, ConsoleCommand, DdnetVersion, DdnetVersionOld, Drop, InputDiff, InputNew,
    NetMessage, PlayerDiff, PlayerName, PlayerNew, TeamSave, UnknownEx,
};
use teehistorian::Chunk;
use twsnap::time::{Duration, Instant};

// Using a wrapper struct, because we can pretty print NetMsgs here too
// They aren't available in the teehistorian crate.
pub struct DisplayChunk<'a>(pub &'a Chunk<'a>);

impl fmt::Display for DisplayChunk<'_> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match &self.0 {
            Chunk::PlayerDiff(player_diff) => {
                let PlayerDiff { cid, dx, dy } = player_diff;
                write!(f, "PlayerDiff cid={cid} diff=({dx}, {dy});")
            }
            Chunk::Eos => write!(f, "Eos;"),
            Chunk::TickSkip { dt } => {
                write!(f, "TickSkip dt={dt};")
            }
            Chunk::PlayerNew(player_new) => {
                let PlayerNew { cid, x, y } = player_new;
                write!(f, "PlayerNew cid={cid} pos=({x}, {y});")
            }
            Chunk::PlayerOld { cid } => {
                write!(f, "PlayerOld cid={cid};")
            }
            Chunk::InputDiff(input_diff) => {
                let InputDiff { cid, dinput } = input_diff;
                let Input {
                    direction,
                    target_x,
                    target_y,
                    jump,
                    fire,
                    hook,
                    player_flags,
                    wanted_weapon,
                    next_weapon,
                    prev_weapon,
                } = Input::from(*dinput);
                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};")
            }
            Chunk::InputNew(input_new) => {
                let InputNew { cid, input } = input_new;
                let Input {
                    direction,
                    target_x,
                    target_y,
                    jump,
                    fire,
                    hook,
                    player_flags,
                    wanted_weapon,
                    next_weapon,
                    prev_weapon,
                } = Input::from(*input);
                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};")
            }
            Chunk::NetMessage(net_msg) => {
                let NetMessage { cid, msg } = net_msg;
                match crate::net_msg::parse_net_msg(msg, &mut NetVersion::Unknown) {
                    Ok(msg) => match msg {
                        ClNetMessage::ClSay(cl_say) => {
                            let ClSay {
                                mode,
                                target,
                                message,
                            } = cl_say;
                            let message = String::from_utf8_lossy(message);
                            write!(f, "NetMsg cid={cid} type=say mode={mode:?} target={target}, message={message:?};")
                        }
                        ClNetMessage::ClSetTeam(team) => {
                            write!(f, "NetMsg cid={cid} type=set_team team={team:?};")
                        }
                        ClNetMessage::ClSetSpectatorMode(set_spectator_mode) => {
                            let ClSetSpectatorMode {
                                spec_mode,
                                spectator_id,
                            } = set_spectator_mode;
                            write!(f, "NetMsg cid={cid} type=set_spectator_mode spec_mode={spec_mode:?} spectator_id={spectator_id};")
                        }
                        ClNetMessage::ClStartInfo(start_info) => {
                            let ClPlayerInfo {
                                name,
                                clan,
                                country,
                                skin,
                            } = start_info;
                            let name = String::from_utf8_lossy(name);
                            let clan = String::from_utf8_lossy(clan);
                            write!(f, "NetMsg cid={cid} type=start_info name={name:?} clan={clan:?} country={country} skin={skin};")
                        }
                        ClNetMessage::ClChangeInfo(change_info) => {
                            let ClPlayerInfo {
                                name,
                                clan,
                                country,
                                skin,
                            } = change_info;
                            let name = String::from_utf8_lossy(name);
                            let clan = String::from_utf8_lossy(clan);
                            write!(f, "NetMsg cid={cid} type=change_info name={name:?} clan={clan:?} country={country} skin={skin};")
                        }
                        ClNetMessage::ClKill => {
                            write!(f, "NetMsg cid={cid} type=kill;")
                        }
                        ClNetMessage::ClEmoticon(emoticon) => {
                            write!(f, "NetMsg cid={cid} type=emoticon emoticon={emoticon:?};")
                        }
                        ClNetMessage::ClVote(vote) => {
                            write!(f, "NetMsg cid={cid} type=vote vote={vote};")
                        }
                        ClNetMessage::ClCallVote(call_vote) => {
                            let ClCallVote {
                                type_,
                                value,
                                reason,
                                force,
                            } = call_vote;
                            let type_ = String::from_utf8_lossy(type_);
                            let value = String::from_utf8_lossy(value);
                            let reason = String::from_utf8_lossy(reason);
                            write!(f, "NetMsg cid={cid} type=call_vote_type={type_} vote value={value:?} reason={reason:?} force={force};")
                        }
                        ClNetMessage::ClIsDdnet(version) => {
                            write!(f, "NetMsg cid={cid} type=id_ddnet version={version};")
                        }
                        ClNetMessage::ClShowOthers(enabled) => {
                            write!(f, "NetMsg cid={cid} type=show_others enabled={enabled};")
                        }
                        ClNetMessage::ClShowDistance(show_distance) => {
                            let ClShowDistance { x, y } = show_distance;
                            write!(f, "NetMsg cid={cid} type=show_distance x={x} y={y};")
                        }
                        ClNetMessage::ClCameraInfo(camera_info) => {
                            let crate::net_msg::ClCameraInfo {
                                zoom,
                                deadzone,
                                follow_factor,
                            } = camera_info;
                            write!(
                                f,
                                "NetMsg cid={cid} type=camera_info zoom={zoom} deadzone={deadzone} follow_factor={follow_factor};"
                            )
                        }
                        ClNetMessage::ClEnableSpectatorCount(enable) => {
                            write!(
                                f,
                                "NetMsg cid={cid} type=enable_spectator_count enable={enable};"
                            )
                        }
                        ClNetMessage::ClCommand(cl_command) => {
                            let ClCommand { name, arguments } = cl_command;
                            let name = String::from_utf8_lossy(name);
                            let arguments = String::from_utf8_lossy(arguments);
                            write!(f, "NetMsg cid={cid} type=command name={name:?} arguments={arguments:?};")
                        }
                    },
                    Err(_err) => {
                        // error parsing the net_msg, just output raw msg
                        let msg = hex::encode(msg);
                        write!(f, "NetMsg cid={cid}, raw_msg={msg}")
                    }
                }
            }
            Chunk::Join { cid } => {
                write!(f, "Join cid={cid};")
            }
            Chunk::Drop(drop) => {
                let Drop { cid, reason } = drop;
                let reason = String::from_utf8_lossy(reason);
                write!(f, "Drop cid={cid} reason={reason:?};")
            }
            Chunk::ConsoleCommand(console_command) => {
                let ConsoleCommand {
                    cid,
                    flags,
                    cmd,
                    ref args,
                } = console_command;
                let cmd = String::from_utf8_lossy(cmd);
                let mut had_previous = false;
                write!(
                    f,
                    "ConsoleCommand cid={cid} flags={flags} cmd={cmd:?} args=["
                )?;
                for arg in args {
                    if had_previous {
                        write!(f, " ")?;
                    } else {
                        had_previous = true;
                    }
                    let arg = String::from_utf8_lossy(arg);
                    write!(f, "{arg:?}")?;
                }
                write!(f, "];")
            }
            Chunk::UnknownEx(unknown_ex) => {
                let UnknownEx { uuid, data } = unknown_ex;
                let data = hex::encode(data);
                write!(f, "UnknownEx uuid={uuid} data={data};")
            }
            Chunk::Test => write!(f, "Test;"),
            Chunk::DdnetVersionOld(ddnet_version_old) => {
                let DdnetVersionOld { cid, version } = ddnet_version_old;
                write!(f, "DdnetVersionOld cid={cid} version={version};")
            }
            Chunk::DdnetVersion(ddnet_version) => {
                let DdnetVersion {
                    cid,
                    connection_id,
                    version,
                    version_str,
                } = ddnet_version;
                let version_str = String::from_utf8_lossy(version_str);
                write!(f, "DdnetVersion cid={cid} connection_id={connection_id} version={version} version_str={version_str:?};")
            }
            Chunk::AuthInit(auth) => {
                let Auth {
                    cid,
                    level,
                    auth_name,
                } = auth;
                let auth_name = String::from_utf8_lossy(auth_name);
                write!(
                    f,
                    "AuthInit cid={cid} level={level} auth_name={auth_name:?};"
                )
            }
            Chunk::AuthLogin(auth) => {
                let Auth {
                    cid,
                    level,
                    auth_name,
                } = auth;
                let auth_name = String::from_utf8_lossy(auth_name);
                write!(
                    f,
                    "AuthLogin cid={cid} level={level} auth_name={auth_name:?};"
                )
            }
            Chunk::AuthLogout { cid } => {
                write!(f, "AuthLogout cid={cid};")
            }
            Chunk::JoinVer6 { cid } => {
                write!(f, "JoinVer6 cid={cid};")
            }
            Chunk::JoinVer7 { cid } => {
                write!(f, "JoinVer7 cid={cid};")
            }
            Chunk::RejoinVer6 { cid } => {
                write!(f, "RejoinVer6 cid={cid};")
            }
            Chunk::TeamSaveSuccess(team_save) => {
                let TeamSave {
                    team,
                    save_id,
                    save,
                } = team_save;
                let save = String::from_utf8_lossy(save);
                write!(
                    f,
                    "TeamSaveSuccess team={team} save_id={save_id} save={save:?};"
                )
            }
            Chunk::TeamSaveFailure { team } => {
                write!(f, "TeamSaveFailure team={team};")
            }
            Chunk::TeamLoadSuccess(team_save) => {
                let TeamSave {
                    team,
                    save_id,
                    save,
                } = team_save;
                let save = String::from_utf8_lossy(save);
                write!(
                    f,
                    "TeamLoadSuccess team={team} save_id={save_id} save={save:?};"
                )
            }
            Chunk::TeamLoadFailure { team } => {
                write!(f, "TeamLoadFailure team={team};")
            }
            Chunk::PlayerTeam { cid, team } => {
                write!(f, "PlayerTeam cid={cid} team={team};")
            }
            Chunk::TeamPractice { team, practice } => {
                write!(f, "TeamPractice team={team} enabled={practice};")
            }
            Chunk::PlayerReady { cid } => {
                write!(f, "PlayerReady cid={cid};")
            }
            Chunk::PlayerSwap { cid1, cid2 } => {
                write!(f, "PlayerSwap cid1={cid1} cid2={cid2};")
            }
            Chunk::Antibot(antibot) => {
                let Antibot { data } = antibot;
                let data = hex::encode(data);
                write!(f, "Antibot data={data};")
            }
            Chunk::PlayerName(player_name) => {
                let PlayerName { cid, name } = player_name;
                let name = String::from_utf8_lossy(name);
                write!(f, "PlayerName cid={cid} name={name:?};")
            }
            Chunk::PlayerFinish { cid, time } => {
                let time = Instant::zero() + Duration::from_ticks(*time);
                write!(f, r#"PlayerFinish cid={cid} time="{time}";"#)
            }
            Chunk::TeamFinish { team, time } => {
                let time = Instant::zero() + Duration::from_ticks(*time);
                write!(f, "TeamFinish team={team} time={time};")
            }
        }
    }
}