From dfd6f56f76ef5c4ef0fc2d2af3e961da0212aee5 Mon Sep 17 00:00:00 2001 From: GIKExe <72767917+GIKExe@users.noreply.github.com> Date: Thu, 8 May 2025 09:40:44 +0300 Subject: [PATCH] =?UTF-8?q?=D0=BD=D0=B5=20=D1=85=D1=83=D0=B9=D0=BD=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Cargo.lock | 512 +++++++++++++++++++++++++++++++++++++++ Cargo.toml | 8 +- src/cycle.rs | 123 ++++++++++ src/data.rs | 82 ------- src/data/async_reader.rs | 106 ++++++++ src/data/component.rs | 212 ++++++++++++++++ src/data/mod.rs | 37 +++ src/data/packet.rs | 97 ++++++++ src/data/packet_id.rs | 272 +++++++++++++++++++++ src/data/reader.rs | 20 ++ src/data/writer.rs | 103 ++++++++ src/main.rs | 34 +-- 12 files changed, 1489 insertions(+), 117 deletions(-) create mode 100644 src/cycle.rs delete mode 100644 src/data.rs create mode 100644 src/data/async_reader.rs create mode 100644 src/data/component.rs create mode 100644 src/data/mod.rs create mode 100644 src/data/packet.rs create mode 100644 src/data/packet_id.rs create mode 100644 src/data/reader.rs create mode 100644 src/data/writer.rs diff --git a/Cargo.lock b/Cargo.lock index 1d679c0..173ab2f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,6 +17,21 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + [[package]] name = "autocfg" version = "1.4.0" @@ -38,36 +53,250 @@ dependencies = [ "windows-targets", ] +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + [[package]] name = "bitflags" version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" +[[package]] +name = "bumpalo" +version = "3.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" + [[package]] name = "bytes" version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" +[[package]] +name = "cc" +version = "1.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8691782945451c1c383942c4874dbe63814f61cb57ef773cda2972682b7bb3c0" +dependencies = [ + "shlex", +] + [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "chrono" +version = "0.4.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c469d952047f47f91b68d1cba3f10d63c11d73e4636f24f08daf0278abf01c4d" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "num-traits", + "serde", + "windows-link", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "crc32fast" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "darling" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn", +] + +[[package]] +name = "darling_macro" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" +dependencies = [ + "darling_core", + "quote", + "syn", +] + +[[package]] +name = "deranged" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e" +dependencies = [ + "powerfmt", + "serde", +] + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "flate2" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ced92e76e966ca2fd84c8f7aa01a4aea65b0eb6648d72f7c8f3e2764a67fece" +dependencies = [ + "crc32fast", + "libz-sys", + "miniz_oxide", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + [[package]] name = "gimli" version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "hashbrown" +version = "0.15.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84b26c544d002229e640969970a2e74021aadf6e2f96372b9c58eff97de08eb3" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "iana-time-zone" +version = "0.1.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "log", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", + "serde", +] + +[[package]] +name = "indexmap" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" +dependencies = [ + "equivalent", + "hashbrown 0.15.3", + "serde", +] + +[[package]] +name = "itoa" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" + +[[package]] +name = "js-sys" +version = "0.3.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + [[package]] name = "libc" version = "0.2.172" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" +[[package]] +name = "libz-sys" +version = "1.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b70e7a7df205e92a1a4cd9aaae7898dac0aa555503cc0a649494d0d60e7651d" +dependencies = [ + "cc", + "pkg-config", + "vcpkg", +] + [[package]] name = "lock_api" version = "0.4.12" @@ -78,6 +307,12 @@ dependencies = [ "scopeguard", ] +[[package]] +name = "log" +version = "0.4.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" + [[package]] name = "memchr" version = "2.7.4" @@ -104,6 +339,21 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + [[package]] name = "object" version = "0.36.7" @@ -113,6 +363,12 @@ dependencies = [ "memchr", ] +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + [[package]] name = "parking_lot" version = "0.12.3" @@ -142,6 +398,18 @@ version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" +[[package]] +name = "pkg-config" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + [[package]] name = "proc-macro2" version = "1.0.95" @@ -173,6 +441,10 @@ dependencies = [ name = "rust_mc_serv" version = "0.1.0" dependencies = [ + "flate2", + "serde", + "serde_json", + "serde_with", "tokio", ] @@ -182,12 +454,92 @@ version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" +[[package]] +name = "rustversion" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2" + +[[package]] +name = "ryu" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" + [[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.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 = "serde_with" +version = "3.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6b6f7f2fcb69f747921f79f3926bd1e203fce4fef62c268dd3abfb6d86029aa" +dependencies = [ + "base64", + "chrono", + "hex", + "indexmap 1.9.3", + "indexmap 2.9.0", + "serde", + "serde_derive", + "serde_json", + "serde_with_macros", + "time", +] + +[[package]] +name = "serde_with_macros" +version = "3.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d00caa5193a3c8362ac2b73be6b9e768aa5a4b2f721d8f4b339600c3cb51f8e" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + [[package]] name = "signal-hook-registry" version = "1.4.5" @@ -213,6 +565,12 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + [[package]] name = "syn" version = "2.0.101" @@ -224,6 +582,37 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "time" +version = "0.3.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40" +dependencies = [ + "deranged", + "itoa", + "num-conv", + "powerfmt", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c" + +[[package]] +name = "time-macros" +version = "0.2.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3526739392ec93fd8b359c8e98514cb3e8e021beb4e5f597b00a0221f8ed8a49" +dependencies = [ + "num-conv", + "time-core", +] + [[package]] name = "tokio" version = "1.45.0" @@ -259,12 +648,135 @@ version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "wasm-bindgen" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" +dependencies = [ + "bumpalo", + "log", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "windows-core" +version = "0.61.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4763c1de310c86d75a878046489e2e5ba02c649d185f21c67d4cf8a56d098980" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-implement" +version = "0.60.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-interface" +version = "0.59.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-link" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38" + +[[package]] +name = "windows-result" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c64fd11a4fd95df68efcfee5f44a294fe71b8bc6a91993e2791938abcc712252" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a2ba9642430ee452d5a7aa78d72907ebe8cfda358e8cb7918a2050581322f97" +dependencies = [ + "windows-link", +] + [[package]] name = "windows-sys" version = "0.52.0" diff --git a/Cargo.toml b/Cargo.toml index 6c6e6ce..52beb62 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,8 +4,8 @@ version = "0.1.0" edition = "2024" [dependencies] -# async-trait = {version = "0.1.88", optional = true} tokio = {version = "1.45.0", features = ["full"]} - -# [features] -# async = ["async-trait"] \ No newline at end of file +flate2 = {version = "1.1.1", features = ["zlib"]} +serde = {version = "1.0.219", features = ["derive"]} +serde_with = "3.12.0" +serde_json = "1.0.140" diff --git a/src/cycle.rs b/src/cycle.rs new file mode 100644 index 0000000..d0ac983 --- /dev/null +++ b/src/cycle.rs @@ -0,0 +1,123 @@ + +use tokio::net::TcpStream; + +use crate::data::{clientbound, serverbound, DataError, AsyncReader, DataWriter, Packet, TextComponentBuilder}; + + +#[derive(Debug)] +pub enum PacketError { + WrongPacketID, + Data(DataError), + NextStateIncorrect, +} + +impl From for PacketError { + fn from(err: DataError) -> Self { + PacketError::Data(err) + } +} + +pub async fn main(mut stream: TcpStream) { + let Ok(addr) = stream.peer_addr() else { + return; + }; + println!("Подключение: {addr}"); + // читаем первый пакет + match read_first_packet(&mut stream).await { + Ok(_) => {}, Err(e) => println!("Ошибка во время обработки пакета: {e:?}") + } + println!("Отключение: {addr}"); + println!(); +} + +async fn read_first_packet(stream: &mut TcpStream) -> Result<(), PacketError> { + let mut packet = stream.read_packet(None).await?; + if packet.id() != 0 { return Err(PacketError::WrongPacketID);} + let version = packet.read_varint().await?; + let host = packet.read_string().await?; + let port = packet.read_short().await?; + let ns = packet.read_varint().await?; + + if version != 770 { + let mut packet = Packet::empty(0x00); + let component = + TextComponentBuilder::new() + .text("Версия игры отличается от 1.21.5") + .color("red") + .build(); + packet.write_string(&component.as_json()?).await?; + return Ok(stream.write_packet(packet, None).await?); + } + + match ns { + 1 => the_status(stream).await, + 2 => the_login(stream, (version, host, port)).await, + _ => Err(PacketError::NextStateIncorrect) + } +} + +async fn the_status(stream: &mut TcpStream) -> Result<(), PacketError> { + let packet = stream.read_packet(None).await?; + if packet.id() != 0 { return Err(PacketError::WrongPacketID); } + let mut p = Packet::empty(clientbound::status::RESPONSE); + let status = "{ + \"version\": { + \"name\": \"1.21.5\", + \"protocol\": 770 + }, + \"players\": { + \"max\": 0, + \"online\": 1 + } + }"; + p.write_string(status).await?; + stream.write_packet(p, None).await?; + + let mut packet = stream.read_packet(None).await?; + if packet.id() != 1 { return Err(PacketError::WrongPacketID); } + let mut p = Packet::empty(clientbound::status::PONG_RESPONSE); + p.write_long(packet.read_long().await?).await?; + stream.write_packet(p, None).await?; + + Ok(()) +} + +async fn the_login(stream: &mut TcpStream, data: (i32, String, u16)) -> Result<(), PacketError> { + + if data.0 != 770 { + let mut packet = Packet::empty(clientbound::login::DISCONNECT); + let component = + TextComponentBuilder::new() + .text("Версия игры отличается от 1.21.5") + .color("red") + .build(); + packet.write_string(&component.as_json()?).await?; + return Ok(stream.write_packet(packet, None).await?); + } + + // println!("Версия протокола: {}", data.0); + // println!("Адрес сервера: {}:{}", data.1, data.2); + + // let mut packet = Packet::empty(clientbound::login::DISCONNECT); + // let component = + // TextComponentBuilder::new() + // .text("Вы кто такие? Я вас не звал. Идите нахуй.") + // .color("red") + // .build(); + // packet.write_string(&component.as_json()?).await?; + // return Ok(stream.write_packet(packet, None).await?); + + let mut packet = stream.read_packet(None).await?; + if packet.id() != serverbound::login::START { return Err(PacketError::WrongPacketID); } + let username = packet.read_string().await?; + let uuid = packet.read_uuid().await?; + + println!("Адрес клиента: {:?}", stream.peer_addr()); + println!("Адрес сервера: {}:{}", data.1, data.2); + println!("Username: {username}\n UUID: {:X}", uuid); + + let mut packet = Packet::empty(clientbound::login::SET_COMPRESSION); + packet.write_varint(512).await?; + + Ok(()) +} \ No newline at end of file diff --git a/src/data.rs b/src/data.rs deleted file mode 100644 index 4ffa7ef..0000000 --- a/src/data.rs +++ /dev/null @@ -1,82 +0,0 @@ -use std::io::{Cursor, Read, Write}; - -use tokio::io::{AsyncRead, AsyncReadExt}; - -use crate::inet::InetError; - -#[derive(Debug)] -pub enum DataError { - ReadError, - WriteError, - VarIntIsSoBig, - StringDecodeError, - Inet(InetError), -} - -pub trait DataReader { - async fn read_bytes(&mut self, size: usize) -> Result, DataError>; - - async fn read_byte(&mut self) -> Result { - Ok(self.read_bytes(1).await?[0]) - } - - async fn read_signed_byte(&mut self) -> Result { - Ok(self.read_byte().await? as i8) - } - - async fn read_short(&mut self) -> Result { - Ok(u16::from_be_bytes( - self.read_bytes(2).await?.try_into().unwrap(), - )) - } - - async fn read_signed_short(&mut self) -> Result { - Ok(self.read_short().await? as i16) - } - - async fn read_varint_size(&mut self) -> Result<(i32, usize), DataError> { - let mut value = 0; - let mut position = 0; - loop { - let byte = self.read_byte().await?; - value |= ((byte & 0x7F) as i32) << (position * 7); - if (byte & 0x80) == 0 { - return Ok((value, position as usize)); - }; - position += 1; - if position >= 5 { - return Err(DataError::VarIntIsSoBig); - }; - } - } - - async fn read_varint(&mut self) -> Result { - Ok(self.read_varint_size().await?.0) - } - - // async fn read_packet(&mut self, threshold: Option) -> Result>, DataError> { - // let size = self.read_varint().await?; - // let mut buf = self.read_bytes(size).await?; - // } - - async fn read_string(&mut self) -> Result { - let size = self.read_varint().await?; - let vec = self.read_bytes(size as usize).await?; - String::from_utf8(vec).map_err(|_| DataError::StringDecodeError) - } -} - -impl DataReader for R { - async fn read_bytes(&mut self, size: usize) -> Result, DataError> { - let mut buf = vec![0; size]; - let mut read = 0; - while read < size { - match AsyncReadExt::read(self, &mut buf[read..]).await { - Ok(0) => return Err(DataError::Inet(InetError::ConnectionClosed)), - Ok(n) => read += n, - Err(_) => return Err(DataError::ReadError), - } - } - Ok(buf) - } -} diff --git a/src/data/async_reader.rs b/src/data/async_reader.rs new file mode 100644 index 0000000..b38eea7 --- /dev/null +++ b/src/data/async_reader.rs @@ -0,0 +1,106 @@ +use std::io::{Cursor, ErrorKind, Read}; + +use tokio::io::{AsyncRead, AsyncReadExt}; + +use crate::inet::InetError; + +use super::{decompress, packet::Packet, DataError}; + + + +pub trait AsyncReader { + async fn read_bytes(&mut self, size: usize) -> Result, DataError>; + + async fn read_byte(&mut self) -> Result { + Ok(self.read_bytes(1).await?[0]) + } + + async fn read_signed_byte(&mut self) -> Result { + Ok(self.read_byte().await? as i8) + } + + async fn read_varint_size(&mut self) -> Result<(i32, usize), DataError> { + let mut value = 0; + let mut position = 0; + loop { + let byte = self.read_byte().await?; + value |= ((byte & 0x7F) as i32) << (position * 7); + if (byte & 0x80) == 0 { + return Ok((value, position as usize)); + }; + position += 1; + if position >= 5 { + return Err(DataError::VarIntIsSoBig); + }; + } + } + + async fn read_varint(&mut self) -> Result { + Ok(self.read_varint_size().await?.0) + } + + async fn read_packet(&mut self, threshold: Option) + -> Result { + let mut data: Vec; + let packet_lenght = self.read_varint().await? as usize; + if threshold.is_some() { + let data_lenght = self.read_varint_size().await?; + data = self.read_bytes(packet_lenght - data_lenght.1).await?; + if data_lenght.0 != 0 { data = decompress(&data)?; } + } else { + data = self.read_bytes(packet_lenght).await?; + } + let mut cursor = Cursor::new(data); + let id = cursor.read_varint().await?; + Ok(Packet::new(id as u8, cursor)) + } + + async fn read_short(&mut self) -> Result { + Ok(u16::from_be_bytes( + self.read_bytes(2).await?.try_into().unwrap() + )) + } + + async fn read_signed_short(&mut self) -> Result { + Ok(self.read_short().await? as i16) + } + + async fn read_string(&mut self) -> Result { + let size = self.read_varint().await?; + let vec = self.read_bytes(size as usize).await?; + String::from_utf8(vec).or( Err(DataError::StringDecodeError)) + } + + async fn read_long(&mut self) -> Result { + Ok(u64::from_be_bytes( + self.read_bytes(8).await?.try_into().unwrap() + )) + } + + async fn read_signed_long(&mut self) -> Result { + Ok(self.read_long().await? as i64) + } + + async fn read_uuid(&mut self) -> Result { + Ok(u128::from_be_bytes( + self.read_bytes(16).await?.try_into().unwrap() + )) + } +} + +impl AsyncReader for R { + async fn read_bytes(&mut self, size: usize) -> Result, DataError> { + let mut buf = vec![0; size]; + match AsyncReadExt::read_exact(self, &mut buf).await { + Ok(_) => Ok(buf), + Err(e) => match e.kind() { + ErrorKind::UnexpectedEof | ErrorKind::BrokenPipe | ErrorKind::ConnectionReset => { + Err(DataError::Inet(InetError::ConnectionClosed)) + } + _ => { + Err(DataError::ReadError) + }, + } + } + } +} \ No newline at end of file diff --git a/src/data/component.rs b/src/data/component.rs new file mode 100644 index 0000000..767aafa --- /dev/null +++ b/src/data/component.rs @@ -0,0 +1,212 @@ +use std::io::Read; + +use serde::{Deserialize, Serialize}; +use serde_with::skip_serializing_none; + +use super::DataError; + + + +#[derive(Debug, Serialize, Deserialize, Clone)] +#[skip_serializing_none] +pub struct TextComponent { + pub text: String, + pub color: Option, + pub bold: Option, + pub italic: Option, + pub underlined: Option, + pub strikethrough: Option, + pub obfuscated: Option, + pub extra: Option>, +} + +impl TextComponent { + pub fn new(text: String) -> Self { + Self { + text, + color: None, + bold: None, + italic: None, + underlined: None, + strikethrough: None, + obfuscated: None, + extra: None, + } + } + + // pub fn rainbow_offset(text: String, offset: i64) -> TextComponent { + // if text.is_empty() { + // return TextComponent::new(text); + // } + + // let children = text + // .char_indices() + // .map(|(i, c)| { + // let hue = (((i as i64 + offset) % text.chars().count() as i64) as f32) + // / (text.chars().count() as f32) + // * 360.0; + // let hsl = Hsl::new(hue, 1.0, 0.5); + // let rgb: Srgb = hsl.into_color(); + // let r = (rgb.red * 255.0).round() as u8; + // let g = (rgb.green * 255.0).round() as u8; + // let b = (rgb.blue * 255.0).round() as u8; + // let mut component = TextComponent::new(c.to_string()); + // component.color = Some(format!("#{:02X}{:02X}{:02X}", r, g, b)); + // component + // }) + // .collect::>(); + + // let mut parent = children[0].clone(); + // if children.len() > 1 { + // parent.extra = Some(children[1..].to_vec()); + // } + // parent + // } + + // pub fn rainbow(text: String) -> TextComponent { + // if text.is_empty() { + // return TextComponent::new(text); + // } + + // let children = text + // .char_indices() + // .map(|(i, c)| { + // let hue = (i as f32) / (text.chars().count() as f32) * 360.0; + // let hsl = Hsl::new(hue, 1.0, 0.5); + // let rgb: Srgb = hsl.into_color(); + // let r = (rgb.red * 255.0).round() as u8; + // let g = (rgb.green * 255.0).round() as u8; + // let b = (rgb.blue * 255.0).round() as u8; + // let mut component = TextComponent::new(c.to_string()); + // component.color = Some(format!("#{:02X}{:02X}{:02X}", r, g, b)); + // component + // }) + // .collect::>(); + + // let mut parent = children[0].clone(); + // if children.len() > 1 { + // parent.extra = Some(children[1..].to_vec()); + // } + // parent + // } + + pub fn builder() -> TextComponentBuilder { + TextComponentBuilder::new() + } + + pub fn as_json(self) -> Result { + serde_json::to_string(&self).or( Err(DataError::SerializationError)) + } + + pub fn from_json(text: &str) -> Result { + serde_json::from_str(text).or( Err(DataError::DeSerializationError)) + } +} + +impl Default for TextComponent { + fn default() -> Self { + Self::new(String::new()) + } +} + +pub struct TextComponentBuilder { + text: String, + color: Option, + bold: Option, + italic: Option, + underlined: Option, + strikethrough: Option, + obfuscated: Option, + extra: Option>, +} + +impl TextComponentBuilder { + pub fn new() -> Self { + Self { + text: String::new(), + color: None, + bold: None, + italic: None, + underlined: None, + strikethrough: None, + obfuscated: None, + extra: None, + } + } + + pub fn text(mut self, text: &str) -> Self { + self.text = text.to_string(); + self + } + + pub fn color(mut self, color: &str) -> Self { + self.color = Some(color.to_string()); + self + } + + pub fn bold(mut self, bold: bool) -> Self { + self.bold = Some(bold); + self + } + + pub fn italic(mut self, italic: bool) -> Self { + self.italic = Some(italic); + self + } + + pub fn underlined(mut self, underlined: bool) -> Self { + self.underlined = Some(underlined); + self + } + + pub fn strikethrough(mut self, strikethrough: bool) -> Self { + self.strikethrough = Some(strikethrough); + self + } + + pub fn obfuscated(mut self, obfuscated: bool) -> Self { + self.obfuscated = Some(obfuscated); + self + } + + pub fn extra(mut self, extra: Vec) -> Self { + self.extra = Some(extra); + self + } + + pub fn build(self) -> TextComponent { + TextComponent { + text: self.text, + color: self.color, + bold: self.bold, + italic: self.italic, + underlined: self.underlined, + strikethrough: self.strikethrough, + obfuscated: self.obfuscated, + extra: self.extra, + } + } +} + +// Реализуем читалку-записывалку текст-компонентов для пакета +// impl ReadWriteNBT for Packet { +// fn read_nbt(&mut self) -> Result { +// let mut data = Vec::new(); +// let pos = self.get_ref().position(); +// self +// .get_mut() +// .read_to_end(&mut data) +// .map_err(|_| ServerError::DeTextComponent)?; +// let (remaining, value) = +// craftflow_nbt::from_slice(&data).map_err(|_| ServerError::DeTextComponent)?; +// self +// .get_mut() +// .set_position(pos + (data.len() - remaining.len()) as u64); +// Ok(value) +// } + +// fn write_nbt(&mut self, val: &TextComponent) -> Result<(), ServerError> { +// craftflow_nbt::to_writer(self.get_mut(), val).map_err(|_| ServerError::SerTextComponent)?; +// Ok(()) +// } +// } \ No newline at end of file diff --git a/src/data/mod.rs b/src/data/mod.rs new file mode 100644 index 0000000..f32c74c --- /dev/null +++ b/src/data/mod.rs @@ -0,0 +1,37 @@ +use std::io::Read; + +use crate::inet::InetError; + +mod async_reader; +mod reader; +mod writer; +mod packet; +mod packet_id; +mod component; + +#[derive(Debug)] +pub enum DataError { + ReadError, + WriteError, + VarIntIsSoBig, + StringDecodeError, + Inet(InetError), + SerializationError, + DeSerializationError, + ZlibError, +} + +pub fn decompress(bytes: &[u8]) -> Result, DataError> { + let mut decoder = ZlibDecoder::new(bytes); + let mut output = Vec::new(); + decoder.read_to_end(&mut output).or(Err(DataError::ZlibError))?; + Ok(output) +} + +pub use async_reader::*; +pub use reader::*; +use flate2::bufread::ZlibDecoder; +pub use writer::*; +pub use packet::*; +pub use packet_id::{clientbound, serverbound}; +pub use component::*; \ No newline at end of file diff --git a/src/data/packet.rs b/src/data/packet.rs new file mode 100644 index 0000000..ad61fab --- /dev/null +++ b/src/data/packet.rs @@ -0,0 +1,97 @@ +use std::io::Cursor; + +use super::{DataError, async_reader::AsyncReader, writer::DataWriter}; + +#[derive(Debug, Clone)] +pub struct Packet { + id: u8, + cursor: Cursor>, +} + +impl Packet { + /// Create new packet from id and buffer + pub fn new(id: u8, cursor: Cursor>) -> Packet { + Packet { id, cursor } + } + + /// Create new packet with id and empty buffer + pub fn empty(id: u8) -> Packet { + Packet { + id, + cursor: Cursor::new(Vec::new()), + } + } + + /// Build packet with lambda + pub fn build(id: u8, builder: F) -> Result + where + F: FnOnce(&mut Packet) -> Result<(), DataError>, + { + let mut packet = Self::empty(id); + builder(&mut packet)?; + Ok(packet) + } + + /// Get packet id + pub fn id(&self) -> u8 { + self.id + } + + /// Set packet id + pub fn set_id(&mut self, id: u8) { + self.id = id; + } + + /// Set packet cursor + pub fn set_cursor(&mut self, cursor: Cursor>) { + self.cursor = cursor; + } + + /// Get cursor length + pub fn len(&self) -> usize { + self.get_bytes().len() - self.cursor.position() as usize + } + + /// Is cursor empty + pub fn is_empty(&self) -> bool { + self.len() == 0 + } + + /// Get cursor remaining bytes + pub fn get_bytes(&self) -> &[u8] { + self.cursor.get_ref() + } + + /// Get mutable reference to cursor + pub fn get_mut(&mut self) -> &mut Cursor> { + &mut self.cursor + } + + /// Get immutable reference to cursor + pub fn get_ref(&self) -> &Cursor> { + &self.cursor + } + + /// Get inner cursor + pub fn into_inner(self) -> Cursor> { + self.cursor + } +} + +impl From for Cursor> { + fn from(val: Packet) -> Self { + val.cursor + } +} + +impl AsyncReader for Packet { + async fn read_bytes(&mut self, size: usize) -> Result, DataError> { + self.cursor.read_bytes(size).await + } +} + +impl DataWriter for Packet { + async fn write_bytes(&mut self, bytes: &[u8]) -> Result<(), DataError> { + self.cursor.write_bytes(bytes).await + } +} diff --git a/src/data/packet_id.rs b/src/data/packet_id.rs new file mode 100644 index 0000000..58f14d1 --- /dev/null +++ b/src/data/packet_id.rs @@ -0,0 +1,272 @@ +/* + +Generated with parse_ids.py + +*/ + +pub mod clientbound { + pub mod status { + pub const RESPONSE: u8 = 0x00; + pub const PONG_RESPONSE: u8 = 0x01; + } + + pub mod login { + pub const DISCONNECT: u8 = 0x00; + pub const ENCRYPTION_REQUEST: u8 = 0x01; + pub const SUCCESS: u8 = 0x02; + pub const SET_COMPRESSION: u8 = 0x03; + pub const PLUGIN_REQUEST: u8 = 0x04; + pub const COOKIE_REQUEST: u8 = 0x05; + } + + pub mod configuration { + pub const COOKIE_REQUEST: u8 = 0x00; + pub const PLUGIN_MESSAGE: u8 = 0x01; + pub const DISCONNECT: u8 = 0x02; + pub const FINISH: u8 = 0x03; + pub const KEEP_ALIVE: u8 = 0x04; + pub const PING: u8 = 0x05; + pub const RESET_CHAT: u8 = 0x06; + pub const REGISTRY_DATA: u8 = 0x07; + pub const REMOVE_RESOURCE_PACK: u8 = 0x08; + pub const ADD_RESOURCE_PACK: u8 = 0x09; + pub const STORE_COOKIE: u8 = 0x0A; + pub const TRANSFER: u8 = 0x0B; + pub const FEATURE_FLAGS: u8 = 0x0C; + pub const UPDATE_TAGS: u8 = 0x0D; + pub const KNOWN_PACKS: u8 = 0x0E; + pub const CUSTOM_REPORT_DETAILS: u8 = 0x0F; + pub const SERVER_LINKS: u8 = 0x10; + } + + pub mod play { + pub const BUNDLE_DELIMITER: u8 = 0x00; + pub const SPAWN_ENTITY: u8 = 0x01; + pub const ENTITY_ANIMATION: u8 = 0x02; + pub const AWARD_STATISTICS: u8 = 0x03; + pub const ACKNOWLEDGE_BLOCK_CHANGE: u8 = 0x04; + pub const SET_BLOCK_DESTROY_STAGE: u8 = 0x05; + pub const BLOCK_ENTITY_DATA: u8 = 0x06; + pub const BLOCK_ACTION: u8 = 0x07; + pub const BLOCK_UPDATE: u8 = 0x08; + pub const BOSS_BAR: u8 = 0x09; + pub const CHANGE_DIFFICULTY: u8 = 0x0A; + pub const CHUNK_BATCH_FINISHED: u8 = 0x0B; + pub const CHUNK_BATCH_START: u8 = 0x0C; + pub const CHUNK_BIOMES: u8 = 0x0D; + pub const CLEAR_TITLES: u8 = 0x0E; + pub const COMMAND_SUGGESTIONS_RESPONSE: u8 = 0x0F; + pub const COMMANDS: u8 = 0x10; + pub const CLOSE_CONTAINER: u8 = 0x11; + pub const SET_CONTAINER_CONTENT: u8 = 0x12; + pub const SET_CONTAINER_PROPERTY: u8 = 0x13; + pub const SET_CONTAINER_SLOT: u8 = 0x14; + pub const COOKIE_REQUEST: u8 = 0x15; + pub const SET_COOLDOWN: u8 = 0x16; + pub const CHAT_SUGGESTIONS: u8 = 0x17; + pub const PLUGIN_MESSAGE: u8 = 0x18; + pub const DAMAGE_EVENT: u8 = 0x19; + pub const DEBUG_SAMPLE: u8 = 0x1A; + pub const DELETE_MESSAGE: u8 = 0x1B; + pub const DISCONNECT: u8 = 0x1C; + pub const DISGUISED_CHAT_MESSAGE: u8 = 0x1D; + pub const ENTITY_EVENT: u8 = 0x1E; + pub const TELEPORT_ENTITY: u8 = 0x1F; + pub const EXPLOSION: u8 = 0x20; + pub const UNLOAD_CHUNK: u8 = 0x21; + pub const GAME_EVENT: u8 = 0x22; + pub const OPEN_HORSE_SCREEN: u8 = 0x23; + pub const HURT_ANIMATION: u8 = 0x24; + pub const INITIALIZE_WORLD_BORDER: u8 = 0x25; + pub const KEEP_ALIVE: u8 = 0x26; + pub const CHUNK_DATA_AND_UPDATE_LIGHT: u8 = 0x27; + pub const WORLD_EVENT: u8 = 0x28; + pub const PARTICLE: u8 = 0x29; + pub const UPDATE_LIGHT: u8 = 0x2A; + pub const LOGIN: u8 = 0x2B; + pub const MAP_DATA: u8 = 0x2C; + pub const MERCHANT_OFFERS: u8 = 0x2D; + pub const UPDATE_ENTITY_POSITION: u8 = 0x2E; + pub const UPDATE_ENTITY_POSITION_AND_ROTATION: u8 = 0x2F; + pub const MOVE_MINECART_ALONG_TRACK: u8 = 0x30; + pub const UPDATE_ENTITY_ROTATION: u8 = 0x31; + pub const MOVE_VEHICLE: u8 = 0x32; + pub const OPEN_BOOK: u8 = 0x33; + pub const OPEN_SCREEN: u8 = 0x34; + pub const OPEN_SIGN_EDITOR: u8 = 0x35; + pub const PING: u8 = 0x36; + pub const PING_RESPONSE: u8 = 0x37; + pub const PLACE_GHOST_RECIPE: u8 = 0x38; + pub const PLAYER_ABILITIES: u8 = 0x39; + pub const PLAYER_CHAT_MESSAGE: u8 = 0x3A; + pub const END_COMBAT: u8 = 0x3B; + pub const ENTER_COMBAT: u8 = 0x3C; + pub const COMBAT_DEATH: u8 = 0x3D; + pub const PLAYER_INFO_REMOVE: u8 = 0x3E; + pub const PLAYER_INFO_UPDATE: u8 = 0x3F; + pub const LOOK_AT: u8 = 0x40; + pub const SYNCHRONIZE_PLAYER_POSITION: u8 = 0x41; + pub const PLAYER_ROTATION: u8 = 0x42; + pub const RECIPE_BOOK_ADD: u8 = 0x43; + pub const RECIPE_BOOK_REMOVE: u8 = 0x44; + pub const RECIPE_BOOK_SETTINGS: u8 = 0x45; + pub const REMOVE_ENTITIES: u8 = 0x46; + pub const REMOVE_ENTITY_EFFECT: u8 = 0x47; + pub const RESET_SCORE: u8 = 0x48; + pub const REMOVE_RESOURCE_PACK: u8 = 0x49; + pub const ADD_RESOURCE_PACK: u8 = 0x4A; + pub const RESPAWN: u8 = 0x4B; + pub const SET_HEAD_ROTATION: u8 = 0x4C; + pub const UPDATE_SECTION_BLOCKS: u8 = 0x4D; + pub const SELECT_ADVANCEMENTS_TAB: u8 = 0x4E; + pub const SERVER_DATA: u8 = 0x4F; + pub const SET_ACTION_BAR_TEXT: u8 = 0x50; + pub const SET_BORDER_CENTER: u8 = 0x51; + pub const SET_BORDER_LERP_SIZE: u8 = 0x52; + pub const SET_BORDER_SIZE: u8 = 0x53; + pub const SET_BORDER_WARNING_DELAY: u8 = 0x54; + pub const SET_BORDER_WARNING_DISTANCE: u8 = 0x55; + pub const SET_CAMERA: u8 = 0x56; + pub const SET_CENTER_CHUNK: u8 = 0x57; + pub const SET_RENDER_DISTANCE: u8 = 0x58; + pub const SET_CURSOR_ITEM: u8 = 0x59; + pub const SET_DEFAULT_SPAWN_POSITION: u8 = 0x5A; + pub const DISPLAY_OBJECTIVE: u8 = 0x5B; + pub const SET_ENTITY_METADATA: u8 = 0x5C; + pub const LINK_ENTITIES: u8 = 0x5D; + pub const SET_ENTITY_VELOCITY: u8 = 0x5E; + pub const SET_EQUIPMENT: u8 = 0x5F; + pub const SET_EXPERIENCE: u8 = 0x60; + pub const SET_HEALTH: u8 = 0x61; + pub const SET_HELD_ITEM: u8 = 0x62; + pub const UPDATE_OBJECTIVES: u8 = 0x63; + pub const SET_PASSENGERS: u8 = 0x64; + pub const SET_PLAYER_INVENTORY_SLOT: u8 = 0x65; + pub const UPDATE_TEAMS: u8 = 0x66; + pub const UPDATE_SCORE: u8 = 0x67; + pub const SET_SIMULATION_DISTANCE: u8 = 0x68; + pub const SET_SUBTITLE_TEXT: u8 = 0x69; + pub const UPDATE_TIME: u8 = 0x6A; + pub const SET_TITLE_TEXT: u8 = 0x6B; + pub const SET_TITLE_ANIMATION_TIMES: u8 = 0x6C; + pub const ENTITY_SOUND_EFFECT: u8 = 0x6D; + pub const SOUND_EFFECT: u8 = 0x6E; + pub const START_CONFIGURATION: u8 = 0x6F; + pub const STOP_SOUND: u8 = 0x70; + pub const STORE_COOKIE: u8 = 0x71; + pub const SYSTEM_CHAT_MESSAGE: u8 = 0x72; + pub const SET_TAB_LIST_HEADER_AND_FOOTER: u8 = 0x73; + pub const TAG_QUERY_RESPONSE: u8 = 0x74; + pub const PICKUP_ITEM: u8 = 0x75; + pub const SYNCHRONIZE_VEHICLE_POSITION: u8 = 0x76; + pub const TEST_INSTANCE_BLOCK_STATUS: u8 = 0x77; + pub const SET_TICKING_STATE: u8 = 0x78; + pub const STEP_TICK: u8 = 0x79; + pub const TRANSFER: u8 = 0x7A; + pub const UPDATE_ADVANCEMENTS: u8 = 0x7B; + pub const UPDATE_ATTRIBUTES: u8 = 0x7C; + pub const ENTITY_EFFECT: u8 = 0x7D; + pub const UPDATE_RECIPES: u8 = 0x7E; + pub const UPDATE_TAGS: u8 = 0x7F; + pub const PROJECTILE_POWER: u8 = 0x80; + pub const CUSTOM_REPORT_DETAILS: u8 = 0x81; + pub const SERVER_LINKS: u8 = 0x82; + } +} + +pub mod serverbound { + pub mod handshake { + pub const HANDSHAKE: u8 = 0x00; + } + + pub mod status { + pub const REQUEST: u8 = 0x00; + pub const PING_REQUEST: u8 = 0x01; + } + + pub mod login { + pub const START: u8 = 0x00; + pub const ENCRYPTION_RESPONSE: u8 = 0x01; + pub const PLUGIN_RESPONSE: u8 = 0x02; + pub const ACKNOWLEDGED: u8 = 0x03; + pub const COOKIE_RESPONSE: u8 = 0x04; + } + + pub mod configuration { + pub const CLIENT_INFORMATION: u8 = 0x00; + pub const COOKIE_RESPONSE: u8 = 0x01; + pub const PLUGIN_MESSAGE: u8 = 0x02; + pub const ACKNOWLEDGE_FINISH: u8 = 0x03; + pub const KEEP_ALIVE: u8 = 0x04; + pub const PONG: u8 = 0x05; + pub const RESOURCE_PACK_RESPONSE: u8 = 0x06; + pub const KNOWN_PACKS: u8 = 0x07; + } + + pub mod play { + pub const CONFIRM_TELEPORTATION: u8 = 0x00; + pub const QUERY_BLOCK_ENTITY_TAG: u8 = 0x01; + pub const BUNDLE_ITEM_SELECTED: u8 = 0x02; + pub const CHANGE_DIFFICULTY: u8 = 0x03; + pub const ACKNOWLEDGE_MESSAGE: u8 = 0x04; + pub const CHAT_COMMAND: u8 = 0x05; + pub const SIGNED_CHAT_COMMAND: u8 = 0x06; + pub const CHAT_MESSAGE: u8 = 0x07; + pub const PLAYER_SESSION: u8 = 0x08; + pub const CHUNK_BATCH_RECEIVED: u8 = 0x09; + pub const CLIENT_STATUS: u8 = 0x0A; + pub const CLIENT_TICK_END: u8 = 0x0B; + pub const CLIENT_INFORMATION: u8 = 0x0C; + pub const COMMAND_SUGGESTIONS_REQUEST: u8 = 0x0D; + pub const ACKNOWLEDGE_CONFIGURATION: u8 = 0x0E; + pub const CLICK_CONTAINER_BUTTON: u8 = 0x0F; + pub const CLICK_CONTAINER: u8 = 0x10; + pub const CLOSE_CONTAINER: u8 = 0x11; + pub const CHANGE_CONTAINER_SLOT_STATE: u8 = 0x12; + pub const COOKIE_RESPONSE: u8 = 0x13; + pub const PLUGIN_MESSAGE: u8 = 0x14; + pub const DEBUG_SAMPLE_SUBSCRIPTION: u8 = 0x15; + pub const EDIT_BOOK: u8 = 0x16; + pub const QUERY_ENTITY_TAG: u8 = 0x17; + pub const INTERACT: u8 = 0x18; + pub const JIGSAW_GENERATE: u8 = 0x19; + pub const KEEP_ALIVE: u8 = 0x1A; + pub const LOCK_DIFFICULTY: u8 = 0x1B; + pub const SET_PLAYER_POSITION: u8 = 0x1C; + pub const SET_PLAYER_POSITION_AND_ROTATION: u8 = 0x1D; + pub const SET_PLAYER_ROTATION: u8 = 0x1E; + pub const SET_PLAYER_MOVEMENT_FLAGS: u8 = 0x1F; + pub const MOVE_VEHICLE: u8 = 0x20; + pub const PADDLE_BOAT: u8 = 0x21; + pub const PICK_ITEM_FROM_BLOCK: u8 = 0x22; + pub const PICK_ITEM_FROM_ENTITY: u8 = 0x23; + pub const PING_REQUEST: u8 = 0x24; + pub const PLACE_RECIPE: u8 = 0x25; + pub const PLAYER_ABILITIES: u8 = 0x26; + pub const PLAYER_ACTION: u8 = 0x27; + pub const PLAYER_COMMAND: u8 = 0x28; + pub const PLAYER_INPUT: u8 = 0x29; + pub const PLAYER_LOADED: u8 = 0x2A; + pub const PONG: u8 = 0x2B; + pub const CHANGE_RECIPE_BOOK_SETTINGS: u8 = 0x2C; + pub const SET_SEEN_RECIPE: u8 = 0x2D; + pub const RENAME_ITEM: u8 = 0x2E; + pub const RESOURCE_PACK_RESPONSE: u8 = 0x2F; + pub const SEEN_ADVANCEMENTS: u8 = 0x30; + pub const SELECT_TRADE: u8 = 0x31; + pub const SET_BEACON_EFFECT: u8 = 0x32; + pub const SET_HELD_ITEM: u8 = 0x33; + pub const PROGRAM_COMMAND_BLOCK: u8 = 0x34; + pub const PROGRAM_COMMAND_BLOCK_MINECART: u8 = 0x35; + pub const SET_CREATIVE_MODE_SLOT: u8 = 0x36; + pub const PROGRAM_JIGSAW_BLOCK: u8 = 0x37; + pub const PROGRAM_STRUCTURE_BLOCK: u8 = 0x38; + pub const SET_TEST_BLOCK: u8 = 0x39; + pub const UPDATE_SIGN: u8 = 0x3A; + pub const SWING_ARM: u8 = 0x3B; + pub const TELEPORT_TO_ENTITY: u8 = 0x3C; + pub const TEST_INSTANCE_BLOCK_ACTION: u8 = 0x3D; + pub const USE_ITEM_ON: u8 = 0x3E; + pub const USE_ITEM: u8 = 0x3F; + } +} diff --git a/src/data/reader.rs b/src/data/reader.rs new file mode 100644 index 0000000..5c3f110 --- /dev/null +++ b/src/data/reader.rs @@ -0,0 +1,20 @@ +use std::io::Read; + +use super::DataError; + + + + +pub trait Reader { + fn read_bytes(&mut self, size: usize) -> Result, DataError>; +} + +impl Reader for R { + fn read_bytes(&mut self, size: usize) -> Result, DataError> { + let mut buf = vec![0; size]; + match self.read_exact(&mut buf) { + Ok(_) => Ok(buf), + Err(_) => Err(DataError::ReadError) + } + } +} \ No newline at end of file diff --git a/src/data/writer.rs b/src/data/writer.rs new file mode 100644 index 0000000..3c42793 --- /dev/null +++ b/src/data/writer.rs @@ -0,0 +1,103 @@ +use std::io::{Cursor, Seek, SeekFrom, Write}; + +use flate2::{write::ZlibEncoder, Compression}; +use tokio::io::{AsyncWrite, AsyncWriteExt}; + +use super::{packet::{self, Packet}, DataError}; + + + +pub fn compress(bytes: &[u8], compression: u32) -> Result, DataError> { + let mut encoder = ZlibEncoder::new(Vec::new(), Compression::new(compression)); + encoder.write_all(bytes).or(Err(DataError::ZlibError))?; + encoder.finish().or(Err(DataError::ZlibError)) +} + +pub trait DataWriter { + async fn write_bytes(&mut self, bytes: &[u8]) -> Result<(), DataError>; + + async fn write_byte(&mut self, value: u8) -> Result<(), DataError> { + self.write_bytes(&[value]).await + } + + async fn write_signed_byte(&mut self, value: i8) -> Result<(), DataError> { + self.write_byte(value as u8).await + } + + async fn write_varint_size(&mut self, value: i32) -> Result { + let mut _value = value as u32; + let mut position = 0; + loop { + let mut byte = (_value & 127) as u8; + position += 1; _value >>= 7; + if _value != 0 { byte += 128; }; + self.write_byte(byte).await?; + if _value == 0 { return Ok(position) } + } + } + + async fn write_varint(&mut self, value: i32) -> Result<(), DataError> { + self.write_varint_size(value).await?; Ok(()) + } + + async fn write_packet(&mut self, packet: Packet, threshold: Option) + -> Result<(), DataError> { + let mut buf = Vec::new(); + + let mut data_buf = Vec::new(); + data_buf.write_varint((packet.id() as u32) as i32).await?; + data_buf.write_bytes(packet.get_bytes()).await?; + + if let Some(threshold) = threshold { + let mut packet_buf = Vec::new(); + + if data_buf.len() > threshold { + packet_buf.write_varint(data_buf.len() as i32).await?; + let compressed_data = compress(&data_buf, 5)?; + Write::write_all(&mut packet_buf, &compressed_data).or(Err(DataError::WriteError))?; + } else { + packet_buf.write_varint(0).await?; + packet_buf.write_bytes(&data_buf).await?; + } + buf.write_varint(packet_buf.len() as i32).await?; + buf.write_bytes(&packet_buf).await?; + } else { + buf.write_varint(data_buf.len() as i32).await?; + buf.write_bytes(&data_buf).await?; + } + self.write_bytes(&buf).await?; + Ok(()) + } + + async fn write_short(&mut self, value: u16) -> Result<(), DataError> { + self.write_bytes(&value.to_be_bytes()).await + } + + async fn write_signed_short(&mut self, value: i16) -> Result<(), DataError> { + self.write_short(value as u16).await + } + + async fn write_string(&mut self, value: &str) -> Result<(), DataError> { + self.write_varint(value.len() as i32).await?; + self.write_bytes(value.as_bytes()).await?; + Ok(()) + } + + async fn write_long(&mut self, value: u64) -> Result<(), DataError> { + self.write_bytes(&value.to_be_bytes()).await + } + + async fn write_signed_long(&mut self, value: i64) -> Result<(), DataError> { + self.write_long(value as u64).await + } + + async fn write_uuid(&mut self, value: u128) -> Result<(), DataError> { + self.write_bytes(&value.to_be_bytes()).await + } +} + +impl DataWriter for W { + async fn write_bytes(&mut self, bytes: &[u8]) -> Result<(), DataError> { + AsyncWriteExt::write_all(self, bytes).await.or(Err(DataError::WriteError)) + } +} \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index a28e4d5..923a66e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,12 +1,11 @@ -use std::{io::Cursor, net::Shutdown}; -use data::{DataError, DataReader}; use inet::Server; -use tokio::{io::AsyncWriteExt, net::TcpStream}; + mod data; mod inet; +mod cycle; #[tokio::main] async fn main() { @@ -17,34 +16,7 @@ async fn main() { loop { let stream = server.accept().await; - tokio::spawn(test(stream)); + tokio::spawn(cycle::main(stream)); } } -async fn test(mut stream: TcpStream) { - let Ok(addr) = stream.peer_addr() else { - return; - }; - println!("Подключение: {addr}"); - // читаем первый пакет - match read_first_packet(&mut stream).await { - Ok(_) => {}, Err(e) => println!("Ошибка во время обработки пакета: {e:?}") - } - println!("Отключение: {addr}"); - println!(); -} - -async fn read_first_packet(stream: &mut TcpStream) -> Result<(), DataError> { - let size = stream.read_varint().await?; - let mut buf = Cursor::new(stream.read_bytes(size as usize).await?); - let id = buf.read_varint().await?; - let version = buf.read_varint().await?; - let host = buf.read_string().await?; - let port = buf.read_short().await?; - let ns = buf.read_varint().await?; - println!("Айди пакета: {id}"); - println!("Версия протокола: {version}"); - println!("Адрес сервера: {host}:{port}"); - println!("Следующее состояние: {ns}"); - Ok(()) -}