4 messages::HwProtocolMessage as ClientMessage, messages::HwServerMessage::*, parser, |
4 messages::HwProtocolMessage as ClientMessage, messages::HwServerMessage::*, parser, |
5 }; |
5 }; |
6 use ini::Ini; |
6 use ini::Ini; |
7 use log::{debug, info, warn}; |
7 use log::{debug, info, warn}; |
8 use netbuf::Buf; |
8 use netbuf::Buf; |
9 use std::{io::Write, net::TcpStream, process::Command, str::FromStr}; |
9 use std::{io::Write, str::FromStr}; |
10 |
10 use tokio::{io, io::AsyncWriteExt, net::TcpStream, process::Command}; |
11 fn check(executable: &str, data_prefix: &str, buffer: &[String]) -> Result<Vec<String>> { |
11 |
|
12 async fn check(executable: &str, data_prefix: &str, buffer: &[String]) -> Result<Vec<String>> { |
12 let mut replay = tempfile::NamedTempFile::new()?; |
13 let mut replay = tempfile::NamedTempFile::new()?; |
13 |
14 |
14 for line in buffer.into_iter() { |
15 for line in buffer.into_iter() { |
15 replay.write(&base64::decode(line)?)?; |
16 replay.write(&base64::decode(line)?)?; |
16 } |
17 } |
29 .arg(data_prefix) |
30 .arg(data_prefix) |
30 .arg("--nomusic") |
31 .arg("--nomusic") |
31 .arg("--nosound") |
32 .arg("--nosound") |
32 .arg("--stats-only") |
33 .arg("--stats-only") |
33 .arg(temp_file_path) |
34 .arg(temp_file_path) |
34 .output()?; |
35 //.spawn()? |
|
36 //.wait_with_output() |
|
37 .output() |
|
38 .await?; |
|
39 |
|
40 debug!("Engine finished!"); |
35 |
41 |
36 let mut result = Vec::new(); |
42 let mut result = Vec::new(); |
37 |
43 |
38 let mut engine_lines = output |
44 let mut engine_lines = output |
39 .stderr |
45 .stderr |
40 .split(|b| *b == '\n' as u8) |
46 .split(|b| *b == '\n' as u8) |
41 .skip_while(|l| *l != b"WINNERS" && *l != b"DRAW"); |
47 .skip_while(|l| *l != b"WINNERS" && *l != b"DRAW"); |
|
48 |
|
49 debug!("Engine lines: {:?}", &engine_lines); |
42 |
50 |
43 loop { |
51 loop { |
44 match engine_lines.next() { |
52 match engine_lines.next() { |
45 Some(b"DRAW") => result.push("DRAW".to_owned()), |
53 Some(b"DRAW") => result.push("DRAW".to_owned()), |
46 Some(b"WINNERS") => { |
54 Some(b"WINNERS") => { |
71 } |
79 } |
72 _ => break, |
80 _ => break, |
73 } |
81 } |
74 } |
82 } |
75 |
83 |
|
84 println!("Engine lines: {:?}", &result); |
|
85 |
76 if result.len() > 0 { |
86 if result.len() > 0 { |
77 Ok(result) |
87 Ok(result) |
78 } else { |
88 } else { |
79 bail!("no data from engine") |
89 bail!("no data from engine") |
80 } |
90 } |
81 } |
91 } |
82 |
92 |
83 fn connect_and_run( |
93 async fn connect_and_run( |
84 username: &str, |
94 username: &str, |
85 password: &str, |
95 password: &str, |
86 protocol_number: u16, |
96 protocol_number: u16, |
87 executable: &str, |
97 executable: &str, |
88 data_prefix: &str, |
98 data_prefix: &str, |
89 ) -> Result<()> { |
99 ) -> Result<()> { |
90 info!("Connecting..."); |
100 info!("Connecting..."); |
91 |
101 |
92 let mut stream = TcpStream::connect("hedgewars.org:46631")?; |
102 let mut stream = TcpStream::connect("hedgewars.org:46631").await?; |
93 stream.set_nonblocking(false)?; |
|
94 |
103 |
95 let mut buf = Buf::new(); |
104 let mut buf = Buf::new(); |
96 |
105 |
|
106 let mut replay_lines: Option<Vec<String>> = None; |
|
107 |
97 loop { |
108 loop { |
98 buf.read_from(&mut stream)?; |
109 let r = if let Some(ref lines) = replay_lines { |
|
110 let r = tokio::select! { |
|
111 _ = stream.readable() => None, |
|
112 r = check(executable, data_prefix, &lines) => Some(r) |
|
113 }; |
|
114 |
|
115 r |
|
116 } else { |
|
117 stream.readable().await?; |
|
118 None |
|
119 }; |
|
120 |
|
121 println!("Loop: {:?}", &r); |
|
122 |
|
123 if let Some(execution_result) = r { |
|
124 replay_lines = None; |
|
125 |
|
126 match execution_result { |
|
127 Ok(result) => { |
|
128 info!("Checked"); |
|
129 debug!("Check result: [{:?}]", result); |
|
130 |
|
131 stream |
|
132 .write( |
|
133 ClientMessage::CheckedOk(result) |
|
134 .to_raw_protocol() |
|
135 .as_bytes(), |
|
136 ) |
|
137 .await?; |
|
138 stream |
|
139 .write(ClientMessage::CheckerReady.to_raw_protocol().as_bytes()) |
|
140 .await?; |
|
141 } |
|
142 Err(e) => { |
|
143 info!("Check failed: {:?}", e); |
|
144 stream |
|
145 .write( |
|
146 ClientMessage::CheckedFail("error".to_owned()) |
|
147 .to_raw_protocol() |
|
148 .as_bytes(), |
|
149 ) |
|
150 .await?; |
|
151 stream |
|
152 .write(ClientMessage::CheckerReady.to_raw_protocol().as_bytes()) |
|
153 .await?; |
|
154 } |
|
155 } |
|
156 } else { |
|
157 let mut msg = [0; 4096]; |
|
158 // Try to read data, this may still fail with `WouldBlock` |
|
159 // if the readiness event is a false positive. |
|
160 match stream.try_read(&mut msg) { |
|
161 Ok(n) => { |
|
162 //println!("{:?}", &msg); |
|
163 buf.write_all(&msg[0..n])?; |
|
164 } |
|
165 Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {} |
|
166 Err(e) => { |
|
167 return Err(e.into()); |
|
168 } |
|
169 } |
|
170 } |
99 |
171 |
100 while let Ok((tail, msg)) = parser::server_message(buf.as_ref()) { |
172 while let Ok((tail, msg)) = parser::server_message(buf.as_ref()) { |
101 let tail_len = tail.len(); |
173 let tail_len = tail.len(); |
102 buf.consume(buf.len() - tail_len); |
174 buf.consume(buf.len() - tail_len); |
103 |
175 |
|
176 println!("Message from server: {:?}", &msg); |
|
177 |
104 match msg { |
178 match msg { |
105 Connected(_, _) => { |
179 Connected(_, _) => { |
106 info!("Connected"); |
180 info!("Connected"); |
107 stream.write( |
181 stream |
108 ClientMessage::Checker( |
182 .write( |
109 protocol_number, |
183 ClientMessage::Checker( |
110 username.to_owned(), |
184 protocol_number, |
111 password.to_owned(), |
185 username.to_owned(), |
|
186 password.to_owned(), |
|
187 ) |
|
188 .to_raw_protocol() |
|
189 .as_bytes(), |
112 ) |
190 ) |
113 .to_raw_protocol() |
191 .await?; |
114 .as_bytes(), |
|
115 )?; |
|
116 } |
192 } |
117 Ping => { |
193 Ping => { |
118 stream.write(ClientMessage::Pong.to_raw_protocol().as_bytes())?; |
194 stream |
|
195 .write(ClientMessage::Pong.to_raw_protocol().as_bytes()) |
|
196 .await?; |
119 } |
197 } |
120 LogonPassed => { |
198 LogonPassed => { |
121 stream.write(ClientMessage::CheckerReady.to_raw_protocol().as_bytes())?; |
199 stream |
|
200 .write(ClientMessage::CheckerReady.to_raw_protocol().as_bytes()) |
|
201 .await?; |
122 } |
202 } |
123 Replay(lines) => { |
203 Replay(lines) => { |
124 info!("Got a replay"); |
204 info!("Got a replay"); |
125 match check(executable, data_prefix, &lines) { |
205 replay_lines = Some(lines); |
126 Ok(result) => { |
|
127 info!("Checked"); |
|
128 debug!("Check result: [{:?}]", result); |
|
129 |
|
130 stream.write( |
|
131 ClientMessage::CheckedOk(result) |
|
132 .to_raw_protocol() |
|
133 .as_bytes(), |
|
134 )?; |
|
135 stream |
|
136 .write(ClientMessage::CheckerReady.to_raw_protocol().as_bytes())?; |
|
137 } |
|
138 Err(e) => { |
|
139 info!("Check failed: {:?}", e); |
|
140 stream.write( |
|
141 ClientMessage::CheckedFail("error".to_owned()) |
|
142 .to_raw_protocol() |
|
143 .as_bytes(), |
|
144 )?; |
|
145 stream |
|
146 .write(ClientMessage::CheckerReady.to_raw_protocol().as_bytes())?; |
|
147 } |
|
148 } |
|
149 } |
206 } |
150 Bye(message) => { |
207 Bye(message) => { |
151 warn!("Received BYE: {}", message); |
208 warn!("Received BYE: {}", message); |
152 return Ok(()); |
209 return Ok(()); |
153 } |
210 } |
215 } |
273 } |
216 |
274 |
217 info!("Executable: {}", exe); |
275 info!("Executable: {}", exe); |
218 info!("Data dir: {}", prefix); |
276 info!("Data dir: {}", prefix); |
219 |
277 |
220 let protocol_number = get_protocol_number(&exe.as_str()).unwrap_or_default(); |
278 let protocol_number = get_protocol_number(&exe.as_str()).await.unwrap_or_default(); |
221 |
279 |
222 info!("Using protocol number {}", protocol_number); |
280 info!("Using protocol number {}", protocol_number); |
223 |
281 |
224 connect_and_run(&username, &password, protocol_number, &exe, &prefix).unwrap(); |
282 connect_and_run(&username, &password, protocol_number, &exe, &prefix).await |
225 } |
283 } |