From e591cd8a4d2a37f60df070f610ed02327f868301 Mon Sep 17 00:00:00 2001 From: Garen Tyler Date: Tue, 14 Mar 2023 17:19:13 -0600 Subject: [PATCH] Extract packets into composition-protocol crate and make into workspace --- .gitignore | 6 +- Cargo.lock | 637 +++++++++++------- Cargo.toml | 36 +- crates/composition-core/Cargo.toml | 31 + build.rs => crates/composition-core/build.rs | 0 .../composition-core/src}/config.rs | 2 +- {src => crates/composition-core/src}/lib.rs | 0 .../composition-core/src}/net/mctypes/mod.rs | 0 .../src}/net/mctypes/numbers.rs | 0 .../src}/net/mctypes/other.rs | 0 .../composition-core/src}/net/mod.rs | 0 .../composition-core/src}/net/packets.rs | 0 crates/composition-core/src/server-icon.png | Bin 0 -> 11088 bytes .../composition-core/src}/server/messages.rs | 0 .../composition-core/src}/server/mod.rs | 0 crates/composition-protocol/Cargo.toml | 14 + .../examples/packet_logger/Cargo.toml | 9 + .../examples/packet_logger/src/main.rs | 117 ++++ crates/composition-protocol/src/lib.rs | 56 ++ .../src/packet/clientbound/login.rs | 308 +++++++++ .../src/packet/clientbound/mod.rs | 7 + .../src/packet/clientbound/play.rs | 494 ++++++++++++++ .../src/packet/clientbound/status.rs | 83 +++ crates/composition-protocol/src/packet/mod.rs | 288 ++++++++ .../src/packet/serverbound/handshake.rs | 72 ++ .../src/packet/serverbound/login.rs | 184 +++++ .../src/packet/serverbound/mod.rs | 9 + .../src/packet/serverbound/play.rs | 266 ++++++++ .../src/packet/serverbound/status.rs | 76 +++ crates/composition-protocol/src/util.rs | 190 ++++++ output.log | 9 + src/main.rs | 15 +- 32 files changed, 2633 insertions(+), 276 deletions(-) create mode 100644 crates/composition-core/Cargo.toml rename build.rs => crates/composition-core/build.rs (100%) rename {src => crates/composition-core/src}/config.rs (99%) rename {src => crates/composition-core/src}/lib.rs (100%) rename {src => crates/composition-core/src}/net/mctypes/mod.rs (100%) rename {src => crates/composition-core/src}/net/mctypes/numbers.rs (100%) rename {src => crates/composition-core/src}/net/mctypes/other.rs (100%) rename {src => crates/composition-core/src}/net/mod.rs (100%) rename {src => crates/composition-core/src}/net/packets.rs (100%) create mode 100644 crates/composition-core/src/server-icon.png rename {src => crates/composition-core/src}/server/messages.rs (100%) rename {src => crates/composition-core/src}/server/mod.rs (100%) create mode 100644 crates/composition-protocol/Cargo.toml create mode 100644 crates/composition-protocol/examples/packet_logger/Cargo.toml create mode 100644 crates/composition-protocol/examples/packet_logger/src/main.rs create mode 100644 crates/composition-protocol/src/lib.rs create mode 100644 crates/composition-protocol/src/packet/clientbound/login.rs create mode 100644 crates/composition-protocol/src/packet/clientbound/mod.rs create mode 100644 crates/composition-protocol/src/packet/clientbound/play.rs create mode 100644 crates/composition-protocol/src/packet/clientbound/status.rs create mode 100644 crates/composition-protocol/src/packet/mod.rs create mode 100644 crates/composition-protocol/src/packet/serverbound/handshake.rs create mode 100644 crates/composition-protocol/src/packet/serverbound/login.rs create mode 100644 crates/composition-protocol/src/packet/serverbound/mod.rs create mode 100644 crates/composition-protocol/src/packet/serverbound/play.rs create mode 100644 crates/composition-protocol/src/packet/serverbound/status.rs create mode 100644 crates/composition-protocol/src/util.rs create mode 100644 output.log diff --git a/.gitignore b/.gitignore index 3e73375..559be7d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,5 @@ .DS_Store -/output.log -/target -/world +composition/output.log +target +composition/world /.cargo diff --git a/Cargo.lock b/Cargo.lock index ae1f64e..478fb66 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9,10 +9,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" [[package]] -name = "anyhow" -version = "1.0.56" +name = "android_system_properties" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4361135be9122e0870de935d7c439aef945b9f9ddd4199a553b5270b49c82a27" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "anyhow" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "224afbd727c3d6e4b90103ece64b8d1b67fbb1973b1046c2281eed3f3803f800" [[package]] name = "arrayref" @@ -20,24 +29,13 @@ version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544" -[[package]] -name = "async-trait" -version = "0.1.53" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed6aa3524a2dfcf9fe180c51eae2b58738348d819517ceadf95789c51fff7600" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "atty" version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" dependencies = [ - "hermit-abi", + "hermit-abi 0.1.19", "libc", "winapi", ] @@ -54,6 +52,12 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bumpalo" +version = "3.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" + [[package]] name = "byteorder" version = "1.4.3" @@ -62,15 +66,15 @@ checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" [[package]] name = "bytes" -version = "1.1.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8" +checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" [[package]] name = "cc" -version = "1.0.73" +version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" +checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" [[package]] name = "cesu8" @@ -92,17 +96,29 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.19" +version = "0.4.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" +checksum = "4e3c5919066adf22df73762e50cffcde3a758f2a848b113b586d1f86728b673b" dependencies = [ - "libc", + "iana-time-zone", + "js-sys", "num-integer", "num-traits", "time", + "wasm-bindgen", "winapi", ] +[[package]] +name = "codespan-reporting" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" +dependencies = [ + "termcolor", + "unicode-width", +] + [[package]] name = "colored" version = "1.9.3" @@ -118,11 +134,18 @@ dependencies = [ name = "composition" version = "0.1.0" dependencies = [ - "async-trait", + "composition-core", + "log", + "tokio", +] + +[[package]] +name = "composition-core" +version = "0.1.0" +dependencies = [ "chrono", "ctrlc", "fern", - "futures", "lazy_static", "log", "quartz_nbt", @@ -135,6 +158,22 @@ dependencies = [ "uuid", ] +[[package]] +name = "composition-protocol" +version = "0.1.0" +dependencies = [ + "anyhow", + "nom", + "serde_json", + "thiserror", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" + [[package]] name = "crc32fast" version = "1.3.2" @@ -146,12 +185,56 @@ dependencies = [ [[package]] name = "ctrlc" -version = "3.2.1" +version = "3.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a19c6cedffdc8c03a3346d723eb20bd85a13362bb96dc2ac000842c6381ec7bf" +checksum = "bbcf33c2a618cbe41ee43ae6e9f2e48368cd9f9db2896f10167d8d762679f639" dependencies = [ "nix", - "winapi", + "windows-sys", +] + +[[package]] +name = "cxx" +version = "1.0.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a140f260e6f3f79013b8bfc65e7ce630c9ab4388c6a89c71e07226f49487b72" +dependencies = [ + "cc", + "cxxbridge-flags", + "cxxbridge-macro", + "link-cplusplus", +] + +[[package]] +name = "cxx-build" +version = "1.0.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da6383f459341ea689374bf0a42979739dc421874f112ff26f829b8040b8e613" +dependencies = [ + "cc", + "codespan-reporting", + "once_cell", + "proc-macro2", + "quote", + "scratch", + "syn", +] + +[[package]] +name = "cxxbridge-flags" +version = "1.0.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90201c1a650e95ccff1c8c0bb5a343213bdd317c6e600a93075bca2eff54ec97" + +[[package]] +name = "cxxbridge-macro" +version = "1.0.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b75aed41bb2e6367cae39e6326ef817a851db13c13e4f3263714ca3cfb8de56" +dependencies = [ + "proc-macro2", + "quote", + "syn", ] [[package]] @@ -166,105 +249,14 @@ dependencies = [ [[package]] name = "flate2" -version = "1.0.23" +version = "1.0.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b39522e96686d38f4bc984b9198e3a0613264abaebaff2c5c918bfa6b6da09af" +checksum = "a8a2db397cb1c8772f31494cb8917e48cd1e64f0fa7efac59fbd741a0a8ce841" dependencies = [ - "cfg-if 1.0.0", "crc32fast", - "libc", "miniz_oxide", ] -[[package]] -name = "futures" -version = "0.3.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f73fe65f54d1e12b726f517d3e2135ca3125a437b6d998caf1962961f7172d9e" -dependencies = [ - "futures-channel", - "futures-core", - "futures-executor", - "futures-io", - "futures-sink", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-channel" -version = "0.3.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3083ce4b914124575708913bca19bfe887522d6e2e6d0952943f5eac4a74010" -dependencies = [ - "futures-core", - "futures-sink", -] - -[[package]] -name = "futures-core" -version = "0.3.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c09fd04b7e4073ac7156a9539b57a484a8ea920f79c7c675d05d289ab6110d3" - -[[package]] -name = "futures-executor" -version = "0.3.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9420b90cfa29e327d0429f19be13e7ddb68fa1cccb09d65e5706b8c7a749b8a6" -dependencies = [ - "futures-core", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-io" -version = "0.3.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc4045962a5a5e935ee2fdedaa4e08284547402885ab326734432bed5d12966b" - -[[package]] -name = "futures-macro" -version = "0.3.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33c1e13800337f4d4d7a316bf45a567dbcb6ffe087f16424852d97e97a91f512" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "futures-sink" -version = "0.3.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21163e139fa306126e6eedaf49ecdb4588f939600f0b1e770f4205ee4b7fa868" - -[[package]] -name = "futures-task" -version = "0.3.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57c66a976bf5909d801bbef33416c41372779507e7a6b3a5e25e4749c58f776a" - -[[package]] -name = "futures-util" -version = "0.3.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8b7abd5d659d9b90c8cba917f6ec750a74e2dc23902ef9cd4cc8c8b22e6036a" -dependencies = [ - "futures-channel", - "futures-core", - "futures-io", - "futures-macro", - "futures-sink", - "futures-task", - "memchr", - "pin-project-lite", - "pin-utils", - "slab", -] - [[package]] name = "hermit-abi" version = "0.1.19" @@ -275,10 +267,52 @@ dependencies = [ ] [[package]] -name = "itoa" -version = "1.0.1" +name = "hermit-abi" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35" +checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" +dependencies = [ + "libc", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.53" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64c122667b287044802d6ce17ee2ddf13207ed924c712de9a66a5814d5b64765" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "winapi", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0703ae284fc167426161c2e3f1da3ea71d94b21bedbcc9494e92b28e334e3dca" +dependencies = [ + "cxx", + "cxx-build", +] + +[[package]] +name = "itoa" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" + +[[package]] +name = "js-sys" +version = "0.3.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730" +dependencies = [ + "wasm-bindgen", +] [[package]] name = "lazy_static" @@ -288,15 +322,24 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.123" +version = "0.2.139" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb691a747a7ab48abc15c5b42066eaafde10dc427e3b6ee2a1cf43db04c763bd" +checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" + +[[package]] +name = "link-cplusplus" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecd207c9c713c34f95a097a5b029ac2ce6010530c7b49d7fea24d977dede04f5" +dependencies = [ + "cc", +] [[package]] name = "lock_api" -version = "0.4.7" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "327fa5b6a6940e4699ec49a9beae1ea4845c6bab9314e4f84ac68742139d8c53" +checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" dependencies = [ "autocfg", "scopeguard", @@ -304,87 +347,73 @@ dependencies = [ [[package]] name = "log" -version = "0.4.16" +version = "0.4.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6389c490849ff5bc16be905ae24bc913a9c8892e19b2341dbc175e14c341c2b8" +checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" dependencies = [ "cfg-if 1.0.0", ] [[package]] name = "memchr" -version = "2.4.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" [[package]] -name = "memoffset" -version = "0.6.5" +name = "minimal-lexical" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" -dependencies = [ - "autocfg", -] +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.5.1" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2b29bd4bc3f33391105ebee3589c19197c4271e3e5a9ec9bfe8127eeff8f082" +checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa" dependencies = [ "adler", ] [[package]] name = "mio" -version = "0.8.2" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52da4364ffb0e4fe33a9841a98a3f3014fb964045ce4f7a45a398243c8d6b0c9" +checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9" dependencies = [ "libc", "log", - "miow", - "ntapi", "wasi 0.11.0+wasi-snapshot-preview1", - "winapi", -] - -[[package]] -name = "miow" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9f1c5b025cda876f66ef43a113f91ebc9f4ccef34843000e0adf6ebbab84e21" -dependencies = [ - "winapi", + "windows-sys", ] [[package]] name = "nix" -version = "0.23.1" +version = "0.26.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f866317acbd3a240710c63f065ffb1e4fd466259045ccb504130b7f668f35c6" +checksum = "bfdda3d196821d6af13126e40375cdf7da646a96114af134d5f417a9a1dc8e1a" dependencies = [ "bitflags", - "cc", "cfg-if 1.0.0", "libc", - "memoffset", + "static_assertions", ] [[package]] -name = "ntapi" -version = "0.3.7" +name = "nom" +version = "7.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c28774a7fd2fbb4f0babd8237ce554b73af68021b5f695a3cebd6c59bac0980f" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" dependencies = [ - "winapi", + "memchr", + "minimal-lexical", ] [[package]] name = "num-integer" -version = "0.1.44" +version = "0.1.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" dependencies = [ "autocfg", "num-traits", @@ -392,34 +421,43 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.14" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" +checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" dependencies = [ "autocfg", ] [[package]] name = "num_cpus" -version = "1.13.1" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1" +checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" dependencies = [ - "hermit-abi", + "hermit-abi 0.2.6", "libc", ] [[package]] name = "once_cell" -version = "1.10.0" +version = "1.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87f3e037eac156d1775da914196f0f37741a274155e34a0b7e427c35d2a2ecb9" +checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" + +[[package]] +name = "packet_logger" +version = "0.1.0" +dependencies = [ + "anyhow", + "composition-protocol", + "tokio", +] [[package]] name = "parking_lot" -version = "0.12.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87f5ec2493a61ac0506c0f4199f99070cbe83857b0337006a30f3e6719b8ef58" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" dependencies = [ "lock_api", "parking_lot_core", @@ -427,9 +465,9 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.2" +version = "0.9.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "995f667a6c822200b0433ac218e05582f0e2efa1b922a3fd2fbaadc5f87bab37" +checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521" dependencies = [ "cfg-if 1.0.0", "libc", @@ -440,23 +478,17 @@ dependencies = [ [[package]] name = "pin-project-lite" -version = "0.2.8" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e280fbe77cc62c91527259e9442153f4688736748d24660126286329742b4c6c" - -[[package]] -name = "pin-utils" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" [[package]] name = "proc-macro2" -version = "1.0.37" +version = "1.0.51" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec757218438d5fda206afc041538b2f6d889286160d649a86a24d37e1235afd1" +checksum = "5d727cae5b39d21da60fa540906919ad737832fe0b1c165da3a34d6548c849d6" dependencies = [ - "unicode-xid", + "unicode-ident", ] [[package]] @@ -486,9 +518,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.18" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1feb54ed693b93a84e14094943b84b7c4eae204c512b7ccb95ab0c66d278ad1" +checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b" dependencies = [ "proc-macro2", ] @@ -505,18 +537,18 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.2.13" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62f25bc4c7e55e0b0b7a1d43fb893f4fa1361d0abe38b9ce4f323c2adfe6ef42" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" dependencies = [ "bitflags", ] [[package]] name = "ryu" -version = "1.0.9" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f" +checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" [[package]] name = "scopeguard" @@ -525,19 +557,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" [[package]] -name = "serde" -version = "1.0.136" +name = "scratch" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce31e24b01e1e524df96f1c2fdd054405f8d7376249a5110886fb4b658484789" +checksum = "1792db035ce95be60c3f8853017b3999209281c24e2ba5bc8e59bf97a0c590c1" + +[[package]] +name = "serde" +version = "1.0.152" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.136" +version = "1.0.152" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08597e7152fcd306f41838ed3e37be9eaeed2b61c42e2117266a554fab4662f9" +checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e" dependencies = [ "proc-macro2", "quote", @@ -546,9 +584,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.79" +version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e8d9fa5c3b304765ce1fd9c4c8a3de2c8db365a5b91be52f186efc675681d95" +checksum = "1c533a59c9d8a93a09c6ab31f0fd5e5f4dd1b8fc9434804029839884765d04ea" dependencies = [ "itoa", "ryu", @@ -557,35 +595,35 @@ dependencies = [ [[package]] name = "signal-hook-registry" -version = "1.4.0" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0" +checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" dependencies = [ "libc", ] -[[package]] -name = "slab" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb703cfe953bccee95685111adeedb76fabe4e97549a58d16f03ea7b9367bb32" - [[package]] name = "smallvec" -version = "1.8.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2dd574626839106c320a323308629dcb1acfc96e32a8cba364ddc61ac23ee83" +checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" [[package]] name = "socket2" -version = "0.4.4" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66d72b759436ae32898a2af0a14218dbf55efde3feeb170eb623637db85ee1e0" +checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" dependencies = [ "libc", "winapi", ] +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + [[package]] name = "substring" version = "1.4.5" @@ -597,20 +635,49 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.91" +version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b683b2b825c8eef438b77c36a06dc262294da3d5a5813fac20da149241dcd44d" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ "proc-macro2", "quote", - "unicode-xid", + "unicode-ident", +] + +[[package]] +name = "termcolor" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "thiserror" +version = "1.0.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5ab016db510546d856297882807df8da66a16fb8c4101cb8b30054b0d5b2d9c" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5420d42e90af0c38c3290abcca25b9b3bdf379fc9f55c528f53a269d9c9a267e" +dependencies = [ + "proc-macro2", + "quote", + "syn", ] [[package]] name = "time" -version = "0.1.44" +version = "0.1.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" +checksum = "1b797afad3f312d1c66a56d11d0316f916356d11bd158fbc6ca6389ff6bf805a" dependencies = [ "libc", "wasi 0.10.0+wasi-snapshot-preview1", @@ -619,29 +686,29 @@ dependencies = [ [[package]] name = "tokio" -version = "1.17.0" +version = "1.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2af73ac49756f3f7c01172e34a23e5d0216f6c32333757c2c61feb2bbff5a5ee" +checksum = "03201d01c3c27a29c8a5cee5b55a93ddae1ccf6f08f65365c2c918f8c1b76f64" dependencies = [ + "autocfg", "bytes", "libc", "memchr", "mio", "num_cpus", - "once_cell", "parking_lot", "pin-project-lite", "signal-hook-registry", "socket2", "tokio-macros", - "winapi", + "windows-sys", ] [[package]] name = "tokio-macros" -version = "1.7.0" +version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b557f72f448c511a979e2564e55d74e6c4432fc96ff4f6241bc6bded342643b7" +checksum = "d266c00fde287f55d3f1c3e96c500c362a2b8c695076ec180f27918820bc6df8" dependencies = [ "proc-macro2", "quote", @@ -650,18 +717,24 @@ dependencies = [ [[package]] name = "toml" -version = "0.5.9" +version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7" +checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" dependencies = [ "serde", ] [[package]] -name = "unicode-xid" -version = "0.2.2" +name = "unicode-ident" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" +checksum = "775c11906edafc97bc378816b94585fbd9a054eabaf86fdd0ced94af449efab7" + +[[package]] +name = "unicode-width" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" [[package]] name = "uuid" @@ -681,6 +754,60 @@ 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.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b" +dependencies = [ + "cfg-if 1.0.0", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d" + [[package]] name = "winapi" version = "0.3.9" @@ -697,6 +824,15 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" @@ -705,43 +841,66 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows-sys" -version = "0.34.0" +version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5acdd78cb4ba54c0045ac14f62d8f94a03d10047904ae2a40afa1e99d8f70825" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e2522491fbfcd58cc84d47aeb2958948c4b8982e9a2d8a2a35bbaed431390e7" +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_msvc" -version = "0.34.0" +name = "windows_aarch64_gnullvm" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17cffbe740121affb56fad0fc0e421804adf0ae00891205213b5cecd30db881d" +checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7" [[package]] name = "windows_i686_gnu" -version = "0.34.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2564fde759adb79129d9b4f54be42b32c89970c18ebf93124ca8870a498688ed" +checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640" [[package]] name = "windows_i686_msvc" -version = "0.34.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cd9d32ba70453522332c14d38814bceeb747d80b3958676007acadd7e166956" +checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605" [[package]] name = "windows_x86_64_gnu" -version = "0.34.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfce6deae227ee8d356d19effc141a509cc503dfd1f850622ec4b0f84428e1f4" +checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463" [[package]] name = "windows_x86_64_msvc" -version = "0.34.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d19538ccc21819d01deaf88d6a17eae6596a12e9aafdbb97916fb49896d89de9" +checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd" diff --git a/Cargo.toml b/Cargo.toml index a5378ca..28a8163 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,33 +1,15 @@ +[workspace] +members = [ "crates/*", "crates/*/examples/*" ] + [package] -authors = ["Garen Tyler "] -description = "An extremely fast Minecraft server" -edition = "2021" -license = "MIT" name = "composition" version = "0.1.0" -build = "build.rs" +edition = "2021" +authors = ["Garen Tyler "] +description = "An extremely fast Minecraft server" +license = "MIT" [dependencies] -async-trait = "0.1.48" -chrono = "0.4.13" -ctrlc = "3.1.8" -# fastnbt = "*" -fern = {version = "0.6", features = ["colored"]} -futures = "0.3.13" -lazy_static = "1.4.0" +composition-core = { path = "./crates/composition-core" } log = "0.4" -radix64 = "0.6.2" -serde = {version = "1.0.114", features = ["serde_derive"]} -serde_json = "1.0.59" -tokio = {version = "1", features = ["full"]} -toml = "0.5" -uuid = "0.8.2" -substring = "1.4.5" -quartz_nbt = {version = "0.2.6", features = ["serde"]} - -# colorful = "0.2.1" -# ozelot = "0.9.0" # Ozelot 0.9.0 supports protocol version 578 (1.15.2) -# toml = "0.5.6" -# base64 = "0.12.3" -# mojang-api = "0.6.1" -# backtrace = "0.3.50" +tokio = "1" diff --git a/crates/composition-core/Cargo.toml b/crates/composition-core/Cargo.toml new file mode 100644 index 0000000..e1ccadc --- /dev/null +++ b/crates/composition-core/Cargo.toml @@ -0,0 +1,31 @@ +[package] +name = "composition-core" +version = "0.1.0" +edition = "2021" +build = "build.rs" + +[dependencies] +chrono = "0.4.13" +ctrlc = "3.1.8" +fern = { version = "0.6", features = ["colored"] } +lazy_static = "1.4.0" +log = "0.4" +quartz_nbt = { version = "0.2.6", features = ["serde"] } +radix64 = "0.6.2" +serde = { version = "1.0.114", features = ["serde_derive"] } +serde_json = "1.0.59" +substring = "1.4.5" +tokio = { version = "1", features = ["full"] } +toml = "0.5" +uuid = "0.8.2" + +# async-trait = "0.1.48" +# backtrace = "0.3.50" +# base64 = "0.12.3" +# colorful = "0.2.1" +# composition-protocol = { path = "../composition-protocol" } +# fastnbt = "*" +# futures = "0.3.13" +# mojang-api = "0.6.1" +# ozelot = "0.9.0" # Ozelot 0.9.0 supports protocol version 578 (1.15.2) +# toml = "0.5.6" diff --git a/build.rs b/crates/composition-core/build.rs similarity index 100% rename from build.rs rename to crates/composition-core/build.rs diff --git a/src/config.rs b/crates/composition-core/src/config.rs similarity index 99% rename from src/config.rs rename to crates/composition-core/src/config.rs index 6a8cf9a..94b3f77 100644 --- a/src/config.rs +++ b/crates/composition-core/src/config.rs @@ -32,7 +32,7 @@ impl Default for Config { max_players: 20, motd: "Hello world!".to_owned(), server_icon: "server-icon.png".to_owned(), - server_icon_bytes: include_bytes!("../server-icon.png").to_vec(), + server_icon_bytes: include_bytes!("./server-icon.png").to_vec(), server_string: server_version.clone(), log_level: if cfg!(debug_assertions) { log::LevelFilter::Debug diff --git a/src/lib.rs b/crates/composition-core/src/lib.rs similarity index 100% rename from src/lib.rs rename to crates/composition-core/src/lib.rs diff --git a/src/net/mctypes/mod.rs b/crates/composition-core/src/net/mctypes/mod.rs similarity index 100% rename from src/net/mctypes/mod.rs rename to crates/composition-core/src/net/mctypes/mod.rs diff --git a/src/net/mctypes/numbers.rs b/crates/composition-core/src/net/mctypes/numbers.rs similarity index 100% rename from src/net/mctypes/numbers.rs rename to crates/composition-core/src/net/mctypes/numbers.rs diff --git a/src/net/mctypes/other.rs b/crates/composition-core/src/net/mctypes/other.rs similarity index 100% rename from src/net/mctypes/other.rs rename to crates/composition-core/src/net/mctypes/other.rs diff --git a/src/net/mod.rs b/crates/composition-core/src/net/mod.rs similarity index 100% rename from src/net/mod.rs rename to crates/composition-core/src/net/mod.rs diff --git a/src/net/packets.rs b/crates/composition-core/src/net/packets.rs similarity index 100% rename from src/net/packets.rs rename to crates/composition-core/src/net/packets.rs diff --git a/crates/composition-core/src/server-icon.png b/crates/composition-core/src/server-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..4ef7bdb6a4e2643c908c1f757414981753d890e9 GIT binary patch literal 11088 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 W@q7V{-&J-10000 anyhow::Result<()> { + let listener = TcpListener::bind("0.0.0.0:25566").await?; + + println!("Proxy listening on port 25566, expecting server at 25565"); + + loop { + let (stream, _) = listener.accept().await?; + process_client(stream).await?; + } +} + +async fn process_client(client: TcpStream) -> anyhow::Result<()> { + use composition_protocol::packet::GenericPacket; + client.readable().await?; + println!("Client connected"); + let server = TcpStream::connect("localhost:25565").await?; + server.writable().await?; + println!("Server connected"); + + let mut client_state = composition_protocol::ClientState::Handshake; + let mut last_client_data_time = std::time::Instant::now(); + let mut last_client_data = vec![]; + let mut last_server_data_time = std::time::Instant::now(); + let mut last_server_data = vec![]; + + loop { + let bytes = copy_bytes(&client, &server).await?; + if !bytes.is_empty() { + last_client_data_time = std::time::Instant::now(); + last_client_data.extend_from_slice(&bytes); + + if let Ok((d, packet)) = + GenericPacket::parse_uncompressed(&client_state, true, &last_client_data) + { + last_client_data = d.to_vec(); + println!("C -> S: {:?}", packet); + match packet { + GenericPacket::SH00Handshake(handshake) => { + client_state = handshake.next_state; + } + GenericPacket::CP17Disconnect(_) => { + break; + } + _ => {} + } + } + } + + let bytes = copy_bytes(&server, &client).await?; + if !bytes.is_empty() { + last_server_data_time = std::time::Instant::now(); + last_server_data.extend_from_slice(&bytes); + + if let Ok((d, packet)) = + GenericPacket::parse_uncompressed(&client_state, false, &last_server_data) + { + last_server_data = d.to_vec(); + println!("S -> C: {:?}", packet); + match packet { + GenericPacket::CS01PingResponse(_) | GenericPacket::CL00Disconnect(_) => { + break; + } + GenericPacket::CL02LoginSuccess(_) => { + client_state = composition_protocol::ClientState::Play; + } + _ => {} + } + } + } + + if last_client_data_time.elapsed() > std::time::Duration::from_secs(10) + || last_server_data_time.elapsed() > std::time::Duration::from_secs(10) + { + println!("timed out"); + break; + } + } + + Ok(()) +} + +async fn copy_bytes(from: &TcpStream, to: &TcpStream) -> anyhow::Result> { + let mut bytes = vec![]; + + loop { + // Read 8kb at a time + let mut buf = vec![0u8; 8192]; + + let num_bytes = match from.try_read(&mut buf) { + Ok(0) => break, + Ok(n) => n, + Err(ref e) if e.kind() == tokio::io::ErrorKind::WouldBlock => { + break; + } + Err(e) => { + return Err(e.into()); + } + }; + + bytes.extend_from_slice(&buf[0..num_bytes]); + + match to.try_write(&buf[0..num_bytes]) { + Ok(_n) => {} + Err(ref e) if e.kind() == tokio::io::ErrorKind::WouldBlock => { + break; + } + Err(e) => { + return Err(e.into()); + } + } + } + + Ok(bytes) +} diff --git a/crates/composition-protocol/src/lib.rs b/crates/composition-protocol/src/lib.rs new file mode 100644 index 0000000..ed3dab4 --- /dev/null +++ b/crates/composition-protocol/src/lib.rs @@ -0,0 +1,56 @@ +pub mod packet; +pub mod util; + +use thiserror::Error; + +pub type Json = serde_json::Value; +pub type Chat = Json; +pub type Uuid = u128; + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum ClientState { + Handshake, + Status, + Login, + Play, + Disconnected, +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum Difficulty { + Peaceful = 0, + Easy = 1, + Normal = 2, + Hard = 3, +} +impl TryFrom for Difficulty { + type Error = (); + fn try_from(value: u8) -> std::result::Result { + match value { + 0 => Ok(Difficulty::Peaceful), + 1 => Ok(Difficulty::Easy), + 2 => Ok(Difficulty::Normal), + 3 => Ok(Difficulty::Hard), + _ => Err(()), + } + } +} + +#[derive(Error, Debug)] +pub enum ProtocolError { + #[error("invalid packet data")] + InvalidPacket, + #[error("not enough data")] + NotEnoughData, + #[error("unexpected end of file")] + UnexpectedEOF, + #[error("stream timed out")] + Timeout, + #[error("communicating to disconnected client")] + Disconnected, + #[error(transparent)] + JsonError(#[from] serde_json::Error), + #[error(transparent)] + Other(#[from] anyhow::Error), +} +pub type Result = std::result::Result; diff --git a/crates/composition-protocol/src/packet/clientbound/login.rs b/crates/composition-protocol/src/packet/clientbound/login.rs new file mode 100644 index 0000000..2a0a533 --- /dev/null +++ b/crates/composition-protocol/src/packet/clientbound/login.rs @@ -0,0 +1,308 @@ +use crate::{ + packet::{GenericPacket, Packet, PacketId}, + util::{ + parse_json, parse_string, parse_uuid, parse_varint, serialize_json, serialize_string, + serialize_uuid, serialize_varint, + }, + Chat, Uuid, +}; + +#[derive(Clone, Debug, PartialEq)] +pub struct CL00Disconnect { + reason: Chat, +} +impl Packet for CL00Disconnect { + fn id() -> PacketId { + 0x00 + } + fn client_state() -> crate::ClientState { + crate::ClientState::Login + } + fn serverbound() -> bool { + false + } + + fn parse_body(data: &[u8]) -> nom::IResult<&[u8], Self> { + let (data, reason) = parse_json(data)?; + Ok((data, CL00Disconnect { reason })) + } + fn serialize_body(&self) -> Vec { + serialize_json(&self.reason) + } +} +impl From for GenericPacket { + fn from(value: CL00Disconnect) -> Self { + GenericPacket::CL00Disconnect(value) + } +} +impl TryFrom for CL00Disconnect { + type Error = (); + + fn try_from(value: GenericPacket) -> Result { + match value { + GenericPacket::CL00Disconnect(packet) => Ok(packet), + _ => Err(()), + } + } +} + +#[derive(Clone, Debug, PartialEq)] +pub struct CL01EncryptionRequest { + server_id: String, + public_key: Vec, + verify_token: Vec, +} +impl Packet for CL01EncryptionRequest { + fn id() -> PacketId { + 0x01 + } + fn client_state() -> crate::ClientState { + crate::ClientState::Login + } + fn serverbound() -> bool { + false + } + + fn parse_body(data: &[u8]) -> nom::IResult<&[u8], Self> { + let (data, server_id) = parse_string(data)?; + let (data, public_key_len) = parse_varint(data)?; + let (data, public_key) = nom::bytes::streaming::take(public_key_len as usize)(data)?; + let (data, verify_token_len) = parse_varint(data)?; + let (data, verify_token) = nom::bytes::streaming::take(verify_token_len as usize)(data)?; + + Ok(( + data, + CL01EncryptionRequest { + server_id, + public_key: public_key.to_vec(), + verify_token: verify_token.to_vec(), + }, + )) + } + fn serialize_body(&self) -> Vec { + let mut output = vec![]; + output.extend_from_slice(&serialize_string(&self.server_id)); + output.extend_from_slice(&serialize_varint(self.public_key.len() as i32)); + output.extend_from_slice(&self.public_key); + output.extend_from_slice(&serialize_varint(self.verify_token.len() as i32)); + output.extend_from_slice(&self.verify_token); + output + } +} +impl From for GenericPacket { + fn from(value: CL01EncryptionRequest) -> Self { + GenericPacket::CL01EncryptionRequest(value) + } +} +impl TryFrom for CL01EncryptionRequest { + type Error = (); + + fn try_from(value: GenericPacket) -> Result { + match value { + GenericPacket::CL01EncryptionRequest(packet) => Ok(packet), + _ => Err(()), + } + } +} + +#[derive(Clone, Debug, PartialEq)] +pub struct CL02LoginSuccess { + uuid: Uuid, + username: String, + properties: Vec, +} +#[derive(Clone, Debug, PartialEq)] +pub struct CL02LoginSuccessProperty { + name: String, + value: String, + signature: Option, +} +impl CL02LoginSuccessProperty { + pub fn parse(data: &[u8]) -> nom::IResult<&[u8], Self> { + let (data, name) = parse_string(data)?; + let (data, value) = parse_string(data)?; + let (data, is_signed) = nom::bytes::streaming::take(1usize)(data)?; + if is_signed == [0x01] { + let (data, signature) = parse_string(data)?; + Ok(( + data, + CL02LoginSuccessProperty { + name, + value, + signature: Some(signature), + }, + )) + } else { + Ok(( + data, + CL02LoginSuccessProperty { + name, + value, + signature: None, + }, + )) + } + } + pub fn serialize(&self) -> Vec { + let mut output = vec![]; + output.extend_from_slice(&serialize_string(&self.name)); + output.extend_from_slice(&serialize_string(&self.value)); + match &self.signature { + Some(signature) => { + output.push(0x01); + output.extend_from_slice(&serialize_string(signature)); + } + None => output.push(0x00), + } + output + } +} +impl Packet for CL02LoginSuccess { + fn id() -> PacketId { + 0x02 + } + fn client_state() -> crate::ClientState { + crate::ClientState::Login + } + fn serverbound() -> bool { + false + } + + fn parse_body(data: &[u8]) -> nom::IResult<&[u8], Self> { + let (data, uuid) = parse_uuid(data)?; + let (data, username) = parse_string(data)?; + let (mut data, properties_len) = parse_varint(data)?; + let mut properties = Vec::with_capacity(properties_len as usize); + for _ in 0..properties_len { + let (d, property) = CL02LoginSuccessProperty::parse(data)?; + data = d; + properties.push(property); + } + + Ok(( + data, + CL02LoginSuccess { + uuid, + username, + properties, + }, + )) + } + fn serialize_body(&self) -> Vec { + let mut output = vec![]; + output.extend_from_slice(&serialize_uuid(&self.uuid)); + output.extend_from_slice(&serialize_string(&self.username)); + output.extend_from_slice(&serialize_varint(self.properties.len() as i32)); + for property in &self.properties { + output.extend_from_slice(&property.serialize()); + } + output + } +} +impl From for GenericPacket { + fn from(value: CL02LoginSuccess) -> Self { + GenericPacket::CL02LoginSuccess(value) + } +} +impl TryFrom for CL02LoginSuccess { + type Error = (); + + fn try_from(value: GenericPacket) -> Result { + match value { + GenericPacket::CL02LoginSuccess(packet) => Ok(packet), + _ => Err(()), + } + } +} + +#[derive(Clone, Debug, PartialEq)] +pub struct CL03SetCompression { + threshold: i32, +} +impl Packet for CL03SetCompression { + fn id() -> PacketId { + 0x03 + } + fn client_state() -> crate::ClientState { + crate::ClientState::Login + } + fn serverbound() -> bool { + false + } + + fn parse_body(data: &[u8]) -> nom::IResult<&[u8], Self> { + let (data, threshold) = parse_varint(data)?; + Ok((data, CL03SetCompression { threshold })) + } + fn serialize_body(&self) -> Vec { + serialize_varint(self.threshold) + } +} +impl From for GenericPacket { + fn from(value: CL03SetCompression) -> Self { + GenericPacket::CL03SetCompression(value) + } +} +impl TryFrom for CL03SetCompression { + type Error = (); + + fn try_from(value: GenericPacket) -> Result { + match value { + GenericPacket::CL03SetCompression(packet) => Ok(packet), + _ => Err(()), + } + } +} + +#[derive(Clone, Debug, PartialEq)] +pub struct CL04LoginPluginRequest { + message_id: i32, + channel: String, + data: Vec, +} +impl Packet for CL04LoginPluginRequest { + fn id() -> PacketId { + 0x04 + } + fn client_state() -> crate::ClientState { + crate::ClientState::Login + } + fn serverbound() -> bool { + false + } + + fn parse_body(data: &[u8]) -> nom::IResult<&[u8], Self> { + let (data, message_id) = parse_varint(data)?; + let (data, channel) = parse_string(data)?; + Ok(( + data, + CL04LoginPluginRequest { + message_id, + channel, + data: data.to_vec(), + }, + )) + } + fn serialize_body(&self) -> Vec { + let mut output = vec![]; + output.extend_from_slice(&serialize_varint(self.message_id)); + output.extend_from_slice(&serialize_string(&self.channel)); + output.extend_from_slice(&self.data); + output + } +} +impl From for GenericPacket { + fn from(value: CL04LoginPluginRequest) -> Self { + GenericPacket::CL04LoginPluginRequest(value) + } +} +impl TryFrom for CL04LoginPluginRequest { + type Error = (); + + fn try_from(value: GenericPacket) -> Result { + match value { + GenericPacket::CL04LoginPluginRequest(packet) => Ok(packet), + _ => Err(()), + } + } +} diff --git a/crates/composition-protocol/src/packet/clientbound/mod.rs b/crates/composition-protocol/src/packet/clientbound/mod.rs new file mode 100644 index 0000000..8aa039a --- /dev/null +++ b/crates/composition-protocol/src/packet/clientbound/mod.rs @@ -0,0 +1,7 @@ +pub mod login; +pub mod play; +pub mod status; + +pub use login::*; +pub use play::*; +pub use status::*; diff --git a/crates/composition-protocol/src/packet/clientbound/play.rs b/crates/composition-protocol/src/packet/clientbound/play.rs new file mode 100644 index 0000000..fbd23c5 --- /dev/null +++ b/crates/composition-protocol/src/packet/clientbound/play.rs @@ -0,0 +1,494 @@ +use crate::{ + packet::{GenericPacket, Packet, PacketId}, + util::{ + parse_json, parse_uuid, parse_varint, serialize_json, serialize_uuid, serialize_varint, + Position, + }, + Chat, Difficulty, Uuid, +}; + +#[derive(Clone, Debug, PartialEq)] +pub struct CP00SpawnEntity { + entity_id: i32, + entity_uuid: Uuid, + kind: i32, + x: f64, + y: f64, + z: f64, + pitch: u8, + yaw: u8, + head_yaw: u8, + data: i32, + velocity_x: i16, + velocity_y: i16, + velocity_z: i16, +} +impl Packet for CP00SpawnEntity { + fn id() -> PacketId { + 0x00 + } + fn client_state() -> crate::ClientState { + crate::ClientState::Play + } + fn serverbound() -> bool { + false + } + + fn parse_body(data: &[u8]) -> nom::IResult<&[u8], Self> { + let (data, entity_id) = parse_varint(data)?; + let (data, entity_uuid) = parse_uuid(data)?; + let (data, kind) = parse_varint(data)?; + let (data, x) = nom::number::streaming::be_f64(data)?; + let (data, y) = nom::number::streaming::be_f64(data)?; + let (data, z) = nom::number::streaming::be_f64(data)?; + let (data, t) = nom::bytes::streaming::take(3usize)(data)?; + let (data, d) = parse_varint(data)?; + let (data, velocity_x) = nom::number::streaming::be_i16(data)?; + let (data, velocity_y) = nom::number::streaming::be_i16(data)?; + let (data, velocity_z) = nom::number::streaming::be_i16(data)?; + Ok(( + data, + CP00SpawnEntity { + entity_id, + entity_uuid, + kind, + x, + y, + z, + pitch: t[0], + yaw: t[1], + head_yaw: t[2], + data: d, + velocity_x, + velocity_y, + velocity_z, + }, + )) + } + fn serialize_body(&self) -> Vec { + let mut output = vec![]; + output.extend_from_slice(&serialize_varint(self.entity_id)); + output.extend_from_slice(&serialize_uuid(&self.entity_uuid)); + output.extend_from_slice(&serialize_varint(self.kind)); + output.extend_from_slice(&self.x.to_be_bytes()); + output.extend_from_slice(&self.y.to_be_bytes()); + output.extend_from_slice(&self.z.to_be_bytes()); + output.push(self.pitch); + output.push(self.yaw); + output.push(self.head_yaw); + output.extend_from_slice(&serialize_varint(self.data)); + output.extend_from_slice(&self.velocity_x.to_be_bytes()); + output.extend_from_slice(&self.velocity_y.to_be_bytes()); + output.extend_from_slice(&self.velocity_z.to_be_bytes()); + output + } +} +impl From for GenericPacket { + fn from(value: CP00SpawnEntity) -> Self { + GenericPacket::CP00SpawnEntity(value) + } +} +impl TryFrom for CP00SpawnEntity { + type Error = (); + + fn try_from(value: GenericPacket) -> Result { + match value { + GenericPacket::CP00SpawnEntity(packet) => Ok(packet), + _ => Err(()), + } + } +} + +#[derive(Clone, Debug, PartialEq)] +pub struct CP0BChangeDifficulty { + difficulty: Difficulty, + is_locked: bool, +} +impl Packet for CP0BChangeDifficulty { + fn id() -> PacketId { + 0x0b + } + fn client_state() -> crate::ClientState { + crate::ClientState::Play + } + fn serverbound() -> bool { + false + } + + fn parse_body(data: &[u8]) -> nom::IResult<&[u8], Self> { + let (data, difficulty) = nom::number::streaming::be_u8(data)?; + let difficulty: Difficulty = difficulty + .try_into() + .expect("TODO: handle incorrect difficulty"); + let (data, is_locked) = nom::number::streaming::be_u8(data)?; + let is_locked = is_locked > 0; + Ok(( + data, + CP0BChangeDifficulty { + difficulty, + is_locked, + }, + )) + } + fn serialize_body(&self) -> Vec { + let mut output = vec![]; + output.push(self.difficulty as u8); + output.push(if self.is_locked { 0x01 } else { 0x00 }); + output + } +} +impl From for GenericPacket { + fn from(value: CP0BChangeDifficulty) -> Self { + GenericPacket::CP0BChangeDifficulty(value) + } +} +impl TryFrom for CP0BChangeDifficulty { + type Error = (); + + fn try_from(value: GenericPacket) -> Result { + match value { + GenericPacket::CP0BChangeDifficulty(packet) => Ok(packet), + _ => Err(()), + } + } +} + +#[derive(Clone, Debug, PartialEq)] +pub struct CP17Disconnect { + reason: Chat, +} +impl Packet for CP17Disconnect { + fn id() -> PacketId { + 0x17 + } + fn client_state() -> crate::ClientState { + crate::ClientState::Play + } + fn serverbound() -> bool { + false + } + + fn parse_body(data: &[u8]) -> nom::IResult<&[u8], Self> { + let (data, reason) = parse_json(data)?; + Ok((data, CP17Disconnect { reason })) + } + fn serialize_body(&self) -> Vec { + serialize_json(&self.reason) + } +} +impl From for GenericPacket { + fn from(value: CP17Disconnect) -> Self { + GenericPacket::CP17Disconnect(value) + } +} +impl TryFrom for CP17Disconnect { + type Error = (); + + fn try_from(value: GenericPacket) -> Result { + match value { + GenericPacket::CP17Disconnect(packet) => Ok(packet), + _ => Err(()), + } + } +} + +#[derive(Copy, Clone, Debug, PartialEq)] +pub struct CP1FKeepAlive { + payload: i64, +} +impl Packet for CP1FKeepAlive { + fn id() -> PacketId { + 0x1f + } + fn client_state() -> crate::ClientState { + crate::ClientState::Play + } + fn serverbound() -> bool { + false + } + + fn parse_body(data: &[u8]) -> nom::IResult<&[u8], Self> { + let (data, payload) = nom::number::streaming::be_i64(data)?; + Ok((data, CP1FKeepAlive { payload })) + } + fn serialize_body(&self) -> Vec { + self.payload.to_be_bytes().to_vec() + } +} +impl From for GenericPacket { + fn from(value: CP1FKeepAlive) -> Self { + GenericPacket::CP1FKeepAlive(value) + } +} +impl TryFrom for CP1FKeepAlive { + type Error = (); + + fn try_from(value: GenericPacket) -> Result { + match value { + GenericPacket::CP1FKeepAlive(packet) => Ok(packet), + _ => Err(()), + } + } +} + +#[derive(Copy, Clone, Debug, PartialEq)] +pub struct CP21WorldEvent { + event: i32, + location: Position, + data: i32, + disable_relative_volume: bool, +} +impl Packet for CP21WorldEvent { + fn id() -> PacketId { + 0x21 + } + fn client_state() -> crate::ClientState { + crate::ClientState::Play + } + fn serverbound() -> bool { + false + } + + fn parse_body(data: &[u8]) -> nom::IResult<&[u8], Self> { + let (data, event) = nom::number::streaming::be_i32(data)?; + let (data, location) = Position::parse(data)?; + let (data, d) = nom::number::streaming::be_i32(data)?; + let (data, disable_relative_volume) = nom::bytes::streaming::take(1usize)(data)?; + let disable_relative_volume = disable_relative_volume == [0x01]; + Ok(( + data, + CP21WorldEvent { + event, + location, + data: d, + disable_relative_volume, + }, + )) + } + fn serialize_body(&self) -> Vec { + let mut output = vec![]; + output.extend_from_slice(&self.event.to_be_bytes()); + output.extend_from_slice(&self.location.serialize()); + output.extend_from_slice(&self.data.to_be_bytes()); + output.push(if self.disable_relative_volume { + 0x01 + } else { + 0x00 + }); + output + } +} +impl From for GenericPacket { + fn from(value: CP21WorldEvent) -> Self { + GenericPacket::CP21WorldEvent(value) + } +} +impl TryFrom for CP21WorldEvent { + type Error = (); + + fn try_from(value: GenericPacket) -> Result { + match value { + GenericPacket::CP21WorldEvent(packet) => Ok(packet), + _ => Err(()), + } + } +} + +#[derive(Clone, Debug, PartialEq)] +pub struct CP50SetEntityVelocity { + entity_id: i32, + velocity_x: i16, + velocity_y: i16, + velocity_z: i16, +} +impl Packet for CP50SetEntityVelocity { + fn id() -> PacketId { + 0x50 + } + fn client_state() -> crate::ClientState { + crate::ClientState::Play + } + fn serverbound() -> bool { + false + } + + fn parse_body(data: &[u8]) -> nom::IResult<&[u8], Self> { + let (data, entity_id) = parse_varint(data)?; + let (data, velocity_x) = nom::number::streaming::be_i16(data)?; + let (data, velocity_y) = nom::number::streaming::be_i16(data)?; + let (data, velocity_z) = nom::number::streaming::be_i16(data)?; + Ok(( + data, + CP50SetEntityVelocity { + entity_id, + velocity_x, + velocity_y, + velocity_z, + }, + )) + } + fn serialize_body(&self) -> Vec { + let mut output = vec![]; + output.extend_from_slice(&serialize_varint(self.entity_id)); + output.extend_from_slice(&self.velocity_x.to_be_bytes()); + output.extend_from_slice(&self.velocity_y.to_be_bytes()); + output.extend_from_slice(&self.velocity_z.to_be_bytes()); + output + } +} +impl From for GenericPacket { + fn from(value: CP50SetEntityVelocity) -> Self { + GenericPacket::CP50SetEntityVelocity(value) + } +} +impl TryFrom for CP50SetEntityVelocity { + type Error = (); + + fn try_from(value: GenericPacket) -> Result { + match value { + GenericPacket::CP50SetEntityVelocity(packet) => Ok(packet), + _ => Err(()), + } + } +} + +#[derive(Clone, Debug, PartialEq)] +pub struct CP52SetExperience { + experience_bar: f32, + total_experience: i32, + level: i32, +} +impl Packet for CP52SetExperience { + fn id() -> PacketId { + 0x52 + } + fn client_state() -> crate::ClientState { + crate::ClientState::Play + } + fn serverbound() -> bool { + false + } + + fn parse_body(data: &[u8]) -> nom::IResult<&[u8], Self> { + let (data, experience_bar) = nom::number::streaming::be_f32(data)?; + let (data, total_experience) = parse_varint(data)?; + let (data, level) = parse_varint(data)?; + Ok(( + data, + CP52SetExperience { + experience_bar, + total_experience, + level, + }, + )) + } + fn serialize_body(&self) -> Vec { + let mut output = vec![]; + output.extend_from_slice(&self.experience_bar.to_be_bytes()); + output.extend_from_slice(&serialize_varint(self.total_experience)); + output.extend_from_slice(&serialize_varint(self.level)); + output + } +} +impl From for GenericPacket { + fn from(value: CP52SetExperience) -> Self { + GenericPacket::CP52SetExperience(value) + } +} +impl TryFrom for CP52SetExperience { + type Error = (); + + fn try_from(value: GenericPacket) -> Result { + match value { + GenericPacket::CP52SetExperience(packet) => Ok(packet), + _ => Err(()), + } + } +} + +#[derive(Clone, Debug, PartialEq)] +pub struct CP68EntityEffect { + entity_id: i32, + effect_id: i32, + amplifier: i8, + duration: i32, + is_ambient: bool, + show_particles: bool, + show_icon: bool, + has_factor_data: bool, + // TODO: factor_codec: NBT +} +impl Packet for CP68EntityEffect { + fn id() -> PacketId { + 0x68 + } + fn client_state() -> crate::ClientState { + crate::ClientState::Play + } + fn serverbound() -> bool { + false + } + + fn parse_body(data: &[u8]) -> nom::IResult<&[u8], Self> { + let (data, entity_id) = parse_varint(data)?; + let (data, effect_id) = parse_varint(data)?; + let (data, amplifier) = nom::number::streaming::be_i8(data)?; + let (data, duration) = parse_varint(data)?; + let (data, flags) = nom::number::streaming::be_i8(data)?; + let is_ambient = flags & 0x01 > 0; + let show_particles = flags & 0x02 > 0; + let show_icon = flags & 0x04 > 0; + let (data, has_factor_data) = nom::number::streaming::be_u8(data)?; + let has_factor_data = has_factor_data > 0; + // TODO: factor_codec + + Ok(( + data, + CP68EntityEffect { + entity_id, + effect_id, + amplifier, + duration, + is_ambient, + show_particles, + show_icon, + has_factor_data, + }, + )) + } + fn serialize_body(&self) -> Vec { + let mut output = vec![]; + output.extend_from_slice(&serialize_varint(self.entity_id)); + output.extend_from_slice(&serialize_varint(self.effect_id)); + output.push(self.amplifier as u8); + output.extend_from_slice(&serialize_varint(self.duration)); + let mut flags = 0x00i8; + if self.is_ambient { + flags |= 0x01; + } + if self.show_particles { + flags |= 0x02; + } + if self.show_icon { + flags |= 0x04; + } + output.push(flags as u8); + // TODO: factor_codec + output + } +} +impl From for GenericPacket { + fn from(value: CP68EntityEffect) -> Self { + GenericPacket::CP68EntityEffect(value) + } +} +impl TryFrom for CP68EntityEffect { + type Error = (); + + fn try_from(value: GenericPacket) -> Result { + match value { + GenericPacket::CP68EntityEffect(packet) => Ok(packet), + _ => Err(()), + } + } +} diff --git a/crates/composition-protocol/src/packet/clientbound/status.rs b/crates/composition-protocol/src/packet/clientbound/status.rs new file mode 100644 index 0000000..257ef7b --- /dev/null +++ b/crates/composition-protocol/src/packet/clientbound/status.rs @@ -0,0 +1,83 @@ +use crate::{ + packet::{GenericPacket, Packet, PacketId}, + util::{parse_json, serialize_json}, + Json, +}; + +#[derive(Clone, Debug, PartialEq)] +pub struct CS00StatusResponse { + response: Json, +} +impl Packet for CS00StatusResponse { + fn id() -> PacketId { + 0x00 + } + fn client_state() -> crate::ClientState { + crate::ClientState::Status + } + fn serverbound() -> bool { + false + } + + fn parse_body(data: &[u8]) -> nom::IResult<&[u8], Self> { + let (data, response) = parse_json(data)?; + Ok((data, CS00StatusResponse { response })) + } + fn serialize_body(&self) -> Vec { + serialize_json(&self.response) + } +} +impl From for GenericPacket { + fn from(value: CS00StatusResponse) -> Self { + GenericPacket::CS00StatusResponse(value) + } +} +impl TryFrom for CS00StatusResponse { + type Error = (); + + fn try_from(value: GenericPacket) -> Result { + match value { + GenericPacket::CS00StatusResponse(packet) => Ok(packet), + _ => Err(()), + } + } +} + +#[derive(Copy, Clone, Debug, PartialEq)] +pub struct CS01PingResponse { + payload: i64, +} +impl Packet for CS01PingResponse { + fn id() -> PacketId { + 0x01 + } + fn client_state() -> crate::ClientState { + crate::ClientState::Status + } + fn serverbound() -> bool { + false + } + + fn parse_body(data: &[u8]) -> nom::IResult<&[u8], Self> { + let (data, payload) = nom::number::streaming::be_i64(data)?; + Ok((data, CS01PingResponse { payload })) + } + fn serialize_body(&self) -> Vec { + self.payload.to_be_bytes().to_vec() + } +} +impl From for GenericPacket { + fn from(value: CS01PingResponse) -> Self { + GenericPacket::CS01PingResponse(value) + } +} +impl TryFrom for CS01PingResponse { + type Error = (); + + fn try_from(value: GenericPacket) -> Result { + match value { + GenericPacket::CS01PingResponse(packet) => Ok(packet), + _ => Err(()), + } + } +} diff --git a/crates/composition-protocol/src/packet/mod.rs b/crates/composition-protocol/src/packet/mod.rs new file mode 100644 index 0000000..897ec1d --- /dev/null +++ b/crates/composition-protocol/src/packet/mod.rs @@ -0,0 +1,288 @@ +pub mod clientbound; +pub mod serverbound; + +use crate::ClientState; + +pub type PacketId = i32; + +pub trait Packet: TryFrom + Into + std::fmt::Debug { + fn id() -> PacketId; + fn client_state() -> crate::ClientState; + fn serverbound() -> bool; + + // The slice given should only be the exact amount of data in the body. + fn parse_body(data: &[u8]) -> nom::IResult<&[u8], Self> + where + Self: Sized; + fn serialize_body(&self) -> Vec; +} + +#[derive(Clone, Debug, PartialEq)] +pub enum GenericPacket { + // Handshake + SH00Handshake(serverbound::SH00Handshake), + + // Status + SS00StatusRequest(serverbound::SS00StatusRequest), + SS01PingRequest(serverbound::SS01PingRequest), + + CS00StatusResponse(clientbound::CS00StatusResponse), + CS01PingResponse(clientbound::CS01PingResponse), + + // Login + SL00LoginStart(serverbound::SL00LoginStart), + SL01EncryptionResponse(serverbound::SL01EncryptionResponse), + SL02LoginPluginResponse(serverbound::SL02LoginPluginResponse), + + CL00Disconnect(clientbound::CL00Disconnect), + CL01EncryptionRequest(clientbound::CL01EncryptionRequest), + CL02LoginSuccess(clientbound::CL02LoginSuccess), + CL03SetCompression(clientbound::CL03SetCompression), + CL04LoginPluginRequest(clientbound::CL04LoginPluginRequest), + + // Play + SP08CommandSuggestionsRequest(serverbound::SP08CommandSuggestionsRequest), + SP11KeepAlive(serverbound::SP11KeepAlive), + SP13SetPlayerPosition(serverbound::SP13SetPlayerPosition), + SP14SetPlayerPositionAndRotation(serverbound::SP14SetPlayerPositionAndRotation), + SP15SetPlayerRotation(serverbound::SP15SetPlayerRotation), + + CP00SpawnEntity(clientbound::CP00SpawnEntity), + CP0BChangeDifficulty(clientbound::CP0BChangeDifficulty), + CP17Disconnect(clientbound::CP17Disconnect), + CP1FKeepAlive(clientbound::CP1FKeepAlive), + CP21WorldEvent(clientbound::CP21WorldEvent), + CP50SetEntityVelocity(clientbound::CP50SetEntityVelocity), + CP52SetExperience(clientbound::CP52SetExperience), + CP68EntityEffect(clientbound::CP68EntityEffect), + + // Until we implement all the packets this will stay. + UnimplementedPacket(PacketId), +} +impl GenericPacket { + pub fn parse_uncompressed<'data>( + client_state: &ClientState, + serverbound: bool, + data: &'data [u8], + ) -> nom::IResult<&'data [u8], Self> { + let (data, packet_length) = crate::util::parse_varint(data)?; + let (data, packet_data) = nom::bytes::streaming::take(packet_length as usize)(data)?; + + let (packet_data, packet_id) = crate::util::parse_varint(packet_data)?; + let (_packet_data, packet_body) = + Self::parse_body(client_state, packet_id, serverbound, packet_data)?; + + // if !packet_data.is_empty() { + // println!("Packet data not empty after parsing!"); + // } + + Ok((data, packet_body)) + } + pub fn parse_body<'data>( + client_state: &ClientState, + packet_id: PacketId, + serverbound: bool, + data: &'data [u8], + ) -> nom::IResult<&'data [u8], Self> { + fn mapper + Sized>( + (data, packet): (&[u8], P), + ) -> (&[u8], GenericPacket) { + (data, Into::::into(packet)) + } + + match (client_state, packet_id, serverbound) { + // Handshake + (ClientState::Handshake, 0x00, true) => { + serverbound::SH00Handshake::parse_body(data).map(mapper) + } + + // Status + (ClientState::Status, 0x00, true) => { + serverbound::SS00StatusRequest::parse_body(data).map(mapper) + } + (ClientState::Status, 0x01, true) => { + serverbound::SS00StatusRequest::parse_body(data).map(mapper) + } + + (ClientState::Status, 0x00, false) => { + clientbound::CS00StatusResponse::parse_body(data).map(mapper) + } + (ClientState::Status, 0x01, false) => { + clientbound::CS01PingResponse::parse_body(data).map(mapper) + } + + // Login + (ClientState::Login, 0x00, true) => { + serverbound::SL00LoginStart::parse_body(data).map(mapper) + } + (ClientState::Login, 0x01, true) => { + serverbound::SL01EncryptionResponse::parse_body(data).map(mapper) + } + (ClientState::Login, 0x02, true) => { + serverbound::SL02LoginPluginResponse::parse_body(data).map(mapper) + } + + (ClientState::Login, 0x00, false) => { + clientbound::CL00Disconnect::parse_body(data).map(mapper) + } + (ClientState::Login, 0x01, false) => { + clientbound::CL01EncryptionRequest::parse_body(data).map(mapper) + } + (ClientState::Login, 0x02, false) => { + clientbound::CL02LoginSuccess::parse_body(data).map(mapper) + } + (ClientState::Login, 0x03, false) => { + clientbound::CL03SetCompression::parse_body(data).map(mapper) + } + (ClientState::Login, 0x04, false) => { + clientbound::CL04LoginPluginRequest::parse_body(data).map(mapper) + } + + // Play + (ClientState::Play, 0x08, true) => { + serverbound::SP08CommandSuggestionsRequest::parse_body(data).map(mapper) + } + (ClientState::Play, 0x11, true) => { + serverbound::SP11KeepAlive::parse_body(data).map(mapper) + } + (ClientState::Play, 0x13, true) => { + serverbound::SP13SetPlayerPosition::parse_body(data).map(mapper) + } + (ClientState::Play, 0x14, true) => { + serverbound::SP14SetPlayerPositionAndRotation::parse_body(data).map(mapper) + } + (ClientState::Play, 0x15, true) => { + serverbound::SP15SetPlayerRotation::parse_body(data).map(mapper) + } + + (ClientState::Play, 0x00, false) => { + clientbound::CP00SpawnEntity::parse_body(data).map(mapper) + } + (ClientState::Play, 0x0b, false) => { + clientbound::CP0BChangeDifficulty::parse_body(data).map(mapper) + } + (ClientState::Play, 0x17, false) => { + clientbound::CP17Disconnect::parse_body(data).map(mapper) + } + (ClientState::Play, 0x1f, false) => { + clientbound::CP1FKeepAlive::parse_body(data).map(mapper) + } + (ClientState::Play, 0x21, false) => { + clientbound::CP21WorldEvent::parse_body(data).map(mapper) + } + (ClientState::Play, 0x50, false) => { + clientbound::CP50SetEntityVelocity::parse_body(data).map(mapper) + } + (ClientState::Play, 0x52, false) => { + clientbound::CP52SetExperience::parse_body(data).map(mapper) + } + (ClientState::Play, 0x68, false) => { + clientbound::CP68EntityEffect::parse_body(data).map(mapper) + } + + _ => Ok((&data[0..0], GenericPacket::UnimplementedPacket(packet_id))), + // Invalid packet + // _ => Err(nom::Err::Failure(nom::error::Error::new( + // data, + // nom::error::ErrorKind::Verify, + // ))), + } + } + pub fn serialize(&self) -> (PacketId, Vec) { + use GenericPacket::*; + + match self { + // Handshake + SH00Handshake(packet) => (serverbound::SH00Handshake::id(), packet.serialize_body()), + + // Status + SS00StatusRequest(packet) => ( + serverbound::SS00StatusRequest::id(), + packet.serialize_body(), + ), + SS01PingRequest(packet) => { + (serverbound::SS01PingRequest::id(), packet.serialize_body()) + } + + CS00StatusResponse(packet) => ( + clientbound::CS00StatusResponse::id(), + packet.serialize_body(), + ), + CS01PingResponse(packet) => { + (clientbound::CS01PingResponse::id(), packet.serialize_body()) + } + + // Login + SL00LoginStart(packet) => (serverbound::SL00LoginStart::id(), packet.serialize_body()), + SL01EncryptionResponse(packet) => ( + serverbound::SL01EncryptionResponse::id(), + packet.serialize_body(), + ), + SL02LoginPluginResponse(packet) => ( + serverbound::SL02LoginPluginResponse::id(), + packet.serialize_body(), + ), + + CL00Disconnect(packet) => (clientbound::CL00Disconnect::id(), packet.serialize_body()), + CL01EncryptionRequest(packet) => ( + clientbound::CL01EncryptionRequest::id(), + packet.serialize_body(), + ), + CL02LoginSuccess(packet) => { + (clientbound::CL02LoginSuccess::id(), packet.serialize_body()) + } + CL03SetCompression(packet) => ( + clientbound::CL03SetCompression::id(), + packet.serialize_body(), + ), + CL04LoginPluginRequest(packet) => ( + clientbound::CL04LoginPluginRequest::id(), + packet.serialize_body(), + ), + + // Play + SP08CommandSuggestionsRequest(packet) => ( + serverbound::SP08CommandSuggestionsRequest::id(), + packet.serialize_body(), + ), + SP11KeepAlive(packet) => (serverbound::SP11KeepAlive::id(), packet.serialize_body()), + SP13SetPlayerPosition(packet) => ( + serverbound::SP13SetPlayerPosition::id(), + packet.serialize_body(), + ), + SP14SetPlayerPositionAndRotation(packet) => ( + serverbound::SP14SetPlayerPositionAndRotation::id(), + packet.serialize_body(), + ), + SP15SetPlayerRotation(packet) => ( + serverbound::SP15SetPlayerRotation::id(), + packet.serialize_body(), + ), + + CP00SpawnEntity(packet) => { + (clientbound::CP00SpawnEntity::id(), packet.serialize_body()) + } + CP0BChangeDifficulty(packet) => ( + clientbound::CP0BChangeDifficulty::id(), + packet.serialize_body(), + ), + CP17Disconnect(packet) => (clientbound::CP17Disconnect::id(), packet.serialize_body()), + CP1FKeepAlive(packet) => (clientbound::CP1FKeepAlive::id(), packet.serialize_body()), + CP21WorldEvent(packet) => (clientbound::CP21WorldEvent::id(), packet.serialize_body()), + CP50SetEntityVelocity(packet) => ( + clientbound::CP50SetEntityVelocity::id(), + packet.serialize_body(), + ), + CP52SetExperience(packet) => ( + clientbound::CP52SetExperience::id(), + packet.serialize_body(), + ), + CP68EntityEffect(packet) => { + (clientbound::CP68EntityEffect::id(), packet.serialize_body()) + } + + // Unimplemented packets get no body. + UnimplementedPacket(packet_id) => (*packet_id, vec![]), + } + } +} diff --git a/crates/composition-protocol/src/packet/serverbound/handshake.rs b/crates/composition-protocol/src/packet/serverbound/handshake.rs new file mode 100644 index 0000000..f3ccbb8 --- /dev/null +++ b/crates/composition-protocol/src/packet/serverbound/handshake.rs @@ -0,0 +1,72 @@ +use crate::{ + packet::{GenericPacket, Packet, PacketId}, + util::{parse_string, parse_varint, serialize_string, serialize_varint}, + ClientState, +}; + +#[derive(Clone, Debug, PartialEq)] +pub struct SH00Handshake { + pub protocol_version: i32, + pub server_address: String, + pub server_port: u16, + pub next_state: ClientState, +} +impl Packet for SH00Handshake { + fn id() -> PacketId { + 0x00 + } + fn client_state() -> crate::ClientState { + crate::ClientState::Handshake + } + fn serverbound() -> bool { + true + } + + fn parse_body(data: &[u8]) -> nom::IResult<&[u8], Self> { + let (data, protocol_version) = parse_varint(data)?; + let (data, server_address) = parse_string(data)?; + let (data, server_port) = nom::number::streaming::be_u16(data)?; + let (data, next_state) = parse_varint(data)?; + + Ok(( + data, + SH00Handshake { + protocol_version, + server_address, + server_port, + next_state: match next_state { + 1 => ClientState::Status, + 2 => ClientState::Login, + _ => todo!("Invalid next state"), + }, + }, + )) + } + fn serialize_body(&self) -> Vec { + let mut output = vec![]; + output.extend_from_slice(&self.protocol_version.to_be_bytes()); + output.extend_from_slice(&serialize_string(&self.server_address)); + output.extend_from_slice(&self.server_port.to_be_bytes()); + output.extend_from_slice(&serialize_varint(match self.next_state { + ClientState::Status => 0x01, + ClientState::Login => 0x02, + _ => panic!("invalid SH00Handshake next_state"), + })); + output + } +} +impl From for GenericPacket { + fn from(value: SH00Handshake) -> Self { + GenericPacket::SH00Handshake(value) + } +} +impl TryFrom for SH00Handshake { + type Error = (); + + fn try_from(value: GenericPacket) -> Result { + match value { + GenericPacket::SH00Handshake(packet) => Ok(packet), + _ => Err(()), + } + } +} diff --git a/crates/composition-protocol/src/packet/serverbound/login.rs b/crates/composition-protocol/src/packet/serverbound/login.rs new file mode 100644 index 0000000..85c786d --- /dev/null +++ b/crates/composition-protocol/src/packet/serverbound/login.rs @@ -0,0 +1,184 @@ +use crate::{ + packet::{GenericPacket, Packet, PacketId}, + util::{ + parse_string, parse_uuid, parse_varint, serialize_string, serialize_uuid, serialize_varint, + }, + Uuid, +}; + +#[derive(Clone, Debug, PartialEq)] +pub struct SL00LoginStart { + name: String, + uuid: Option, +} +impl Packet for SL00LoginStart { + fn id() -> PacketId { + 0x00 + } + fn client_state() -> crate::ClientState { + crate::ClientState::Login + } + fn serverbound() -> bool { + true + } + + fn parse_body(data: &[u8]) -> nom::IResult<&[u8], Self> { + let (data, name) = parse_string(data)?; + let (data, has_uuid) = nom::bytes::streaming::take(1usize)(data)?; + if has_uuid == [0x01] { + let (data, uuid) = parse_uuid(data)?; + Ok(( + data, + SL00LoginStart { + name, + uuid: Some(uuid), + }, + )) + } else { + Ok((data, SL00LoginStart { name, uuid: None })) + } + } + fn serialize_body(&self) -> Vec { + let mut output = vec![]; + output.extend_from_slice(&serialize_string(&self.name)); + match self.uuid { + Some(uuid) => { + output.push(0x01); + output.extend_from_slice(&serialize_uuid(&uuid)); + } + None => output.push(0x00), + } + output + } +} +impl From for GenericPacket { + fn from(value: SL00LoginStart) -> Self { + GenericPacket::SL00LoginStart(value) + } +} +impl TryFrom for SL00LoginStart { + type Error = (); + + fn try_from(value: GenericPacket) -> Result { + match value { + GenericPacket::SL00LoginStart(packet) => Ok(packet), + _ => Err(()), + } + } +} + +#[derive(Clone, Debug, PartialEq)] +pub struct SL01EncryptionResponse { + shared_secret: Vec, + verify_token: Vec, +} +impl Packet for SL01EncryptionResponse { + fn id() -> PacketId { + 0x01 + } + fn client_state() -> crate::ClientState { + crate::ClientState::Login + } + fn serverbound() -> bool { + true + } + + fn parse_body(data: &[u8]) -> nom::IResult<&[u8], Self> { + let (data, shared_secret_len) = parse_varint(data)?; + let (data, shared_secret) = nom::bytes::streaming::take(shared_secret_len as usize)(data)?; + let (data, verify_token_len) = parse_varint(data)?; + let (data, verify_token) = nom::bytes::streaming::take(verify_token_len as usize)(data)?; + + Ok(( + data, + SL01EncryptionResponse { + shared_secret: shared_secret.to_vec(), + verify_token: verify_token.to_vec(), + }, + )) + } + fn serialize_body(&self) -> Vec { + let mut output = vec![]; + output.extend_from_slice(&serialize_varint(self.shared_secret.len() as i32)); + output.extend_from_slice(&self.shared_secret); + output.extend_from_slice(&serialize_varint(self.verify_token.len() as i32)); + output.extend_from_slice(&self.verify_token); + output + } +} +impl From for GenericPacket { + fn from(value: SL01EncryptionResponse) -> Self { + GenericPacket::SL01EncryptionResponse(value) + } +} +impl TryFrom for SL01EncryptionResponse { + type Error = (); + + fn try_from(value: GenericPacket) -> Result { + match value { + GenericPacket::SL01EncryptionResponse(packet) => Ok(packet), + _ => Err(()), + } + } +} + +#[derive(Clone, Debug, PartialEq)] +pub struct SL02LoginPluginResponse { + message_id: i32, + successful: bool, + data: Vec, +} +impl Packet for SL02LoginPluginResponse { + fn id() -> PacketId { + 0x02 + } + fn client_state() -> crate::ClientState { + crate::ClientState::Login + } + fn serverbound() -> bool { + true + } + + fn parse_body(data: &[u8]) -> nom::IResult<&[u8], Self> { + let (data, message_id) = parse_varint(data)?; + let (data, successful) = nom::bytes::streaming::take(1usize)(data)?; + let successful = successful == [0x01]; + Ok(( + data, + SL02LoginPluginResponse { + message_id, + successful, + data: match successful { + true => data.to_vec(), + false => vec![], + }, + }, + )) + } + fn serialize_body(&self) -> Vec { + let mut output = vec![]; + output.extend_from_slice(&serialize_varint(self.message_id)); + if self.successful { + output.push(0x01); + output.extend_from_slice(&self.data); + } else { + output.push(0x00); + } + output + } +} +impl From for GenericPacket { + fn from(value: SL02LoginPluginResponse) -> Self { + GenericPacket::SL02LoginPluginResponse(value) + } +} +impl TryFrom for SL02LoginPluginResponse { + type Error = (); + + fn try_from(value: GenericPacket) -> Result { + match value { + GenericPacket::SL02LoginPluginResponse(packet) => Ok(packet), + _ => Err(()), + } + } +} diff --git a/crates/composition-protocol/src/packet/serverbound/mod.rs b/crates/composition-protocol/src/packet/serverbound/mod.rs new file mode 100644 index 0000000..bc1f6df --- /dev/null +++ b/crates/composition-protocol/src/packet/serverbound/mod.rs @@ -0,0 +1,9 @@ +pub mod handshake; +pub mod login; +pub mod play; +pub mod status; + +pub use handshake::*; +pub use login::*; +pub use play::*; +pub use status::*; diff --git a/crates/composition-protocol/src/packet/serverbound/play.rs b/crates/composition-protocol/src/packet/serverbound/play.rs new file mode 100644 index 0000000..defc588 --- /dev/null +++ b/crates/composition-protocol/src/packet/serverbound/play.rs @@ -0,0 +1,266 @@ +use crate::{ + packet::{GenericPacket, Packet, PacketId}, + util::{parse_string, parse_varint, serialize_string, serialize_varint}, +}; + +#[derive(Clone, Debug, PartialEq)] +pub struct SP08CommandSuggestionsRequest { + transaction_id: i32, + text: String, +} +impl Packet for SP08CommandSuggestionsRequest { + fn id() -> PacketId { + 0x08 + } + fn client_state() -> crate::ClientState { + crate::ClientState::Play + } + fn serverbound() -> bool { + true + } + + fn parse_body(data: &[u8]) -> nom::IResult<&[u8], Self> { + let (data, transaction_id) = parse_varint(data)?; + let (data, text) = parse_string(data)?; + Ok(( + data, + SP08CommandSuggestionsRequest { + transaction_id, + text, + }, + )) + } + fn serialize_body(&self) -> Vec { + let mut output = vec![]; + output.extend_from_slice(&serialize_varint(self.transaction_id)); + output.extend_from_slice(&serialize_string(&self.text)); + output + } +} +impl From for GenericPacket { + fn from(value: SP08CommandSuggestionsRequest) -> Self { + GenericPacket::SP08CommandSuggestionsRequest(value) + } +} +impl TryFrom for SP08CommandSuggestionsRequest { + type Error = (); + + fn try_from(value: GenericPacket) -> Result { + match value { + GenericPacket::SP08CommandSuggestionsRequest(packet) => Ok(packet), + _ => Err(()), + } + } +} + +#[derive(Copy, Clone, Debug, PartialEq)] +pub struct SP11KeepAlive { + payload: i64, +} +impl Packet for SP11KeepAlive { + fn id() -> PacketId { + 0x11 + } + fn client_state() -> crate::ClientState { + crate::ClientState::Play + } + fn serverbound() -> bool { + true + } + + fn parse_body(data: &[u8]) -> nom::IResult<&[u8], Self> { + let (data, payload) = nom::number::streaming::be_i64(data)?; + Ok((data, SP11KeepAlive { payload })) + } + fn serialize_body(&self) -> Vec { + self.payload.to_be_bytes().to_vec() + } +} +impl From for GenericPacket { + fn from(value: SP11KeepAlive) -> Self { + GenericPacket::SP11KeepAlive(value) + } +} +impl TryFrom for SP11KeepAlive { + type Error = (); + + fn try_from(value: GenericPacket) -> Result { + match value { + GenericPacket::SP11KeepAlive(packet) => Ok(packet), + _ => Err(()), + } + } +} + +#[derive(Copy, Clone, Debug, PartialEq)] +pub struct SP13SetPlayerPosition { + x: f64, + y: f64, + z: f64, + on_ground: bool, +} +impl Packet for SP13SetPlayerPosition { + fn id() -> PacketId { + 0x13 + } + fn client_state() -> crate::ClientState { + crate::ClientState::Play + } + fn serverbound() -> bool { + true + } + + fn parse_body(data: &[u8]) -> nom::IResult<&[u8], Self> { + let (data, x) = nom::number::streaming::be_f64(data)?; + let (data, y) = nom::number::streaming::be_f64(data)?; + let (data, z) = nom::number::streaming::be_f64(data)?; + let (data, on_ground) = nom::bytes::streaming::take(1usize)(data)?; + let on_ground = on_ground == [0x01]; + Ok((data, SP13SetPlayerPosition { x, y, z, on_ground })) + } + fn serialize_body(&self) -> Vec { + let mut output = vec![]; + output.extend_from_slice(&self.x.to_be_bytes()); + output.extend_from_slice(&self.y.to_be_bytes()); + output.extend_from_slice(&self.z.to_be_bytes()); + output.push(if self.on_ground { 0x01 } else { 0x00 }); + output + } +} +impl From for GenericPacket { + fn from(value: SP13SetPlayerPosition) -> Self { + GenericPacket::SP13SetPlayerPosition(value) + } +} +impl TryFrom for SP13SetPlayerPosition { + type Error = (); + + fn try_from(value: GenericPacket) -> Result { + match value { + GenericPacket::SP13SetPlayerPosition(packet) => Ok(packet), + _ => Err(()), + } + } +} + +#[derive(Copy, Clone, Debug, PartialEq)] +pub struct SP14SetPlayerPositionAndRotation { + x: f64, + y: f64, + z: f64, + yaw: f32, + pitch: f32, + on_ground: bool, +} +impl Packet for SP14SetPlayerPositionAndRotation { + fn id() -> PacketId { + 0x14 + } + fn client_state() -> crate::ClientState { + crate::ClientState::Play + } + fn serverbound() -> bool { + true + } + + fn parse_body(data: &[u8]) -> nom::IResult<&[u8], Self> { + let (data, x) = nom::number::streaming::be_f64(data)?; + let (data, y) = nom::number::streaming::be_f64(data)?; + let (data, z) = nom::number::streaming::be_f64(data)?; + let (data, yaw) = nom::number::streaming::be_f32(data)?; + let (data, pitch) = nom::number::streaming::be_f32(data)?; + let (data, on_ground) = nom::bytes::streaming::take(1usize)(data)?; + let on_ground = on_ground == [0x01]; + Ok(( + data, + SP14SetPlayerPositionAndRotation { + x, + y, + z, + yaw, + pitch, + on_ground, + }, + )) + } + fn serialize_body(&self) -> Vec { + let mut output = vec![]; + output.extend_from_slice(&self.x.to_be_bytes()); + output.extend_from_slice(&self.y.to_be_bytes()); + output.extend_from_slice(&self.z.to_be_bytes()); + output.extend_from_slice(&self.yaw.to_be_bytes()); + output.extend_from_slice(&self.pitch.to_be_bytes()); + output.push(if self.on_ground { 0x01 } else { 0x00 }); + output + } +} +impl From for GenericPacket { + fn from(value: SP14SetPlayerPositionAndRotation) -> Self { + GenericPacket::SP14SetPlayerPositionAndRotation(value) + } +} +impl TryFrom for SP14SetPlayerPositionAndRotation { + type Error = (); + + fn try_from(value: GenericPacket) -> Result { + match value { + GenericPacket::SP14SetPlayerPositionAndRotation(packet) => Ok(packet), + _ => Err(()), + } + } +} + +#[derive(Copy, Clone, Debug, PartialEq)] +pub struct SP15SetPlayerRotation { + yaw: f32, + pitch: f32, + on_ground: bool, +} +impl Packet for SP15SetPlayerRotation { + fn id() -> PacketId { + 0x15 + } + fn client_state() -> crate::ClientState { + crate::ClientState::Play + } + fn serverbound() -> bool { + true + } + + fn parse_body(data: &[u8]) -> nom::IResult<&[u8], Self> { + let (data, yaw) = nom::number::streaming::be_f32(data)?; + let (data, pitch) = nom::number::streaming::be_f32(data)?; + let (data, on_ground) = nom::bytes::streaming::take(1usize)(data)?; + let on_ground = on_ground == [0x01]; + Ok(( + data, + SP15SetPlayerRotation { + yaw, + pitch, + on_ground, + }, + )) + } + fn serialize_body(&self) -> Vec { + let mut output = vec![]; + output.extend_from_slice(&self.yaw.to_be_bytes()); + output.extend_from_slice(&self.pitch.to_be_bytes()); + output.push(if self.on_ground { 0x01 } else { 0x00 }); + output + } +} +impl From for GenericPacket { + fn from(value: SP15SetPlayerRotation) -> Self { + GenericPacket::SP15SetPlayerRotation(value) + } +} +impl TryFrom for SP15SetPlayerRotation { + type Error = (); + + fn try_from(value: GenericPacket) -> Result { + match value { + GenericPacket::SP15SetPlayerRotation(packet) => Ok(packet), + _ => Err(()), + } + } +} diff --git a/crates/composition-protocol/src/packet/serverbound/status.rs b/crates/composition-protocol/src/packet/serverbound/status.rs new file mode 100644 index 0000000..6973f5e --- /dev/null +++ b/crates/composition-protocol/src/packet/serverbound/status.rs @@ -0,0 +1,76 @@ +use crate::packet::{GenericPacket, Packet, PacketId}; + +#[derive(Copy, Clone, Debug, PartialEq)] +pub struct SS00StatusRequest; +impl Packet for SS00StatusRequest { + fn id() -> PacketId { + 0x00 + } + fn client_state() -> crate::ClientState { + crate::ClientState::Status + } + fn serverbound() -> bool { + true + } + + fn parse_body(data: &[u8]) -> nom::IResult<&[u8], Self> { + Ok((data, SS00StatusRequest)) + } + fn serialize_body(&self) -> Vec { + vec![] + } +} +impl From for GenericPacket { + fn from(value: SS00StatusRequest) -> Self { + GenericPacket::SS00StatusRequest(value) + } +} +impl TryFrom for SS00StatusRequest { + type Error = (); + + fn try_from(value: GenericPacket) -> Result { + match value { + GenericPacket::SS00StatusRequest(packet) => Ok(packet), + _ => Err(()), + } + } +} + +#[derive(Copy, Clone, Debug, PartialEq)] +pub struct SS01PingRequest { + payload: i64, +} +impl Packet for SS01PingRequest { + fn id() -> PacketId { + 0x01 + } + fn client_state() -> crate::ClientState { + crate::ClientState::Status + } + fn serverbound() -> bool { + true + } + + fn parse_body(data: &[u8]) -> nom::IResult<&[u8], Self> { + let (data, payload) = nom::number::streaming::be_i64(data)?; + Ok((data, SS01PingRequest { payload })) + } + fn serialize_body(&self) -> Vec { + self.payload.to_be_bytes().to_vec() + } +} +impl From for GenericPacket { + fn from(value: SS01PingRequest) -> Self { + GenericPacket::SS01PingRequest(value) + } +} +impl TryFrom for SS01PingRequest { + type Error = (); + + fn try_from(value: GenericPacket) -> Result { + match value { + GenericPacket::SS01PingRequest(packet) => Ok(packet), + _ => Err(()), + } + } +} diff --git a/crates/composition-protocol/src/util.rs b/crates/composition-protocol/src/util.rs new file mode 100644 index 0000000..a632472 --- /dev/null +++ b/crates/composition-protocol/src/util.rs @@ -0,0 +1,190 @@ +use nom::error::FromExternalError; + +pub fn parse_varint(mut data: &[u8]) -> nom::IResult<&[u8], i32> { + let mut output = 0i32; + let mut bytes_read = 0i32; + + loop { + let (d, next_byte) = nom::bytes::streaming::take(1usize)(data)?; + data = d; + + output |= ((next_byte[0] & 0x7f) as i32) << (bytes_read * 7); + bytes_read += 1; + if next_byte[0] & 0x80 != 0x80 { + break; + } + if bytes_read >= 5 { + break; + } + } + nom::IResult::Ok((data, output)) +} +pub fn serialize_varint(value: i32) -> Vec { + let mut value = value as u32; + let mut output = vec![]; + loop { + let data = (value & 0x7f) as u8; + value >>= 7; + + if value == 0 { + output.push(data); + break; + } else { + output.push(data | 0x80); + } + } + output +} + +pub fn parse_string(data: &[u8]) -> nom::IResult<&[u8], String> { + let (data, len) = parse_varint(data)?; + let (data, str_bytes) = nom::bytes::streaming::take(len as usize)(data)?; + let s = String::from_utf8_lossy(str_bytes).to_string(); + nom::IResult::Ok((data, s)) +} +pub fn serialize_string(value: &str) -> Vec { + let mut output = vec![]; + output.extend_from_slice(&serialize_varint(value.len() as i32)); + output.extend_from_slice(value.as_bytes()); + output +} + +pub fn parse_json(data: &[u8]) -> nom::IResult<&[u8], crate::Json> { + use nom::error::{Error, ErrorKind}; + let (data, json) = parse_string(data)?; + let json = serde_json::from_str(&json) + .map_err(|e| nom::Err::Error(Error::from_external_error(data, ErrorKind::Verify, e)))?; + Ok((data, json)) +} +pub fn serialize_json(value: &crate::Json) -> Vec { + serialize_string(&serde_json::to_string(value).expect("valid json")) +} + +pub fn parse_chat(data: &[u8]) -> nom::IResult<&[u8], crate::Chat> { + parse_json(data) +} +pub fn serialize_chat(value: &crate::Chat) -> Vec { + serialize_json(value) +} + +pub fn parse_uuid(data: &[u8]) -> nom::IResult<&[u8], crate::Uuid> { + nom::number::streaming::be_u128(data) +} +pub fn serialize_uuid(value: &crate::Uuid) -> Vec { + value.to_be_bytes().to_vec() +} + +#[derive(Debug, Copy, Clone, PartialEq)] +pub struct Position { + x: i32, + y: i32, + z: i32, +} +impl Position { + pub fn new(x: i32, y: i32, z: i32) -> Self { + Position { x, y, z } + } + pub fn parse(data: &[u8]) -> nom::IResult<&[u8], Self> { + let (data, i) = nom::number::streaming::be_i64(data)?; + + // x: i26, z: i26, y: i12 + let x = i >> 38; + let mut y = i & 0xFFF; + if y >= 0x800 { + y -= 0x1000; + } + let z = i << 26 >> 38; + + Ok((data, Position::new(x as i32, y as i32, z as i32))) + } + pub fn serialize(&self) -> Vec { + let i: i64 = ((self.x as i64 & 0x3FF_FFFF) << 38) + | ((self.z as i64 & 0x3FF_FFFF) << 12) + | (self.y as i64 & 0xFFF); + i.to_be_bytes().to_vec() + } +} + +#[cfg(test)] +mod tests { + use super::*; + + fn get_varints() -> Vec<(i32, Vec)> { + vec![ + (0, vec![0x00]), + (1, vec![0x01]), + (2, vec![0x02]), + (127, vec![0x7f]), + (128, vec![0x80, 0x01]), + (255, vec![0xff, 0x01]), + (25565, vec![0xdd, 0xc7, 0x01]), + (2097151, vec![0xff, 0xff, 0x7f]), + (2147483647, vec![0xff, 0xff, 0xff, 0xff, 0x07]), + (-1, vec![0xff, 0xff, 0xff, 0xff, 0x0f]), + (-2147483648, vec![0x80, 0x80, 0x80, 0x80, 0x08]), + ] + } + #[test] + fn parse_varint_works() { + for (value, bytes) in get_varints() { + assert_eq!(value, parse_varint(&bytes).unwrap().1); + } + } + #[test] + fn serialize_varint_works() { + for (value, bytes) in get_varints() { + assert_eq!(bytes, serialize_varint(value)); + } + } + + fn get_strings() -> Vec<(&'static str, Vec)> { + let s_127 = "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456"; + vec![ + ("", vec![0x00]), + ("A", vec![0x01, 0x41]), + ("AB", vec![0x02, 0x41, 0x42]), + (s_127, { + let mut v = vec![0x7f]; + v.extend_from_slice(s_127.as_bytes()); + v + }), + ] + } + #[test] + fn parse_string_works() { + for (value, bytes) in get_strings() { + assert_eq!(value, parse_string(&bytes).unwrap().1); + } + } + #[test] + fn serialize_string_works() { + for (value, bytes) in get_strings() { + assert_eq!(bytes, serialize_string(value)); + } + } + + fn get_positions() -> Vec<(Position, Vec)> { + vec![ + // x: 01000110000001110110001100 z: 10110000010101101101001000 y: 001100111111 + ( + Position::new(18357644, 831, -20882616), + vec![ + 0b01000110, 0b00000111, 0b01100011, 0b00101100, 0b00010101, 0b10110100, + 0b10000011, 0b00111111, + ], + ), + ] + } + #[test] + fn parse_position_works() { + for (value, bytes) in get_positions() { + assert_eq!(value, Position::parse(&bytes).unwrap().1); + } + } + #[test] + fn serialize_position_works() { + for (value, bytes) in get_positions() { + assert_eq!(bytes, value.serialize()); + } + } +} diff --git a/output.log b/output.log new file mode 100644 index 0000000..feb6b52 --- /dev/null +++ b/output.log @@ -0,0 +1,9 @@ +[2023-03-14 17:09:13][composition_core::config][WARN] Server icon file does not exist, creating server-icon.png +[2023-03-14 17:09:13][composition][INFO] Starting composition/0.1.0 (cc68d3a9) on port 25565 +[2023-03-14 17:09:13][composition][INFO] Done! Start took 709.336µs +[2023-03-14 17:09:31][composition][INFO] Starting composition/0.1.0 (cc68d3a9) on port 25565 +[2023-03-14 17:09:31][composition][INFO] Done! Start took 487.541µs +[2023-03-14 17:09:53][composition][INFO] Starting composition/0.1.0 (cc68d3a9) on port 25565 +[2023-03-14 17:09:53][composition][INFO] Done! Start took 567.591µs +[2023-03-14 17:10:08][composition][INFO] Starting composition/0.1.0 (cc68d3a9) on port 25565 +[2023-03-14 17:10:08][composition][INFO] Done! Start took 2.56877ms diff --git a/src/main.rs b/src/main.rs index cbeaad7..3d3e619 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,17 +1,20 @@ -use log::*; +use log::info; use std::sync::mpsc::TryRecvError; use std::time::Duration; #[tokio::main] pub async fn main() { - let ctrlc_rx = composition::init(); + let ctrlc_rx = composition_core::init(); info!( "Starting {} on port {}", - composition::CONFIG.server_version, - composition::CONFIG.port + composition_core::CONFIG.server_version, + composition_core::CONFIG.port + ); + let mut server = composition_core::start_server().await; + info!( + "Done! Start took {:?}", + composition_core::START_TIME.elapsed() ); - let mut server = composition::start_server().await; - info!("Done! Start took {:?}", composition::START_TIME.elapsed()); // The main server loop. loop {