diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..46380ae --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,470 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "addr2line" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "async-trait" +version = "0.1.75" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdf6721fb0140e4f897002dd086c06f6c27775df19cfe1fccb21181a48fd2c98" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "backtrace" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + +[[package]] +name = "base64" +version = "0.21.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bytes" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" + +[[package]] +name = "cc" +version = "1.0.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +dependencies = [ + "libc", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "getrandom" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "gimli" +version = "0.28.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" + +[[package]] +name = "hermit-abi" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" + +[[package]] +name = "itoa" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" + +[[package]] +name = "libc" +version = "0.2.151" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4" + +[[package]] +name = "lock_api" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "memchr" +version = "2.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" + +[[package]] +name = "miniz_oxide" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +dependencies = [ + "adler", +] + +[[package]] +name = "mio" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09" +dependencies = [ + "libc", + "wasi", + "windows-sys", +] + +[[package]] +name = "num_cpus" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "object" +version = "0.32.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0" +dependencies = [ + "memchr", +] + +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "proc-macro2" +version = "1.0.71" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75cb1540fadbd5b8fbccc4dddad2734eba435053f725621c070711a14bb5f4b8" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "purple_cello_mc_protocol" +version = "0.1.0" +dependencies = [ + "async-trait", + "base64", + "rand", + "serde", + "serde_json", + "tokio", +] + +[[package]] +name = "quote" +version = "1.0.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "redox_syscall" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +dependencies = [ + "bitflags", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" + +[[package]] +name = "ryu" +version = "1.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c" + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "serde" +version = "1.0.193" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.193" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.108" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" +dependencies = [ + "libc", +] + +[[package]] +name = "smallvec" +version = "1.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970" + +[[package]] +name = "socket2" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9" +dependencies = [ + "libc", + "windows-sys", +] + +[[package]] +name = "syn" +version = "2.0.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b7d0a2c048d661a1a59fcd7355baa232f7ed34e0ee4df2eef3c1c1c0d3852d8" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tokio" +version = "1.35.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c89b4efa943be685f629b149f53829423f8f5531ea21249408e8e2f8671ec104" +dependencies = [ + "backtrace", + "bytes", + "libc", + "mio", + "num_cpus", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys", +] + +[[package]] +name = "tokio-macros" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..1149db5 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "purple_cello_mc_protocol" +version = "0.1.0" +edition = "2021" +description = "Purple Cello implementation of the Minecraft multiplayer protocol" +license = "MIT" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +tokio = { version = "1", features = ["full"] } +serde = { version = "1", features = ["derive"] } +serde_json = "1" +base64 = "0.21.5" +rand = "0.8.5" +async-trait = "0.1.75" diff --git a/src/handshake.rs b/src/handshake.rs new file mode 100644 index 0000000..8537dfb --- /dev/null +++ b/src/handshake.rs @@ -0,0 +1,57 @@ +// Yeahbut December 2023 + +pub mod serverbound { + + use tokio::net::tcp::OwnedReadHalf; + + use crate::mc_types::{self, Result, Packet, PacketError}; + + pub enum HandshakeEnum { + Handshake(Handshake), + } + + impl HandshakeEnum { + pub async fn read(stream: &mut OwnedReadHalf) -> Result { + let mut data = mc_types::read_data(stream).await?; + let packet_id = mc_types::get_var_int(&mut data)?; + if packet_id == Handshake::packet_id() { + return Ok(Self::Handshake(Handshake::get(&mut data)?)) + } else { + return Err(Box::new(PacketError::InvalidPacketId)) + } + } + } + + pub struct Handshake { + pub protocol_version: i32, + pub server_address: String, + pub server_port: u16, + pub next_state: i32, + } + + impl Packet for Handshake { + + fn packet_id() -> i32 {0} + + fn get(data: &mut Vec) -> Result { + Ok(Self { + protocol_version: mc_types::get_var_int(data)?, + server_address: mc_types::get_string(data)?, + server_port: mc_types::get_u16(data), + next_state: mc_types::get_var_int(data)?, + }) + } + + fn convert(&self) -> Vec { + let mut data: Vec = vec![]; + data.append(&mut mc_types::convert_var_int(Self::packet_id())); + data.append(&mut mc_types::convert_var_int(self.protocol_version)); + data.append(&mut mc_types::convert_string(&self.server_address)); + data.append(&mut mc_types::convert_u16(self.server_port)); + data.append(&mut mc_types::convert_var_int(self.next_state)); + + data + } + + } +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..b64644a --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,6 @@ +// Yeahbut December 2023 + +pub mod mc_types; +pub mod handshake; +pub mod status; +pub mod login; diff --git a/src/login.rs b/src/login.rs new file mode 100644 index 0000000..95048aa --- /dev/null +++ b/src/login.rs @@ -0,0 +1,169 @@ +// Yeahbut December 2023 + +pub mod clientbound { + + use tokio::net::tcp::OwnedReadHalf; + + use crate::mc_types::{self, Result, Packet, PacketArray, PacketError}; + + pub enum Login { + Disconnect(Disconnect), + EncryptionRequest(EncryptionRequest), + LoginSuccess(LoginSuccess), + } + + impl Login { + pub async fn read(stream: &mut OwnedReadHalf) -> Result { + let mut data = mc_types::read_data(stream).await?; + let packet_id = mc_types::get_var_int(&mut data)?; + if packet_id == Disconnect::packet_id() { + return Ok(Self::Disconnect(Disconnect::get(&mut data)?)) + } else if packet_id == EncryptionRequest::packet_id() { + return Ok(Self::EncryptionRequest( + EncryptionRequest::get(&mut data)?)) + } else if packet_id == LoginSuccess::packet_id() { + return Ok(Self::LoginSuccess(LoginSuccess::get(&mut data)?)) + } else { + return Err(Box::new(PacketError::InvalidPacketId)) + } + } + } + + pub struct Disconnect { + pub reason: String + } + + impl Packet for Disconnect { + + fn packet_id() -> i32 {0} + + fn get(mut data: &mut Vec) -> Result { + Ok(Self { + reason: mc_types::get_string(&mut data)? + }) + } + + fn convert(&self) -> Vec { + let mut data: Vec = vec![]; + data.append(&mut mc_types::convert_var_int(Self::packet_id())); + data.append(&mut mc_types::convert_string(&self.reason)); + + data + } + + } + + pub struct EncryptionRequest { + pub server_id: String, + pub public_key: Vec, + pub verify_token: Vec, + } + + impl Packet for EncryptionRequest { + + fn packet_id() -> i32 {1} + + fn get(mut data: &mut Vec) -> Result { + Ok(Self { + server_id: mc_types::get_string(&mut data)?, + public_key: mc_types::get_byte_array(&mut data)?, + verify_token: mc_types::get_byte_array(&mut data)?, + }) + } + + fn convert(&self) -> Vec { + let mut data: Vec = vec![]; + data.append(&mut mc_types::convert_var_int(Self::packet_id())); + data.append(&mut mc_types::convert_string(&self.server_id)); + data.append(&mut mc_types::convert_byte_array( + &mut self.public_key.clone())); + data.append(&mut mc_types::convert_byte_array( + &mut self.verify_token.clone())); + + data + } + + } + + pub struct LoginSuccess { + pub uuid: u128, + pub username: String, + pub properties: Vec, + } + + impl Packet for LoginSuccess { + + fn packet_id() -> i32 {2} + + fn get(mut data: &mut Vec) -> Result { + Ok(Self { + uuid: mc_types::get_uuid(&mut data), + username: mc_types::get_string(&mut data)?, + properties: LoginSuccessProperty::get_array(&mut data)?, + }) + } + + fn convert(&self) -> Vec { + let mut data: Vec = vec![]; + data.append(&mut mc_types::convert_var_int(Self::packet_id())); + data.append(&mut mc_types::convert_uuid(self.uuid)); + data.append(&mut mc_types::convert_string(&self.username)); + data.append(&mut LoginSuccessProperty::convert_array( + &mut self.properties.clone())); + + data + } + + } + + pub struct LoginSuccessProperty { + name: String, + value: String, + signature: Option, + } + + impl Clone for LoginSuccessProperty { + fn clone(&self) -> Self { + Self { + name: self.name.clone(), + value: self.value.clone(), + signature: self.signature.clone(), + } + } + } + + impl PacketArray for LoginSuccessProperty { + + fn get(mut data: &mut Vec) -> Result { + let name = mc_types::get_string(&mut data)?; + let value = mc_types::get_string(&mut data)?; + let is_signed = mc_types::get_bool(&mut data); + let mut signature: Option = None; + if is_signed { + signature = Some(mc_types::get_string(&mut data)?); + } + Ok(Self { + name, + value, + signature, + }) + } + + fn convert(&self) -> Vec { + let mut data: Vec = vec![]; + data.append(&mut mc_types::convert_string(&self.name)); + data.append(&mut mc_types::convert_string(&self.value)); + match &self.signature { + Some(value) => { + data.append(&mut &mut mc_types::convert_bool(true)); + data.append(&mut mc_types::convert_string(&value)); + }, + None => data.append(&mut &mut mc_types::convert_bool(false)) + } + + data + } + + } + +} diff --git a/src/mc_types.rs b/src/mc_types.rs new file mode 100644 index 0000000..f9d6b2a --- /dev/null +++ b/src/mc_types.rs @@ -0,0 +1,347 @@ +// Yeahbut December 2023 + +use std::error::Error; +use std::fmt; + +use tokio::net::tcp::{OwnedReadHalf, OwnedWriteHalf}; +use tokio::io::{AsyncReadExt, AsyncWriteExt}; +use serde::{Serialize, Deserialize}; +use async_trait::async_trait; + +pub type Result = std::result::Result>; + +pub const VERSION_NAME: &str = "1.19.4"; +pub const VERSION_PROTOCOL: i32 = 762; + +const SEGMENT_BITS: u8 = 0x7F; +const CONTINUE_BIT: u8 = 0x80; + +#[derive(Debug)] +pub enum PacketError { + ValueTooLarge, + RanOutOfBytes, + InvalidPacketId, +} + +impl fmt::Display for PacketError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + PacketError::ValueTooLarge => + write!(f, "VarInt value is too large"), + PacketError::RanOutOfBytes => + write!(f, "Ran out of bytes while reading VarInt"), + PacketError::InvalidPacketId => + write!(f, "Invalid packet id"), + } + } +} + +impl Error for PacketError {} + +#[derive(Serialize, Deserialize)] +pub struct Chat { + pub text: String, +} + +#[async_trait] +pub trait Packet: Sized { + fn packet_id() -> i32; + fn get(data: &mut Vec) -> Result; + fn convert(&self) -> Vec; + + async fn read(stream: &mut OwnedReadHalf) -> Result { + let mut data = read_data(stream).await?; + let packet_id = get_var_int(&mut data)?; + if packet_id == Self::packet_id() { + return Ok(Self::get(&mut data)?) + } else { + return Err(Box::new(PacketError::InvalidPacketId)) + } + } + + async fn write(&self, stream: &mut OwnedWriteHalf) -> Result<()> { + write_data(stream, &mut self.convert()).await + } +} + +pub async fn read_data(stream: &mut OwnedReadHalf) -> Result> { + let length = read_var_int_stream(stream).await? as usize; + + let mut buffer: Vec = vec![0; length]; + stream.read_exact(&mut buffer).await?; + + Ok(buffer) +} +pub async fn write_data( + stream: &mut OwnedWriteHalf, + data: &mut Vec, +) -> Result<()> { + let mut out_data = convert_var_int(data.len() as i32); + out_data.append(data); + + stream.write_all(&out_data).await?; + + Ok(()) +} +async fn read_var_int_stream(stream: &mut OwnedReadHalf) -> Result { + let mut data: Vec = vec![]; + + loop { + let current_byte = stream.read_u8().await?; + + data.append(&mut vec![current_byte]); + + if (current_byte & CONTINUE_BIT) == 0 { + break; + } + } + + let varint = get_var_int(&mut data)?; + + Ok(varint) +} + +pub trait PacketArray: Sized { + fn get(data: &mut Vec) -> Result; + fn convert(&self) -> Vec; + + fn get_array(data: &mut Vec) -> Result> { + let length = get_var_int(data)?; + let mut out_data: Vec = vec![]; + for _ in 0..length { + out_data.push(Self::get(data)?); + } + Ok(out_data) + } + + fn convert_array(array: &mut Vec) -> Vec { + let length = array.len() as i32; + let mut data: Vec = convert_var_int(length); + for element in array { + data.append(&mut Self::convert(element)); + } + data + } +} + +pub fn get_bool(data: &mut Vec) -> bool { + data.remove(0) != 0 +} +pub fn convert_bool(value: bool) -> Vec { + vec![value as u8] +} + +pub fn get_u8(data: &mut Vec) -> u8 { + data.remove(0) +} +pub fn convert_u8(value: u8) -> Vec { + vec![value] +} + +pub fn get_i8(data: &mut Vec) -> i8 { + get_u8(data) as i8 +} +pub fn convert_i8(value: i8) -> Vec { + convert_u8(value as u8) +} + +pub fn get_u16(data: &mut Vec) -> u16 { + ((data.remove(0) as u16) << 8) | + (data.remove(0) as u16) +} +pub fn convert_u16(value: u16) -> Vec { + vec![ + ((value & 0xFF00) >> 8) as u8, + (value & 0xFF) as u8, + ] +} + +pub fn get_i16(data: &mut Vec) -> i16 { + get_u16(data) as i16 +} +pub fn convert_i16(value: i16) -> Vec { + convert_u16(value as u16) +} + +pub fn get_u32(data: &mut Vec) -> u32 { + ((data.remove(0) as u32) << 24) | + ((data.remove(0) as u32) << 16) | + ((data.remove(0) as u32) << 8) | + (data.remove(0) as u32) +} +pub fn convert_u32(value: u32) -> Vec { + vec![ + ((value & 0xFF0000) >> 24) as u8, + ((value & 0xFF0000) >> 16) as u8, + ((value & 0xFF00) >> 8) as u8, + (value & 0xFF) as u8, + ] +} + +pub fn get_i32(data: &mut Vec) -> i32 { + get_u32(data) as i32 +} +pub fn convert_i32(value: i32) -> Vec { + convert_u32(value as u32) +} + +pub fn get_f32(data: &mut Vec) -> f32 { + get_u32(data) as f32 +} +pub fn convert_f32(value: f32) -> Vec { + convert_u32(value as u32) +} + +pub fn get_u64(data: &mut Vec) -> u64 { + ((data.remove(0) as u64) << 56) | + ((data.remove(0) as u64) << 48) | + ((data.remove(0) as u64) << 40) | + ((data.remove(0) as u64) << 32) | + ((data.remove(0) as u64) << 24) | + ((data.remove(0) as u64) << 16) | + ((data.remove(0) as u64) << 8) | + (data.remove(0) as u64) +} +pub fn convert_u64(value: u64) -> Vec { + vec![ + ((value & 0xFF00000000000000) >> 56) as u8, + ((value & 0xFF000000000000) >> 48) as u8, + ((value & 0xFF0000000000) >> 40) as u8, + ((value & 0xFF00000000) >> 32) as u8, + ((value & 0xFF000000) >> 24) as u8, + ((value & 0xFF0000) >> 16) as u8, + ((value & 0xFF00) >> 8) as u8, + (value & 0xFF) as u8, + ] +} + +pub fn get_i64(data: &mut Vec) -> i64 { + get_u64(data) as i64 +} +pub fn convert_i64(value: i64) -> Vec { + convert_u64(value as u64) +} + +pub fn get_f64(data: &mut Vec) -> f64 { + get_u64(data) as f64 +} +pub fn convert_f64(value: f64) -> Vec { + convert_u64(value as u64) +} + +pub fn get_uuid(data: &mut Vec) -> u128 { + ((data.remove(0) as u128) << 120) | + ((data.remove(0) as u128) << 112) | + ((data.remove(0) as u128) << 104) | + ((data.remove(0) as u128) << 96) | + ((data.remove(0) as u128) << 88) | + ((data.remove(0) as u128) << 80) | + ((data.remove(0) as u128) << 72) | + ((data.remove(0) as u128) << 64) | + ((data.remove(0) as u128) << 56) | + ((data.remove(0) as u128) << 48) | + ((data.remove(0) as u128) << 40) | + ((data.remove(0) as u128) << 32) | + ((data.remove(0) as u128) << 24) | + ((data.remove(0) as u128) << 16) | + ((data.remove(0) as u128) << 8) | + (data.remove(0) as u128) +} +pub fn convert_uuid(value: u128) -> Vec { + vec![ + ((value & 0xFF000000000000000000000000000000) >> 120) as u8, + ((value & 0xFF0000000000000000000000000000) >> 112) as u8, + ((value & 0xFF00000000000000000000000000) >> 104) as u8, + ((value & 0xFF000000000000000000000000) >> 96) as u8, + ((value & 0xFF0000000000000000000000) >> 88) as u8, + ((value & 0xFF00000000000000000000) >> 80) as u8, + ((value & 0xFF000000000000000000) >> 72) as u8, + ((value & 0xFF0000000000000000) >> 64) as u8, + ((value & 0xFF00000000000000) >> 56) as u8, + ((value & 0xFF000000000000) >> 48) as u8, + ((value & 0xFF0000000000) >> 40) as u8, + ((value & 0xFF00000000) >> 32) as u8, + ((value & 0xFF000000) >> 24) as u8, + ((value & 0xFF0000) >> 16) as u8, + ((value & 0xFF00) >> 8) as u8, + (value & 0xFF) as u8, + ] +} + +pub fn get_var_int(data: &mut Vec) -> Result { + Ok(get_var(data, 32)? as i32) +} +pub fn convert_var_int(value: i32) -> Vec { + convert_var(value as i64) +} + +pub fn get_var_long(data: &mut Vec) -> Result { + get_var(data, 64) +} +pub fn convert_var_long(value: i64) -> Vec { + convert_var(value) +} + +fn get_var(data: &mut Vec, size: u8) -> Result { + let mut value: i64 = 0; + let mut position: u8 = 0; + + loop { + if data.is_empty() { + return Err(Box::new(PacketError::RanOutOfBytes)); + } + + let current_byte = data.remove(0); + value |= ((current_byte & SEGMENT_BITS) as i64) << position; + + if (current_byte & CONTINUE_BIT) == 0 { + break; + } + + position += 7; + + if position >= size { + return Err(Box::new(PacketError::ValueTooLarge)); + } + } + + Ok(value) +} +fn convert_var(mut value: i64) -> Vec { + let mut data: Vec = vec![]; + loop { + if (value & !(SEGMENT_BITS as i64)) == 0 { + data.append(&mut vec![value as u8]); + return data; + } + data.append( + &mut vec![(value & (SEGMENT_BITS as i64)) as u8 | CONTINUE_BIT]); + value >>= 7; + } +} + +pub fn get_string(data: &mut Vec) -> Result { + let length = get_var_int(data)? as usize; + let buffer = data[..length].to_vec(); + for _ in 0..length { data.remove(0); } + Ok(String::from_utf8_lossy(&buffer).to_string()) +} +pub fn convert_string(s: &str) -> Vec { + let length = s.len() as i32; + let mut data = convert_var_int(length); + data.append(&mut s.as_bytes().to_vec()); + data +} + +pub fn get_byte_array(data: &mut Vec) -> Result> { + let length = get_var_int(data)? as usize; + let buffer = data[..length].to_vec(); + for _ in 0..length { data.remove(0); } + Ok(buffer) +} +pub fn convert_byte_array(mut s: &mut Vec) -> Vec { + let length = s.len() as i32; + let mut data = convert_var_int(length); + data.append(&mut s); + data +} diff --git a/src/status.rs b/src/status.rs new file mode 100644 index 0000000..efec893 --- /dev/null +++ b/src/status.rs @@ -0,0 +1,194 @@ +// Yeahbut December 2023 + +pub mod clientbound { + + use tokio::net::tcp::OwnedReadHalf; + use serde::{Serialize, Deserialize}; + + use crate::mc_types::{self, Result, Packet, PacketError}; + + #[derive(Serialize, Deserialize)] + pub struct StatusVersion { + pub name: String, + pub protocol: i32, + } + + #[derive(Serialize, Deserialize)] + pub struct StatusPlayerInfo { + pub name: String, + pub id: String, + } + + #[derive(Serialize, Deserialize)] + pub struct StatusPlayers { + pub max: i32, + pub online: i32, + #[serde(skip_serializing_if = "Option::is_none")] + pub sample: Option> + } + + #[derive(Serialize, Deserialize)] + pub struct StatusResponseData { + pub version: StatusVersion, + pub description: mc_types::Chat, + pub players: StatusPlayers, + #[serde(skip_serializing_if = "Option::is_none")] + pub favicon: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub enforcesSecureChat: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub previewsChat: Option, + } + + pub enum StatusPackets { + Status(Status), + Ping(Ping), + } + + impl StatusPackets { + pub async fn read(stream: &mut OwnedReadHalf) -> Result { + let mut data = mc_types::read_data(stream).await?; + let packet_id = mc_types::get_var_int(&mut data)?; + if packet_id == Status::packet_id() { + return Ok(Self::Status(Status::get(&mut data)?)) + } else if packet_id == Ping::packet_id() { + return Ok(Self::Ping(Ping::get(&mut data)?)) + } else { + return Err(Box::new(PacketError::InvalidPacketId)) + } + } + } + + pub struct Status { + pub response: String + } + + impl Status { + + pub fn from_json(data: StatusResponseData) -> Result { + Ok(Self { + response: serde_json::to_string(&data)? + }) + } + + pub fn get_json(&self) -> Result { + Ok(serde_json::from_str(&self.response)?) + } + + } + + impl Packet for Status { + + fn packet_id() -> i32 {0} + + fn get(mut data: &mut Vec) -> Result { + Ok(Self { + response: mc_types::get_string(&mut data)? + }) + } + + fn convert(&self) -> Vec { + let mut data: Vec = vec![]; + data.append(&mut mc_types::convert_var_int(Self::packet_id())); + data.append(&mut mc_types::convert_string(&self.response)); + + data + } + + } + + pub struct Ping { + pub payload: i64 + } + + impl Packet for Ping { + + fn packet_id() -> i32 {1} + + fn get(mut data: &mut Vec) -> Result { + Ok(Self { + payload: mc_types::get_i64(&mut data) + }) + } + + fn convert(&self) -> Vec { + let mut data: Vec = vec![]; + data.append(&mut mc_types::convert_var_int(Self::packet_id())); + data.append(&mut mc_types::convert_i64(self.payload)); + + data + } + + } + +} + +pub mod serverbound { + + use tokio::net::tcp::OwnedReadHalf; + + use crate::mc_types::{self, Result, Packet, PacketError}; + + pub enum StatusPackets { + Status(Status), + Ping(Ping), + } + + impl StatusPackets { + pub async fn read(stream: &mut OwnedReadHalf) -> Result { + let mut data = mc_types::read_data(stream).await?; + let packet_id = mc_types::get_var_int(&mut data)?; + if packet_id == Status::packet_id() { + return Ok(Self::Status(Status::get(&mut data)?)) + } else if packet_id == Ping::packet_id() { + return Ok(Self::Ping(Ping::get(&mut data)?)) + } else { + return Err(Box::new(PacketError::InvalidPacketId)) + } + } + } + + pub struct Status {} + + impl Packet for Status { + + fn packet_id() -> i32 {0} + + fn get(_data: &mut Vec) -> Result { + Ok(Self {}) + } + + fn convert(&self) -> Vec { + let mut data: Vec = vec![]; + data.append(&mut mc_types::convert_var_int(Self::packet_id())); + + data + } + + } + + pub struct Ping { + pub payload: i64 + } + + impl Packet for Ping { + + fn packet_id() -> i32 {1} + + fn get(mut data: &mut Vec) -> Result { + Ok(Self { + payload: mc_types::get_i64(&mut data) + }) + } + + fn convert(&self) -> Vec { + let mut data: Vec = vec![]; + data.append(&mut mc_types::convert_var_int(Self::packet_id())); + data.append(&mut mc_types::convert_i64(self.payload)); + + data + } + + } + +}