|
1 #[macro_use] |
|
2 extern crate log; |
|
3 extern crate argparse; |
|
4 extern crate dirs; |
|
5 extern crate ini; |
|
6 extern crate netbuf; |
|
7 extern crate stderrlog; |
|
8 |
|
9 use argparse::{ArgumentParser, Store}; |
|
10 use ini::Ini; |
|
11 use netbuf::Buf; |
|
12 use std::io::{Result, Write}; |
|
13 use std::net::TcpStream; |
|
14 use std::process::Command; |
|
15 use std::str::FromStr; |
|
16 |
|
17 fn extract_packet(buf: &mut Buf) -> Option<netbuf::Buf> { |
|
18 let packet_end = (&buf[..]).windows(2).position(|window| window == b"\n\n")?; |
|
19 |
|
20 let mut tail = buf.split_off(packet_end); |
|
21 |
|
22 std::mem::swap(&mut tail, buf); |
|
23 |
|
24 buf.consume(2); |
|
25 |
|
26 Some(tail) |
|
27 } |
|
28 |
|
29 fn connect_and_run( |
|
30 username: &str, |
|
31 password: &str, |
|
32 protocol_number: u32, |
|
33 executable: &str, |
|
34 data_prefix: &str, |
|
35 ) -> Result<()> { |
|
36 info!("Connecting..."); |
|
37 |
|
38 let mut stream = TcpStream::connect("hedgewars.org:46631")?; |
|
39 stream.set_nonblocking(false)?; |
|
40 |
|
41 let mut buf = Buf::new(); |
|
42 |
|
43 loop { |
|
44 buf.read_from(&mut stream)?; |
|
45 |
|
46 while let Some(msg) = extract_packet(&mut buf) { |
|
47 if msg[..].starts_with(b"CONNECTED") { |
|
48 info!("Connected"); |
|
49 let p = format!( |
|
50 "CHECKER\n{}\n{}\n{}\n\n", |
|
51 protocol_number, username, password |
|
52 ); |
|
53 stream.write(p.as_bytes())?; |
|
54 } else if msg[..].starts_with(b"PING") { |
|
55 stream.write(b"PONG\n\n")?; |
|
56 } else if msg[..].starts_with(b"LOGONPASSED") { |
|
57 info!("Logged in"); |
|
58 stream.write(b"READY\n\n")?; |
|
59 } else if msg[..].starts_with(b"BYE") { |
|
60 warn!("Received BYE: {}", String::from_utf8_lossy(&msg[..])); |
|
61 return Ok(()); |
|
62 } else { |
|
63 warn!( |
|
64 "Unknown protocol command: {}", |
|
65 String::from_utf8_lossy(&msg[..]) |
|
66 ) |
|
67 } |
|
68 } |
|
69 } |
|
70 } |
|
71 |
|
72 fn get_protocol_number(executable: &str) -> Result<u32> { |
|
73 let output = Command::new(executable).arg("--protocol").output()?; |
|
74 |
|
75 Ok(u32::from_str(&String::from_utf8(output.stdout).unwrap().as_str()).unwrap_or(55)) |
|
76 } |
|
77 |
|
78 fn main() { |
|
79 stderrlog::new() |
|
80 .verbosity(3) |
|
81 .timestamp(stderrlog::Timestamp::Second) |
|
82 .module(module_path!()) |
|
83 .init() |
|
84 .unwrap(); |
|
85 |
|
86 let mut frontend_settings = dirs::home_dir().unwrap(); |
|
87 frontend_settings.push(".hedgewars/settings.ini"); |
|
88 |
|
89 let i = Ini::load_from_file(frontend_settings.to_str().unwrap()).unwrap(); |
|
90 let username = i.get_from(Some("net"), "nick").unwrap(); |
|
91 let password = i.get_from(Some("net"), "passwordhash").unwrap(); |
|
92 |
|
93 let mut exe = "/usr/local/bin/hwengine".to_string(); |
|
94 let mut prefix = "/usr/local/share/hedgewars/Data".to_string(); |
|
95 { |
|
96 let mut ap = ArgumentParser::new(); |
|
97 ap.set_description("Game replay checker for hedgewars."); |
|
98 ap.refer(&mut exe) |
|
99 .add_option(&["--exe"], Store, "Path to hwengine executable"); |
|
100 ap.refer(&mut prefix) |
|
101 .add_option(&["--prefix"], Store, "Path main Data dir"); |
|
102 ap.parse_args_or_exit(); |
|
103 } |
|
104 |
|
105 info!("Executable: {}", exe); |
|
106 info!("Data dir: {}", prefix); |
|
107 |
|
108 let protocol_number = get_protocol_number(&exe.as_str()).unwrap_or_default(); |
|
109 |
|
110 info!("Using protocol number {}", protocol_number); |
|
111 |
|
112 connect_and_run(&username, &password, protocol_number, &exe, &prefix); |
|
113 } |
|
114 |
|
115 #[cfg(test)] |
|
116 #[test] |
|
117 fn test() { |
|
118 let mut buf = Buf::new(); |
|
119 buf.extend(b"Hell"); |
|
120 if let Some(_) = extract_packet(&mut buf) { |
|
121 assert!(false) |
|
122 } |
|
123 |
|
124 buf.extend(b"o\n\nWorld"); |
|
125 |
|
126 let packet2 = extract_packet(&mut buf).unwrap(); |
|
127 assert_eq!(&buf[..], b"World"); |
|
128 assert_eq!(&packet2[..], b"Hello"); |
|
129 |
|
130 if let Some(_) = extract_packet(&mut buf) { |
|
131 assert!(false) |
|
132 } |
|
133 } |