Skip to main content

twgame_core/
console.rs

1use std::num::IntErrorKind;
2use teehistorian::chunks::ConsoleCommand;
3
4pub enum Command<'a> {
5    Team(i32),
6    ToggleLock,
7    SetLock(bool),
8    SaveEmpty,
9    Save(&'a [u8]),
10    Load(&'a [u8]),
11    Kill,
12    Team0Mode,
13    Pause,
14    Spec,
15}
16
17impl<'a> Command<'a> {
18    pub fn serialize(&self) -> String {
19        match self {
20            Command::Team(team) => format!("team {team}"),
21            Command::ToggleLock => "lock".to_string(),
22            Command::SetLock(lock) => format!("lock {}", *lock as u8),
23            Command::SaveEmpty => "save".to_string(),
24            Command::Save(s) => format!("save {}", std::str::from_utf8(s).unwrap()),
25            Command::Load(s) => format!("load {}", std::str::from_utf8(s).unwrap()),
26            Command::Kill => "kill".to_string(),
27            Command::Team0Mode => "team0mode".to_string(),
28            Command::Pause => "pause".to_string(),
29            Command::Spec => "spec".to_string(),
30        }
31    }
32
33    pub fn i32(param: &[u8]) -> i32 {
34        let _p: i64 = 0;
35        // TODO: match function parsing int in ddnet
36
37        // starting with text -> unlock (false)
38        // text behind digits -> ignore
39        // positive out of range -> lock(true)
40        // negative out of range -> unlock (false)
41        // u64 % u32::max == 0 -> unlock (false)
42        let param: &str = std::str::from_utf8(param).unwrap_or("0");
43        match param.parse::<i64>() {
44            Ok(i) => i as i32,
45            Err(err) => match err.kind() {
46                IntErrorKind::PosOverflow => i32::MAX,
47                IntErrorKind::NegOverflow => 0,
48                _ => 0,
49            },
50        }
51    }
52
53    pub fn from_teehistorian(cmd: &ConsoleCommand<'a>) -> Option<Self> {
54        match cmd.cmd {
55            b"team" => {
56                let arg = cmd.args.first()?;
57                let arg = std::str::from_utf8(arg).ok()?;
58                let team = arg.parse::<i32>().ok()?;
59                Some(Command::Team(team))
60            }
61            b"lock" => {
62                if let Some(arg) = cmd.args.first() {
63                    let i = Command::i32(arg);
64                    Some(Command::SetLock(i != 0))
65                } else {
66                    Some(Command::ToggleLock)
67                }
68            }
69            b"unlock" => Some(Command::SetLock(false)),
70            b"load" => cmd.args.first().map(|arg| Command::Load(arg)),
71            b"save" => {
72                if let Some(arg) = cmd.args.first() {
73                    Some(Command::Save(arg))
74                } else {
75                    Some(Command::SaveEmpty)
76                }
77            }
78            b"kill" => Some(Command::Kill),
79            b"team0mode" => Some(Command::Team0Mode),
80            b"pause" => Some(Command::Pause),
81            b"spec" => Some(Command::Spec),
82            _ => None,
83        }
84    }
85}
86
87#[cfg(test)]
88mod tests {
89    use super::Command;
90
91    #[test]
92    fn serialize_commands() {
93        assert_eq!(Command::Team(1).serialize(), "team 1");
94        assert_eq!(Command::ToggleLock.serialize(), "lock");
95        assert_eq!(Command::SetLock(true).serialize(), "lock 1");
96        assert_eq!(Command::SetLock(false).serialize(), "lock 0");
97        assert_eq!(Command::Kill.serialize(), "kill");
98        assert_eq!(Command::Pause.serialize(), "pause");
99        assert_eq!(Command::Spec.serialize(), "spec");
100        assert_eq!(Command::SaveEmpty.serialize(), "save");
101        assert_eq!(Command::Save(b"abc").serialize(), "save abc");
102        assert_eq!(Command::Load(b"123").serialize(), "load 123");
103        assert_eq!(Command::Load(b"").serialize(), "load ");
104        assert_eq!(Command::Load(b"abc def").serialize(), "load abc def");
105    }
106}