rust/hedgewars-checker/src/main.rs
changeset 15833 a855f32ab3ca
parent 14417 694c96fb3a06
child 15834 8c39a11f7756
--- a/rust/hedgewars-checker/src/main.rs	Wed Jun 30 00:18:53 2021 +0200
+++ b/rust/hedgewars-checker/src/main.rs	Wed Jun 30 23:06:54 2021 +0200
@@ -1,32 +1,17 @@
+use anyhow::{bail, Result};
 use argparse::{ArgumentParser, Store};
+use hedgewars_network_protocol::{
+    messages::HwProtocolMessage as ClientMessage, messages::HwServerMessage::*, parser,
+};
 use ini::Ini;
+use log::{debug, info, warn};
 use netbuf::Buf;
-use log::{debug, warn, info};
-use std::{
-    io::Write,
-    net::TcpStream,
-    process::Command,
-    str::FromStr
-};
-
-type CheckError = Box<std::error::Error>;
+use std::{io::Write, net::TcpStream, process::Command, str::FromStr};
 
-fn extract_packet(buf: &mut Buf) -> Option<netbuf::Buf> {
-    let packet_end = (&buf[..]).windows(2).position(|window| window == b"\n\n")?;
-
-    let mut tail = buf.split_off(packet_end);
-
-    std::mem::swap(&mut tail, buf);
-
-    buf.consume(2);
-
-    Some(tail)
-}
-
-fn check(executable: &str, data_prefix: &str, buffer: &[u8]) -> Result<Vec<Vec<u8>>, CheckError> {
+fn check(executable: &str, data_prefix: &str, buffer: &[String]) -> Result<Vec<String>> {
     let mut replay = tempfile::NamedTempFile::new()?;
 
-    for line in buffer.split(|b| *b == '\n' as u8) {
+    for line in buffer.into_iter() {
         replay.write(&base64::decode(line)?)?;
     }
 
@@ -57,31 +42,31 @@
 
     loop {
         match engine_lines.next() {
-            Some(b"DRAW") => result.push(b"DRAW".to_vec()),
+            Some(b"DRAW") => result.push("DRAW".to_owned()),
             Some(b"WINNERS") => {
-                result.push(b"WINNERS".to_vec());
+                result.push("WINNERS".to_owned());
                 let winners = engine_lines.next().unwrap();
                 let winners_num = u32::from_str(&String::from_utf8(winners.to_vec())?)?;
-                result.push(winners.to_vec());
+                result.push(String::from_utf8(winners.to_vec())?);
 
                 for _i in 0..winners_num {
-                    result.push(engine_lines.next().unwrap().to_vec());
+                    result.push(String::from_utf8(engine_lines.next().unwrap().to_vec())?);
                 }
             }
             Some(b"GHOST_POINTS") => {
-                result.push(b"GHOST_POINTS".to_vec());
+                result.push("GHOST_POINTS".to_owned());
                 let points = engine_lines.next().unwrap();
                 let points_num = u32::from_str(&String::from_utf8(points.to_vec())?)? * 2;
-                result.push(points.to_vec());
+                result.push(String::from_utf8(points.to_vec())?);
 
                 for _i in 0..points_num {
-                    result.push(engine_lines.next().unwrap().to_vec());
+                    result.push(String::from_utf8(engine_lines.next().unwrap().to_vec())?);
                 }
             }
             Some(b"ACHIEVEMENT") => {
-                result.push(b"ACHIEVEMENT".to_vec());
+                result.push("ACHIEVEMENT".to_owned());
                 for _i in 0..4 {
-                    result.push(engine_lines.next().unwrap().to_vec());
+                    result.push(String::from_utf8(engine_lines.next().unwrap().to_vec())?);
                 }
             }
             _ => break,
@@ -91,17 +76,17 @@
     if result.len() > 0 {
         Ok(result)
     } else {
-        Err("no data from engine".into())
+        bail!("no data from engine")
     }
 }
 
 fn connect_and_run(
     username: &str,
     password: &str,
-    protocol_number: u32,
+    protocol_number: u16,
     executable: &str,
     data_prefix: &str,
-) -> Result<(), CheckError> {
+) -> Result<()> {
     info!("Connecting...");
 
     let mut stream = TcpStream::connect("hedgewars.org:46631")?;
@@ -112,70 +97,93 @@
     loop {
         buf.read_from(&mut stream)?;
 
-        while let Some(msg) = extract_packet(&mut buf) {
-            if msg[..].starts_with(b"CONNECTED") {
-                info!("Connected");
-                let p = format!(
-                    "CHECKER\n{}\n{}\n{}\n\n",
-                    protocol_number, username, password
-                );
-                stream.write(p.as_bytes())?;
-            } else if msg[..].starts_with(b"PING") {
-                stream.write(b"PONG\n\n")?;
-            } else if msg[..].starts_with(b"LOGONPASSED") {
-                info!("Logged in");
-                stream.write(b"READY\n\n")?;
-            } else if msg[..].starts_with(b"REPLAY") {
-                info!("Got a replay");
-                match check(executable, data_prefix, &msg[7..]) {
-                    Ok(result) => {
-                        info!("Checked");
-                        debug!(
-                            "Check result: [{}]",
-                            String::from_utf8_lossy(&result.join(&(',' as u8)))
-                        );
+        while let Ok((tail, msg)) = parser::server_message(buf.as_ref()) {
+            buf.consume(buf.len() - tail.len());
 
-                        stream.write(b"CHECKED\nOK\n")?;
-                        stream.write(&result.join(&('\n' as u8)))?;
-                        stream.write(b"\n\nREADY\n\n")?;
-                    }
-                    Err(e) => {
-                        info!("Check failed: {:?}", e);
-                        stream.write(b"CHECKED\nFAIL\nerror\n\nREADY\n\n")?;
+            match msg {
+                Connected(_, _) => {
+                    info!("Connected");
+                    stream.write(
+                        ClientMessage::Checker(
+                            protocol_number,
+                            username.to_owned(),
+                            password.to_owned(),
+                        )
+                        .to_raw_protocol()
+                        .as_bytes(),
+                    )?;
+                }
+                Ping => {
+                    stream.write(ClientMessage::Pong.to_raw_protocol().as_bytes())?;
+                }
+                LogonPassed => {
+                    stream.write(ClientMessage::CheckerReady.to_raw_protocol().as_bytes())?;
+                }
+                Replay(lines) => {
+                    info!("Got a replay");
+                    match check(executable, data_prefix, &lines) {
+                        Ok(result) => {
+                            info!("Checked");
+                            debug!("Check result: [{:?}]", result);
+
+                            stream.write(
+                                ClientMessage::CheckedOk(result)
+                                    .to_raw_protocol()
+                                    .as_bytes(),
+                            )?;
+                            stream
+                                .write(ClientMessage::CheckerReady.to_raw_protocol().as_bytes())?;
+                        }
+                        Err(e) => {
+                            info!("Check failed: {:?}", e);
+                            stream.write(
+                                ClientMessage::CheckedFail("error".to_owned())
+                                    .to_raw_protocol()
+                                    .as_bytes(),
+                            )?;
+                            stream
+                                .write(ClientMessage::CheckerReady.to_raw_protocol().as_bytes())?;
+                        }
                     }
                 }
-            } else if msg[..].starts_with(b"BYE") {
-                warn!("Received BYE: {}", String::from_utf8_lossy(&msg[..]));
-                return Ok(());
-            } else if msg[..].starts_with(b"CHAT") {
-                let body = String::from_utf8_lossy(&msg[5..]);
-                let mut l = body.lines();
-                info!("Chat [{}]: {}", l.next().unwrap(), l.next().unwrap());
-            } else if msg[..].starts_with(b"ROOM") {
-                let body = String::from_utf8_lossy(&msg[5..]);
-                let mut l = body.lines();
-                if let Some(action) = l.next() {
-                    if action == "ADD" {
-                        info!("Room added: {}", l.skip(1).next().unwrap());
+                Bye(message) => {
+                    warn!("Received BYE: {}", message);
+                    return Ok(());
+                }
+                ChatMsg { nick, msg } => {
+                    info!("Chat [{}]: {}", nick, msg);
+                }
+                RoomAdd(fields) => {
+                    let mut l = fields.into_iter();
+                    info!("Room added: {}", l.skip(1).next().unwrap());
+                }
+                RoomUpdated(name, fields) => {
+                    let mut l = fields.into_iter();
+                    let new_name = l.skip(1).next().unwrap();
+
+                    if (name != new_name) {
+                        info!("Room renamed: {}", new_name);
                     }
                 }
-            } else if msg[..].starts_with(b"ERROR") {
-                warn!("Received ERROR: {}", String::from_utf8_lossy(&msg[..]));
-                return Ok(());
-            } else {
-                warn!(
-                    "Unknown protocol command: {}",
-                    String::from_utf8_lossy(&msg[..])
-                )
+                RoomRemove(_) => {
+                    // ignore
+                }
+                Error(message) => {
+                    warn!("Received ERROR: {}", message);
+                    return Ok(());
+                }
+                something => {
+                    warn!("Unexpected protocol command: {:?}", something)
+                }
             }
         }
     }
 }
 
-fn get_protocol_number(executable: &str) -> std::io::Result<u32> {
+fn get_protocol_number(executable: &str) -> std::io::Result<u16> {
     let output = Command::new(executable).arg("--protocol").output()?;
 
-    Ok(u32::from_str(&String::from_utf8(output.stdout).unwrap().trim()).unwrap_or(55))
+    Ok(u16::from_str(&String::from_utf8(output.stdout).unwrap().trim()).unwrap_or(55))
 }
 
 fn main() {
@@ -214,23 +222,3 @@
 
     connect_and_run(&username, &password, protocol_number, &exe, &prefix).unwrap();
 }
-
-#[cfg(test)]
-#[test]
-fn test() {
-    let mut buf = Buf::new();
-    buf.extend(b"Hell");
-    if let Some(_) = extract_packet(&mut buf) {
-        assert!(false)
-    }
-
-    buf.extend(b"o\n\nWorld");
-
-    let packet2 = extract_packet(&mut buf).unwrap();
-    assert_eq!(&buf[..], b"World");
-    assert_eq!(&packet2[..], b"Hello");
-
-    if let Some(_) = extract_packet(&mut buf) {
-        assert!(false)
-    }
-}