diff --git a/.gitignore b/.gitignore index 37283417..e6e72965 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,5 @@ **/target .idea +/src/crates/ferrumc_net/NBT_CODEC.nbt +/src/crates/ferrumc_net/NBT_CODEC.zlib +**/rustc-ice* diff --git a/Cargo.lock b/Cargo.lock index 76178247..195a5830 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -124,7 +124,7 @@ checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ "proc-macro2", "quote", - "syn 2.0.67", + "syn 2.0.68", ] [[package]] @@ -135,7 +135,7 @@ checksum = "c980ee35e870bd1a4d2c8294d4c04d0499e67bca1e4b5cefcc693c2fa00caea9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.67", + "syn 2.0.68", ] [[package]] @@ -249,6 +249,12 @@ dependencies = [ "generic-array", ] +[[package]] +name = "bumpalo" +version = "3.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" + [[package]] name = "bytemuck" version = "1.16.1" @@ -276,6 +282,12 @@ dependencies = [ "libc", ] +[[package]] +name = "cesu8" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" + [[package]] name = "cfg-if" version = "1.0.0" @@ -284,9 +296,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clap" -version = "4.5.7" +version = "4.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5db83dced34638ad474f39f250d7fea9598bdd239eaced1bdf45d597da0f433f" +checksum = "84b3edb18336f4df585bc9aa31dd99c036dfa5dc5e9a2939a722a188f3a8970d" dependencies = [ "clap_builder", "clap_derive", @@ -294,9 +306,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.7" +version = "4.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7e204572485eb3fbf28f871612191521df159bc3e15a9f5064c66dba3a8c05f" +checksum = "c1c09dd5ada6c6c78075d6fd0da3f90d8080651e2d6cc8eb2f1aaa4034ced708" dependencies = [ "anstream", "anstyle", @@ -306,14 +318,14 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.5" +version = "4.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c780290ccf4fb26629baa7a1081e68ced113f1d3ec302fa5948f1c381ebf06c6" +checksum = "2bac35c6dafb060fd4d275d9a4ffae97917c13a6327903a8be2153cd964f7085" dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.67", + "syn 2.0.68", ] [[package]] @@ -519,34 +531,23 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" [[package]] -name = "env_filter" -version = "0.1.0" +name = "equivalent" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a009aa4810eb158359dda09d0c87378e4bbb89b5a801f016885a4707ba24f7ea" -dependencies = [ - "log", - "regex", -] +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] -name = "env_logger" -version = "0.11.3" +name = "fastnbt" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38b35839ba51819680ba087cd351788c9a3c476841207e0b8cee0b04722343b9" +checksum = "7d4a73a95dc65551ccd98e1ecd1adb5d1ba5361146963b31f481ca42fc0520a3" dependencies = [ - "anstream", - "anstyle", - "env_filter", - "humantime", - "log", + "byteorder", + "cesu8", + "serde", + "serde_bytes", ] -[[package]] -name = "equivalent" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" - [[package]] name = "ferrumc2_0" version = "0.1.1" @@ -555,19 +556,18 @@ dependencies = [ "byteorder", "clap", "config", - "console-subscriber", - "env_logger", "ferrumc_macros", "ferrumc_net", "ferrumc_utils", "include-flate", "lazy_static", - "log", "serde", "spinners", "thiserror", "tokio", "toml", + "tracing", + "tracing-subscriber", ] [[package]] @@ -575,8 +575,9 @@ name = "ferrumc_macros" version = "0.1.0" dependencies = [ "ferrumc_utils", + "proc-macro2", "quote", - "syn 2.0.67", + "syn 2.0.68", "tokio", ] @@ -589,16 +590,22 @@ dependencies = [ "byteorder", "console-subscriber", "dashmap", + "fastnbt", "ferrumc_macros", "ferrumc_utils", + "include-flate", "lariv", "lazy_static", "log", "rand", "serde", + "serde_derive", "serde_json", "tokio", + "tracing", + "tracing-subscriber", "uuid", + "zopfli", ] [[package]] @@ -608,6 +615,7 @@ dependencies = [ "anyhow", "byteorder", "config", + "fastnbt", "lazy_static", "log", "serde", @@ -866,7 +874,7 @@ dependencies = [ "libflate", "proc-macro2", "quote", - "syn 2.0.67", + "syn 2.0.68", ] [[package]] @@ -979,11 +987,17 @@ dependencies = [ "scopeguard", ] +[[package]] +name = "lockfree-object-pool" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9374ef4228402d4b7e403e5838cb880d9ee663314b0a900d5a6aabf0c213552e" + [[package]] name = "log" -version = "0.4.21" +version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" [[package]] name = "maplit" @@ -1006,6 +1020,16 @@ version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" +[[package]] +name = "md-5" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf" +dependencies = [ + "cfg-if", + "digest", +] + [[package]] name = "memchr" version = "2.7.1" @@ -1054,6 +1078,16 @@ dependencies = [ "minimal-lexical", ] +[[package]] +name = "nu-ansi-term" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +dependencies = [ + "overload", + "winapi", +] + [[package]] name = "num-traits" version = "0.2.19" @@ -1098,6 +1132,12 @@ dependencies = [ "hashbrown 0.13.2", ] +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + [[package]] name = "parking_lot" version = "0.12.3" @@ -1164,7 +1204,7 @@ dependencies = [ "pest_meta", "proc-macro2", "quote", - "syn 2.0.67", + "syn 2.0.68", ] [[package]] @@ -1195,7 +1235,7 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ "proc-macro2", "quote", - "syn 2.0.67", + "syn 2.0.68", ] [[package]] @@ -1245,7 +1285,7 @@ dependencies = [ "itertools", "proc-macro2", "quote", - "syn 2.0.67", + "syn 2.0.68", ] [[package]] @@ -1410,6 +1450,15 @@ dependencies = [ "serde_derive", ] +[[package]] +name = "serde_bytes" +version = "0.11.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "387cc504cb06bb40a96c8e04e951fe01854cf6bc921053c954e4a606d9675c6a" +dependencies = [ + "serde", +] + [[package]] name = "serde_derive" version = "1.0.203" @@ -1418,14 +1467,14 @@ checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba" dependencies = [ "proc-macro2", "quote", - "syn 2.0.67", + "syn 2.0.68", ] [[package]] name = "serde_json" -version = "1.0.117" +version = "1.0.119" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3" +checksum = "e8eddb61f0697cc3989c5d64b452f5488e2b8a60fd7d5076a3045076ffef8cb0" dependencies = [ "itoa", "ryu", @@ -1441,6 +1490,12 @@ dependencies = [ "serde", ] +[[package]] +name = "sha1_smol" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae1a47186c03a32177042e55dbc5fd5aee900b8e0069a8d70fba96a9375cd012" + [[package]] name = "sha2" version = "0.10.8" @@ -1470,6 +1525,12 @@ dependencies = [ "libc", ] +[[package]] +name = "simd-adler32" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" + [[package]] name = "slab" version = "0.4.9" @@ -1547,9 +1608,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.67" +version = "2.0.68" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff8655ed1d86f3af4ee3fd3263786bc14245ad17c4c7e85ba7187fb3ae028c90" +checksum = "901fa70d88b9d6c98022e23b4136f9f3e54e4662c3bc1bd1d84a42a9a0f0c1e9" dependencies = [ "proc-macro2", "quote", @@ -1579,7 +1640,7 @@ checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533" dependencies = [ "proc-macro2", "quote", - "syn 2.0.67", + "syn 2.0.68", ] [[package]] @@ -1639,7 +1700,7 @@ checksum = "5f5ae998a069d4b5aba8ee9dad856af7d520c3699e6159b185c2acd48155d39a" dependencies = [ "proc-macro2", "quote", - "syn 2.0.67", + "syn 2.0.68", ] [[package]] @@ -1778,7 +1839,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.67", + "syn 2.0.68", ] [[package]] @@ -1791,6 +1852,17 @@ dependencies = [ "valuable", ] +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + [[package]] name = "tracing-subscriber" version = "0.3.18" @@ -1798,12 +1870,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" dependencies = [ "matchers", + "nu-ansi-term", "once_cell", "regex", "sharded-slab", + "smallvec", "thread_local", "tracing", "tracing-core", + "tracing-log", ] [[package]] @@ -1844,12 +1919,14 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.8.0" +version = "1.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a183cf7feeba97b4dd1c0d46788634f6221d87fa961b305bed08c851829efcc0" +checksum = "5de17fd2f7da591098415cff336e12965a28061ddace43b59cb3c430179c9439" dependencies = [ "getrandom", + "md-5", "rand", + "sha1_smol", ] [[package]] @@ -1879,6 +1956,28 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + [[package]] name = "windows-sys" version = "0.48.0" @@ -1894,7 +1993,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.0", + "windows-targets 0.52.5", ] [[package]] @@ -1914,17 +2013,18 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.52.0" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" +checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" dependencies = [ - "windows_aarch64_gnullvm 0.52.0", - "windows_aarch64_msvc 0.52.0", - "windows_i686_gnu 0.52.0", - "windows_i686_msvc 0.52.0", - "windows_x86_64_gnu 0.52.0", - "windows_x86_64_gnullvm 0.52.0", - "windows_x86_64_msvc 0.52.0", + "windows_aarch64_gnullvm 0.52.5", + "windows_aarch64_msvc 0.52.5", + "windows_i686_gnu 0.52.5", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.5", + "windows_x86_64_gnu 0.52.5", + "windows_x86_64_gnullvm 0.52.5", + "windows_x86_64_msvc 0.52.5", ] [[package]] @@ -1935,9 +2035,9 @@ checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_gnullvm" -version = "0.52.0" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" +checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" [[package]] name = "windows_aarch64_msvc" @@ -1947,9 +2047,9 @@ checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_aarch64_msvc" -version = "0.52.0" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" +checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" [[package]] name = "windows_i686_gnu" @@ -1959,9 +2059,15 @@ checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_gnu" -version = "0.52.0" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" +checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" [[package]] name = "windows_i686_msvc" @@ -1971,9 +2077,9 @@ checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_i686_msvc" -version = "0.52.0" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" +checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" [[package]] name = "windows_x86_64_gnu" @@ -1983,9 +2089,9 @@ checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnu" -version = "0.52.0" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" +checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" [[package]] name = "windows_x86_64_gnullvm" @@ -1995,9 +2101,9 @@ checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.0" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" +checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" [[package]] name = "windows_x86_64_msvc" @@ -2007,9 +2113,9 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "windows_x86_64_msvc" -version = "0.52.0" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" +checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" [[package]] name = "winnow" @@ -2046,5 +2152,19 @@ checksum = "15e934569e47891f7d9411f1a451d947a60e000ab3bd24fbb970f000387d1b3b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.67", + "syn 2.0.68", +] + +[[package]] +name = "zopfli" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5019f391bac5cf252e93bbcc53d039ffd62c7bfb7c150414d61369afe57e946" +dependencies = [ + "bumpalo", + "crc32fast", + "lockfree-object-pool", + "log", + "once_cell", + "simd-adler32", ] diff --git a/Cargo.toml b/Cargo.toml index ea2651a4..eecd17e9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,19 +7,18 @@ edition = "2021" [dependencies] # Async -tokio = {version = "1.38.0", features = ["full"]} +tokio = { version = "1.38.0", features = ["full"] } # Error handling anyhow = "1.0.86" thiserror = "1.0.61" # Logging -log = "0.4.21" -env_logger = "0.11.3" -console-subscriber = "0.3.0" +tracing = "0.1.40" +tracing-subscriber = "0.3.18" # config / ser / de -serde = { version = "1.0.203", features = ["derive"]} +serde = { version = "1.0.203", features = ["derive"] } toml = "0.8.14" config = "0.14.0" lazy_static = "1.4.0" @@ -28,11 +27,11 @@ lazy_static = "1.4.0" byteorder = "1.5.0" include-flate = "0.3.0" -# Display +# CLI spinners = "4.1.1" +clap = { version = "4.5.8", features = ["derive"] } # Custom crates -ferrumc_net = { path = "src/crates/ferrumc_net"} -ferrumc_macros = { path = "src/crates/ferrumc_macros"} -ferrumc_utils = { path = "src/crates/ferrumc_utils"} -clap = { version = "4.5.7", features = ["derive"] } +ferrumc_net = { path = "src/crates/ferrumc_net" } +ferrumc_macros = { path = "src/crates/ferrumc_macros" } +ferrumc_utils = { path = "src/crates/ferrumc_utils" } diff --git a/README.md b/README.md index 5f422600..1b53def1 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,16 @@ # FerrumC + ![GitHub License](https://img.shields.io/github/license/Sweattypalms/ferrumc) ![GitHub code size in bytes](https://img.shields.io/github/languages/code-size/Sweattypalms/ferrumc) ![GitHub Actions Workflow Status](https://img.shields.io/github/actions/workflow/status/Sweattypalms/ferrumc/rust.yml) +![Lines of code](https://www.aschey.tech/tokei/github.com/Sweattypalms/ferrumc) A high performance Minecraft server written in Rust. ## Description -FerrumC is a Minecraft server written from the ground up in rust with performance in mind. Utilizing the power of the -Rust language, FerrumC is able to achieve high performance and low latency. FerrumC is designed to be a drop-in +FerrumC is a Minecraft server written from the ground up in rust with performance in mind. Utilizing the power of the +Rust language, FerrumC is able to achieve high performance and low latency. FerrumC is designed to be a drop-in replacement for the vanilla Minecraft server, and aims to be compatible with all vanilla clients. The open-source nature of FerrumC allows for the community to contribute to the project and help shape the future of the server. It also allows for easy tweaking and customization of the server to fit the needs of the user. @@ -21,13 +23,14 @@ also allows for easy tweaking and customization of the server to fit the needs o ### Installing -You can either download the source code and compile it yourself to get bleeding edge updates, or download a pre-compiled +You can either download the source code and compile it yourself to get bleeding edge updates, or download a pre-compiled binary from the releases page. ### Using FerrumC #### Compiling from source -``` + +```bash git clone https://github.com/Sweattypalms/ferrumc cd ferrumc cargo build --release # This will take a while @@ -35,8 +38,10 @@ cd target/release ``` #### Running the server + * Move the binary to a directory of your choice (it will create a few files in the directory it is run from) -* Run the binary with `./ferrumc.exe` on Windows or `./ferrumc` on Linux/Mac to start up the server with the default settings +* Run the binary with `./ferrumc.exe` on Windows or `./ferrumc` on Linux/Mac to start up the server with the default + settings * You can also run `./ferrumc --setup` to generate a config file and then edit it to your liking ## License @@ -45,7 +50,10 @@ This project is licensed under the MIT License - see the LICENSE.md file for det ## Acknowledgments -Inspiration, code snippets, dependencies etc. -* [Tokio Runtime](https://github.com/tokio-rs/tokio) -* [Valence](https://github.com/valence-rs/valence) -* [wiki.vg](https://wiki.vg) \ No newline at end of file +* The madlads behind the [Tokio Runtime](https://github.com/tokio-rs/tokio) for basically everything +* The amazing people working on [wiki.vg](https://wiki.vg) for the incredible amount of information it provides +* The [fastanvil](https://crates.io/crates/fastanvil), [simdnbt](https://github.com/azalea-rs/simdnbt) and + [fastnbt](https://crates.io/crates/fastnbt) crates for saving me from the pain of decoding NBT and anvil data on my + own +* The kind denizens of multiple discord server for answering my stupid questions and helping me out with stuff I don't + understand \ No newline at end of file diff --git a/src/crates/ferrumc_macros/Cargo.lock b/src/crates/ferrumc_macros/Cargo.lock index 1739c145..4ade2116 100644 --- a/src/crates/ferrumc_macros/Cargo.lock +++ b/src/crates/ferrumc_macros/Cargo.lock @@ -97,6 +97,12 @@ version = "1.0.99" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96c51067fd44124faa7f870b4b1c969379ad32b2ba805aa959430ceaa384f695" +[[package]] +name = "cesu8" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" + [[package]] name = "cfg-if" version = "1.0.0" @@ -202,11 +208,24 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" +[[package]] +name = "fastnbt" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d4a73a95dc65551ccd98e1ecd1adb5d1ba5361146963b31f481ca42fc0520a3" +dependencies = [ + "byteorder", + "cesu8", + "serde", + "serde_bytes", +] + [[package]] name = "ferrumc_macros" version = "0.1.0" dependencies = [ "ferrumc_utils", + "proc-macro2", "quote", "syn", "tokio", @@ -219,6 +238,7 @@ dependencies = [ "anyhow", "byteorder", "config", + "fastnbt", "lazy_static", "log", "serde", @@ -329,9 +349,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.21" +version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" [[package]] name = "memchr" @@ -566,6 +586,15 @@ dependencies = [ "serde_derive", ] +[[package]] +name = "serde_bytes" +version = "0.11.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "387cc504cb06bb40a96c8e04e951fe01854cf6bc921053c954e4a606d9675c6a" +dependencies = [ + "serde", +] + [[package]] name = "serde_derive" version = "1.0.203" @@ -635,9 +664,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.67" +version = "2.0.68" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff8655ed1d86f3af4ee3fd3263786bc14245ad17c4c7e85ba7187fb3ae028c90" +checksum = "901fa70d88b9d6c98022e23b4136f9f3e54e4662c3bc1bd1d84a42a9a0f0c1e9" dependencies = [ "proc-macro2", "quote", diff --git a/src/crates/ferrumc_macros/Cargo.toml b/src/crates/ferrumc_macros/Cargo.toml index c9afbf85..255a5f9f 100644 --- a/src/crates/ferrumc_macros/Cargo.toml +++ b/src/crates/ferrumc_macros/Cargo.toml @@ -7,8 +7,9 @@ edition = "2021" proc-macro = true [dependencies] +proc-macro2 = "1.0.86" quote = "1.0.36" -syn = "2.0.67" +syn = "2.0.68" tokio = "1.38.0" -ferrumc_utils = { path = "../ferrumc_utils"} +ferrumc_utils = { path = "../ferrumc_utils" } diff --git a/src/crates/ferrumc_macros/src/encode.rs b/src/crates/ferrumc_macros/src/encode.rs new file mode 100644 index 00000000..98faf703 --- /dev/null +++ b/src/crates/ferrumc_macros/src/encode.rs @@ -0,0 +1,170 @@ +use quote::{format_ident, quote}; +use syn::DeriveInput; + +pub(crate) fn generate_encode_func(input: DeriveInput) -> proc_macro2::TokenStream { + let name = &input.ident; + let mut is_packet_type = false; + + struct FieldAttribs { + field_name: syn::Ident, + raw_bytes: Option, + } + struct RawBytes { + prepend_length: bool, + } + + let mut field_attribs: Vec = Vec::new(); + + let fields = match input.data { + syn::Data::Struct(syn::DataStruct { + fields: syn::Fields::Named(fields), + .. + }) => fields, + _ => panic!("Only structs are supported"), + }; + + for field in fields.named { + let field_name = field.ident.unwrap(); + + if field_name.to_string() == "packet_id" { + is_packet_type = true; + } + + let mut field_attrib = FieldAttribs { + field_name, + raw_bytes: None, + }; + + let attribs = field.attrs; + + for attrib in attribs { + // [encode(raw_bytes(prepend_length=true))] + // [encode(raw_bytes)] + + let attrib_name = attrib.path().get_ident().unwrap(); + + + if attrib_name != "encode" { + continue; + } + + attrib.parse_nested_meta(|meta| { + if meta.path.is_ident("raw_bytes") { + let mut prepend = false; + + meta.parse_nested_meta(|meta| { + if meta.path.is_ident("prepend_length") { + let value = meta.value().unwrap(); + let value = value.parse::().unwrap(); + let value = value.value(); + prepend = value; + } + + Ok(()) + }).unwrap(); + + field_attrib.raw_bytes = Some(RawBytes { + prepend_length: prepend, + }); + } + + Ok(()) + }).unwrap(); + } + + field_attribs.push(field_attrib); + } + + + let mut field_statements = Vec::new(); + + for field_attrib in field_attribs { + let field_name = field_attrib.field_name; + + let mut statement: proc_macro2::TokenStream; + + // declare var names + let cursor = format_ident!("__cursor_{}", field_name); + let bytes = format_ident!("__bytes_{}", field_name); + let len = format_ident!("__len_{}", field_name); + + if let Some(raw_bytes) = field_attrib.raw_bytes { + statement = quote! { + let mut #cursor = std::io::Cursor::new(Vec::new()); + }; + + if raw_bytes.prepend_length { + statement = quote! { + #statement + + let #len = self.#field_name.len(); + let #len = ferrumc_utils::encoding::varint::VarInt::new(#len as i32); + + #len.encode(&mut #cursor).await?; + } + } + + statement = quote! { + #statement + tokio::io::AsyncWriteExt::write_all(&mut #cursor, &self.#field_name).await?; + + let mut #bytes = #cursor.into_inner(); + tokio::io::AsyncWriteExt::write_all(bytes, &#bytes).await?; + }; + } else { + statement = quote! { + self.#field_name.encode(bytes).await?; + }; + } + + field_statements.push(statement); + } + + let expanded: proc_macro2::TokenStream; + + if is_packet_type { + expanded = quote! { + impl ferrumc_utils::type_impls::Encode for #name { + async fn encode(&self, bytes_out: &mut T) -> std::result::Result<(), ferrumc_utils::error::Error> + where + T: tokio::io::AsyncWrite + tokio::io::AsyncSeek + std::marker::Unpin + { + use tokio::io::AsyncWriteExt; + let mut bytes_ = std::io::Cursor::new(Vec::new()); + let mut bytes = &mut bytes_; + + #(#field_statements)* + + let __packet_data = bytes_.into_inner(); + + let __length = __packet_data.len() as i32; + let __length: ferrumc_utils::encoding::varint::VarInt = ferrumc_utils::encoding::varint::VarInt::new(__length); + + let mut __cursor = std::io::Cursor::new(Vec::new()); + __length.encode(&mut __cursor).await?; + + __cursor.write_all(&__packet_data).await?; + + let __encoded = __cursor.into_inner(); + bytes_out.write_all(&__encoded).await?; + + Ok(()) + } + } + } + }else { + expanded = quote! { + impl ferrumc_utils::type_impls::Encode for #name { + async fn encode(&self, bytes: &mut T) -> std::result::Result<(), ferrumc_utils::error::Error> + where + T: tokio::io::AsyncWrite + tokio::io::AsyncSeek + std::marker::Unpin + { + use tokio::io::AsyncWriteExt; + #(#field_statements)* + Ok(()) + } + } + } + } + expanded +} \ No newline at end of file diff --git a/src/crates/ferrumc_macros/src/lib.rs b/src/crates/ferrumc_macros/src/lib.rs index d9720081..d1c24af6 100644 --- a/src/crates/ferrumc_macros/src/lib.rs +++ b/src/crates/ferrumc_macros/src/lib.rs @@ -8,6 +8,8 @@ use std::path::Path; use quote::quote; use syn::{DeriveInput, LitInt, LitStr, parse_macro_input}; +mod encode; + #[proc_macro_derive(Decode)] pub fn decode_derive(input: TokenStream) -> TokenStream { // Parse the input tokens into a syntax tree @@ -62,11 +64,17 @@ pub fn decode_derive(input: TokenStream) -> TokenStream { TokenStream::from(expanded) } -#[proc_macro_derive(Encode)] +#[proc_macro_derive(Encode, attributes(encode))] pub fn encode_derive(input: TokenStream) -> TokenStream { - // Parse the input tokens into a syntax tree let input = parse_macro_input!(input as DeriveInput); + let output = encode::generate_encode_func(input); + + TokenStream::from(output) + + + /*// Parse the input tokens into a syntax tree + // Used to store all field encoding statements let mut field_statements = Vec::new(); let mut has_packet_id = false; @@ -132,7 +140,7 @@ pub fn encode_derive(input: TokenStream) -> TokenStream { // Hand the output tokens back to the compiler - TokenStream::from(expanded) + TokenStream::from(expanded)*/ } /// Just exists, so we can use it in the packet attribute @@ -260,7 +268,7 @@ pub fn bake_packet_registry(input: TokenStream) -> TokenStream { match_arms.push(quote! { (#packet_id, #state) => { let packet = crate::packets::incoming::#struct_name_lowercase::#struct_name::decode(cursor).await?; - packet.handle(&mut conn).await?; + packet.handle(conn_owned).await?; }, }); } @@ -273,11 +281,10 @@ pub fn bake_packet_registry(input: TokenStream) -> TokenStream { let match_arms = match_arms.into_iter(); let output = quote! { - pub async fn handle_packet(packet_id: u8, conn_owned: &mut Arc>, cursor: &mut std::io::Cursor>) -> ferrumc_utils::prelude::Result<()> { - let mut conn = conn_owned.write().await; - match (packet_id, conn.state.as_str()) { + pub async fn handle_packet(packet_id: u8, conn_owned: &mut crate::Connection, cursor: &mut std::io::Cursor>) -> ferrumc_utils::prelude::Result<()> { + match (packet_id, conn_owned.state.as_str()) { #(#match_arms)* - _ => println!("No packet found for ID: 0x{:02X} in state: {}", packet_id, conn_owned.read().await.state.as_str()), + _ => println!("No packet found for ID: 0x{:02X} in state: {}", packet_id, conn_owned.state.as_str()), } Ok(()) diff --git a/src/crates/ferrumc_net/Cargo.lock b/src/crates/ferrumc_net/Cargo.lock index d99dac6d..248c8320 100644 --- a/src/crates/ferrumc_net/Cargo.lock +++ b/src/crates/ferrumc_net/Cargo.lock @@ -17,6 +17,24 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +[[package]] +name = "adler32" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234" + +[[package]] +name = "ahash" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", + "zerocopy", +] + [[package]] name = "aho-corasick" version = "1.1.3" @@ -26,6 +44,12 @@ dependencies = [ "memchr", ] +[[package]] +name = "allocator-api2" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" + [[package]] name = "anyhow" version = "1.0.86" @@ -176,6 +200,12 @@ dependencies = [ "generic-array", ] +[[package]] +name = "bumpalo" +version = "3.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" + [[package]] name = "bytemuck" version = "1.16.1" @@ -200,6 +230,12 @@ version = "1.0.99" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96c51067fd44124faa7f870b4b1c969379ad32b2ba805aa959430ceaa384f695" +[[package]] +name = "cesu8" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" + [[package]] name = "cfg-if" version = "1.0.0" @@ -293,6 +329,15 @@ dependencies = [ "unicode-segmentation", ] +[[package]] +name = "core2" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b49ba7ef1ad6107f8824dbe97de947cbaac53c44e7f9756a1fba0d37c1eec505" +dependencies = [ + "memchr", +] + [[package]] name = "cpufeatures" version = "0.2.12" @@ -342,6 +387,12 @@ dependencies = [ "typenum", ] +[[package]] +name = "dary_heap" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7762d17f1241643615821a8455a0b2c3e803784b058693d990b11f2dce25a0ca" + [[package]] name = "dashmap" version = "6.0.1" @@ -387,11 +438,24 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" +[[package]] +name = "fastnbt" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d4a73a95dc65551ccd98e1ecd1adb5d1ba5361146963b31f481ca42fc0520a3" +dependencies = [ + "byteorder", + "cesu8", + "serde", + "serde_bytes", +] + [[package]] name = "ferrumc_macros" version = "0.1.0" dependencies = [ "ferrumc_utils", + "proc-macro2", "quote", "syn", "tokio", @@ -406,16 +470,22 @@ dependencies = [ "byteorder", "console-subscriber", "dashmap", + "fastnbt", "ferrumc_macros", "ferrumc_utils", + "include-flate", "lariv", "lazy_static", "log", "rand", "serde", + "serde_derive", "serde_json", "tokio", + "tracing", + "tracing-subscriber", "uuid", + "zopfli", ] [[package]] @@ -425,6 +495,7 @@ dependencies = [ "anyhow", "byteorder", "config", + "fastnbt", "lazy_static", "log", "serde", @@ -551,6 +622,10 @@ name = "hashbrown" version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +dependencies = [ + "ahash", + "allocator-api2", +] [[package]] name = "hdrhistogram" @@ -647,6 +722,29 @@ dependencies = [ "tokio-io-timeout", ] +[[package]] +name = "include-flate" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df49c16750695486c1f34de05da5b7438096156466e7f76c38fcdf285cf0113e" +dependencies = [ + "include-flate-codegen", + "lazy_static", + "libflate", +] + +[[package]] +name = "include-flate-codegen" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c5b246c6261be723b85c61ecf87804e8ea4a35cb68be0ff282ed84b95ffe7d7" +dependencies = [ + "libflate", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "indexmap" version = "1.9.3" @@ -711,6 +809,30 @@ version = "0.2.155" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" +[[package]] +name = "libflate" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45d9dfdc14ea4ef0900c1cddbc8dcd553fbaacd8a4a282cf4018ae9dd04fb21e" +dependencies = [ + "adler32", + "core2", + "crc32fast", + "dary_heap", + "libflate_lz77", +] + +[[package]] +name = "libflate_lz77" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e0d73b369f386f1c44abd9c570d5318f55ccde816ff4b562fa452e5182863d" +dependencies = [ + "core2", + "hashbrown 0.14.5", + "rle-decode-fast", +] + [[package]] name = "linked-hash-map" version = "0.5.6" @@ -727,11 +849,17 @@ dependencies = [ "scopeguard", ] +[[package]] +name = "lockfree-object-pool" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9374ef4228402d4b7e403e5838cb880d9ee663314b0a900d5a6aabf0c213552e" + [[package]] name = "log" -version = "0.4.21" +version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" [[package]] name = "matchers" @@ -748,6 +876,16 @@ version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" +[[package]] +name = "md-5" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf" +dependencies = [ + "cfg-if", + "digest", +] + [[package]] name = "memchr" version = "2.7.4" @@ -796,6 +934,16 @@ dependencies = [ "minimal-lexical", ] +[[package]] +name = "nu-ansi-term" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +dependencies = [ + "overload", + "winapi", +] + [[package]] name = "num-traits" version = "0.2.19" @@ -840,6 +988,12 @@ dependencies = [ "hashbrown 0.13.2", ] +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + [[package]] name = "parking_lot" version = "0.12.3" @@ -1091,6 +1245,12 @@ version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" +[[package]] +name = "rle-decode-fast" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3582f63211428f83597b51b2ddb88e2a91a9d52d12831f9d08f5e624e8977422" + [[package]] name = "ron" version = "0.8.1" @@ -1146,6 +1306,15 @@ dependencies = [ "serde_derive", ] +[[package]] +name = "serde_bytes" +version = "0.11.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "387cc504cb06bb40a96c8e04e951fe01854cf6bc921053c954e4a606d9675c6a" +dependencies = [ + "serde", +] + [[package]] name = "serde_derive" version = "1.0.203" @@ -1159,9 +1328,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.117" +version = "1.0.119" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3" +checksum = "e8eddb61f0697cc3989c5d64b452f5488e2b8a60fd7d5076a3045076ffef8cb0" dependencies = [ "itoa", "ryu", @@ -1177,6 +1346,12 @@ dependencies = [ "serde", ] +[[package]] +name = "sha1_smol" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae1a47186c03a32177042e55dbc5fd5aee900b8e0069a8d70fba96a9375cd012" + [[package]] name = "sha2" version = "0.10.8" @@ -1206,6 +1381,12 @@ dependencies = [ "libc", ] +[[package]] +name = "simd-adler32" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" + [[package]] name = "slab" version = "0.4.9" @@ -1233,9 +1414,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.67" +version = "2.0.68" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff8655ed1d86f3af4ee3fd3263786bc14245ad17c4c7e85ba7187fb3ae028c90" +checksum = "901fa70d88b9d6c98022e23b4136f9f3e54e4662c3bc1bd1d84a42a9a0f0c1e9" dependencies = [ "proc-macro2", "quote", @@ -1477,6 +1658,17 @@ dependencies = [ "valuable", ] +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + [[package]] name = "tracing-subscriber" version = "0.3.18" @@ -1484,12 +1676,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" dependencies = [ "matchers", + "nu-ansi-term", "once_cell", "regex", "sharded-slab", + "smallvec", "thread_local", "tracing", "tracing-core", + "tracing-log", ] [[package]] @@ -1524,12 +1719,14 @@ checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" [[package]] name = "uuid" -version = "1.8.0" +version = "1.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a183cf7feeba97b4dd1c0d46788634f6221d87fa961b305bed08c851829efcc0" +checksum = "5de17fd2f7da591098415cff336e12965a28061ddace43b59cb3c430179c9439" dependencies = [ "getrandom", + "md-5", "rand", + "sha1_smol", ] [[package]] @@ -1559,6 +1756,28 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + [[package]] name = "windows-sys" version = "0.48.0" @@ -1715,3 +1934,37 @@ checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" dependencies = [ "linked-hash-map", ] + +[[package]] +name = "zerocopy" +version = "0.7.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae87e3fcd617500e5d106f0380cf7b77f3c6092aae37191433159dda23cfb087" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15e934569e47891f7d9411f1a451d947a60e000ab3bd24fbb970f000387d1b3b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zopfli" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5019f391bac5cf252e93bbcc53d039ffd62c7bfb7c150414d61369afe57e946" +dependencies = [ + "bumpalo", + "crc32fast", + "lockfree-object-pool", + "log", + "once_cell", + "simd-adler32", +] diff --git a/src/crates/ferrumc_net/Cargo.toml b/src/crates/ferrumc_net/Cargo.toml index 648958c8..41bced7c 100644 --- a/src/crates/ferrumc_net/Cargo.toml +++ b/src/crates/ferrumc_net/Cargo.toml @@ -8,14 +8,20 @@ dashmap = "6.0.1" lariv = "0.3.2" lazy_static = "1.4.0" tokio = { version = "1.38.0", features = ["full"] } -uuid = { version = "1.8.0", features = ["v4", "fast-rng"] } +uuid = { version = "1.9.1", features = ["v4", "fast-rng", "v3", "v5"] } rand = "0.8.5" atomic = "0.6.0" byteorder = "1.5.0" -log = "0.4.21" ferrumc_utils = { path = "../ferrumc_utils" } ferrumc_macros = { path = "../ferrumc_macros" } serde = { version = "1.0.117", features = ["derive"] } -serde_json = "1.0.117" +serde_json = "1.0.119" base64 = "0.22.1" console-subscriber = "0.3.0" +serde_derive = "1.0.203" +fastnbt = "2.5.0" +zopfli = "0.8.1" +tracing = "0.1" +tracing-subscriber = "0.3" +log = { version = "0.4.22", features = [] } +include-flate = "0.3.0" \ No newline at end of file diff --git a/src/crates/ferrumc_net/codec.json b/src/crates/ferrumc_net/codec.json new file mode 100644 index 00000000..78cfac80 --- /dev/null +++ b/src/crates/ferrumc_net/codec.json @@ -0,0 +1 @@ +{"minecraft:chat_type":{"type":"minecraft:chat_type","value":[{"element":{"chat":{"parameters":["sender","content"],"translation_key":"chat.type.text"},"narration":{"parameters":["sender","content"],"translation_key":"chat.type.text.narrate"}},"id":0,"name":"minecraft:chat"},{"element":{"chat":{"parameters":["sender","content"],"translation_key":"chat.type.emote"},"narration":{"parameters":["sender","content"],"translation_key":"chat.type.emote"}},"id":1,"name":"minecraft:emote_command"},{"element":{"chat":{"parameters":["sender","content"],"style":{"color":"gray","italic":1},"translation_key":"commands.message.display.incoming"},"narration":{"parameters":["sender","content"],"translation_key":"chat.type.text.narrate"}},"id":2,"name":"minecraft:msg_command_incoming"},{"element":{"chat":{"parameters":["target","content"],"style":{"color":"gray","italic":1},"translation_key":"commands.message.display.outgoing"},"narration":{"parameters":["sender","content"],"translation_key":"chat.type.text.narrate"}},"id":3,"name":"minecraft:msg_command_outgoing"},{"element":{"chat":{"parameters":["sender","content"],"translation_key":"chat.type.announcement"},"narration":{"parameters":["sender","content"],"translation_key":"chat.type.text.narrate"}},"id":4,"name":"minecraft:say_command"},{"element":{"chat":{"parameters":["target","sender","content"],"translation_key":"chat.type.team.text"},"narration":{"parameters":["sender","content"],"translation_key":"chat.type.text.narrate"}},"id":5,"name":"minecraft:team_msg_command_incoming"},{"element":{"chat":{"parameters":["target","sender","content"],"translation_key":"chat.type.team.sent"},"narration":{"parameters":["sender","content"],"translation_key":"chat.type.text.narrate"}},"id":6,"name":"minecraft:team_msg_command_outgoing"}]},"minecraft:damage_type":{"type":"minecraft:damage_type","value":[{"element":{"exhaustion":0.1,"message_id":"arrow","scaling":"when_caused_by_living_non_player"},"id":0,"name":"minecraft:arrow"},{"element":{"death_message_type":"intentional_game_design","exhaustion":0.1,"message_id":"badRespawnPoint","scaling":"always"},"id":1,"name":"minecraft:bad_respawn_point"},{"element":{"exhaustion":0.1,"message_id":"cactus","scaling":"when_caused_by_living_non_player"},"id":2,"name":"minecraft:cactus"},{"element":{"exhaustion":0,"message_id":"cramming","scaling":"when_caused_by_living_non_player"},"id":3,"name":"minecraft:cramming"},{"element":{"exhaustion":0,"message_id":"dragonBreath","scaling":"when_caused_by_living_non_player"},"id":4,"name":"minecraft:dragon_breath"},{"element":{"effects":"drowning","exhaustion":0,"message_id":"drown","scaling":"when_caused_by_living_non_player"},"id":5,"name":"minecraft:drown"},{"element":{"exhaustion":0.1,"message_id":"dryout","scaling":"when_caused_by_living_non_player"},"id":6,"name":"minecraft:dry_out"},{"element":{"exhaustion":0.1,"message_id":"explosion","scaling":"always"},"id":7,"name":"minecraft:explosion"},{"element":{"death_message_type":"fall_variants","exhaustion":0,"message_id":"fall","scaling":"when_caused_by_living_non_player"},"id":8,"name":"minecraft:fall"},{"element":{"exhaustion":0.1,"message_id":"anvil","scaling":"when_caused_by_living_non_player"},"id":9,"name":"minecraft:falling_anvil"},{"element":{"exhaustion":0.1,"message_id":"fallingBlock","scaling":"when_caused_by_living_non_player"},"id":10,"name":"minecraft:falling_block"},{"element":{"exhaustion":0.1,"message_id":"fallingStalactite","scaling":"when_caused_by_living_non_player"},"id":11,"name":"minecraft:falling_stalactite"},{"element":{"effects":"burning","exhaustion":0.1,"message_id":"fireball","scaling":"when_caused_by_living_non_player"},"id":12,"name":"minecraft:fireball"},{"element":{"exhaustion":0.1,"message_id":"fireworks","scaling":"when_caused_by_living_non_player"},"id":13,"name":"minecraft:fireworks"},{"element":{"exhaustion":0,"message_id":"flyIntoWall","scaling":"when_caused_by_living_non_player"},"id":14,"name":"minecraft:fly_into_wall"},{"element":{"effects":"freezing","exhaustion":0,"message_id":"freeze","scaling":"when_caused_by_living_non_player"},"id":15,"name":"minecraft:freeze"},{"element":{"exhaustion":0,"message_id":"generic","scaling":"when_caused_by_living_non_player"},"id":16,"name":"minecraft:generic"},{"element":{"exhaustion":0,"message_id":"genericKill","scaling":"when_caused_by_living_non_player"},"id":17,"name":"minecraft:generic_kill"},{"element":{"effects":"burning","exhaustion":0.1,"message_id":"hotFloor","scaling":"when_caused_by_living_non_player"},"id":18,"name":"minecraft:hot_floor"},{"element":{"effects":"burning","exhaustion":0.1,"message_id":"inFire","scaling":"when_caused_by_living_non_player"},"id":19,"name":"minecraft:in_fire"},{"element":{"exhaustion":0,"message_id":"inWall","scaling":"when_caused_by_living_non_player"},"id":20,"name":"minecraft:in_wall"},{"element":{"exhaustion":0,"message_id":"indirectMagic","scaling":"when_caused_by_living_non_player"},"id":21,"name":"minecraft:indirect_magic"},{"element":{"effects":"burning","exhaustion":0.1,"message_id":"lava","scaling":"when_caused_by_living_non_player"},"id":22,"name":"minecraft:lava"},{"element":{"exhaustion":0.1,"message_id":"lightningBolt","scaling":"when_caused_by_living_non_player"},"id":23,"name":"minecraft:lightning_bolt"},{"element":{"exhaustion":0,"message_id":"magic","scaling":"when_caused_by_living_non_player"},"id":24,"name":"minecraft:magic"},{"element":{"exhaustion":0.1,"message_id":"mob","scaling":"when_caused_by_living_non_player"},"id":25,"name":"minecraft:mob_attack"},{"element":{"exhaustion":0.1,"message_id":"mob","scaling":"when_caused_by_living_non_player"},"id":26,"name":"minecraft:mob_attack_no_aggro"},{"element":{"exhaustion":0.1,"message_id":"mob","scaling":"when_caused_by_living_non_player"},"id":27,"name":"minecraft:mob_projectile"},{"element":{"effects":"burning","exhaustion":0,"message_id":"onFire","scaling":"when_caused_by_living_non_player"},"id":28,"name":"minecraft:on_fire"},{"element":{"exhaustion":0,"message_id":"outOfWorld","scaling":"when_caused_by_living_non_player"},"id":29,"name":"minecraft:out_of_world"},{"element":{"exhaustion":0,"message_id":"outsideBorder","scaling":"when_caused_by_living_non_player"},"id":30,"name":"minecraft:outside_border"},{"element":{"exhaustion":0.1,"message_id":"player","scaling":"when_caused_by_living_non_player"},"id":31,"name":"minecraft:player_attack"},{"element":{"exhaustion":0.1,"message_id":"explosion.player","scaling":"always"},"id":32,"name":"minecraft:player_explosion"},{"element":{"exhaustion":0,"message_id":"sonic_boom","scaling":"always"},"id":33,"name":"minecraft:sonic_boom"},{"element":{"exhaustion":0,"message_id":"stalagmite","scaling":"when_caused_by_living_non_player"},"id":34,"name":"minecraft:stalagmite"},{"element":{"exhaustion":0,"message_id":"starve","scaling":"when_caused_by_living_non_player"},"id":35,"name":"minecraft:starve"},{"element":{"exhaustion":0.1,"message_id":"sting","scaling":"when_caused_by_living_non_player"},"id":36,"name":"minecraft:sting"},{"element":{"effects":"poking","exhaustion":0.1,"message_id":"sweetBerryBush","scaling":"when_caused_by_living_non_player"},"id":37,"name":"minecraft:sweet_berry_bush"},{"element":{"effects":"thorns","exhaustion":0.1,"message_id":"thorns","scaling":"when_caused_by_living_non_player"},"id":38,"name":"minecraft:thorns"},{"element":{"exhaustion":0.1,"message_id":"thrown","scaling":"when_caused_by_living_non_player"},"id":39,"name":"minecraft:thrown"},{"element":{"exhaustion":0.1,"message_id":"trident","scaling":"when_caused_by_living_non_player"},"id":40,"name":"minecraft:trident"},{"element":{"effects":"burning","exhaustion":0.1,"message_id":"onFire","scaling":"when_caused_by_living_non_player"},"id":41,"name":"minecraft:unattributed_fireball"},{"element":{"exhaustion":0,"message_id":"wither","scaling":"when_caused_by_living_non_player"},"id":42,"name":"minecraft:wither"},{"element":{"exhaustion":0.1,"message_id":"witherSkull","scaling":"when_caused_by_living_non_player"},"id":43,"name":"minecraft:wither_skull"}]},"minecraft:dimension_type":{"type":"minecraft:dimension_type","value":[{"element":{"ambient_light":0,"bed_works":1,"coordinate_scale":1,"effects":"minecraft:overworld","has_ceiling":0,"has_raids":1,"has_skylight":1,"height":384,"infiniburn":"#minecraft:infiniburn_overworld","logical_height":384,"min_y":-64,"monster_spawn_block_light_limit":0,"monster_spawn_light_level":{"type":"minecraft:uniform","value":{"max_inclusive":7,"min_inclusive":0}},"natural":1,"piglin_safe":0,"respawn_anchor_works":0,"ultrawarm":0},"id":0,"name":"minecraft:overworld"},{"element":{"ambient_light":0,"bed_works":1,"coordinate_scale":1,"effects":"minecraft:overworld","has_ceiling":1,"has_raids":1,"has_skylight":1,"height":384,"infiniburn":"#minecraft:infiniburn_overworld","logical_height":384,"min_y":-64,"monster_spawn_block_light_limit":0,"monster_spawn_light_level":{"type":"minecraft:uniform","value":{"max_inclusive":7,"min_inclusive":0}},"natural":1,"piglin_safe":0,"respawn_anchor_works":0,"ultrawarm":0},"id":1,"name":"minecraft:overworld_caves"},{"element":{"ambient_light":0,"bed_works":0,"coordinate_scale":1,"effects":"minecraft:the_end","fixed_time":6000,"has_ceiling":0,"has_raids":1,"has_skylight":0,"height":256,"infiniburn":"#minecraft:infiniburn_end","logical_height":256,"min_y":0,"monster_spawn_block_light_limit":0,"monster_spawn_light_level":{"type":"minecraft:uniform","value":{"max_inclusive":7,"min_inclusive":0}},"natural":0,"piglin_safe":0,"respawn_anchor_works":0,"ultrawarm":0},"id":2,"name":"minecraft:the_end"},{"element":{"ambient_light":0.1,"bed_works":0,"coordinate_scale":8,"effects":"minecraft:the_nether","fixed_time":18000,"has_ceiling":1,"has_raids":0,"has_skylight":0,"height":256,"infiniburn":"#minecraft:infiniburn_nether","logical_height":128,"min_y":0,"monster_spawn_block_light_limit":15,"monster_spawn_light_level":7,"natural":0,"piglin_safe":1,"respawn_anchor_works":1,"ultrawarm":1},"id":3,"name":"minecraft:the_nether"}]},"minecraft:trim_material":{"type":"minecraft:trim_material","value":[{"element":{"asset_name":"amethyst","description":{"color":"#9A5CC6","translate":"trim_material.minecraft.amethyst"},"ingredient":"minecraft:amethyst_shard","item_model_index":1},"id":0,"name":"minecraft:amethyst"},{"element":{"asset_name":"copper","description":{"color":"#B4684","translate":"trim_material.minecraft.copper"},"ingredient":"minecraft:copper_ingot","item_model_index":0.5},"id":1,"name":"minecraft:copper"},{"element":{"asset_name":"diamond","description":{"color":"#6EECD2","translate":"trim_material.minecraft.diamond"},"ingredient":"minecraft:diamond","item_model_index":0.8,"override_armor_materials":{"diamond":"diamond_darker"}},"id":2,"name":"minecraft:diamond"},{"element":{"asset_name":"emerald","description":{"color":"#11A036","translate":"trim_material.minecraft.emerald"},"ingredient":"minecraft:emerald","item_model_index":0.7},"id":3,"name":"minecraft:emerald"},{"element":{"asset_name":"gold","description":{"color":"#DEB12","translate":"trim_material.minecraft.gold"},"ingredient":"minecraft:gold_ingot","item_model_index":0.6,"override_armor_materials":{"gold":"gold_darker"}},"id":4,"name":"minecraft:gold"},{"element":{"asset_name":"iron","description":{"color":"#ECECEC","translate":"trim_material.minecraft.iron"},"ingredient":"minecraft:iron_ingot","item_model_index":0.2,"override_armor_materials":{"iron":"iron_darker"}},"id":5,"name":"minecraft:iron"},{"element":{"asset_name":"lapis","description":{"color":"#416E97","translate":"trim_material.minecraft.lapis"},"ingredient":"minecraft:lapis_lazuli","item_model_index":0.9},"id":6,"name":"minecraft:lapis"},{"element":{"asset_name":"netherite","description":{"color":"#625859","translate":"trim_material.minecraft.netherite"},"ingredient":"minecraft:netherite_ingot","item_model_index":0.3,"override_armor_materials":{"netherite":"netherite_darker"}},"id":7,"name":"minecraft:netherite"},{"element":{"asset_name":"quartz","description":{"color":"#E34C4","translate":"trim_material.minecraft.quartz"},"ingredient":"minecraft:quartz","item_model_index":0.1},"id":8,"name":"minecraft:quartz"},{"element":{"asset_name":"redstone","description":{"color":"#971607","translate":"trim_material.minecraft.redstone"},"ingredient":"minecraft:redstone","item_model_index":0.4},"id":9,"name":"minecraft:redstone"}]},"minecraft:trim_pattern":{"type":"minecraft:trim_pattern","value":[{"element":{"asset_id":"minecraft:coast","description":{"translate":"trim_pattern.minecraft.coast"},"template_item":"minecraft:coast_armor_trim_smithing_template"},"id":0,"name":"minecraft:coast"},{"element":{"asset_id":"minecraft:dune","description":{"translate":"trim_pattern.minecraft.dune"},"template_item":"minecraft:dune_armor_trim_smithing_template"},"id":1,"name":"minecraft:dune"},{"element":{"asset_id":"minecraft:eye","description":{"translate":"trim_pattern.minecraft.eye"},"template_item":"minecraft:eye_armor_trim_smithing_template"},"id":2,"name":"minecraft:eye"},{"element":{"asset_id":"minecraft:host","description":{"translate":"trim_pattern.minecraft.host"},"template_item":"minecraft:host_armor_trim_smithing_template"},"id":3,"name":"minecraft:host"},{"element":{"asset_id":"minecraft:raiser","description":{"translate":"trim_pattern.minecraft.raiser"},"template_item":"minecraft:raiser_armor_trim_smithing_template"},"id":4,"name":"minecraft:raiser"},{"element":{"asset_id":"minecraft:rib","description":{"translate":"trim_pattern.minecraft.rib"},"template_item":"minecraft:rib_armor_trim_smithing_template"},"id":5,"name":"minecraft:rib"},{"element":{"asset_id":"minecraft:sentry","description":{"translate":"trim_pattern.minecraft.sentry"},"template_item":"minecraft:sentry_armor_trim_smithing_template"},"id":6,"name":"minecraft:sentry"},{"element":{"asset_id":"minecraft:shaper","description":{"translate":"trim_pattern.minecraft.shaper"},"template_item":"minecraft:shaper_armor_trim_smithing_template"},"id":7,"name":"minecraft:shaper"},{"element":{"asset_id":"minecraft:silence","description":{"translate":"trim_pattern.minecraft.silence"},"template_item":"minecraft:silence_armor_trim_smithing_template"},"id":8,"name":"minecraft:silence"},{"element":{"asset_id":"minecraft:snout","description":{"translate":"trim_pattern.minecraft.snout"},"template_item":"minecraft:snout_armor_trim_smithing_template"},"id":9,"name":"minecraft:snout"},{"element":{"asset_id":"minecraft:spire","description":{"translate":"trim_pattern.minecraft.spire"},"template_item":"minecraft:spire_armor_trim_smithing_template"},"id":10,"name":"minecraft:spire"},{"element":{"asset_id":"minecraft:tide","description":{"translate":"trim_pattern.minecraft.tide"},"template_item":"minecraft:tide_armor_trim_smithing_template"},"id":11,"name":"minecraft:tide"},{"element":{"asset_id":"minecraft:vex","description":{"translate":"trim_pattern.minecraft.vex"},"template_item":"minecraft:vex_armor_trim_smithing_template"},"id":12,"name":"minecraft:vex"},{"element":{"asset_id":"minecraft:ward","description":{"translate":"trim_pattern.minecraft.ward"},"template_item":"minecraft:ward_armor_trim_smithing_template"},"id":13,"name":"minecraft:ward"},{"element":{"asset_id":"minecraft:wayfinder","description":{"translate":"trim_pattern.minecraft.wayfinder"},"template_item":"minecraft:wayfinder_armor_trim_smithing_template"},"id":14,"name":"minecraft:wayfinder"},{"element":{"asset_id":"minecraft:wild","description":{"translate":"trim_pattern.minecraft.wild"},"template_item":"minecraft:wild_armor_trim_smithing_template"},"id":15,"name":"minecraft:wild"}]},"minecraft:worldgen/biome":{"type":"minecraft:worldgen/biome","value":[{"element":{"downfall":0,"effects":{"fog_color":12638463,"foliage_color":10387789,"grass_color":9470285,"mood_sound":{"block_search_extent":8,"offset":2,"sound":"minecraft:ambient.cave","tick_delay":6000},"music":{"max_delay":24000,"min_delay":12000,"replace_current_music":0,"sound":"minecraft:music.overworld.badlands"},"sky_color":7254527,"water_color":4159204,"water_fog_color":329011},"has_precipitation":0,"temperature":2},"id":0,"name":"minecraft:badlands"},{"element":{"downfall":0.9,"effects":{"fog_color":12638463,"mood_sound":{"block_search_extent":8,"offset":2,"sound":"minecraft:ambient.cave","tick_delay":6000},"music":{"max_delay":24000,"min_delay":12000,"replace_current_music":0,"sound":"minecraft:music.overworld.bamboo_jungle"},"sky_color":7842047,"water_color":4159204,"water_fog_color":329011},"has_precipitation":1,"temperature":0.95},"id":1,"name":"minecraft:bamboo_jungle"},{"element":{"downfall":0,"effects":{"additions_sound":{"sound":"minecraft:ambient.basalt_deltas.additions","tick_chance":0.0111},"ambient_sound":"minecraft:ambient.basalt_deltas.loop","fog_color":6840176,"mood_sound":{"block_search_extent":8,"offset":2,"sound":"minecraft:ambient.basalt_deltas.mood","tick_delay":6000},"music":{"max_delay":24000,"min_delay":12000,"replace_current_music":0,"sound":"minecraft:music.nether.basalt_deltas"},"particle":{"options":{"type":"minecraft:white_ash"},"probability":0.118093334},"sky_color":7254527,"water_color":4159204,"water_fog_color":329011},"has_precipitation":0,"temperature":2},"id":2,"name":"minecraft:basalt_deltas"},{"element":{"downfall":0.4,"effects":{"fog_color":12638463,"mood_sound":{"block_search_extent":8,"offset":2,"sound":"minecraft:ambient.cave","tick_delay":6000},"sky_color":7907327,"water_color":4159204,"water_fog_color":329011},"has_precipitation":1,"temperature":0.8},"id":3,"name":"minecraft:beach"},{"element":{"downfall":0.6,"effects":{"fog_color":12638463,"mood_sound":{"block_search_extent":8,"offset":2,"sound":"minecraft:ambient.cave","tick_delay":6000},"music":{"max_delay":24000,"min_delay":12000,"replace_current_music":0,"sound":"minecraft:music.overworld.forest"},"sky_color":8037887,"water_color":4159204,"water_fog_color":329011},"has_precipitation":1,"temperature":0.6},"id":4,"name":"minecraft:birch_forest"},{"element":{"downfall":0.8,"effects":{"fog_color":12638463,"foliage_color":11983713,"grass_color":11983713,"mood_sound":{"block_search_extent":8,"offset":2,"sound":"minecraft:ambient.cave","tick_delay":6000},"music":{"max_delay":24000,"min_delay":12000,"replace_current_music":0,"sound":"minecraft:music.overworld.cherry_grove"},"sky_color":8103167,"water_color":6141935,"water_fog_color":6141935},"has_precipitation":1,"temperature":0.5},"id":5,"name":"minecraft:cherry_grove"},{"element":{"downfall":0.5,"effects":{"fog_color":12638463,"mood_sound":{"block_search_extent":8,"offset":2,"sound":"minecraft:ambient.cave","tick_delay":6000},"sky_color":8103167,"water_color":4020182,"water_fog_color":329011},"has_precipitation":1,"temperature":0.5},"id":6,"name":"minecraft:cold_ocean"},{"element":{"downfall":0,"effects":{"additions_sound":{"sound":"minecraft:ambient.crimson_forest.additions","tick_chance":0.0111},"ambient_sound":"minecraft:ambient.crimson_forest.loop","fog_color":3343107,"mood_sound":{"block_search_extent":8,"offset":2,"sound":"minecraft:ambient.crimson_forest.mood","tick_delay":6000},"music":{"max_delay":24000,"min_delay":12000,"replace_current_music":0,"sound":"minecraft:music.nether.crimson_forest"},"particle":{"options":{"type":"minecraft:crimson_spore"},"probability":0.025},"sky_color":7254527,"water_color":4159204,"water_fog_color":329011},"has_precipitation":0,"temperature":2},"id":7,"name":"minecraft:crimson_forest"},{"element":{"downfall":0.8,"effects":{"fog_color":12638463,"grass_color_modifier":"dark_forest","mood_sound":{"block_search_extent":8,"offset":2,"sound":"minecraft:ambient.cave","tick_delay":6000},"music":{"max_delay":24000,"min_delay":12000,"replace_current_music":0,"sound":"minecraft:music.overworld.forest"},"sky_color":7972607,"water_color":4159204,"water_fog_color":329011},"has_precipitation":1,"temperature":0.7},"id":8,"name":"minecraft:dark_forest"},{"element":{"downfall":0.5,"effects":{"fog_color":12638463,"mood_sound":{"block_search_extent":8,"offset":2,"sound":"minecraft:ambient.cave","tick_delay":6000},"sky_color":8103167,"water_color":4020182,"water_fog_color":329011},"has_precipitation":1,"temperature":0.5},"id":9,"name":"minecraft:deep_cold_ocean"},{"element":{"downfall":0.4,"effects":{"fog_color":12638463,"mood_sound":{"block_search_extent":8,"offset":2,"sound":"minecraft:ambient.cave","tick_delay":6000},"music":{"max_delay":24000,"min_delay":12000,"replace_current_music":0,"sound":"minecraft:music.overworld.deep_dark"},"sky_color":7907327,"water_color":4159204,"water_fog_color":329011},"has_precipitation":1,"temperature":0.8},"id":10,"name":"minecraft:deep_dark"},{"element":{"downfall":0.5,"effects":{"fog_color":12638463,"mood_sound":{"block_search_extent":8,"offset":2,"sound":"minecraft:ambient.cave","tick_delay":6000},"sky_color":8103167,"water_color":3750089,"water_fog_color":329011},"has_precipitation":1,"temperature":0.5,"temperature_modifier":"frozen"},"id":11,"name":"minecraft:deep_frozen_ocean"},{"element":{"downfall":0.5,"effects":{"fog_color":12638463,"mood_sound":{"block_search_extent":8,"offset":2,"sound":"minecraft:ambient.cave","tick_delay":6000},"sky_color":8103167,"water_color":4566514,"water_fog_color":267827},"has_precipitation":1,"temperature":0.5},"id":12,"name":"minecraft:deep_lukewarm_ocean"},{"element":{"downfall":0.5,"effects":{"fog_color":12638463,"mood_sound":{"block_search_extent":8,"offset":2,"sound":"minecraft:ambient.cave","tick_delay":6000},"sky_color":8103167,"water_color":4159204,"water_fog_color":329011},"has_precipitation":1,"temperature":0.5},"id":13,"name":"minecraft:deep_ocean"},{"element":{"downfall":0,"effects":{"fog_color":12638463,"mood_sound":{"block_search_extent":8,"offset":2,"sound":"minecraft:ambient.cave","tick_delay":6000},"music":{"max_delay":24000,"min_delay":12000,"replace_current_music":0,"sound":"minecraft:music.overworld.desert"},"sky_color":7254527,"water_color":4159204,"water_fog_color":329011},"has_precipitation":0,"temperature":2},"id":14,"name":"minecraft:desert"},{"element":{"downfall":0.4,"effects":{"fog_color":12638463,"mood_sound":{"block_search_extent":8,"offset":2,"sound":"minecraft:ambient.cave","tick_delay":6000},"music":{"max_delay":24000,"min_delay":12000,"replace_current_music":0,"sound":"minecraft:music.overworld.dripstone_caves"},"sky_color":7907327,"water_color":4159204,"water_fog_color":329011},"has_precipitation":1,"temperature":0.8},"id":15,"name":"minecraft:dripstone_caves"},{"element":{"downfall":0.5,"effects":{"fog_color":10518688,"mood_sound":{"block_search_extent":8,"offset":2,"sound":"minecraft:ambient.cave","tick_delay":6000},"sky_color":0,"water_color":4159204,"water_fog_color":329011},"has_precipitation":0,"temperature":0.5},"id":16,"name":"minecraft:end_barrens"},{"element":{"downfall":0.5,"effects":{"fog_color":10518688,"mood_sound":{"block_search_extent":8,"offset":2,"sound":"minecraft:ambient.cave","tick_delay":6000},"sky_color":0,"water_color":4159204,"water_fog_color":329011},"has_precipitation":0,"temperature":0.5},"id":17,"name":"minecraft:end_highlands"},{"element":{"downfall":0.5,"effects":{"fog_color":10518688,"mood_sound":{"block_search_extent":8,"offset":2,"sound":"minecraft:ambient.cave","tick_delay":6000},"sky_color":0,"water_color":4159204,"water_fog_color":329011},"has_precipitation":0,"temperature":0.5},"id":18,"name":"minecraft:end_midlands"},{"element":{"downfall":0,"effects":{"fog_color":12638463,"foliage_color":10387789,"grass_color":9470285,"mood_sound":{"block_search_extent":8,"offset":2,"sound":"minecraft:ambient.cave","tick_delay":6000},"music":{"max_delay":24000,"min_delay":12000,"replace_current_music":0,"sound":"minecraft:music.overworld.badlands"},"sky_color":7254527,"water_color":4159204,"water_fog_color":329011},"has_precipitation":0,"temperature":2},"id":19,"name":"minecraft:eroded_badlands"},{"element":{"downfall":0.8,"effects":{"fog_color":12638463,"mood_sound":{"block_search_extent":8,"offset":2,"sound":"minecraft:ambient.cave","tick_delay":6000},"music":{"max_delay":24000,"min_delay":12000,"replace_current_music":0,"sound":"minecraft:music.overworld.flower_forest"},"sky_color":7972607,"water_color":4159204,"water_fog_color":329011},"has_precipitation":1,"temperature":0.7},"id":20,"name":"minecraft:flower_forest"},{"element":{"downfall":0.8,"effects":{"fog_color":12638463,"mood_sound":{"block_search_extent":8,"offset":2,"sound":"minecraft:ambient.cave","tick_delay":6000},"music":{"max_delay":24000,"min_delay":12000,"replace_current_music":0,"sound":"minecraft:music.overworld.forest"},"sky_color":7972607,"water_color":4159204,"water_fog_color":329011},"has_precipitation":1,"temperature":0.7},"id":21,"name":"minecraft:forest"},{"element":{"downfall":0.5,"effects":{"fog_color":12638463,"mood_sound":{"block_search_extent":8,"offset":2,"sound":"minecraft:ambient.cave","tick_delay":6000},"sky_color":8364543,"water_color":3750089,"water_fog_color":329011},"has_precipitation":1,"temperature":0,"temperature_modifier":"frozen"},"id":22,"name":"minecraft:frozen_ocean"},{"element":{"downfall":0.9,"effects":{"fog_color":12638463,"mood_sound":{"block_search_extent":8,"offset":2,"sound":"minecraft:ambient.cave","tick_delay":6000},"music":{"max_delay":24000,"min_delay":12000,"replace_current_music":0,"sound":"minecraft:music.overworld.frozen_peaks"},"sky_color":8756735,"water_color":4159204,"water_fog_color":329011},"has_precipitation":1,"temperature":-0.7},"id":23,"name":"minecraft:frozen_peaks"},{"element":{"downfall":0.5,"effects":{"fog_color":12638463,"mood_sound":{"block_search_extent":8,"offset":2,"sound":"minecraft:ambient.cave","tick_delay":6000},"sky_color":8364543,"water_color":3750089,"water_fog_color":329011},"has_precipitation":1,"temperature":0},"id":24,"name":"minecraft:frozen_river"},{"element":{"downfall":0.8,"effects":{"fog_color":12638463,"mood_sound":{"block_search_extent":8,"offset":2,"sound":"minecraft:ambient.cave","tick_delay":6000},"music":{"max_delay":24000,"min_delay":12000,"replace_current_music":0,"sound":"minecraft:music.overworld.grove"},"sky_color":8495359,"water_color":4159204,"water_fog_color":329011},"has_precipitation":1,"temperature":-0.2},"id":25,"name":"minecraft:grove"},{"element":{"downfall":0.5,"effects":{"fog_color":12638463,"mood_sound":{"block_search_extent":8,"offset":2,"sound":"minecraft:ambient.cave","tick_delay":6000},"sky_color":8364543,"water_color":4159204,"water_fog_color":329011},"has_precipitation":1,"temperature":0},"id":26,"name":"minecraft:ice_spikes"},{"element":{"downfall":0.9,"effects":{"fog_color":12638463,"mood_sound":{"block_search_extent":8,"offset":2,"sound":"minecraft:ambient.cave","tick_delay":6000},"music":{"max_delay":24000,"min_delay":12000,"replace_current_music":0,"sound":"minecraft:music.overworld.jagged_peaks"},"sky_color":8756735,"water_color":4159204,"water_fog_color":329011},"has_precipitation":1,"temperature":-0.7},"id":27,"name":"minecraft:jagged_peaks"},{"element":{"downfall":0.9,"effects":{"fog_color":12638463,"mood_sound":{"block_search_extent":8,"offset":2,"sound":"minecraft:ambient.cave","tick_delay":6000},"music":{"max_delay":24000,"min_delay":12000,"replace_current_music":0,"sound":"minecraft:music.overworld.jungle"},"sky_color":7842047,"water_color":4159204,"water_fog_color":329011},"has_precipitation":1,"temperature":0.95},"id":28,"name":"minecraft:jungle"},{"element":{"downfall":0.5,"effects":{"fog_color":12638463,"mood_sound":{"block_search_extent":8,"offset":2,"sound":"minecraft:ambient.cave","tick_delay":6000},"sky_color":8103167,"water_color":4566514,"water_fog_color":267827},"has_precipitation":1,"temperature":0.5},"id":29,"name":"minecraft:lukewarm_ocean"},{"element":{"downfall":0.5,"effects":{"fog_color":12638463,"mood_sound":{"block_search_extent":8,"offset":2,"sound":"minecraft:ambient.cave","tick_delay":6000},"music":{"max_delay":24000,"min_delay":12000,"replace_current_music":0,"sound":"minecraft:music.overworld.lush_caves"},"sky_color":8103167,"water_color":4159204,"water_fog_color":329011},"has_precipitation":1,"temperature":0.5},"id":30,"name":"minecraft:lush_caves"},{"element":{"downfall":0.9,"effects":{"fog_color":12638463,"foliage_color":9285927,"grass_color_modifier":"swamp","mood_sound":{"block_search_extent":8,"offset":2,"sound":"minecraft:ambient.cave","tick_delay":6000},"music":{"max_delay":24000,"min_delay":12000,"replace_current_music":0,"sound":"minecraft:music.overworld.swamp"},"sky_color":7907327,"water_color":3832426,"water_fog_color":5077600},"has_precipitation":1,"temperature":0.8},"id":31,"name":"minecraft:mangrove_swamp"},{"element":{"downfall":0.8,"effects":{"fog_color":12638463,"mood_sound":{"block_search_extent":8,"offset":2,"sound":"minecraft:ambient.cave","tick_delay":6000},"music":{"max_delay":24000,"min_delay":12000,"replace_current_music":0,"sound":"minecraft:music.overworld.meadow"},"sky_color":8103167,"water_color":937679,"water_fog_color":329011},"has_precipitation":1,"temperature":0.5},"id":32,"name":"minecraft:meadow"},{"element":{"downfall":1,"effects":{"fog_color":12638463,"mood_sound":{"block_search_extent":8,"offset":2,"sound":"minecraft:ambient.cave","tick_delay":6000},"sky_color":7842047,"water_color":4159204,"water_fog_color":329011},"has_precipitation":1,"temperature":0.9},"id":33,"name":"minecraft:mushroom_fields"},{"element":{"downfall":0,"effects":{"additions_sound":{"sound":"minecraft:ambient.nether_wastes.additions","tick_chance":0.0111},"ambient_sound":"minecraft:ambient.nether_wastes.loop","fog_color":3344392,"mood_sound":{"block_search_extent":8,"offset":2,"sound":"minecraft:ambient.nether_wastes.mood","tick_delay":6000},"music":{"max_delay":24000,"min_delay":12000,"replace_current_music":0,"sound":"minecraft:music.nether.nether_wastes"},"sky_color":7254527,"water_color":4159204,"water_fog_color":329011},"has_precipitation":0,"temperature":2},"id":34,"name":"minecraft:nether_wastes"},{"element":{"downfall":0.5,"effects":{"fog_color":12638463,"mood_sound":{"block_search_extent":8,"offset":2,"sound":"minecraft:ambient.cave","tick_delay":6000},"sky_color":8103167,"water_color":4159204,"water_fog_color":329011},"has_precipitation":1,"temperature":0.5},"id":35,"name":"minecraft:ocean"},{"element":{"downfall":0.6,"effects":{"fog_color":12638463,"mood_sound":{"block_search_extent":8,"offset":2,"sound":"minecraft:ambient.cave","tick_delay":6000},"music":{"max_delay":24000,"min_delay":12000,"replace_current_music":0,"sound":"minecraft:music.overworld.forest"},"sky_color":8037887,"water_color":4159204,"water_fog_color":329011},"has_precipitation":1,"temperature":0.6},"id":36,"name":"minecraft:old_growth_birch_forest"},{"element":{"downfall":0.8,"effects":{"fog_color":12638463,"mood_sound":{"block_search_extent":8,"offset":2,"sound":"minecraft:ambient.cave","tick_delay":6000},"music":{"max_delay":24000,"min_delay":12000,"replace_current_music":0,"sound":"minecraft:music.overworld.old_growth_taiga"},"sky_color":8168447,"water_color":4159204,"water_fog_color":329011},"has_precipitation":1,"temperature":0.3},"id":37,"name":"minecraft:old_growth_pine_taiga"},{"element":{"downfall":0.8,"effects":{"fog_color":12638463,"mood_sound":{"block_search_extent":8,"offset":2,"sound":"minecraft:ambient.cave","tick_delay":6000},"music":{"max_delay":24000,"min_delay":12000,"replace_current_music":0,"sound":"minecraft:music.overworld.old_growth_taiga"},"sky_color":8233983,"water_color":4159204,"water_fog_color":329011},"has_precipitation":1,"temperature":0.25},"id":38,"name":"minecraft:old_growth_spruce_taiga"},{"element":{"downfall":0.4,"effects":{"fog_color":12638463,"mood_sound":{"block_search_extent":8,"offset":2,"sound":"minecraft:ambient.cave","tick_delay":6000},"sky_color":7907327,"water_color":4159204,"water_fog_color":329011},"has_precipitation":1,"temperature":0.8},"id":39,"name":"minecraft:plains"},{"element":{"downfall":0.5,"effects":{"fog_color":12638463,"mood_sound":{"block_search_extent":8,"offset":2,"sound":"minecraft:ambient.cave","tick_delay":6000},"sky_color":8103167,"water_color":4159204,"water_fog_color":329011},"has_precipitation":1,"temperature":0.5},"id":40,"name":"minecraft:river"},{"element":{"downfall":0,"effects":{"fog_color":12638463,"mood_sound":{"block_search_extent":8,"offset":2,"sound":"minecraft:ambient.cave","tick_delay":6000},"sky_color":7254527,"water_color":4159204,"water_fog_color":329011},"has_precipitation":0,"temperature":2},"id":41,"name":"minecraft:savanna"},{"element":{"downfall":0,"effects":{"fog_color":12638463,"mood_sound":{"block_search_extent":8,"offset":2,"sound":"minecraft:ambient.cave","tick_delay":6000},"sky_color":7254527,"water_color":4159204,"water_fog_color":329011},"has_precipitation":0,"temperature":2},"id":42,"name":"minecraft:savanna_plateau"},{"element":{"downfall":0.5,"effects":{"fog_color":10518688,"mood_sound":{"block_search_extent":8,"offset":2,"sound":"minecraft:ambient.cave","tick_delay":6000},"sky_color":0,"water_color":4159204,"water_fog_color":329011},"has_precipitation":0,"temperature":0.5},"id":43,"name":"minecraft:small_end_islands"},{"element":{"downfall":0.3,"effects":{"fog_color":12638463,"mood_sound":{"block_search_extent":8,"offset":2,"sound":"minecraft:ambient.cave","tick_delay":6000},"sky_color":8364543,"water_color":4020182,"water_fog_color":329011},"has_precipitation":1,"temperature":0.05},"id":44,"name":"minecraft:snowy_beach"},{"element":{"downfall":0.5,"effects":{"fog_color":12638463,"mood_sound":{"block_search_extent":8,"offset":2,"sound":"minecraft:ambient.cave","tick_delay":6000},"sky_color":8364543,"water_color":4159204,"water_fog_color":329011},"has_precipitation":1,"temperature":0},"id":45,"name":"minecraft:snowy_plains"},{"element":{"downfall":0.9,"effects":{"fog_color":12638463,"mood_sound":{"block_search_extent":8,"offset":2,"sound":"minecraft:ambient.cave","tick_delay":6000},"music":{"max_delay":24000,"min_delay":12000,"replace_current_music":0,"sound":"minecraft:music.overworld.snowy_slopes"},"sky_color":8560639,"water_color":4159204,"water_fog_color":329011},"has_precipitation":1,"temperature":-0.3},"id":46,"name":"minecraft:snowy_slopes"},{"element":{"downfall":0.4,"effects":{"fog_color":12638463,"mood_sound":{"block_search_extent":8,"offset":2,"sound":"minecraft:ambient.cave","tick_delay":6000},"sky_color":8625919,"water_color":4020182,"water_fog_color":329011},"has_precipitation":1,"temperature":-0.5},"id":47,"name":"minecraft:snowy_taiga"},{"element":{"downfall":0,"effects":{"additions_sound":{"sound":"minecraft:ambient.soul_sand_valley.additions","tick_chance":0.0111},"ambient_sound":"minecraft:ambient.soul_sand_valley.loop","fog_color":1787717,"mood_sound":{"block_search_extent":8,"offset":2,"sound":"minecraft:ambient.soul_sand_valley.mood","tick_delay":6000},"music":{"max_delay":24000,"min_delay":12000,"replace_current_music":0,"sound":"minecraft:music.nether.soul_sand_valley"},"particle":{"options":{"type":"minecraft:ash"},"probability":0.00625},"sky_color":7254527,"water_color":4159204,"water_fog_color":329011},"has_precipitation":0,"temperature":2},"id":48,"name":"minecraft:soul_sand_valley"},{"element":{"downfall":0.8,"effects":{"fog_color":12638463,"mood_sound":{"block_search_extent":8,"offset":2,"sound":"minecraft:ambient.cave","tick_delay":6000},"music":{"max_delay":24000,"min_delay":12000,"replace_current_music":0,"sound":"minecraft:music.overworld.sparse_jungle"},"sky_color":7842047,"water_color":4159204,"water_fog_color":329011},"has_precipitation":1,"temperature":0.95},"id":49,"name":"minecraft:sparse_jungle"},{"element":{"downfall":0.3,"effects":{"fog_color":12638463,"mood_sound":{"block_search_extent":8,"offset":2,"sound":"minecraft:ambient.cave","tick_delay":6000},"music":{"max_delay":24000,"min_delay":12000,"replace_current_music":0,"sound":"minecraft:music.overworld.stony_peaks"},"sky_color":7776511,"water_color":4159204,"water_fog_color":329011},"has_precipitation":1,"temperature":1},"id":50,"name":"minecraft:stony_peaks"},{"element":{"downfall":0.3,"effects":{"fog_color":12638463,"mood_sound":{"block_search_extent":8,"offset":2,"sound":"minecraft:ambient.cave","tick_delay":6000},"sky_color":8233727,"water_color":4159204,"water_fog_color":329011},"has_precipitation":1,"temperature":0.2},"id":51,"name":"minecraft:stony_shore"},{"element":{"downfall":0.4,"effects":{"fog_color":12638463,"mood_sound":{"block_search_extent":8,"offset":2,"sound":"minecraft:ambient.cave","tick_delay":6000},"sky_color":7907327,"water_color":4159204,"water_fog_color":329011},"has_precipitation":1,"temperature":0.8},"id":52,"name":"minecraft:sunflower_plains"},{"element":{"downfall":0.9,"effects":{"fog_color":12638463,"foliage_color":6975545,"grass_color_modifier":"swamp","mood_sound":{"block_search_extent":8,"offset":2,"sound":"minecraft:ambient.cave","tick_delay":6000},"music":{"max_delay":24000,"min_delay":12000,"replace_current_music":0,"sound":"minecraft:music.overworld.swamp"},"sky_color":7907327,"water_color":6388580,"water_fog_color":2302743},"has_precipitation":1,"temperature":0.8},"id":53,"name":"minecraft:swamp"},{"element":{"downfall":0.8,"effects":{"fog_color":12638463,"mood_sound":{"block_search_extent":8,"offset":2,"sound":"minecraft:ambient.cave","tick_delay":6000},"sky_color":8233983,"water_color":4159204,"water_fog_color":329011},"has_precipitation":1,"temperature":0.25},"id":54,"name":"minecraft:taiga"},{"element":{"downfall":0.5,"effects":{"fog_color":10518688,"mood_sound":{"block_search_extent":8,"offset":2,"sound":"minecraft:ambient.cave","tick_delay":6000},"sky_color":0,"water_color":4159204,"water_fog_color":329011},"has_precipitation":0,"temperature":0.5},"id":55,"name":"minecraft:the_end"},{"element":{"downfall":0.5,"effects":{"fog_color":12638463,"mood_sound":{"block_search_extent":8,"offset":2,"sound":"minecraft:ambient.cave","tick_delay":6000},"sky_color":8103167,"water_color":4159204,"water_fog_color":329011},"has_precipitation":0,"temperature":0.5},"id":56,"name":"minecraft:the_void"},{"element":{"downfall":0.5,"effects":{"fog_color":12638463,"mood_sound":{"block_search_extent":8,"offset":2,"sound":"minecraft:ambient.cave","tick_delay":6000},"sky_color":8103167,"water_color":4445678,"water_fog_color":270131},"has_precipitation":1,"temperature":0.5},"id":57,"name":"minecraft:warm_ocean"},{"element":{"downfall":0,"effects":{"additions_sound":{"sound":"minecraft:ambient.warped_forest.additions","tick_chance":0.0111},"ambient_sound":"minecraft:ambient.warped_forest.loop","fog_color":1705242,"mood_sound":{"block_search_extent":8,"offset":2,"sound":"minecraft:ambient.warped_forest.mood","tick_delay":6000},"music":{"max_delay":24000,"min_delay":12000,"replace_current_music":0,"sound":"minecraft:music.nether.warped_forest"},"particle":{"options":{"type":"minecraft:warped_spore"},"probability":0.01428},"sky_color":7254527,"water_color":4159204,"water_fog_color":329011},"has_precipitation":0,"temperature":2},"id":58,"name":"minecraft:warped_forest"},{"element":{"downfall":0.3,"effects":{"fog_color":12638463,"mood_sound":{"block_search_extent":8,"offset":2,"sound":"minecraft:ambient.cave","tick_delay":6000},"sky_color":8233727,"water_color":4159204,"water_fog_color":329011},"has_precipitation":1,"temperature":0.2},"id":59,"name":"minecraft:windswept_forest"},{"element":{"downfall":0.3,"effects":{"fog_color":12638463,"mood_sound":{"block_search_extent":8,"offset":2,"sound":"minecraft:ambient.cave","tick_delay":6000},"sky_color":8233727,"water_color":4159204,"water_fog_color":329011},"has_precipitation":1,"temperature":0.2},"id":60,"name":"minecraft:windswept_gravelly_hills"},{"element":{"downfall":0.3,"effects":{"fog_color":12638463,"mood_sound":{"block_search_extent":8,"offset":2,"sound":"minecraft:ambient.cave","tick_delay":6000},"sky_color":8233727,"water_color":4159204,"water_fog_color":329011},"has_precipitation":1,"temperature":0.2},"id":61,"name":"minecraft:windswept_hills"},{"element":{"downfall":0,"effects":{"fog_color":12638463,"mood_sound":{"block_search_extent":8,"offset":2,"sound":"minecraft:ambient.cave","tick_delay":6000},"sky_color":7254527,"water_color":4159204,"water_fog_color":329011},"has_precipitation":0,"temperature":2},"id":62,"name":"minecraft:windswept_savanna"},{"element":{"downfall":0,"effects":{"fog_color":12638463,"foliage_color":10387789,"grass_color":9470285,"mood_sound":{"block_search_extent":8,"offset":2,"sound":"minecraft:ambient.cave","tick_delay":6000},"music":{"max_delay":24000,"min_delay":12000,"replace_current_music":0,"sound":"minecraft:music.overworld.badlands"},"sky_color":7254527,"water_color":4159204,"water_fog_color":329011},"has_precipitation":0,"temperature":2},"id":63,"name":"minecraft:wooded_badlands"}]}} \ No newline at end of file diff --git a/src/crates/ferrumc_net/src/lib.rs b/src/crates/ferrumc_net/src/lib.rs index f3915027..6a443d5c 100644 --- a/src/crates/ferrumc_net/src/lib.rs +++ b/src/crates/ferrumc_net/src/lib.rs @@ -3,23 +3,25 @@ use std::cmp::PartialEq; use std::fmt::Display; use std::io::Cursor; +use std::ops::DerefMut; use std::sync::{Arc, atomic, OnceLock}; use std::sync::atomic::AtomicU32; +use std::time::Duration; use dashmap::DashMap; -use log::{debug, trace}; use rand::random; -use tokio::io::AsyncReadExt; -use tokio::io::AsyncWriteExt; +use tokio::io::{AsyncReadExt, AsyncWriteExt}; use tokio::sync::RwLock; -use ferrumc_utils::config::get_global_config; +use tracing::{debug, error, trace}; +use ferrumc_utils::config::get_global_config; use ferrumc_utils::encoding::varint::read_varint; use ferrumc_utils::prelude::*; use crate::packets::handle_packet; pub mod packets; +pub mod the_dimension_codec; #[allow(non_snake_case)] pub fn CONNECTIONS() -> &'static ConnectionList { @@ -30,7 +32,6 @@ pub fn CONNECTIONS() -> &'static ConnectionList { }) } - #[derive(PartialEq, Debug)] pub enum State { Unknown, @@ -58,6 +59,9 @@ impl State { } } +/// A list of connections, with a counter for the number of connections. +/// +/// In desperate need of reworking. pub struct ConnectionList { // The connections, keyed with random values. The value also contains the connection id for ease of access. // pub connections: DashMap, @@ -66,19 +70,20 @@ pub struct ConnectionList { pub connection_count: AtomicU32, } -#[derive()] +/// A connection to a client. +/// +/// - `id`: The numerical ID for the connection. Is also the key for it's [ConnectionList] entry. +/// - `socket`: The TCP socket for the connection ([tokio::net::TcpStream]). +/// - `player_uuid`: The UUID of the player, if the connection is authenticated ([uuid::Uuid]). +/// - `state`: The current state of the connection ([State]). +/// - `metadata`: Metadata for the connection ([ConnectionMetadata]). +/// - `drop`: Whether to drop and clean up the connection after this network tick. pub struct Connection { - // The connection id. pub id: u32, - // The socket. pub socket: tokio::net::TcpStream, - // The player uuid, if the connection is authenticated. pub player_uuid: Option, - // State pub state: State, - // Metadata pub metadata: ConnectionMetadata, - // Whether to drop and clean up the connection pub drop: bool, } @@ -91,57 +96,80 @@ pub fn setup_tracer() { console_subscriber::init(); } -pub async fn handle_connection(socket: tokio::net::TcpStream) -> Result<()> { +/// Handles a connection. This is the main entry point for a connection. +/// +/// - `socket`: The TCP socket for the connection ([tokio::net::TcpStream]). +/// +/// Creates a new [Connection] and adds it to the [ConnectionList]. Passes the connection to [manage_conn]. +pub async fn init_connection(socket: tokio::net::TcpStream) -> Result<()> { let mut id = random(); - // check if we have a collision (1 in 4.2 billion chance) and if so, generate a new id while CONNECTIONS().connections.contains_key(&id) { id = random(); } - let conn = Arc::new(RwLock::new( - Connection { - id, - socket, - player_uuid: None, - state: State::Handshake, - metadata: ConnectionMetadata::default(), - drop: false, - })); - - // Add the connection to the connections list - CONNECTIONS().connections.insert(id, conn); + + let conn = Connection { + id, + socket, + player_uuid: None, + state: State::Handshake, + metadata: ConnectionMetadata::default(), + drop: false, + }; + let conn = Arc::new(RwLock::new(conn)); + + // Doesn't matter if we clone, since actual value is not cloned + CONNECTIONS().connections.insert(id, conn.clone()); CONNECTIONS() .connection_count .fetch_add(1, atomic::Ordering::Relaxed); - debug!("Connection established with id: {}. Current connection count: {}", id, CONNECTIONS().connection_count.load(atomic::Ordering::Relaxed)); + let current_amount = CONNECTIONS() + .connection_count + .load(atomic::Ordering::Relaxed); + debug!( + "Connection established with id: {}. Current connection count: {}", + id, current_amount + ); - // Get a reference to the connection - let mut conn_ref = CONNECTIONS().connections.view(&id, |_k, v| { v.clone() }).unwrap(); - manage_conn(&mut conn_ref).await?; + let res = manage_conn(conn).await; + + if let Err(e) = res { + error!("Error occurred in {:?}: {:?}, dropping connection", id, e); + drop_conn(id).await?; + } Ok(()) } -pub async fn manage_conn(conn: &mut Arc>) -> Result<()> { - debug!("Starting receiver for the same addr: {:?}", conn.read().await.socket.peer_addr()?); +/// Manages a connection. This is the main loop for a connection. +/// +/// - `conn`: The connection to manage ([Arc>]). +/// +/// Reads packets from the connection and passes them to [handle_packet]. The handle_packet function +/// is generated at compile time by [ferrumc_macros::bake_packet_registry]. +pub async fn manage_conn(conn: Arc>) -> Result<()> { + debug!( + "Starting receiver for the same addr: {:?}", + conn.read().await.socket.peer_addr()? + ); loop { // Get the length of the packet let mut length_buffer = vec![0u8; 1]; - { - let mut conn_write = conn.write().await; - conn_write.socket.read_exact(&mut length_buffer).await?; - } + + trace!("Reading length buffer"); + + let mut conn_write = conn.write().await; + conn_write.socket.read_exact(&mut length_buffer).await?; + + trace!("Length buffer: {:?}", length_buffer); let length = length_buffer[0] as usize; // Get the rest of the packet let mut buffer = vec![0u8; length]; - { - let mut conn_write = conn.write().await; - conn_write.socket.read_exact(&mut buffer).await?; - } + conn_write.socket.read_exact(&mut buffer).await?; let buffer = vec![length_buffer, buffer].concat(); @@ -154,37 +182,51 @@ pub async fn manage_conn(conn: &mut Arc>) -> Result<()> { trace!("Packet Length: {}", packet_length); trace!("Packet ID: {}", packet_id); + let packet_id = packet_id.get_val() as u8; + let actual_connection = conn_write.deref_mut(); // Handle the packet - handle_packet(packet_id.get_val() as u8, conn, &mut cursor).await?; + handle_packet(packet_id, actual_connection, &mut cursor).await?; + + // drop the handle to the write lock. to allow other tasks to write/read + drop(conn_write); + + let read = conn.read().await; - // Check if we need to drop the connection - let do_drop = conn.read().await.drop; - let id = conn.read().await.id; + // drop if the connection is marked for drop + let do_drop = read.drop; + let id = read.id; + + drop(read); - // Drop the connection if needed if do_drop { drop_conn(id).await?; - conn.write().await.socket.shutdown().await?; break; } - - tokio::time::sleep( - if get_global_config().network_tick_rate > 0 { - std::time::Duration::from_millis(1000 / get_global_config().network_tick_rate as u64) - } else { - std::time::Duration::from_millis(0) - } - ).await; + + let tick_rate = get_global_config().network_tick_rate; + let sleep_duration_millis: u64 = if tick_rate > 0 { + 1000 / tick_rate as u64 + } else { + 0 + }; + + tokio::time::sleep(Duration::from_millis(sleep_duration_millis)).await; } + #[allow(unreachable_code)] Ok(()) } async fn drop_conn(connection_id: u32) -> Result<()> { debug!("Dropping connection with id: {}", connection_id); - CONNECTIONS().connections.remove(&connection_id); - CONNECTIONS().connection_count.fetch_sub(1, atomic::Ordering::Relaxed); + let connection = CONNECTIONS().connections.remove(&connection_id); + let Some((_, conn_arc)) = connection else { + return Err(Error::ConnectionNotFound(connection_id)); + }; + CONNECTIONS() + .connection_count + .fetch_sub(1, atomic::Ordering::Relaxed); + // drop the connection in the end, just in case it errors out + let mut conn = conn_arc.write().await; + conn.socket.shutdown().await?; Ok(()) } - - - diff --git a/src/crates/ferrumc_net/src/packets/incoming/handshake.rs b/src/crates/ferrumc_net/src/packets/incoming/handshake.rs index 494fc4ad..a74b5d9d 100644 --- a/src/crates/ferrumc_net/src/packets/incoming/handshake.rs +++ b/src/crates/ferrumc_net/src/packets/incoming/handshake.rs @@ -1,9 +1,13 @@ use ferrumc_macros::{Decode, packet}; use ferrumc_utils::encoding::varint::VarInt; +use ferrumc_utils::prelude::*; use crate::{Connection, State}; use crate::packets::IncomingPacket; +/// The first packet sent by the client to the server. +/// +/// This packet is used to negotiate the protocol version, server address, server port, and the next state. #[derive(Decode)] #[packet(packet_id = 0x00, state = "handshake")] pub struct Handshake { @@ -14,13 +18,13 @@ pub struct Handshake { } impl IncomingPacket for Handshake { - async fn handle(&self, conn: &mut tokio::sync::RwLockWriteGuard<'_, Connection>) -> Result<(), Error> { + async fn handle(&self, conn: &mut Connection) -> Result<()> { - conn.metadata.protocol_version = 763; + conn.metadata.protocol_version = self.protocol_version.get_val(); conn.state = match self.next_state.get_val() { 1 => State::Status, 2 => State::Login, - s => {return Err(Error::InvalidState(s as u32))} + s => {return Err(Error::InvalidState(s))} }; Ok(()) diff --git a/src/crates/ferrumc_net/src/packets/incoming/loginstart.rs b/src/crates/ferrumc_net/src/packets/incoming/loginstart.rs new file mode 100644 index 00000000..5736e7da --- /dev/null +++ b/src/crates/ferrumc_net/src/packets/incoming/loginstart.rs @@ -0,0 +1,115 @@ +use include_flate::flate; +use tokio::io::AsyncWriteExt; +use tracing::debug; +use uuid::Uuid; + +use ferrumc_macros::{Decode, packet}; +use ferrumc_utils::encoding::position::Position; +use ferrumc_utils::encoding::varint::VarInt; +use ferrumc_utils::prelude::*; +use ferrumc_utils::type_impls::Encode; + +use crate::Connection; +use crate::packets::IncomingPacket; +use crate::State::Play; + +/// The login start packet is sent by the client to the server to start the login process. +/// +/// Server responds with [crate::packets::outgoing::login_success::LoginSuccess], +/// [crate::packets::outgoing::login_play::LoginPlay], and +/// [crate::packets::outgoing::defaultspawnposition::DefaultSpawnPosition] packets in that order. +/// No response is required from the client while these are being sent. +/// +/// This is the final stage in the login process. The client is now in the play state. +#[derive(Decode)] +#[packet(packet_id = 0x00, state = "login")] +pub struct LoginStart { + pub username: String, + pub uuid: u128, +} + +// MAKE SURE YOU RUN THE TEST IN THE login_play.rs FILE TO GENERATE THE NBT FILE +// The NBT encoded data for the dimension codec. Using flate_include cos the codec file is like 40kb +#[cfg(not(test))] +flate!(pub static NBT_CODEC: [u8] from "codec.json"); + +#[cfg(test)] +const NBT_CODEC: &[u8] = &[0u8; 1]; + +impl IncomingPacket for LoginStart { + async fn handle(&self, conn: &mut Connection) -> Result<()> { + { + debug!("LoginStart packet received"); + debug!("Username: {}", self.username); + let uuid = Uuid::from_u128(self.uuid); + debug!("UUID: {uuid}"); + + let namespace_uuid = Uuid::new_v5(&Uuid::NAMESPACE_URL, "OfflinePlayer".as_bytes()); + let uuid = Uuid::new_v3(&namespace_uuid, self.username.as_bytes()); + + let response = crate::packets::outgoing::login_success::LoginSuccess { + packet_id: VarInt::from(0x02), + uuid: uuid.as_bytes().into(), + username: "OfflinePlayer".to_string(), + property_count: VarInt::new(0), + properties: vec![], + }; + + let mut cursor = std::io::Cursor::new(Vec::new()); + response.encode(&mut cursor).await?; + let response = cursor.into_inner(); + + conn.socket.write_all(&*response).await?; + } + + { + let play_packet = crate::packets::outgoing::login_play::LoginPlay { + packet_id: VarInt::from(0x28), + entity_id: 0, + hardcore: false, + gamemode: 0, + previous_gamemode: -1, + dimension_length: VarInt::new(1), + dimension_names: vec!["minecraft:overworld".to_string()], + registry_codec: NBT_CODEC.to_vec(), + dimension_type: "minecraft:overworld".to_string(), + dimension_name: "minecraft:overworld".to_string(), + seed_hash: 0, + max_players: VarInt::new(20), + view_distance: VarInt::new(10), + simulation_distance: VarInt::new(10), + reduced_debug_info: false, + enable_respawn_screen: true, + is_debug: false, + is_flat: false, + has_death_location: false, + portal_cooldown: VarInt::new(0), + }; + + let mut cursor = std::io::Cursor::new(Vec::new()); + play_packet.encode(&mut cursor).await?; + let play_packet = cursor.into_inner(); + + conn.socket.write_all(&*play_packet).await?; + } + + { + let spawn_position = + crate::packets::outgoing::defaultspawnposition::DefaultSpawnPosition { + packet_id: VarInt::from(0x50), + location: Position { x: 0, y: 0, z: 0 }, + angle: 0.0, + }; + + let mut cursor = std::io::Cursor::new(Vec::new()); + spawn_position.encode(&mut cursor).await?; + let spawn_position = cursor.into_inner(); + + conn.socket.write_all(&*spawn_position).await?; + } + + conn.state = Play; + + Ok(()) + } +} diff --git a/src/crates/ferrumc_net/src/packets/incoming/mod.rs b/src/crates/ferrumc_net/src/packets/incoming/mod.rs index 1bf3226a..3e1a629a 100644 --- a/src/crates/ferrumc_net/src/packets/incoming/mod.rs +++ b/src/crates/ferrumc_net/src/packets/incoming/mod.rs @@ -1,3 +1,5 @@ pub mod handshake; pub mod status; -pub mod ping; \ No newline at end of file +pub mod ping; +pub mod loginstart; +pub mod setplayerposition; \ No newline at end of file diff --git a/src/crates/ferrumc_net/src/packets/incoming/ping.rs b/src/crates/ferrumc_net/src/packets/incoming/ping.rs index b6ecc796..9572e3d8 100644 --- a/src/crates/ferrumc_net/src/packets/incoming/ping.rs +++ b/src/crates/ferrumc_net/src/packets/incoming/ping.rs @@ -1,13 +1,17 @@ -use log::info; -use tokio::io::AsyncWriteExt; - use ferrumc_macros::{Decode, packet}; use ferrumc_utils::encoding::varint::VarInt; - +use ferrumc_utils::prelude::*; +use ferrumc_utils::type_impls::Encode; +use tokio::io::AsyncWriteExt; +use tracing::info; use crate::Connection; use crate::packets::IncomingPacket; use crate::packets::outgoing::ping::OutgoingPing; +/// This ping packet is sent by the client to the server to request a pong. +/// +/// The payload is a random number that the server should return in the pong. +/// For some reason, seems to be required for the client to acknowledge the server's status response. #[derive(Decode)] #[packet(packet_id = 0x01, state = "status")] pub struct Ping { @@ -15,18 +19,21 @@ pub struct Ping { } impl IncomingPacket for Ping { - async fn handle(&self, conn: &mut tokio::sync::RwLockWriteGuard<'_, Connection>) -> Result<(), ferrumc_utils::error::Error> { + async fn handle(&self, conn: &mut Connection) -> Result<()> { info!("Handling ping packet"); + // tokio::io::AsyncWriteExt::write_all() let response = OutgoingPing { packet_id: VarInt::from(0x01), payload: self.payload, }; - let response = response.encode().await?; - + let mut cursor = std::io::Cursor::new(Vec::new()); + response.encode(&mut cursor).await?; + let response = cursor.into_inner(); + conn.drop = true; - conn.socket.write_all(&response).await.map_err(|e| e.into()) + conn.socket.write_all(&*response).await.map_err(|e| e.into()) } } \ No newline at end of file diff --git a/src/crates/ferrumc_net/src/packets/incoming/setplayerposition.rs b/src/crates/ferrumc_net/src/packets/incoming/setplayerposition.rs new file mode 100644 index 00000000..10cf9412 --- /dev/null +++ b/src/crates/ferrumc_net/src/packets/incoming/setplayerposition.rs @@ -0,0 +1,24 @@ +use tracing::{info, trace}; +use ferrumc_macros::{Decode, packet}; +use crate::Connection; +use crate::packets::IncomingPacket; + +/// The set player position packet is sent by the client to the server to update the player's position. +#[derive(Decode)] +#[packet(packet_id = 0x14, state = "play")] +pub struct SetPlayerPosition { + pub x: f64, + pub y: f64, + pub z: f64, + pub on_ground: bool +} + +impl IncomingPacket for SetPlayerPosition { + async fn handle(&self, _: &mut Connection) -> ferrumc_utils::prelude::Result<()> { + trace!("SetPlayerPosition packet received"); + trace!("X: {}", self.x); + trace!("Y: {}", self.y); + trace!("Z: {}", self.z); + Ok(()) + } +} \ No newline at end of file diff --git a/src/crates/ferrumc_net/src/packets/incoming/status.rs b/src/crates/ferrumc_net/src/packets/incoming/status.rs index 2bc1314b..e9286b1e 100644 --- a/src/crates/ferrumc_net/src/packets/incoming/status.rs +++ b/src/crates/ferrumc_net/src/packets/incoming/status.rs @@ -1,21 +1,28 @@ use base64::Engine; -use log::info; use serde::Serialize; use tokio::io::{AsyncReadExt, AsyncWriteExt}; use tokio::sync::OnceCell; +use tracing::info; use ferrumc_macros::{Decode, packet}; use ferrumc_utils::config; use ferrumc_utils::encoding::varint::VarInt; +use ferrumc_utils::prelude::*; +use ferrumc_utils::type_impls::Encode; use crate::Connection; use crate::packets::IncomingPacket; use crate::packets::outgoing::status::OutgoingStatusResponse; +/// The status packet is sent by the client to the server to request the server's status. +/// +/// Usually sent after handshaking is completed. #[derive(Decode)] #[packet(packet_id = 0x00, state = "status")] pub struct Status; +/// The response to the status packet. +/// Sent as json. #[derive(Serialize)] struct JsonResponse { version: Version, @@ -49,7 +56,7 @@ struct Description { } impl IncomingPacket for Status { - async fn handle(&self, conn: &mut tokio::sync::RwLockWriteGuard<'_, Connection>) -> Result<(), Error> { + async fn handle(&self, conn: &mut Connection) -> Result<()> { info!("Handling status request packet"); let config = config::get_global_config(); @@ -79,23 +86,36 @@ impl IncomingPacket for Status { text: config.motd.clone(), }, favicon: get_encoded_favicon().await, - }).unwrap(), + }) + .unwrap(), }; - let response = response.encode().await?; + let mut cursor = std::io::Cursor::new(Vec::new()); + response.encode(&mut cursor).await?; + let response = cursor.into_inner(); - conn.socket.write_all(&response).await.map_err(|e| e.into()) + let response = &*response; + + conn.socket.write(response).await?; + + Ok(()) } } + +/// Get the favicon as a base64 encoded string. +/// +/// This is cached in a `OnceCell` to avoid reading the file every time. async fn get_encoded_favicon() -> &'static String { - static FAVICON: OnceCell = OnceCell:: const_new(); - FAVICON.get_or_init(|| async { - let mut data = Vec::new(); - let Ok(mut image) = tokio::fs::File::open("icon-64.png").await else { - return String::new(); - }; - image.read_to_end(&mut data).await.unwrap_or_default(); - let data = base64::engine::general_purpose::STANDARD.encode(&data); - format!("data:image/png;base64,{}", data) - }).await -} \ No newline at end of file + static FAVICON: OnceCell = OnceCell::const_new(); + FAVICON + .get_or_init(|| async { + let mut data = Vec::new(); + let Ok(mut image) = tokio::fs::File::open("icon-64.png").await else { + return String::new(); + }; + image.read_to_end(&mut data).await.unwrap_or_default(); + let data = base64::engine::general_purpose::STANDARD.encode(&data); + format!("data:image/png;base64,{}", data) + }) + .await +} diff --git a/src/crates/ferrumc_net/src/packets/mod.rs b/src/crates/ferrumc_net/src/packets/mod.rs index d427082e..e29a9daa 100644 --- a/src/crates/ferrumc_net/src/packets/mod.rs +++ b/src/crates/ferrumc_net/src/packets/mod.rs @@ -1,9 +1,5 @@ -use std::sync::Arc; - -use tokio::sync::RwLock; - use ferrumc_macros::bake_packet_registry; -use ferrumc_utils::error::Error; +use ferrumc_utils::prelude::*; use crate::Connection; @@ -12,7 +8,7 @@ pub mod outgoing; pub trait IncomingPacket { #[allow(async_fn_in_trait)] - async fn handle(&self, conn: &mut tokio::sync::RwLockWriteGuard) -> Result<(), Error>; + async fn handle(&self, conn: &mut Connection) -> Result<()>; } diff --git a/src/crates/ferrumc_net/src/packets/outgoing/defaultspawnposition.rs b/src/crates/ferrumc_net/src/packets/outgoing/defaultspawnposition.rs new file mode 100644 index 00000000..108cf3ec --- /dev/null +++ b/src/crates/ferrumc_net/src/packets/outgoing/defaultspawnposition.rs @@ -0,0 +1,11 @@ +use ferrumc_macros::Encode; +use ferrumc_utils::encoding::position::Position; +use ferrumc_utils::encoding::varint::VarInt; + +/// The default spawn position packet is sent by the server to the client to set the player's spawn position. +#[derive(Encode)] +pub struct DefaultSpawnPosition { + pub packet_id: VarInt, + pub location: Position, + pub angle: f32 +} \ No newline at end of file diff --git a/src/crates/ferrumc_net/src/packets/outgoing/login_disconnect.rs b/src/crates/ferrumc_net/src/packets/outgoing/login_disconnect.rs new file mode 100644 index 00000000..94db0361 --- /dev/null +++ b/src/crates/ferrumc_net/src/packets/outgoing/login_disconnect.rs @@ -0,0 +1,10 @@ +use ferrumc_macros::Encode; +use ferrumc_utils::encoding::varint::VarInt; + +/// The login disconnect packet is sent by the server to the client to disconnect the client. +/// Used to cancel the login process. +#[derive(Encode)] +pub struct LoginDisconnect { + pub packet_id: VarInt, + pub reason: String, +} \ No newline at end of file diff --git a/src/crates/ferrumc_net/src/packets/outgoing/login_play.rs b/src/crates/ferrumc_net/src/packets/outgoing/login_play.rs new file mode 100644 index 00000000..2a384713 --- /dev/null +++ b/src/crates/ferrumc_net/src/packets/outgoing/login_play.rs @@ -0,0 +1,44 @@ +use ferrumc_macros::Encode; +use ferrumc_utils::encoding::varint::VarInt; + +/// The login play packet is sent by the server to the client to start the play state. +/// Contains info about the world +#[derive(Encode)] +pub struct LoginPlay { + pub packet_id: VarInt, + pub entity_id: i32, + pub hardcore: bool, + pub gamemode: u8, + pub previous_gamemode: i8, + pub dimension_length: VarInt, + pub dimension_names: Vec, + /// The codec for the dimension. Baked into the binary, see [crate::packets::incoming::loginstart::LoginStart::decode]. + #[encode(raw_bytes(prepend_length = false))] + pub registry_codec: Vec, + pub dimension_type: String, + pub dimension_name: String, + pub seed_hash: i64, + pub max_players: VarInt, + pub view_distance: VarInt, + pub simulation_distance: VarInt, + pub reduced_debug_info: bool, + pub enable_respawn_screen: bool, + pub is_debug: bool, + pub is_flat: bool, + pub has_death_location: bool, + // pub death_dimension_name: Option, + // pub death_location: Option, + pub portal_cooldown: VarInt, +} + +// A test to just produce the codec file +#[cfg(test)] +#[test] +fn generate_codec() { + use crate::the_dimension_codec::Root; + let codec_file = std::fs::File::open("../ferrumc_net/codec.json").unwrap(); + let reader = std::io::BufReader::new(codec_file); + let mut codec: Root = serde_json::from_reader(reader).unwrap(); + let mut codec_nbt_file = std::fs::File::create("../ferrumc_net/nbt_codec.nbt").unwrap(); + fastnbt::to_writer(&mut codec_nbt_file, &mut codec).unwrap(); +} diff --git a/src/crates/ferrumc_net/src/packets/outgoing/login_success.rs b/src/crates/ferrumc_net/src/packets/outgoing/login_success.rs new file mode 100644 index 00000000..fba54b76 --- /dev/null +++ b/src/crates/ferrumc_net/src/packets/outgoing/login_success.rs @@ -0,0 +1,23 @@ +use ferrumc_macros::{Encode}; +use ferrumc_utils::encoding::varint::VarInt; + +/// Sent by the server to the client to start the play state. +#[derive(Encode)] +pub struct LoginSuccess { + pub packet_id: VarInt, + pub uuid: Vec, + pub username: String, + // Just set this to 0 + pub property_count: VarInt, + // TODO: Figure out how what in the everloving fuck this is + pub properties: Vec, +} + +#[derive(Encode)] +pub struct Property { + pub name: String, + pub value: String, + pub is_signed: bool, + // Only if is_signed is true + pub signature: String +} \ No newline at end of file diff --git a/src/crates/ferrumc_net/src/packets/outgoing/mod.rs b/src/crates/ferrumc_net/src/packets/outgoing/mod.rs index 8a4d8d7f..8d9a2e9e 100644 --- a/src/crates/ferrumc_net/src/packets/outgoing/mod.rs +++ b/src/crates/ferrumc_net/src/packets/outgoing/mod.rs @@ -1,2 +1,6 @@ pub mod status; -pub mod ping; \ No newline at end of file +pub mod ping; +pub mod login_disconnect; +pub mod login_success; +pub mod login_play; +pub mod defaultspawnposition; \ No newline at end of file diff --git a/src/crates/ferrumc_net/src/packets/outgoing/ping.rs b/src/crates/ferrumc_net/src/packets/outgoing/ping.rs index 665819e0..0506eb6a 100644 --- a/src/crates/ferrumc_net/src/packets/outgoing/ping.rs +++ b/src/crates/ferrumc_net/src/packets/outgoing/ping.rs @@ -1,5 +1,8 @@ use ferrumc_macros::Encode; +use ferrumc_utils::encoding::varint::VarInt; +/// The outgoing ping packet is sent by the server to the client to check the connection. +/// Payload is just the same as whatever the client sent. #[derive(Encode)] pub struct OutgoingPing { pub packet_id: VarInt, diff --git a/src/crates/ferrumc_net/src/packets/outgoing/status.rs b/src/crates/ferrumc_net/src/packets/outgoing/status.rs index ebe09084..611c0f53 100644 --- a/src/crates/ferrumc_net/src/packets/outgoing/status.rs +++ b/src/crates/ferrumc_net/src/packets/outgoing/status.rs @@ -1,5 +1,8 @@ use ferrumc_macros::Encode; +use ferrumc_utils::encoding::varint::VarInt; +/// The outgoing status response packet is sent by the server to the client to respond to a status request. +/// Contains the JSON response. #[derive(Encode)] pub struct OutgoingStatusResponse { pub packet_id: VarInt, diff --git a/src/crates/ferrumc_net/src/the_dimension_codec.rs b/src/crates/ferrumc_net/src/the_dimension_codec.rs new file mode 100644 index 00000000..7385b7fd --- /dev/null +++ b/src/crates/ferrumc_net/src/the_dimension_codec.rs @@ -0,0 +1,323 @@ +use serde::{Deserialize, Serialize}; +use serde_json::Value; + +#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct Root { + #[serde(rename = "minecraft:chat_type")] + pub minecraft_chat_type: MinecraftChatType, + #[serde(rename = "minecraft:damage_type")] + pub minecraft_damage_type: MinecraftDamageType, + #[serde(rename = "minecraft:dimension_type")] + pub minecraft_dimension_type: MinecraftDimensionType, + #[serde(rename = "minecraft:trim_material")] + pub minecraft_trim_material: MinecraftTrimMaterial, + #[serde(rename = "minecraft:trim_pattern")] + pub minecraft_trim_pattern: MinecraftTrimPattern, + #[serde(rename = "minecraft:worldgen/biome")] + pub minecraft_worldgen_biome: MinecraftWorldgenBiome, +} + +#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct MinecraftChatType { + #[serde(rename = "type")] + pub type_field: String, + pub value: Vec, +} + +#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct InternalValue { + pub element: Element, + pub id: i64, + pub name: String, +} + +#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct Element { + pub chat: Chat, + pub narration: Narration, +} + +#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct Chat { + pub parameters: Vec, + #[serde(rename = "translation_key")] + pub translation_key: String, + pub style: Option