commit
						5ef70901e6
					
				| @ -0,0 +1 @@ | ||||
| /target | ||||
| @ -0,0 +1,102 @@ | ||||
| # This file is automatically @generated by Cargo. | ||||
| # It is not intended for manual editing. | ||||
| version = 4 | ||||
| 
 | ||||
| [[package]] | ||||
| name = "client" | ||||
| version = "0.1.0" | ||||
| dependencies = [ | ||||
|  "serde_json", | ||||
|  "varint-rs", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "itoa" | ||||
| version = "1.0.15" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "memchr" | ||||
| version = "2.7.4" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "proc-macro2" | ||||
| version = "1.0.94" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84" | ||||
| dependencies = [ | ||||
|  "unicode-ident", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "quote" | ||||
| version = "1.0.40" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" | ||||
| dependencies = [ | ||||
|  "proc-macro2", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "ryu" | ||||
| version = "1.0.20" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "serde" | ||||
| version = "1.0.219" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" | ||||
| dependencies = [ | ||||
|  "serde_derive", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "serde_derive" | ||||
| version = "1.0.219" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" | ||||
| dependencies = [ | ||||
|  "proc-macro2", | ||||
|  "quote", | ||||
|  "syn", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "serde_json" | ||||
| version = "1.0.140" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" | ||||
| dependencies = [ | ||||
|  "itoa", | ||||
|  "memchr", | ||||
|  "ryu", | ||||
|  "serde", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "syn" | ||||
| version = "2.0.100" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0" | ||||
| dependencies = [ | ||||
|  "proc-macro2", | ||||
|  "quote", | ||||
|  "unicode-ident", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "unicode-ident" | ||||
| version = "1.0.18" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "varint-rs" | ||||
| version = "2.2.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "8f54a172d0620933a27a4360d3db3e2ae0dd6cceae9730751a036bbf182c4b23" | ||||
| @ -0,0 +1,8 @@ | ||||
| [package] | ||||
| name = "client" | ||||
| version = "0.1.0" | ||||
| edition = "2024" | ||||
| 
 | ||||
| [dependencies] | ||||
| serde_json = "1.0.140" | ||||
| varint-rs = "2.2.0" | ||||
| @ -0,0 +1,83 @@ | ||||
| mod readers; | ||||
| mod text; | ||||
| 
 | ||||
| use std::{ | ||||
|     io::{Read, Write}, | ||||
|     net::TcpStream, | ||||
| }; | ||||
| 
 | ||||
| use readers::{read_packet, read_string}; | ||||
| use text::Message; | ||||
| use varint_rs::{VarintReader, VarintWriter}; | ||||
| 
 | ||||
| fn main() { | ||||
|     let mut stream = TcpStream::connect("localhost:25565").unwrap(); | ||||
| 
 | ||||
|     // Handshake
 | ||||
|     let mut buf: Vec<u8> = Vec::new(); | ||||
|     buf.write_u8_varint(0).unwrap(); | ||||
|     buf.write_u8_varint(5).unwrap(); | ||||
|     let addr = "localhost"; | ||||
|     buf.write_usize_varint(addr.len()).unwrap(); | ||||
|     buf.write_all(addr.as_bytes()).unwrap(); | ||||
|     buf.write_all(&25565u16.to_le_bytes()).unwrap(); | ||||
|     buf.write_u8_varint(2).unwrap(); | ||||
| 
 | ||||
|     stream.write_usize_varint(buf.len()).unwrap(); | ||||
|     stream.write_all(&buf).unwrap(); | ||||
| 
 | ||||
|     // Login start
 | ||||
|     let mut buf: Vec<u8> = Vec::new(); | ||||
|     buf.write_u8_varint(0).unwrap(); | ||||
|     let addr = "CursedChat"; | ||||
|     buf.write_usize_varint(addr.len()).unwrap(); | ||||
|     buf.write_all(addr.as_bytes()).unwrap(); | ||||
| 
 | ||||
|     stream.write_usize_varint(buf.len()).unwrap(); | ||||
|     stream.write_all(&buf).unwrap(); | ||||
| 
 | ||||
|     let mut buf = [0u8; 128]; | ||||
|     dbg!(Read::read(&mut stream, &mut buf).unwrap()); | ||||
|     println!("{:X?}", buf); | ||||
| 
 | ||||
|     let mut iter = buf.into_iter(); | ||||
|     let _len = iter.next().unwrap(); | ||||
|     let id = iter.next().unwrap(); | ||||
|     assert!(id == 2); | ||||
|     let uuid = read_string(&mut iter); | ||||
|     let username = read_string(&mut iter); | ||||
|     dbg!(uuid, username); | ||||
| 
 | ||||
|     loop { | ||||
|         let packet = read_packet(&mut stream); | ||||
|         let packetid = (&packet[..]).read_usize_varint().unwrap(); | ||||
|         match packetid { | ||||
|             0x00 => handle_keepalive(&mut stream, &packet), | ||||
|             0x02 => handle_chat(&packet), | ||||
|             0x40 => break, | ||||
|             _ => {} | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| fn handle_keepalive(stream: &mut TcpStream, mut packet: &[u8]) { | ||||
|     let _packetid = packet.read_usize_varint().unwrap(); | ||||
|     let mut id = [0u8; 4]; | ||||
|     packet.read_exact(&mut id).unwrap(); | ||||
| 
 | ||||
|     let mut buf: Vec<u8> = Vec::new(); | ||||
|     buf.write_u8_varint(0).unwrap(); | ||||
|     buf.write_all(&id).unwrap(); | ||||
| 
 | ||||
|     stream.write_usize_varint(buf.len()).unwrap(); | ||||
|     stream.write_all(&buf).unwrap(); | ||||
| } | ||||
| 
 | ||||
| fn handle_chat(mut packet: &[u8]) { | ||||
|     let _packetid = packet.read_usize_varint().unwrap(); | ||||
| 
 | ||||
|     let mut iter = packet.iter().cloned(); | ||||
|     let msg = read_string(&mut iter); | ||||
|     let msg = Message::from_string(msg.unwrap()); | ||||
|     dbg!(msg); | ||||
| } | ||||
| @ -0,0 +1,19 @@ | ||||
| use std::{io::Read as _, net::TcpStream}; | ||||
| 
 | ||||
| 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_packet(stream: &mut TcpStream) -> Vec<u8> { | ||||
|     let len = stream.read_usize_varint().unwrap(); | ||||
| 
 | ||||
|     let mut buf = vec![0u8; len]; | ||||
|     stream.read_exact(&mut buf).unwrap(); | ||||
| 
 | ||||
|     buf | ||||
| } | ||||
| @ -0,0 +1,159 @@ | ||||
| use serde_json::Value; | ||||
| 
 | ||||
| #[derive(Debug)] | ||||
| pub struct Message { | ||||
|     content: MessageContent, | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug)] | ||||
| pub enum MessageContent { | ||||
|     Text(String), | ||||
|     Translate { | ||||
|         translate: String, | ||||
|         fallback: Option<String>, | ||||
|         with: Option<Vec<Value>>, | ||||
|     }, | ||||
|     Score { | ||||
|         name: String, | ||||
|         objective: String, | ||||
|     }, | ||||
|     Selector { | ||||
|         selector: String, | ||||
|         seperator: Value, | ||||
|     }, | ||||
|     Keybind(String), | ||||
|     Nbt { | ||||
|         source: String, | ||||
|         nbt: String, | ||||
|         interpret: bool, | ||||
|         seperator: String, | ||||
|     }, | ||||
| } | ||||
| 
 | ||||
| impl Message { | ||||
|     pub fn from_string(string: String) -> Option<Self> { | ||||
|         let json: Value = serde_json::from_str(&string).ok()?; | ||||
|         Self::from_json(json) | ||||
|     } | ||||
| 
 | ||||
|     fn from_json(message: Value) -> Option<Self> { | ||||
|         let message = message.as_object()?; | ||||
| 
 | ||||
|         match message.get("type") { | ||||
|             Some(typ) => match typ.as_str()? { | ||||
|                 "text" => Some(Message { | ||||
|                     content: MessageContent::Text(message.get("text")?.as_str()?.to_string()), | ||||
|                 }), | ||||
|                 "translatable" => Some(Message { | ||||
|                     content: MessageContent::Translate { | ||||
|                         translate: message.get("translate")?.as_str()?.to_owned(), | ||||
|                         fallback: message | ||||
|                             .get("fallback") | ||||
|                             .map(|v| v.as_str().map(str::to_string)) | ||||
|                             .flatten(), | ||||
|                         with: message.get("with").map(|a| a.as_array().cloned()).flatten(), | ||||
|                     }, | ||||
|                 }), | ||||
|                 "score" => { | ||||
|                     let score = message.get("score")?.as_object()?; | ||||
|                     Some(Message { | ||||
|                         content: MessageContent::Score { | ||||
|                             name: score.get("name")?.as_str()?.to_string(), | ||||
|                             objective: score.get("objective")?.as_str()?.to_string(), | ||||
|                         }, | ||||
|                     }) | ||||
|                 } | ||||
|                 "selector" => Some(Message { | ||||
|                     content: MessageContent::Selector { | ||||
|                         selector: message.get("selector")?.as_str()?.to_string(), | ||||
|                         seperator: message.get("seperator")?.clone(), | ||||
|                     }, | ||||
|                 }), | ||||
|                 "keybind" => Some(Message { | ||||
|                     content: MessageContent::Keybind(message.get("keybind")?.as_str()?.to_string()), | ||||
|                 }), | ||||
|                 "nbt" => Some(Message { | ||||
|                     content: MessageContent::Nbt { | ||||
|                         source: message | ||||
|                             .get(message.get("source")?.as_str()?)? | ||||
|                             .as_str()? | ||||
|                             .to_string(), | ||||
|                         nbt: message.get("nbt")?.as_str()?.to_string(), | ||||
|                         interpret: message | ||||
|                             .get("interpret") | ||||
|                             .map(|v| v.as_bool()) | ||||
|                             .flatten() | ||||
|                             .unwrap_or(false), | ||||
|                         seperator: message | ||||
|                             .get("seperator") | ||||
|                             .map(|v| v.as_str()) | ||||
|                             .flatten() | ||||
|                             .unwrap_or(", ") | ||||
|                             .to_string(), | ||||
|                     }, | ||||
|                 }), | ||||
|                 _ => return None, | ||||
|             }, | ||||
|             None => { | ||||
|                 if let Some(txt) = message.get("text") { | ||||
|                     Some(Message { | ||||
|                         content: MessageContent::Text(txt.as_str()?.to_string()), | ||||
|                     }) | ||||
|                 } else if let Some(translate) = message.get("translate") { | ||||
|                     Some(Message { | ||||
|                         content: MessageContent::Translate { | ||||
|                             translate: translate.as_str()?.to_owned(), | ||||
|                             fallback: message | ||||
|                                 .get("fallback") | ||||
|                                 .map(|v| v.as_str().map(str::to_string)) | ||||
|                                 .flatten(), | ||||
|                             with: message.get("with").map(|a| a.as_array().cloned()).flatten(), | ||||
|                         }, | ||||
|                     }) | ||||
|                 } else if let Some(score) = message.get("score") { | ||||
|                     let score = score.as_object()?; | ||||
|                     Some(Message { | ||||
|                         content: MessageContent::Score { | ||||
|                             name: score.get("name")?.as_str()?.to_string(), | ||||
|                             objective: score.get("objective")?.as_str()?.to_string(), | ||||
|                         }, | ||||
|                     }) | ||||
|                 } else if let Some(selector) = message.get("selector") { | ||||
|                     Some(Message { | ||||
|                         content: MessageContent::Selector { | ||||
|                             selector: selector.as_str()?.to_string(), | ||||
|                             seperator: message.get("seperator")?.clone(), | ||||
|                         }, | ||||
|                     }) | ||||
|                 } else if let Some(keybind) = message.get("keybind") { | ||||
|                     Some(Message { | ||||
|                         content: MessageContent::Keybind(keybind.as_str()?.to_string()), | ||||
|                     }) | ||||
|                 } else if let Some(nbt) = message.get("nbt") { | ||||
|                     Some(Message { | ||||
|                         content: MessageContent::Nbt { | ||||
|                             source: message | ||||
|                                 .get(message.get("source")?.as_str()?)? | ||||
|                                 .as_str()? | ||||
|                                 .to_string(), | ||||
|                             nbt: nbt.as_str()?.to_string(), | ||||
|                             interpret: message | ||||
|                                 .get("interpret") | ||||
|                                 .map(|v| v.as_bool()) | ||||
|                                 .flatten() | ||||
|                                 .unwrap_or(false), | ||||
|                             seperator: message | ||||
|                                 .get("seperator") | ||||
|                                 .map(|v| v.as_str()) | ||||
|                                 .flatten() | ||||
|                                 .unwrap_or(", ") | ||||
|                                 .to_string(), | ||||
|                         }, | ||||
|                     }) | ||||
|                 } else { | ||||
|                     None | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
					Loading…
					
					
				
		Reference in new issue