minecraft_client_rs/
client.rs1use std::error::Error;
2use std::fmt;
3use std::io::prelude::*;
4use std::net::{Shutdown, TcpStream};
5use std::sync::atomic::{AtomicI32, Ordering};
6
7use crate::message;
8
9const MAX_MESSAGE_SIZE: usize = 4110; #[derive(Debug)]
12struct RequestIDMismatchError;
13
14impl Error for RequestIDMismatchError {}
15
16impl fmt::Display for RequestIDMismatchError {
17 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
18 write!(f, "received error response from server")
19 }
20}
21
22pub struct Client {
23 conn: TcpStream,
24 last_id: AtomicI32,
25}
26
27impl Client {
28 pub fn new(hostport: String) -> Result<Client, Box<dyn Error>> {
29 let conn = TcpStream::connect(hostport)?;
30 Ok(Client{
31 conn: conn,
32 last_id: AtomicI32::new(0),
33 })
34 }
35
36 pub fn close(&mut self) -> Result<(), Box<dyn Error>> {
37 self.conn.shutdown(Shutdown::Both)?;
38 Ok(())
39 }
40
41 pub fn authenticate(&mut self, password: String) -> Result<message::Message, Box<dyn Error>> {
42 self.send_message(message::MessageType::Authenticate as i32, password)
43 }
44
45 pub fn send_command(&mut self, command: String) -> Result<message::Message, Box<dyn Error>> {
46 self.send_message(message::MessageType::Command as i32, command)
47 }
48
49 fn next_id(&self) -> i32 {
50 let prev = self.last_id.load(Ordering::Relaxed);
51 let next = prev + 1;
52 self.last_id.store(next, Ordering::Relaxed);
53 next
54 }
55
56 fn send_message(&mut self, msg_type: i32, msg_body: String) -> Result<message::Message, Box<dyn Error>> {
57 let req_id = self.next_id();
58 let req = message::Message{
59 size: msg_body.len() as i32 + message::HEADER_SIZE,
60 id: req_id.clone(),
61 msg_type: msg_type,
62 body: msg_body,
63 };
64
65 self.conn.write_all(&message::encode_message(req)[..])?;
66 let mut resp_bytes = [0u8; MAX_MESSAGE_SIZE];
67 self.conn.read(&mut resp_bytes)?;
68 let resp = message::decode_message(resp_bytes.to_vec())?;
69
70 if req_id == resp.id {
71 Ok(resp)
72 } else {
73 Err(Box::new(RequestIDMismatchError))
74 }
75 }
76}