parent
							
								
									5ef70901e6
								
							
						
					
					
						commit
						188e2b7610
					
				| @ -0,0 +1,246 @@ | ||||
| use std::{ | ||||
|     collections::HashMap, | ||||
|     io::{Read, Write}, | ||||
| }; | ||||
| 
 | ||||
| use varint_rs::{VarintReader, VarintWriter}; | ||||
| 
 | ||||
| use crate::{ | ||||
|     Connected, Server, Status, | ||||
|     error::ChatError, | ||||
|     error::Result, | ||||
|     readers::{read_packet, read_plugin_msg, read_string}, | ||||
|     status::ServerType, | ||||
|     writers::{write_packet, write_plugin_msg, write_string}, | ||||
| }; | ||||
| 
 | ||||
| impl Server<Status> { | ||||
|     pub fn connect(self) -> Result<Server<Connected>> { | ||||
|         let Self { | ||||
|             mut stream, | ||||
|             addr, | ||||
|             port, | ||||
|             state: | ||||
|                 Status { | ||||
|                     typ, | ||||
|                     description, | ||||
|                     mods, | ||||
|                 }, | ||||
|         } = self; | ||||
| 
 | ||||
|         let (uuid, username) = match typ { | ||||
|             ServerType::Vanilla => { | ||||
|                 // Handshake
 | ||||
|                 write_packet(&mut stream, |buf| { | ||||
|                     buf.write_u8_varint(0)?; | ||||
|                     buf.write_u8_varint(5)?; | ||||
|                     buf.write_usize_varint(addr.len())?; | ||||
|                     buf.write_all(addr.as_bytes())?; | ||||
|                     buf.write_all(&port.to_le_bytes())?; | ||||
|                     buf.write_u8_varint(2)?; | ||||
|                     Ok(()) | ||||
|                 })?; | ||||
| 
 | ||||
|                 // Login start
 | ||||
|                 write_packet(&mut stream, |buf| { | ||||
|                     buf.write_u8_varint(0)?; | ||||
|                     let uname = "McChatClient"; | ||||
|                     write_string(buf, uname)?; | ||||
|                     Ok(()) | ||||
|                 })?; | ||||
| 
 | ||||
|                 read_packet(&mut stream, |packet| { | ||||
|                     let id = packet.read_usize_varint()?; | ||||
|                     assert!(id == 2); | ||||
|                     let uuid = read_string(packet)?; | ||||
|                     let username = read_string(packet)?; | ||||
|                     Ok((uuid, username)) | ||||
|                 })? | ||||
|             } | ||||
|             ServerType::Forge => { | ||||
|                 // Handshake
 | ||||
| 
 | ||||
|                 write_packet(&mut stream, |buf| { | ||||
|                     buf.write_u8_varint(0)?; | ||||
|                     buf.write_u8_varint(5)?; | ||||
|                     let addr = format!("{addr}\0FML\0"); | ||||
|                     buf.write_usize_varint(addr.len())?; | ||||
|                     buf.write_all(addr.as_bytes())?; | ||||
|                     buf.write_all(&port.to_le_bytes())?; | ||||
|                     buf.write_u8_varint(2)?; | ||||
|                     Ok(()) | ||||
|                 })?; | ||||
| 
 | ||||
|                 // Login start
 | ||||
|                 write_packet(&mut stream, |buf| { | ||||
|                     buf.write_u8_varint(0)?; | ||||
|                     let uname = "McChatClient"; | ||||
|                     write_string(buf, uname)?; | ||||
|                     Ok(()) | ||||
|                 })?; | ||||
| 
 | ||||
|                 // Login Success
 | ||||
|                 let (uuid, username) = read_packet(&mut stream, |packet| { | ||||
|                     let id = packet.read_usize_varint()?; | ||||
|                     assert!(id == 2); | ||||
|                     let uuid = read_string(packet)?; | ||||
|                     let username = read_string(packet)?; | ||||
|                     Ok((uuid, username)) | ||||
|                 })?; | ||||
| 
 | ||||
|                 // Server Register Channels
 | ||||
|                 let (_channels, channels_buf) = | ||||
|                     read_plugin_msg(&mut stream, "REGISTER", |packet| { | ||||
|                         Ok(( | ||||
|                             packet | ||||
|                                 .split(|b| *b == 0) | ||||
|                                 .map(|b| String::from_utf8(b.to_vec()).map_err(ChatError::from)) | ||||
|                                 .collect::<Result<Vec<_>>>()?, | ||||
|                             packet.to_vec(), | ||||
|                         )) | ||||
|                     })?; | ||||
| 
 | ||||
|                 // Server Hello
 | ||||
|                 let _dim = read_plugin_msg(&mut stream, "FML|HS", |packet| { | ||||
|                     let discriminant = VarintReader::read(packet)?; | ||||
|                     assert_eq!(discriminant, 0); | ||||
|                     let proto_ver = VarintReader::read(packet)?; | ||||
|                     assert_eq!(proto_ver, 2); | ||||
|                     let mut buf = [0u8; 4]; | ||||
|                     packet.read_exact(&mut buf)?; | ||||
|                     let dim = i32::from_be_bytes(buf); | ||||
|                     Ok(dim) | ||||
|                 })?; | ||||
| 
 | ||||
|                 // Client Register Channels
 | ||||
|                 write_plugin_msg(&mut stream, "REGISTER", |body| { | ||||
|                     body.write_all(&channels_buf)?; | ||||
|                     Ok(()) | ||||
|                 })?; | ||||
| 
 | ||||
|                 // Client Hello
 | ||||
|                 write_plugin_msg(&mut stream, "FML|HS", |body| { | ||||
|                     body.write_all(&[0x1])?; | ||||
|                     body.write_all(&[0x2])?; | ||||
|                     Ok(()) | ||||
|                 })?; | ||||
| 
 | ||||
|                 // Client Modlist
 | ||||
|                 write_plugin_msg(&mut stream, "FML|HS", |body| { | ||||
|                     body.write_all(&[0x2])?; | ||||
|                     let mods = mods.as_ref().unwrap(); | ||||
|                     body.write_usize_varint(mods.len())?; | ||||
| 
 | ||||
|                     for (id, ver) in mods { | ||||
|                         write_string(body, id)?; | ||||
|                         write_string(body, ver)?; | ||||
|                     } | ||||
|                     Ok(()) | ||||
|                 })?; | ||||
| 
 | ||||
|                 // Server Modlist
 | ||||
|                 let _mods = read_plugin_msg(&mut stream, "FML|HS", |packet| { | ||||
|                     let discriminant = VarintReader::read(packet)?; | ||||
|                     assert_eq!(discriminant, 2); | ||||
|                     let num_mods = VarintReader::read(packet)?; | ||||
| 
 | ||||
|                     let mut mods = HashMap::new(); | ||||
|                     for _ in 0..num_mods { | ||||
|                         let id = read_string(packet)?; | ||||
|                         let ver = read_string(packet)?; | ||||
|                         mods.insert(id, ver); | ||||
|                     } | ||||
|                     Ok(mods) | ||||
|                 })?; | ||||
| 
 | ||||
|                 // HandshakeAck waitingseverdata
 | ||||
|                 write_plugin_msg(&mut stream, "FML|HS", |body| { | ||||
|                     body.write_all(&[0xFF, 0x02])?; | ||||
|                     Ok(()) | ||||
|                 })?; | ||||
| 
 | ||||
|                 // Server RegistryData
 | ||||
|                 read_plugin_msg(&mut stream, "FML|HS", |packet| { | ||||
|                     let discriminant = VarintReader::read(packet)?; | ||||
|                     assert_eq!(discriminant, 3); | ||||
|                     let num_ids = packet.read_usize_varint()?; | ||||
| 
 | ||||
|                     for _ in 0..num_ids { | ||||
|                         let _name = read_string(packet)?; | ||||
|                         let _id = packet.read_usize_varint()?; | ||||
|                     } | ||||
| 
 | ||||
|                     if packet.len() > 0 { | ||||
|                         let len = packet.read_usize_varint()?; | ||||
|                         for _ in 0..len { | ||||
|                             read_string(packet)?; | ||||
|                         } | ||||
| 
 | ||||
|                         let len = packet.read_usize_varint()?; | ||||
|                         for _ in 0..len { | ||||
|                             read_string(packet)?; | ||||
|                         } | ||||
|                     } | ||||
|                     Ok(()) | ||||
|                 })?; | ||||
| 
 | ||||
|                 // Server HandshakeAck waitingack
 | ||||
|                 read_plugin_msg(&mut stream, "FML|HS", |packet| { | ||||
|                     let discriminant = VarintReader::read(packet)?; | ||||
|                     assert_eq!(discriminant, 255); | ||||
|                     let phrase = VarintReader::read(packet)?; | ||||
|                     assert_eq!(phrase, 2); | ||||
|                     Ok(()) | ||||
|                 })?; | ||||
| 
 | ||||
|                 // HandshakeAck waitingsevercomplete
 | ||||
|                 write_plugin_msg(&mut stream, "FML|HS", |body| { | ||||
|                     body.write_all(&[0xFF, 0x03])?; | ||||
|                     Ok(()) | ||||
|                 })?; | ||||
| 
 | ||||
|                 // Serrver FluidIdmap
 | ||||
|                 read_plugin_msg(&mut stream, "FORGE", |packet| { | ||||
|                     let discriminant = VarintReader::read(packet)?; | ||||
|                     assert_eq!(discriminant, 2); | ||||
|                     Ok(()) | ||||
|                 })?; | ||||
| 
 | ||||
|                 // HandshakeAck pendingcomplete
 | ||||
|                 write_plugin_msg(&mut stream, "FML|HS", |body| { | ||||
|                     body.write_all(&[0xFF, 0x04])?; | ||||
|                     Ok(()) | ||||
|                 })?; | ||||
| 
 | ||||
|                 // Server HandshakeAck complete
 | ||||
|                 read_plugin_msg(&mut stream, "FML|HS", |packet| { | ||||
|                     let discriminant = VarintReader::read(packet)?; | ||||
|                     assert_eq!(discriminant, 255); | ||||
|                     let phrase = VarintReader::read(packet)?; | ||||
|                     assert_eq!(phrase, 3); | ||||
|                     Ok(()) | ||||
|                 })?; | ||||
| 
 | ||||
|                 // HandshakeAck complete
 | ||||
|                 write_plugin_msg(&mut stream, "FML|HS", |body| { | ||||
|                     body.write_all(&[0xFF, 0x05])?; | ||||
|                     Ok(()) | ||||
|                 })?; | ||||
| 
 | ||||
|                 (uuid, username) | ||||
|             } | ||||
|         }; | ||||
| 
 | ||||
|         Ok(Server { | ||||
|             stream, | ||||
|             addr, | ||||
|             port, | ||||
|             state: Connected { | ||||
|                 typ, | ||||
|                 description, | ||||
|                 uuid, | ||||
|                 username, | ||||
|             }, | ||||
|         }) | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,44 @@ | ||||
| use serde_json::Value; | ||||
| use std::{io, result::Result as StdResult, string::FromUtf8Error}; | ||||
| 
 | ||||
| #[derive(Debug)] | ||||
| pub enum ChatError { | ||||
|     IoError(io::Error), | ||||
|     FromUtf8Error(FromUtf8Error), | ||||
|     JsonError(JsonError), | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug)] | ||||
| pub enum JsonError { | ||||
|     SerdeError(serde_json::Error), | ||||
|     ExpectedObject(Value), | ||||
|     ExpectedArray, | ||||
|     ExpectedString, | ||||
|     ExpectedMember(String), | ||||
| } | ||||
| 
 | ||||
| impl From<io::Error> for ChatError { | ||||
|     fn from(value: io::Error) -> Self { | ||||
|         Self::IoError(value) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl From<FromUtf8Error> for ChatError { | ||||
|     fn from(value: FromUtf8Error) -> Self { | ||||
|         Self::FromUtf8Error(value) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl From<JsonError> for ChatError { | ||||
|     fn from(value: JsonError) -> Self { | ||||
|         Self::JsonError(value) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl From<serde_json::Error> for JsonError { | ||||
|     fn from(value: serde_json::Error) -> Self { | ||||
|         Self::SerdeError(value) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| pub type Result<T> = StdResult<T, ChatError>; | ||||
| @ -1,19 +1,47 @@ | ||||
| use std::{io::Read as _, net::TcpStream}; | ||||
| use std::{io::Read, net::TcpStream}; | ||||
| 
 | ||||
| use crate::error::Result; | ||||
| 
 | ||||
| use varint_rs::VarintReader as _; | ||||
| 
 | ||||
| pub fn read_string<I: Iterator<Item = u8>>(iter: &mut I) -> Option<String> { | ||||
|     let len = iter.next()?.into(); | ||||
|     let bytes = iter.take(len).collect::<Vec<_>>(); | ||||
|     (len == bytes.len()).then_some(())?; | ||||
|     String::from_utf8(bytes).ok() | ||||
| pub fn read_string(iter: &mut &[u8]) -> Result<String> { | ||||
|     let len = iter.read_usize_varint()?; | ||||
|     let mut buf = vec![0u8; len]; | ||||
|     iter.read_exact(&mut buf)?; | ||||
|     String::from_utf8(buf).map_err(Into::into) | ||||
| } | ||||
| 
 | ||||
| pub fn read_packet(stream: &mut TcpStream) -> Vec<u8> { | ||||
|     let len = stream.read_usize_varint().unwrap(); | ||||
| pub fn read_packet<T, F: Fn(&mut &[u8]) -> Result<T>>( | ||||
|     stream: &mut TcpStream, | ||||
|     read_body: F, | ||||
| ) -> Result<T> { | ||||
|     let len = stream.read_usize_varint()?; | ||||
| 
 | ||||
|     let mut buf = vec![0u8; len]; | ||||
|     stream.read_exact(&mut buf).unwrap(); | ||||
|     stream.read_exact(&mut buf)?; | ||||
| 
 | ||||
|     let mut packet = &buf[..]; | ||||
|     read_body(&mut packet) | ||||
| } | ||||
| 
 | ||||
| pub fn read_plugin_msg<T, F: Fn(&mut &[u8]) -> Result<T>>( | ||||
|     stream: &mut TcpStream, | ||||
|     channel: &str, | ||||
|     read_body: F, | ||||
| ) -> Result<T> { | ||||
|     read_packet(stream, |packet| { | ||||
|         let id = packet.read_usize_varint()?; | ||||
|         assert!(id == 63); | ||||
| 
 | ||||
|         let channel_read = read_string(packet)?; | ||||
|         assert_eq!(channel_read, channel); | ||||
|         let mut buf = [0u8; 2]; | ||||
|         packet.read_exact(&mut buf)?; | ||||
|         let len = u16::from_be_bytes(buf); | ||||
|         let mut buf = vec![0u8; len as usize]; | ||||
|         packet.read_exact(&mut buf)?; | ||||
| 
 | ||||
|     buf | ||||
|         let mut packet = &buf[..]; | ||||
|         read_body(&mut packet) | ||||
|     }) | ||||
| } | ||||
|  | ||||
| @ -0,0 +1,118 @@ | ||||
| use std::{collections::HashMap, io::Write, net::TcpStream}; | ||||
| 
 | ||||
| use serde_json::Value; | ||||
| use varint_rs::{VarintReader, VarintWriter}; | ||||
| 
 | ||||
| use crate::{ | ||||
|     Disconnected, Server, Status, | ||||
|     error::ChatError, | ||||
|     error::JsonError, | ||||
|     error::Result, | ||||
|     readers::{read_packet, read_string}, | ||||
|     writers::write_packet, | ||||
| }; | ||||
| 
 | ||||
| #[derive(Debug)] | ||||
| pub enum ServerType { | ||||
|     Vanilla, | ||||
|     Forge, | ||||
| } | ||||
| 
 | ||||
| impl Server<Disconnected> { | ||||
|     pub fn status(self) -> Result<Server<Status>> { | ||||
|         let Self { | ||||
|             mut stream, | ||||
|             addr, | ||||
|             port, | ||||
|             .. | ||||
|         } = self; | ||||
| 
 | ||||
|         // Handshake
 | ||||
| 
 | ||||
|         write_packet(&mut stream, |buf| { | ||||
|             buf.write_u8_varint(0)?; | ||||
|             buf.write_u8_varint(5)?; | ||||
|             buf.write_usize_varint(addr.len())?; | ||||
|             buf.write_all(addr.as_bytes())?; | ||||
|             buf.write_all(&port.to_le_bytes())?; | ||||
|             buf.write_u8_varint(1)?; | ||||
|             Ok(()) | ||||
|         })?; | ||||
| 
 | ||||
|         write_packet(&mut stream, |buf| { | ||||
|             buf.write_all(&[0x0])?; | ||||
|             Ok(()) | ||||
|         })?; | ||||
| 
 | ||||
|         let json: Value = read_packet(&mut stream, |packet| { | ||||
|             let id = packet.read_usize_varint()?; | ||||
|             assert!(id == 0); | ||||
|             let json = read_string(packet)?; | ||||
|             serde_json::from_str(&json) | ||||
|                 .map_err(<JsonError as From<serde_json::Error>>::from) | ||||
|                 .map_err(ChatError::from) | ||||
|         })?; | ||||
| 
 | ||||
|         let json = json | ||||
|             .as_object() | ||||
|             .ok_or(JsonError::ExpectedObject(json.clone()))?; | ||||
|         let description = json | ||||
|             .get("description") | ||||
|             .ok_or(JsonError::ExpectedMember("description".to_string()))? | ||||
|             .as_str() | ||||
|             .ok_or(JsonError::ExpectedString)? | ||||
|             .to_string(); | ||||
| 
 | ||||
|         let (typ, mods) = match json.get("modinfo") { | ||||
|             Some(info) => { | ||||
|                 if info | ||||
|                     .as_object() | ||||
|                     .ok_or(JsonError::ExpectedObject(info.clone()))? | ||||
|                     .get("type") | ||||
|                     .ok_or(JsonError::ExpectedMember("type".to_string()))? | ||||
|                     .as_str() | ||||
|                     .ok_or(JsonError::ExpectedString)? | ||||
|                     == "FML" | ||||
|                 { | ||||
|                     let mod_list = info | ||||
|                         .get("modList") | ||||
|                         .ok_or(JsonError::ExpectedMember("modList".to_string()))?; | ||||
|                     let mod_list = mod_list.as_array().ok_or(JsonError::ExpectedArray)?; | ||||
|                     let mut mods = HashMap::new(); | ||||
|                     for mod_ in mod_list { | ||||
|                         let mod_ = mod_ | ||||
|                             .as_object() | ||||
|                             .ok_or(JsonError::ExpectedObject(mod_.clone()))?; | ||||
|                         let id = mod_ | ||||
|                             .get("modid") | ||||
|                             .ok_or(JsonError::ExpectedMember("modid".to_string()))?; | ||||
|                         let ver = mod_ | ||||
|                             .get("version") | ||||
|                             .ok_or(JsonError::ExpectedMember("version".to_string()))?; | ||||
|                         mods.insert( | ||||
|                             id.as_str().ok_or(JsonError::ExpectedString)?.to_string(), | ||||
|                             ver.as_str().ok_or(JsonError::ExpectedString)?.to_string(), | ||||
|                         ); | ||||
|                     } | ||||
|                     (ServerType::Forge, Some(mods)) | ||||
|                 } else { | ||||
|                     panic!() | ||||
|                 } | ||||
|             } | ||||
|             None => (ServerType::Vanilla, None), | ||||
|         }; | ||||
| 
 | ||||
|         stream.shutdown(std::net::Shutdown::Both)?; | ||||
| 
 | ||||
|         Ok(Server { | ||||
|             stream: TcpStream::connect(format!("{addr}:{port}"))?, | ||||
|             addr, | ||||
|             port, | ||||
|             state: Status { | ||||
|                 typ, | ||||
|                 description, | ||||
|                 mods, | ||||
|             }, | ||||
|         }) | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,41 @@ | ||||
| use std::{io::Write, net::TcpStream}; | ||||
| 
 | ||||
| use varint_rs::VarintWriter; | ||||
| 
 | ||||
| use crate::error::Result; | ||||
| 
 | ||||
| pub fn write_packet<F: Fn(&mut Vec<u8>) -> Result<()>>( | ||||
|     stream: &mut TcpStream, | ||||
|     build_packet: F, | ||||
| ) -> Result<()> { | ||||
|     let mut buf: Vec<u8> = Vec::new(); | ||||
|     build_packet(&mut buf)?; | ||||
| 
 | ||||
|     stream.write_usize_varint(buf.len())?; | ||||
|     stream.write_all(&buf)?; | ||||
| 
 | ||||
|     Ok(()) | ||||
| } | ||||
| 
 | ||||
| pub fn write_string<W: Write>(buf: &mut W, val: &str) -> Result<()> { | ||||
|     buf.write_usize_varint(val.len())?; | ||||
|     buf.write_all(val.as_bytes())?; | ||||
|     Ok(()) | ||||
| } | ||||
| 
 | ||||
| pub fn write_plugin_msg<F: Fn(&mut Vec<u8>) -> Result<()>>( | ||||
|     stream: &mut TcpStream, | ||||
|     channel: &str, | ||||
|     build_body: F, | ||||
| ) -> Result<()> { | ||||
|     let mut body: Vec<u8> = Vec::new(); | ||||
|     build_body(&mut body)?; | ||||
| 
 | ||||
|     write_packet(stream, |mut buf| { | ||||
|         buf.write_u8_varint(0x17)?; | ||||
|         write_string(&mut buf, channel)?; | ||||
|         buf.write_all(&(body.len() as u16).to_be_bytes())?; | ||||
|         buf.write_all(&body)?; | ||||
|         Ok(()) | ||||
|     }) | ||||
| } | ||||
					Loading…
					
					
				
		Reference in new issue