diff --git a/.cargo/config.toml b/.cargo/config_fast_builds similarity index 100% rename from .cargo/config.toml rename to .cargo/config_fast_builds diff --git a/.gitignore b/.gitignore index c5b441e73..99c2e43ed 100755 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,9 @@ /doc .vscode +.cargo/config +.cargo/config.toml + # created by azalea-auth/examples/auth, defined in the main .gitignore because # the example could be run from anywhere example_cache.json diff --git a/Cargo.lock b/Cargo.lock index 5e33e17f3..1b4f1e745 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -70,15 +70,15 @@ checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" [[package]] name = "anstyle" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b84bf0a05bbb2a83e5eb6fa36bb6e87baa08193c35ff52bbf6b38d8af2890e46" +checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87" [[package]] name = "anyhow" -version = "1.0.75" +version = "1.0.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" +checksum = "c9d19de80eff169429ac1e9f48fffb163916b448a44e8e046186232046d9e1f9" [[package]] name = "as-any" @@ -93,44 +93,33 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35" dependencies = [ "concurrent-queue", - "event-listener", + "event-listener 2.5.3", "futures-core", ] -[[package]] -name = "async-compression" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc2d0cfb2a7388d34f590e76686704c494ed7aaceed62ee1ba35cbf363abc2a5" -dependencies = [ - "flate2", - "futures-core", - "memchr", - "pin-project-lite", - "tokio", -] - [[package]] name = "async-executor" -version = "1.5.4" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c1da3ae8dabd9c00f453a329dfe1fb28da3c0a72e2478cdcd93171740c20499" +checksum = "17ae5ebefcc48e7452b4987947920dac9450be1110cadf34d1b8c116bdbaf97c" dependencies = [ "async-lock", "async-task", "concurrent-queue", "fastrand 2.0.1", - "futures-lite 1.13.0", + "futures-lite 2.1.0", "slab", ] [[package]] name = "async-lock" -version = "2.8.0" +version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "287272293e9d8c41773cec55e365490fe034813a2f172f502d6ddcf75b2f582b" +checksum = "7125e42787d53db9dd54261812ef17e937c95a51e4d291373b670342fa44310c" dependencies = [ - "event-listener", + "event-listener 4.0.1", + "event-listener-strategy", + "pin-project-lite", ] [[package]] @@ -141,24 +130,24 @@ checksum = "5fd55a5ba1179988837d24ab4c7cc8ed6efdeff578ede0416b4225a5fca35bd0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.43", ] [[package]] name = "async-task" -version = "4.4.1" +version = "4.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9441c6b2fe128a7c2bf680a44c34d0df31ce09e5b7e401fcca3faa483dbc921" +checksum = "e1d90cd0b264dfdd8eb5bad0a2c217c1f88fa96a8573f40e7b12de23fb468f46" [[package]] name = "async-trait" -version = "0.1.74" +version = "0.1.75" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a66537f1bb974b254c98ed142ff995236e81b9d0fe4db0575f46612cb15eb0f9" +checksum = "fdf6721fb0140e4f897002dd086c06f6c27775df19cfe1fccb21181a48fd2c98" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.43", ] [[package]] @@ -169,13 +158,14 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "azalea" -version = "0.8.0" +version = "0.9.0" dependencies = [ "anyhow", "async-trait", "azalea-auth", "azalea-block", "azalea-brigadier", + "azalea-buf", "azalea-chat", "azalea-client", "azalea-core", @@ -193,7 +183,7 @@ dependencies = [ "criterion", "derive_more", "futures", - "futures-lite 2.0.1", + "futures-lite 2.1.0", "nohash-hasher", "num-traits", "parking_lot", @@ -208,13 +198,14 @@ dependencies = [ [[package]] name = "azalea-auth" -version = "0.8.0" +version = "0.9.0" dependencies = [ "azalea-buf", "azalea-crypto", "base64", "chrono", "env_logger", + "md-5", "num-bigint", "once_cell", "reqwest", @@ -229,7 +220,7 @@ dependencies = [ [[package]] name = "azalea-block" -version = "0.8.0" +version = "0.9.0" dependencies = [ "azalea-block-macros", "azalea-buf", @@ -238,16 +229,16 @@ dependencies = [ [[package]] name = "azalea-block-macros" -version = "0.8.0" +version = "0.9.0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.43", ] [[package]] name = "azalea-brigadier" -version = "0.8.0" +version = "0.9.0" dependencies = [ "azalea-buf", "azalea-chat", @@ -256,7 +247,7 @@ dependencies = [ [[package]] name = "azalea-buf" -version = "0.8.0" +version = "0.9.0" dependencies = [ "azalea-buf-macros", "byteorder", @@ -269,28 +260,30 @@ dependencies = [ [[package]] name = "azalea-buf-macros" -version = "0.8.0" +version = "0.9.0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.43", ] [[package]] name = "azalea-chat" -version = "0.8.0" +version = "0.9.0" dependencies = [ "azalea-buf", "azalea-language", + "azalea-registry", "once_cell", "serde", "serde_json", + "simdnbt", "tracing", ] [[package]] name = "azalea-client" -version = "0.8.0" +version = "0.9.0" dependencies = [ "anyhow", "async-trait", @@ -313,6 +306,7 @@ dependencies = [ "bevy_time", "derive_more", "futures", + "minecraft_folder_path", "nohash-hasher", "once_cell", "parking_lot", @@ -330,7 +324,7 @@ dependencies = [ [[package]] name = "azalea-core" -version = "0.8.0" +version = "0.9.0" dependencies = [ "azalea-buf", "azalea-inventory", @@ -347,7 +341,7 @@ dependencies = [ [[package]] name = "azalea-crypto" -version = "0.8.0" +version = "0.9.0" dependencies = [ "aes", "azalea-buf", @@ -364,7 +358,7 @@ dependencies = [ [[package]] name = "azalea-entity" -version = "0.8.0" +version = "0.9.0" dependencies = [ "azalea-block", "azalea-buf", @@ -387,7 +381,7 @@ dependencies = [ [[package]] name = "azalea-inventory" -version = "0.8.0" +version = "0.9.0" dependencies = [ "azalea-buf", "azalea-inventory-macros", @@ -397,16 +391,16 @@ dependencies = [ [[package]] name = "azalea-inventory-macros" -version = "0.8.0" +version = "0.9.0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.43", ] [[package]] name = "azalea-language" -version = "0.8.0" +version = "0.9.0" dependencies = [ "once_cell", "serde", @@ -415,7 +409,7 @@ dependencies = [ [[package]] name = "azalea-physics" -version = "0.8.0" +version = "0.9.0" dependencies = [ "azalea-block", "azalea-core", @@ -434,10 +428,9 @@ dependencies = [ [[package]] name = "azalea-protocol" -version = "0.8.0" +version = "0.9.0" dependencies = [ "anyhow", - "async-compression", "async-recursion", "azalea-auth", "azalea-block", @@ -456,8 +449,9 @@ dependencies = [ "bytes", "flate2", "futures", - "futures-lite 2.0.1", + "futures-lite 2.1.0", "futures-util", + "log", "once_cell", "serde", "serde_json", @@ -474,16 +468,16 @@ dependencies = [ [[package]] name = "azalea-protocol-macros" -version = "0.8.0" +version = "0.9.0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.43", ] [[package]] name = "azalea-registry" -version = "0.8.0" +version = "0.9.0" dependencies = [ "azalea-buf", "azalea-registry-macros", @@ -493,16 +487,16 @@ dependencies = [ [[package]] name = "azalea-registry-macros" -version = "0.8.0" +version = "0.9.0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.43", ] [[package]] name = "azalea-world" -version = "0.8.0" +version = "0.9.0" dependencies = [ "azalea-block", "azalea-buf", @@ -554,9 +548,9 @@ checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" [[package]] name = "bevy_app" -version = "0.12.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "172d532ea812e5954fa814dae003c207f2a0b20c6e50431787c94a7159677ece" +checksum = "d41731817993f92e4363dd3335558e779e290bc71eefc0b5547052b85810907e" dependencies = [ "bevy_derive", "bevy_ecs", @@ -570,20 +564,20 @@ dependencies = [ [[package]] name = "bevy_derive" -version = "0.12.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24bf40259be12a1a24d9fd536f5ff18d31eeb5665b77e2732899783be6edc5d6" +checksum = "f484318350462c58ba3942a45a656c1fd6b6e484a6b6b7abc3a787ad1a51e500" dependencies = [ "bevy_macro_utils", "quote", - "syn 2.0.39", + "syn 2.0.43", ] [[package]] name = "bevy_ecs" -version = "0.12.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae11a1f467c372b50e9d4b55e78370f5420c9db7416200cc441cc84f08174dd3" +checksum = "7709fbd22f81fb681534cd913c41e1cd18b17143368743281195d7f024b61aea" dependencies = [ "async-channel", "bevy_ecs_macros", @@ -592,7 +586,7 @@ dependencies = [ "bevy_tasks", "bevy_utils", "downcast-rs", - "event-listener", + "event-listener 2.5.3", "fixedbitset", "rustc-hash", "serde", @@ -602,50 +596,50 @@ dependencies = [ [[package]] name = "bevy_ecs_macros" -version = "0.12.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f642c2b67c4d0daf8edf15074f6351457eb487a34b3de1290c760d8f3ac9ec16" +checksum = "a8843aa489f159f25cdcd9fee75cd7d221a7098a71eaa72cb2d6b40ac4e3f1ba" dependencies = [ "bevy_macro_utils", "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.43", ] [[package]] name = "bevy_log" -version = "0.12.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "011417debf7868b45932bb97fc0d5bfdeaf9304e324aa94840e2f1e6deeed69d" +checksum = "0dc10ba1d225a8477b9e80a1bf797d8a8b8274e83c9b24fb4d9351aec9229755" dependencies = [ "android_log-sys", "bevy_app", "bevy_ecs", "bevy_utils", "console_error_panic_hook", - "tracing-log 0.1.3", + "tracing-log 0.1.4", "tracing-subscriber", "tracing-wasm", ] [[package]] name = "bevy_macro_utils" -version = "0.12.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf6fba87c6d069fcbcd8a48625ca8ab4392ad40d2b260863ce7d641a0f42986d" +checksum = "e566640c6b6dced73d2006c764c2cffebe1a82be4809486c4a5d7b4b50efed4d" dependencies = [ "proc-macro2", "quote", "rustc-hash", - "syn 2.0.39", + "syn 2.0.43", "toml_edit", ] [[package]] name = "bevy_math" -version = "0.12.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "752764558a1f429c20704c3b836a019fa308961c43fdfef4f08e339d456c96be" +checksum = "58ddc2b76783939c530178f88e5711a1b01044d7b02db4033e2eb8b43b6cf4ec" dependencies = [ "glam", "serde", @@ -653,15 +647,15 @@ dependencies = [ [[package]] name = "bevy_ptr" -version = "0.12.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "308a02679f6ce21ef71de20fae6d6a2016c07baa21d8e8d0558e6b7851e8adf2" +checksum = "c77ec20c8fafcdc196508ef5ccb4f0400a8d193cb61f7b14a36ed9a25ad423cf" [[package]] name = "bevy_reflect" -version = "0.12.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdd56914a8ad57621d7a1a099f7e6b1f7482c9c76cedc9c3d4c175a203939c5d" +checksum = "d7921f15fc944c9c8ad01d7dbcea6505b8909c6655cd9382bab1407181556038" dependencies = [ "bevy_math", "bevy_ptr", @@ -678,22 +672,22 @@ dependencies = [ [[package]] name = "bevy_reflect_derive" -version = "0.12.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25f627907c40ac552f798423447fc331fc1ddacd94c5f7a2a70942eb06bc8447" +checksum = "b4a8c5475f216e751ef4452a1306b00711f33d2d04d9f149e4c845dfeb6753a0" dependencies = [ "bevy_macro_utils", "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.43", "uuid", ] [[package]] name = "bevy_tasks" -version = "0.12.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a45be906618192515bc613e46546150089adbb4a82178dc462045acd1e89e92" +checksum = "f4fefa7fe0da8923525f7500e274f1bd60dbd79918a25cf7d0dfa0a6ba15c1cf" dependencies = [ "async-channel", "async-executor", @@ -705,9 +699,9 @@ dependencies = [ [[package]] name = "bevy_time" -version = "0.12.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b29709cadf22d318a0b7c79f763e9c5ac414292bd0e850066fa935959021b276" +checksum = "e6250d76eed3077128b6a3d004f9f198b01107800b9824051e32bb658054e837" dependencies = [ "bevy_app", "bevy_ecs", @@ -719,14 +713,14 @@ dependencies = [ [[package]] name = "bevy_utils" -version = "0.12.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8e75d4a34ef0b15dffd1ee9079ef1f0f5139527e192b9d5708b3e158777c753" +checksum = "7915222f4a08ccc782e08d10b751b42e5f9d786e697d0cb3fd09333cb7e8b6ea" dependencies = [ "ahash", "bevy_utils_proc_macros", "getrandom", - "hashbrown 0.14.2", + "hashbrown 0.14.3", "instant", "nonmax", "petgraph", @@ -737,13 +731,13 @@ dependencies = [ [[package]] name = "bevy_utils_proc_macros" -version = "0.12.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7dfd3735a61a1b681ed1e176afe4eae731bbb03e51ad871e9eb39e76a2d170e" +checksum = "7aafecc952b6b8eb1a93c12590bd867d25df2f4ae1033a01dfdfc3c35ebccfff" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.43", ] [[package]] @@ -870,18 +864,18 @@ dependencies = [ [[package]] name = "clap" -version = "4.4.3" +version = "4.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84ed82781cea27b43c9b106a979fe450a13a31aab0500595fb3fc06616de08e6" +checksum = "dcfab8ba68f3668e89f6ff60f5b205cea56aa7b769451a59f34b8682f51c056d" dependencies = [ "clap_builder", ] [[package]] name = "clap_builder" -version = "4.4.2" +version = "4.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bb9faaa7c2ef94b2743a21f5a29e6f0010dff4caa69ac8e9d6cf8b6fa74da08" +checksum = "fb7fb5e4e979aec3be7791562fcba452f94ad85e954da024396433e0e25a79e9" dependencies = [ "anstyle", "clap_lex", @@ -889,15 +883,15 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.5.1" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd7cc57abe963c6d3b9d8be5b06ba7c8957a930305ca90304f24ef040aa6f961" +checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" [[package]] name = "concurrent-queue" -version = "2.3.0" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f057a694a54f12365049b0958a1685bb52d567f5593b355fbf685838e873d400" +checksum = "d16048cd947b08fa32c24458a22f5dc5e835264f689f4f5653210c69fd107363" dependencies = [ "crossbeam-utils", ] @@ -914,9 +908,9 @@ dependencies = [ [[package]] name = "const-oid" -version = "0.9.5" +version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28c122c3980598d243d63d9a704629a2d748d101f278052ff068be5a4423ab6f" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" [[package]] name = "convert_case" @@ -926,9 +920,9 @@ checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" [[package]] name = "core-foundation" -version = "0.9.3" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" dependencies = [ "core-foundation-sys", "libc", @@ -936,15 +930,15 @@ dependencies = [ [[package]] name = "core-foundation-sys" -version = "0.8.4" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" [[package]] name = "cpufeatures" -version = "0.2.9" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1" +checksum = "ce420fe07aecd3e67c5f910618fe65e94158f6dcc0adf44e00d69ce2bdfe0fd0" dependencies = [ "libc", ] @@ -996,9 +990,9 @@ dependencies = [ [[package]] name = "crossbeam-channel" -version = "0.5.8" +version = "0.5.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200" +checksum = "82a9b73a36529d9c47029b9fb3a6f0ea3cc916a261195352ba19e770fc1748b2" dependencies = [ "cfg-if", "crossbeam-utils", @@ -1006,9 +1000,9 @@ dependencies = [ [[package]] name = "crossbeam-deque" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef" +checksum = "fca89a0e215bab21874660c67903c5f143333cab1da83d041c7ded6053774751" dependencies = [ "cfg-if", "crossbeam-epoch", @@ -1017,22 +1011,20 @@ dependencies = [ [[package]] name = "crossbeam-epoch" -version = "0.9.15" +version = "0.9.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae211234986c545741a7dc064309f67ee1e5ad243d0e48335adc0484d960bcc7" +checksum = "0e3681d554572a651dda4186cd47240627c3d0114d45a95f6ad27f2f22e7548d" dependencies = [ "autocfg", "cfg-if", "crossbeam-utils", - "memoffset", - "scopeguard", ] [[package]] name = "crossbeam-utils" -version = "0.8.16" +version = "0.8.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" +checksum = "c3a430a770ebd84726f584a90ee7f020d28db52c6d02138900f22341f866d39c" dependencies = [ "cfg-if", ] @@ -1049,9 +1041,9 @@ dependencies = [ [[package]] name = "data-encoding" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308" +checksum = "7e962a19be5cfc3f3bf6dd8f61eb50107f356ad6270fbb3ed41476571db78be5" [[package]] name = "der" @@ -1118,7 +1110,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.43", ] [[package]] @@ -1151,12 +1143,12 @@ dependencies = [ [[package]] name = "errno" -version = "0.3.5" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3e13f66a2f95e32a39eaa81f6b95d42878ca0e1db0c7543723dfe12557e860" +checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" dependencies = [ "libc", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -1165,6 +1157,27 @@ version = "2.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" +[[package]] +name = "event-listener" +version = "4.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84f2cdcf274580f2d63697192d744727b3198894b1bf02923643bf59e2c26712" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] + +[[package]] +name = "event-listener-strategy" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "958e4d70b6d5e81971bebec42271ec641e7ff4e170a6fa605f2b8a8b65cb97d3" +dependencies = [ + "event-listener 4.0.1", + "pin-project-lite", +] + [[package]] name = "fastrand" version = "1.9.0" @@ -1204,18 +1217,18 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "form_urlencoded" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" dependencies = [ "percent-encoding", ] [[package]] name = "futures" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da0290714b38af9b4a7b094b8a37086d1b4e61f2df9122c3cad2577669145335" +checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" dependencies = [ "futures-channel", "futures-core", @@ -1228,9 +1241,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff4dd66668b557604244583e3e1e1eada8c5c2e96a6d0d6653ede395b78bbacb" +checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" dependencies = [ "futures-core", "futures-sink", @@ -1238,15 +1251,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb1d22c66e66d9d72e1758f0bd7d4fd0bee04cad842ee34587d68c07e45d088c" +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" [[package]] name = "futures-executor" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f4fb8693db0cf099eadcca0efe2a5a22e4550f98ed16aba6c48700da29597bc" +checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" dependencies = [ "futures-core", "futures-task", @@ -1255,9 +1268,9 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bf34a163b5c4c52d0478a4d757da8fb65cabef42ba90515efee0f6f9fa45aaa" +checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" [[package]] name = "futures-lite" @@ -1276,46 +1289,45 @@ dependencies = [ [[package]] name = "futures-lite" -version = "2.0.1" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3831c2651acb5177cbd83943f3d9c8912c5ad03c76afcc0e9511ba568ec5ebb" +checksum = "aeee267a1883f7ebef3700f262d2d54de95dfaf38189015a74fdc4e0c7ad8143" dependencies = [ "fastrand 2.0.1", "futures-core", "futures-io", - "memchr", "parking", "pin-project-lite", ] [[package]] name = "futures-macro" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53b153fd91e4b0147f4aced87be237c98248656bb01050b96bf3ee89220a8ddb" +checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.43", ] [[package]] name = "futures-sink" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e36d3378ee38c2a36ad710c5d30c2911d752cb941c00c72dbabfb786a7970817" +checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" [[package]] name = "futures-task" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efd193069b0ddadc69c46389b740bbccdd97203899b48d09c5f7969591d6bae2" +checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" [[package]] name = "futures-util" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a19526d624e703a3179b3d322efec918b6246ea0fa51d41124525f00f1cc8104" +checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" dependencies = [ "futures-channel", "futures-core", @@ -1341,9 +1353,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.10" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" +checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f" dependencies = [ "cfg-if", "js-sys", @@ -1354,9 +1366,9 @@ dependencies = [ [[package]] name = "gimli" -version = "0.28.0" +version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0" +checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" [[package]] name = "glam" @@ -1370,9 +1382,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.3.21" +version = "0.3.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91fc23aa11be92976ef4729127f1a74adf36d8436f7816b185d18df956790833" +checksum = "4d6250322ef6e60f93f9a2162799302cd6f68f79f6e5d85c8c16f14d1d958178" dependencies = [ "bytes", "fnv", @@ -1380,7 +1392,7 @@ dependencies = [ "futures-sink", "futures-util", "http", - "indexmap 1.9.3", + "indexmap 2.1.0", "slab", "tokio", "tokio-util", @@ -1401,9 +1413,9 @@ checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" [[package]] name = "hashbrown" -version = "0.14.2" +version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f93e7192158dbcda357bdec5fb5788eebf8bbac027f3f33e719d29135ae84156" +checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" dependencies = [ "ahash", "allocator-api2", @@ -1424,9 +1436,9 @@ checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" [[package]] name = "http" -version = "0.2.9" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" +checksum = "8947b1a6fad4393052c7ba1f4cd97bed3e953a95c79c92ad9b051a04611d9fbb" dependencies = [ "bytes", "fnv", @@ -1435,9 +1447,9 @@ dependencies = [ [[package]] name = "http-body" -version = "0.4.5" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" +checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" dependencies = [ "bytes", "http", @@ -1464,9 +1476,9 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "hyper" -version = "0.14.27" +version = "0.14.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffb1cfd654a8219eaef89881fdb3bb3b1cdc5fa75ded05d6933b2b382e395468" +checksum = "bf96e135eb83a2a8ddf766e426a841d8ddd7449d5f00d34ea02b41d2f19eef80" dependencies = [ "bytes", "futures-channel", @@ -1479,7 +1491,7 @@ dependencies = [ "httpdate", "itoa", "pin-project-lite", - "socket2 0.4.9", + "socket2", "tokio", "tower-service", "tracing", @@ -1488,9 +1500,9 @@ dependencies = [ [[package]] name = "hyper-rustls" -version = "0.24.1" +version = "0.24.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d78e1e73ec14cf7375674f74d7dde185c8206fd9dea6fb6295e8a98098aaa97" +checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" dependencies = [ "futures-util", "http", @@ -1510,6 +1522,16 @@ dependencies = [ "unicode-normalization", ] +[[package]] +name = "idna" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + [[package]] name = "indexmap" version = "1.9.3" @@ -1527,7 +1549,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" dependencies = [ "equivalent", - "hashbrown 0.14.2", + "hashbrown 0.14.3", ] [[package]] @@ -1553,19 +1575,19 @@ dependencies = [ [[package]] name = "ipnet" -version = "2.8.0" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28b29a3cd74f0f4598934efe3aeba42bae0eb4680554128851ebbecb02af14e6" +checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" [[package]] name = "is-terminal" -version = "0.4.9" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" +checksum = "0bad00257d07be169d870ab665980b06cdb366d792ad690bf2e76876dc503455" dependencies = [ "hermit-abi", "rustix", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -1579,15 +1601,15 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.9" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" +checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" [[package]] name = "js-sys" -version = "0.3.64" +version = "0.3.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" +checksum = "cee9c64da59eae3b50095c18d3e74f8b73c0b86d2792824ff01bbce68ba229ca" dependencies = [ "wasm-bindgen", ] @@ -1598,14 +1620,14 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" dependencies = [ - "spin", + "spin 0.5.2", ] [[package]] name = "libc" -version = "0.2.150" +version = "0.2.151" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c" +checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4" [[package]] name = "libm" @@ -1621,9 +1643,9 @@ checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" [[package]] name = "linux-raw-sys" -version = "0.4.10" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da2479e8c062e40bf0066ffa0bc823de0a9368974af99c9f6df941d2c231e03f" +checksum = "c4cd1a83af159aa67994778be9070f0ae1bd732942279cabb14f86f986a21456" [[package]] name = "lock_api" @@ -1660,19 +1682,20 @@ dependencies = [ ] [[package]] -name = "memchr" -version = "2.6.4" +name = "md-5" +version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" +checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf" +dependencies = [ + "cfg-if", + "digest", +] [[package]] -name = "memoffset" -version = "0.9.0" +name = "memchr" +version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" -dependencies = [ - "autocfg", -] +checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" [[package]] name = "mime" @@ -1680,6 +1703,12 @@ version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" +[[package]] +name = "minecraft_folder_path" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d60a6352e005f1f86008644a9fe336a66f74c94428182162cc69eb8c6fff458d" + [[package]] name = "miniz_oxide" version = "0.7.1" @@ -1691,13 +1720,13 @@ dependencies = [ [[package]] name = "mio" -version = "0.8.9" +version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3dce281c5e46beae905d4de1870d8b1509a9142b62eedf18b443b011ca8343d0" +checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09" dependencies = [ "libc", "wasi", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -1708,9 +1737,9 @@ checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451" [[package]] name = "nonmax" -version = "0.5.3" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99756f5493e135528f0cd660ac67b4c3a542bb65a3565efe92bb2c2317eb3669" +checksum = "610a5acd306ec67f907abe5567859a3c693fb9886eb1f012ab8f2a47bef3db51" [[package]] name = "nu-ansi-term" @@ -1828,18 +1857,18 @@ dependencies = [ [[package]] name = "object" -version = "0.32.1" +version = "0.32.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0" +checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" dependencies = [ "memchr", ] [[package]] name = "once_cell" -version = "1.18.0" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "oorandom" @@ -1879,10 +1908,10 @@ dependencies = [ "cfg-if", "libc", "petgraph", - "redox_syscall 0.4.1", + "redox_syscall", "smallvec", "thread-id", - "windows-targets", + "windows-targets 0.48.5", ] [[package]] @@ -1896,9 +1925,9 @@ dependencies = [ [[package]] name = "percent-encoding" -version = "2.3.0" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "petgraph" @@ -1989,9 +2018,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.69" +version = "1.0.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" +checksum = "75cb1540fadbd5b8fbccc4dddad2734eba435053f725621c070711a14bb5f4b8" dependencies = [ "unicode-ident", ] @@ -2037,9 +2066,9 @@ dependencies = [ [[package]] name = "rayon" -version = "1.7.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b" +checksum = "9c27db03db7734835b3f53954b534c91069375ce6ccaa2e065441e07d9b6cdb1" dependencies = [ "either", "rayon-core", @@ -2047,23 +2076,12 @@ dependencies = [ [[package]] name = "rayon-core" -version = "1.11.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d" +checksum = "5ce3fb6ad83f861aac485e76e1985cd109d9a3713802152be56c3b1f0e0658ed" dependencies = [ - "crossbeam-channel", "crossbeam-deque", "crossbeam-utils", - "num_cpus", -] - -[[package]] -name = "redox_syscall" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" -dependencies = [ - "bitflags 1.3.2", ] [[package]] @@ -2121,9 +2139,9 @@ checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" [[package]] name = "reqwest" -version = "0.11.22" +version = "0.11.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "046cd98826c46c2ac8ddecae268eb5c2e58628688a5fc7a2643704a73faba95b" +checksum = "37b1ae8d9ac08420c66222fb9096fc5de435c3c48542bc5336c51892cffafb41" dependencies = [ "base64", "bytes", @@ -2176,24 +2194,23 @@ dependencies = [ [[package]] name = "ring" -version = "0.16.20" +version = "0.17.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" +checksum = "688c63d65483050968b2a8937f7995f443e27041a0f7700aa59b0822aedebb74" dependencies = [ "cc", + "getrandom", "libc", - "once_cell", - "spin", + "spin 0.9.8", "untrusted", - "web-sys", - "winapi", + "windows-sys 0.48.0", ] [[package]] name = "rsa" -version = "0.9.3" +version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86ef35bf3e7fe15a53c4ab08a998e42271eab13eb0db224126bc7bc4c4bad96d" +checksum = "5d0e5124fcb30e76a7e79bfee683a2746db83784b86289f6251b54b7950a0dfc" dependencies = [ "const-oid", "digest", @@ -2244,22 +2261,22 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.20" +version = "0.38.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67ce50cb2e16c2903e30d1cbccfd8387a74b9d4c938b6a4c5ec6cc7556f7a8a0" +checksum = "72e572a5e8ca657d7366229cdde4bd14c4eb5499a9573d4d366fe1b599daa316" dependencies = [ "bitflags 2.4.1", "errno", "libc", "linux-raw-sys", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] name = "rustls" -version = "0.21.7" +version = "0.21.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd8d6c9f025a446bc4d18ad9632e69aec8f287aa84499ee335599fabd20c3fd8" +checksum = "f9d5a6813c0759e4609cd494e8e725babae6a2ca7b62a5536a13daaec6fcb7ba" dependencies = [ "log", "ring", @@ -2269,18 +2286,18 @@ dependencies = [ [[package]] name = "rustls-pemfile" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d3987094b1d07b653b7dfdc3f70ce9a1da9c51ac18c1b06b662e4f9a0e9f4b2" +checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" dependencies = [ "base64", ] [[package]] name = "rustls-webpki" -version = "0.101.6" +version = "0.101.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c7d5dece342910d9ba34d259310cae3e0154b873b35408b787b59bce53d34fe" +checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" dependencies = [ "ring", "untrusted", @@ -2288,9 +2305,9 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.15" +version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" +checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c" [[package]] name = "same-file" @@ -2309,9 +2326,9 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "sct" -version = "0.7.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4" +checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" dependencies = [ "ring", "untrusted", @@ -2319,28 +2336,28 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.18" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0293b4b29daaf487284529cc2f5675b8e57c61f70167ba415a463651fd6a918" +checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090" [[package]] name = "serde" -version = "1.0.192" +version = "1.0.193" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bca2a08484b285dcb282d0f67b26cadc0df8b19f8c12502c13d966bf9482f001" +checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.192" +version = "1.0.193" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6c7207fbec9faa48073f3e3074cbe553af6ea512d7c21ba46e434e70ea9fbc1" +checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.43", ] [[package]] @@ -2408,9 +2425,9 @@ dependencies = [ [[package]] name = "signature" -version = "2.1.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e1788eed21689f9cf370582dfc467ef36ed9c707f073528ddafa8d83e3b8500" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" dependencies = [ "digest", "rand_core", @@ -2418,9 +2435,8 @@ dependencies = [ [[package]] name = "simdnbt" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89e3431666c99066308d860437da87ac1dfdf7225b80b551aa007cbd3a46a086" +version = "0.3.0" +source = "git+https://github.com/azalea-rs/simdnbt#c255ab673a7439c52dd299eac53ea59566cdda9d" dependencies = [ "byteorder", "flate2", @@ -2431,13 +2447,12 @@ dependencies = [ [[package]] name = "simdnbt-derive" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "840927b3f00258339cb4ccb4658a33f572409a1bf42e1b98a3f872a995894b4e" +version = "0.3.0" +source = "git+https://github.com/azalea-rs/simdnbt#c255ab673a7439c52dd299eac53ea59566cdda9d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.43", ] [[package]] @@ -2463,9 +2478,9 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.11.1" +version = "1.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "942b4a808e05215192e39f4ab80813e599068285906cc91aa64f923db842bd5a" +checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970" dependencies = [ "serde", ] @@ -2479,16 +2494,6 @@ dependencies = [ "serde", ] -[[package]] -name = "socket2" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" -dependencies = [ - "libc", - "winapi", -] - [[package]] name = "socket2" version = "0.5.5" @@ -2496,7 +2501,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9" dependencies = [ "libc", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -2519,11 +2524,17 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" + [[package]] name = "spki" -version = "0.7.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d1e996ef02c474957d681f1b05213dfb0abab947b446a62d37770b23500184a" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" dependencies = [ "base64ct", "der", @@ -2548,9 +2559,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.39" +version = "2.0.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a" +checksum = "ee659fb5f3d355364e1f3e5bc10fb82068efbf824a1e9d1c9504244a6469ad53" dependencies = [ "proc-macro2", "quote", @@ -2580,41 +2591,40 @@ dependencies = [ [[package]] name = "termcolor" -version = "1.3.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6093bad37da69aab9d123a8091e4be0aa4a03e4d601ec641c327398315f62b64" +checksum = "ff1bc3d3f05aff0403e8ac0d92ced918ec05b666a43f83297ccef5bea8a3d449" dependencies = [ "winapi-util", ] [[package]] name = "thiserror" -version = "1.0.50" +version = "1.0.52" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2" +checksum = "83a48fd946b02c0a526b2e9481c8e2a17755e47039164a86c4070446e3a4614d" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.50" +version = "1.0.52" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8" +checksum = "e7fbe9b594d6568a6a1443250a7e67d80b74e1e96f6d1715e1e21cc1888291d3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.43", ] [[package]] name = "thread-id" -version = "4.2.0" +version = "4.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79474f573561cdc4871a0de34a51c92f7f5a56039113fbb5b9c9f96bdb756669" +checksum = "f0ec81c46e9eb50deaa257be2f148adf052d1fb7701cfd55ccfab2525280b70b" dependencies = [ "libc", - "redox_syscall 0.2.16", "winapi", ] @@ -2655,9 +2665,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.34.0" +version = "1.35.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0c014766411e834f7af5b8f4cf46257aab4036ca95e9d2c144a10f59ad6f5b9" +checksum = "c89b4efa943be685f629b149f53829423f8f5531ea21249408e8e2f8671ec104" dependencies = [ "backtrace", "bytes", @@ -2667,9 +2677,9 @@ dependencies = [ "parking_lot", "pin-project-lite", "signal-hook-registry", - "socket2 0.5.5", + "socket2", "tokio-macros", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -2680,7 +2690,7 @@ checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.43", ] [[package]] @@ -2749,7 +2759,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.43", ] [[package]] @@ -2764,12 +2774,12 @@ dependencies = [ [[package]] name = "tracing-log" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ddad33d2d10b1ed7eb9d1f518a5674713876e97e5bb9b7345a7984fbb4f922" +checksum = "f751112709b4e791d8ce53e32c4ed2d353565a795ce84da2285393f41557bdf2" dependencies = [ - "lazy_static", "log", + "once_cell", "tracing-core", ] @@ -2826,7 +2836,7 @@ dependencies = [ "futures-channel", "futures-io", "futures-util", - "idna", + "idna 0.4.0", "ipnet", "once_cell", "rand", @@ -2859,9 +2869,9 @@ dependencies = [ [[package]] name = "try-lock" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] name = "typenum" @@ -2871,9 +2881,9 @@ checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" [[package]] name = "unicode-bidi" -version = "0.3.13" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" +checksum = "6f2528f27a9eb2b21e69c95319b30bd0efd85d09c379741b0f78ea1d86be2416" [[package]] name = "unicode-ident" @@ -2892,28 +2902,29 @@ dependencies = [ [[package]] name = "untrusted" -version = "0.7.1" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "url" -version = "2.4.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "143b538f18257fac9cad154828a57c6bf5157e1aa604d4816b5995bf6de87ae5" +checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" dependencies = [ "form_urlencoded", - "idna", + "idna 0.5.0", "percent-encoding", ] [[package]] name = "uuid" -version = "1.5.0" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88ad59a7560b41a70d191093a945f0b87bc1deeda46fb237479708a1d6b6cdfc" +checksum = "5e395fcf16a7a3d8127ec99782007af141946b4795001f876d54fb0d55978560" dependencies = [ "getrandom", + "md-5", "serde", ] @@ -2962,9 +2973,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.87" +version = "0.2.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" +checksum = "0ed0d4f68a3015cc185aff4db9506a015f4b96f95303897bfa23f846db54064e" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -2972,24 +2983,24 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.87" +version = "0.2.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" +checksum = "1b56f625e64f3a1084ded111c4d5f477df9f8c92df113852fa5a374dbda78826" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.43", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.37" +version = "0.4.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c02dbc21516f9f1f04f187958890d7e6026df8d16540b7ad9492bc34a67cea03" +checksum = "ac36a15a220124ac510204aec1c3e5db8a22ab06fd6706d881dc6149f8ed9a12" dependencies = [ "cfg-if", "js-sys", @@ -2999,9 +3010,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.87" +version = "0.2.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" +checksum = "0162dbf37223cd2afce98f3d0785506dcb8d266223983e4b5b525859e6e182b2" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -3009,28 +3020,28 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.87" +version = "0.2.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" +checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.43", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.87" +version = "0.2.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" +checksum = "7ab9b36309365056cd639da3134bf87fa8f3d86008abf99e612384a6eecd459f" [[package]] name = "web-sys" -version = "0.3.64" +version = "0.3.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b" +checksum = "50c24a44ec86bb68fbecd1b3efed7e85ea5621b39b35ef2766b66cd984f8010f" dependencies = [ "js-sys", "wasm-bindgen", @@ -3038,9 +3049,9 @@ dependencies = [ [[package]] name = "webpki-roots" -version = "0.25.2" +version = "0.25.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14247bb57be4f377dfb94c72830b8ce8fc6beac03cf4bf7b9732eadd414123fc" +checksum = "1778a42e8b3b90bff8d0f5032bf22250792889a5cdc752aa0020c84abe3aaf10" [[package]] name = "winapi" @@ -3079,7 +3090,16 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ - "windows-targets", + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.0", ] [[package]] @@ -3088,13 +3108,28 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" +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", ] [[package]] @@ -3103,47 +3138,89 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" + [[package]] name = "windows_aarch64_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" + [[package]] name = "windows_i686_gnu" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" +[[package]] +name = "windows_i686_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" + [[package]] name = "windows_i686_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" +[[package]] +name = "windows_i686_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" + [[package]] name = "windows_x86_64_gnu" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" + [[package]] name = "windows_x86_64_gnullvm" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" + [[package]] name = "windows_x86_64_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" + [[package]] name = "winnow" -version = "0.5.16" +version = "0.5.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "037711d82167854aff2018dfd193aa0fef5370f456732f0d5a0c59b0f1b4b907" +checksum = "97a4882e6b134d6c28953a387571f1acdd3496830d5e36c5e3a1075580ea641c" dependencies = [ "memchr", ] @@ -3155,31 +3232,31 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" dependencies = [ "cfg-if", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] name = "zerocopy" -version = "0.7.26" +version = "0.7.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e97e415490559a91254a2979b4829267a57d2fcd741a98eee8b722fb57289aa0" +checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.7.26" +version = "0.7.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd7e48ccf166952882ca8bd778a43502c64f33bf94c12ebe2a7f08e5a0f6689f" +checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.43", ] [[package]] name = "zeroize" -version = "1.6.0" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9" +checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" diff --git a/README.md b/README.md index 603b0e34c..a110e76e7 100755 --- a/README.md +++ b/README.md @@ -9,20 +9,21 @@ A collection of Rust crates for making Minecraft bots, clients, and tools.

-*Currently supported Minecraft version: `1.20.2`.* -> **Warning** +_Currently supported Minecraft version: `1.20.4`._ + +> [!WARNING] > Azalea is still very unfinished, though most crates are in a somewhat useable state ## Features -- [Accurate physics](https://github.com/azalea-rs/azalea/blob/main/azalea-physics/src/lib.rs) (but some features like entity collisions and water physics aren't yet implemented) -- [Pathfinder](https://azalea.matdoes.dev/azalea/pathfinder/index.html) -- [Swarms](https://azalea.matdoes.dev/azalea/swarm/index.html) -- [Breaking blocks](https://azalea.matdoes.dev/azalea/struct.Client.html#method.mine) -- [Block interactions & building](https://azalea.matdoes.dev/azalea/struct.Client.html#method.block_interact) (this doesn't predict the block interactions/placement on the client yet but it's usually fine) -- [Inventories](https://azalea.matdoes.dev/azalea/struct.Client.html#impl-ContainerClientExt-for-Client) -- [Attacking entities](https://azalea.matdoes.dev/azalea/struct.Client.html#method.attack) (but you can't get the entity at the crosshair yet) +- [Accurate physics](https://github.com/azalea-rs/azalea/blob/main/azalea-physics/src/lib.rs) (but some features like entity collisions and water physics aren't yet implemented) +- [Pathfinder](https://azalea.matdoes.dev/azalea/pathfinder/index.html) +- [Swarms](https://azalea.matdoes.dev/azalea/swarm/index.html) +- [Breaking blocks](https://azalea.matdoes.dev/azalea/struct.Client.html#method.mine) +- [Block interactions & building](https://azalea.matdoes.dev/azalea/struct.Client.html#method.block_interact) (this doesn't predict the block interactions/placement on the client yet but it's usually fine) +- [Inventories](https://azalea.matdoes.dev/azalea/struct.Client.html#impl-ContainerClientExt-for-Client) +- [Attacking entities](https://azalea.matdoes.dev/azalea/struct.Client.html#method.attack) (but you can't get the entity at the crosshair yet) ## Docs @@ -32,30 +33,27 @@ The "stable" documentation is available at [docs.rs/azalea](https://docs.rs/azal If you'd like to chat about Azalea, you can join the Matrix space at [#azalea:matdoes.dev](https://matrix.to/#/#azalea:matdoes.dev) (recommended) or the Discord server at [discord.gg/FaRey6ytmC](https://discord.gg/FaRey6ytmC) (they're bridged so you don't need to join both). -## Why - -For fun, mostly. I wasn't satisfied with the current state of Minecraft bot libraries, and this seemed like a good way to learn Rust. - ## Goals -- Do everything a vanilla client can do. -- Be intuitive and easy to use. -- Make it easy to have many bots working at the same time. -- Don't trigger anticheats. -- Support the latest Minecraft version. -- Be fast and memory efficient. +- Do everything a vanilla client can do. +- Be intuitive and easy to use. +- Make it easy to have many bots working at the same time. +- Don't trigger anticheats. +- Support the latest Minecraft version. +- Be fast and memory efficient. ## Non-goals -- Supporting several versions of Minecraft on the same branch. -- Bedrock edition. -- Graphics. +- Supporting several versions of Minecraft on the same branch[\*](https://github.com/azalea-rs/azalea-viaversion). +- Bedrock edition. +- Graphics. ## Branches There are several branches in the Azalea repository that target older Minecraft versions. It is not guaranteed that they will be up-to-date with the latest version of Azalea. If you'd like to update them or add more, please open a PR. -- [1.20-1.20.1](https://github.com/azalea-rs/azalea/tree/1.20.1) -- [1.19.4](https://github.com/azalea-rs/azalea/tree/1.19.4) -- [1.19.3](https://github.com/azalea-rs/azalea/tree/1.19.3) -- [1.19.2](https://github.com/azalea-rs/azalea/tree/1.19.2) +- [1.20.2](https://github.com/azalea-rs/azalea/tree/1.20.2) +- [1.20-1.20.1](https://github.com/azalea-rs/azalea/tree/1.20.1) +- [1.19.4](https://github.com/azalea-rs/azalea/tree/1.19.4) +- [1.19.3](https://github.com/azalea-rs/azalea/tree/1.19.3) +- [1.19.2](https://github.com/azalea-rs/azalea/tree/1.19.2) diff --git a/azalea-auth/Cargo.toml b/azalea-auth/Cargo.toml index 3e25b6a43..82420abea 100644 --- a/azalea-auth/Cargo.toml +++ b/azalea-auth/Cargo.toml @@ -4,13 +4,13 @@ edition = "2021" license = "MIT" name = "azalea-auth" repository = "https://github.com/azalea-rs/azalea/tree/main/azalea-auth" -version = "0.8.0" +version = "0.9.0" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -azalea-buf = { path = "../azalea-buf", version = "0.8.0" } -azalea-crypto = { path = "../azalea-crypto", version = "0.8.0" } +azalea-buf = { path = "../azalea-buf", version = "0.9.0" } +azalea-crypto = { path = "../azalea-crypto", version = "0.9.0" } base64 = "0.21.5" chrono = { version = "0.4.31", default-features = false, features = ["serde"] } tracing = "0.1.40" @@ -20,12 +20,13 @@ reqwest = { version = "0.11.22", default-features = false, features = [ "json", "rustls-tls", ] } -rsa = "0.9.3" -serde = { version = "1.0.192", features = ["derive"] } +rsa = "0.9.6" +serde = { version = "1.0.193", features = ["derive"] } serde_json = "1.0.108" thiserror = "1.0.50" tokio = { version = "1.34.0", features = ["fs"] } -uuid = { version = "1.5.0", features = ["serde"] } +uuid = { version = "1.6.1", features = ["serde", "v3"] } +md-5 = "0.10.6" [dev-dependencies] env_logger = "0.10.1" diff --git a/azalea-auth/src/auth.rs b/azalea-auth/src/auth.rs index 0c3b98c67..67a45aa3c 100755 --- a/azalea-auth/src/auth.rs +++ b/azalea-auth/src/auth.rs @@ -58,7 +58,7 @@ pub enum AuthError { /// /// If you want to use your own code to cache or show the auth code to the user /// in a different way, use [`get_ms_link_code`], [`get_ms_auth_token`], -/// [`get_minecraft_token`] and [`get_profile`] instead instead. +/// [`get_minecraft_token`] and [`get_profile`] instead. pub async fn auth(email: &str, opts: AuthOpts) -> Result { let cached_account = if let Some(cache_file) = &opts.cache_file { cache::get_account_in_cache(cache_file, email).await @@ -84,7 +84,14 @@ pub async fn auth(email: &str, opts: AuthOpts) -> Result }; if msa.is_expired() { tracing::trace!("refreshing Microsoft auth token"); - msa = refresh_ms_auth_token(&client, &msa.data.refresh_token).await?; + match refresh_ms_auth_token(&client, &msa.data.refresh_token).await { + Ok(new_msa) => msa = new_msa, + Err(e) => { + // can't refresh, ask the user to auth again + tracing::error!("Error refreshing Microsoft auth token: {}", e); + msa = interactive_get_ms_auth_token(&client, email).await?; + } + } } let msa_token = &msa.data.access_token; @@ -361,13 +368,15 @@ pub async fn interactive_get_ms_auth_token( pub enum RefreshMicrosoftAuthTokenError { #[error("Http error: {0}")] Http(#[from] reqwest::Error), + #[error("Error parsing JSON: {0}")] + Json(#[from] serde_json::Error), } pub async fn refresh_ms_auth_token( client: &reqwest::Client, refresh_token: &str, ) -> Result, RefreshMicrosoftAuthTokenError> { - let access_token_response = client + let access_token_response_text = client .post("https://login.live.com/oauth20_token.srf") .form(&vec![ ("scope", "service::user.auth.xboxlive.com::MBI_SSL"), @@ -377,8 +386,10 @@ pub async fn refresh_ms_auth_token( ]) .send() .await? - .json::() + .text() .await?; + let access_token_response: AccessTokenResponse = + serde_json::from_str(&access_token_response_text)?; let expires_at = SystemTime::now() + std::time::Duration::from_secs(access_token_response.expires_in); @@ -438,7 +449,7 @@ async fn auth_with_xbox_live( Ok(ExpiringValue { data: XboxLiveAuth { token: res.token, - user_hash: res.display_claims["xui"].get(0).unwrap()["uhs"].clone(), + user_hash: res.display_claims["xui"].first().unwrap()["uhs"].clone(), }, expires_at, }) @@ -511,7 +522,7 @@ pub enum CheckOwnershipError { Http(#[from] reqwest::Error), } -async fn check_ownership( +pub async fn check_ownership( client: &reqwest::Client, minecraft_access_token: &str, ) -> Result { diff --git a/azalea-auth/src/lib.rs b/azalea-auth/src/lib.rs index bd151eb32..1643bf04f 100755 --- a/azalea-auth/src/lib.rs +++ b/azalea-auth/src/lib.rs @@ -4,6 +4,7 @@ mod auth; pub mod cache; pub mod certs; pub mod game_profile; +pub mod offline; pub mod sessionserver; pub use auth::*; diff --git a/azalea-auth/src/offline.rs b/azalea-auth/src/offline.rs new file mode 100644 index 000000000..737555e86 --- /dev/null +++ b/azalea-auth/src/offline.rs @@ -0,0 +1,17 @@ +use md5::{Digest, Md5}; +use uuid::Uuid; + +pub fn generate_uuid(username: &str) -> Uuid { + uuid::Builder::from_md5_bytes(hash(format!("OfflinePlayer:{username}").as_bytes())).into_uuid() +} + +fn hash(data: &[u8]) -> [u8; 16] { + let mut hasher = Md5::new(); + + hasher.update(data); + + let mut bytes = [0; 16]; + bytes.copy_from_slice(&hasher.finalize()[..16]); + + bytes +} diff --git a/azalea-auth/src/sessionserver.rs b/azalea-auth/src/sessionserver.rs index e6469cefe..9c73fbf0b 100755 --- a/azalea-auth/src/sessionserver.rs +++ b/azalea-auth/src/sessionserver.rs @@ -69,6 +69,15 @@ pub async fn join( private_key, )); + join_with_server_id_hash(&client, access_token, uuid, &server_hash).await +} + +pub async fn join_with_server_id_hash( + client: &reqwest::Client, + access_token: &str, + uuid: &Uuid, + server_hash: &str, +) -> Result<(), ClientSessionServerError> { let mut encode_buffer = Uuid::encode_buffer(); let undashed_uuid = uuid.simple().encode_lower(&mut encode_buffer); diff --git a/azalea-block/.gitignore b/azalea-block/.gitignore new file mode 100644 index 000000000..390f6fd52 --- /dev/null +++ b/azalea-block/.gitignore @@ -0,0 +1,3 @@ +# cargo expand generated -p azalea-block > azalea-block/out.rs +/out.rs + diff --git a/azalea-block/Cargo.toml b/azalea-block/Cargo.toml index f950e43fa..1f3a829f4 100644 --- a/azalea-block/Cargo.toml +++ b/azalea-block/Cargo.toml @@ -4,13 +4,13 @@ edition = "2021" license = "MIT" name = "azalea-block" repository = "https://github.com/azalea-rs/azalea/tree/main/azalea-block" -version = "0.8.0" +version = "0.9.0" [lib] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -azalea-block-macros = { path = "./azalea-block-macros", version = "0.8.0" } -azalea-buf = { path = "../azalea-buf", version = "0.8.0" } -azalea-registry = { version = "0.8.0", path = "../azalea-registry" } +azalea-block-macros = { path = "./azalea-block-macros", version = "0.9.0" } +azalea-buf = { path = "../azalea-buf", version = "0.9.0" } +azalea-registry = { version = "0.9.0", path = "../azalea-registry" } diff --git a/azalea-block/azalea-block-macros/Cargo.toml b/azalea-block/azalea-block-macros/Cargo.toml index b77b541e0..720e44804 100644 --- a/azalea-block/azalea-block-macros/Cargo.toml +++ b/azalea-block/azalea-block-macros/Cargo.toml @@ -4,7 +4,7 @@ edition = "2021" license = "MIT" name = "azalea-block-macros" repository = "https://github.com/azalea-rs/azalea/tree/main/azalea-block/azalea-block-macros" -version = "0.8.0" +version = "0.9.0" [lib] proc-macro = true @@ -12,6 +12,6 @@ proc-macro = true # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -proc-macro2 = "1.0.69" +proc-macro2 = "1.0.70" quote = "1.0.33" syn = "2.0.39" diff --git a/azalea-block/azalea-block-macros/src/lib.rs b/azalea-block/azalea-block-macros/src/lib.rs index 11510a0af..3b983fb43 100755 --- a/azalea-block/azalea-block-macros/src/lib.rs +++ b/azalea-block/azalea-block-macros/src/lib.rs @@ -10,40 +10,45 @@ use std::fmt::Write; use syn::{ self, braced, ext::IdentExt, + parenthesized, parse::{Parse, ParseStream, Result}, parse_macro_input, punctuated::Punctuated, - Expr, Ident, LitStr, Token, + token, Expr, Ident, LitStr, Token, }; use utils::{combinations_of, to_pascal_case}; enum PropertyType { /// `Axis { X, Y, Z }` Enum { - type_name: Ident, + enum_name: Ident, variants: Punctuated, }, - /// `bool` - Boolean, + /// `Snowy(bool)` + Boolean { struct_name: Ident }, } -/// `"snowy" => bool` +/// `"snowy" => Snowy(bool)` struct PropertyDefinition { name: LitStr, property_type: PropertyType, } -/// Comma separated PropertyDefinitions (`"snowy" => bool,`) +/// Comma separated PropertyDefinitions (`"snowy" => Snowy(bool),`) struct PropertyDefinitions { properties: Vec, } -/// `snowy: false` or `axis: properties::Axis::Y` +/// `snowy: Snowy(false)` or `axis: properties::Axis::Y` #[derive(Debug)] struct PropertyWithNameAndDefault { + // "snowy" / "axis" name: Ident, + // Snowy / Axis property_type: Ident, + property_value_type: Ident, is_enum: bool, + // false / properties::Axis::Y default: proc_macro2::TokenStream, } @@ -59,31 +64,46 @@ struct BlockDefinition { } impl Parse for PropertyWithNameAndDefault { fn parse(input: ParseStream) -> Result { - // `snowy: false` or `axis: properties::Axis::Y` + // `snowy: Snowy(false)` or `axis: properties::Axis::Y` let property_name = input.parse()?; input.parse::()?; let first_ident = input.call(Ident::parse_any)?; - let first_ident_string = first_ident.to_string(); let mut property_default = quote! { #first_ident }; let property_type: Ident; + let property_value_type: Ident; let mut is_enum = false; if input.parse::().is_ok() { + // enum is_enum = true; - property_type = first_ident; + property_type = first_ident.clone(); + property_value_type = first_ident; let variant = input.parse::()?; property_default = quote! { properties::#property_default::#variant }; - } else if first_ident_string == "true" || first_ident_string == "false" { - property_type = Ident::new("bool", first_ident.span()); } else { - return Err(input.error("Expected a boolean or an enum variant")); + // must be a unit struct if it's not an enum + let content; + let _paren_token: token::Paren = parenthesized!(content in input); + // we use this instead of .parse so it works with rust keywords like true and + // false + let unit_struct_inner = content.call(Ident::parse_any)?; + let unit_struct_inner_string = unit_struct_inner.to_string(); + + if matches!(unit_struct_inner_string.as_str(), "true" | "false") { + property_value_type = Ident::new("bool", first_ident.span()); + property_type = first_ident; + property_default = quote! { #unit_struct_inner }; + } else { + return Err(input.error("Expected a boolean or an enum variant")); + } }; Ok(PropertyWithNameAndDefault { name: property_name, property_type, + property_value_type, is_enum, default: property_default, }) @@ -100,20 +120,40 @@ struct MakeBlockStates { impl Parse for PropertyType { fn parse(input: ParseStream) -> Result { - // like `Axis { X, Y, Z }` or `bool` + // like `Axis { X, Y, Z }` or `Waterlogged(bool)` let keyword = Ident::parse(input)?; - let keyword_string = keyword.to_string(); - if keyword_string == "bool" { - Ok(Self::Boolean) - } else { + + fn parse_braced(input: ParseStream) -> Result> { let content; braced!(content in input); let variants = content.parse_terminated(Ident::parse, Token![,])?; + Ok(variants) + } + + fn parse_paren(input: ParseStream) -> Result { + let content; + parenthesized!(content in input); + let inner = content.parse::()?; + Ok(inner) + } + + if let Ok(variants) = parse_braced(input) { Ok(Self::Enum { - type_name: keyword, + enum_name: keyword, variants, }) + } else if let Ok(inner) = parse_paren(input) { + assert_eq!( + inner.to_string(), + "bool", + "Currently only bool unit structs are supported" + ); + Ok(Self::Boolean { + struct_name: keyword, + }) + } else { + Err(input.error("Expected a unit struct or an enum")) } } } @@ -227,6 +267,12 @@ impl Parse for MakeBlockStates { } } +struct PropertyVariantData { + pub block_state_ids: Vec, + pub ident: Ident, + pub is_enum: bool, +} + #[proc_macro] pub fn make_block_states(input: TokenStream) -> TokenStream { let input = parse_macro_input!(input as MakeBlockStates); @@ -238,21 +284,24 @@ pub fn make_block_states(input: TokenStream) -> TokenStream { let mut state_id: u32 = 0; for property in &input.property_definitions.properties { - let property_type_name: Ident; + let property_struct_name: Ident; + // this is usually the same as property_struct_name except for bool + let property_value_name: Ident; let mut property_variant_types = Vec::new(); match &property.property_type { PropertyType::Enum { - type_name, + enum_name, variants, } => { let mut property_enum_variants = quote! {}; let mut property_from_number_variants = quote! {}; - property_type_name = type_name.clone(); + property_value_name = enum_name.clone(); + property_struct_name = enum_name.clone(); property_struct_names_to_names.insert( - property_type_name.to_string(), + property_struct_name.to_string(), property.name.clone().value(), ); @@ -271,19 +320,19 @@ pub fn make_block_states(input: TokenStream) -> TokenStream { // i_lit is used here instead of i because otherwise it says 0size // in the expansion and that looks uglier property_from_number_variants.extend(quote! { - #i_lit => #property_type_name::#variant, + #i_lit => #property_struct_name::#variant, }); property_variant_types.push(variant.to_string()); } property_enums.extend(quote! { - #[derive(Debug, Clone, Copy)] - pub enum #property_type_name { + #[derive(Debug, Clone, Copy, PartialEq, Eq)] + pub enum #property_struct_name { #property_enum_variants } - impl From for #property_type_name { + impl From for #property_struct_name { fn from(value: u32) -> Self { match value { #property_from_number_variants @@ -293,15 +342,28 @@ pub fn make_block_states(input: TokenStream) -> TokenStream { } }); } - PropertyType::Boolean => { - property_type_name = Ident::new("bool", proc_macro2::Span::call_site()); - // property_type_name = - // Ident::new(&property.name.value(), proc_macro2::Span::call_site()); + PropertyType::Boolean { struct_name } => { + property_value_name = Ident::new("bool", proc_macro2::Span::call_site()); + property_struct_name = struct_name.clone(); property_variant_types = vec!["true".to_string(), "false".to_string()]; + + property_enums.extend(quote! { + #[derive(Debug, Clone, Copy, PartialEq, Eq)] + pub struct #property_struct_name(pub bool); + + impl From for #property_struct_name { + fn from(value: u32) -> Self { + match value { + 0 => Self(false), + 1 => Self(true), + _ => panic!("Invalid property value: {}", value), + } + } + } + }); } } - properties_map.insert(property_type_name.to_string(), property_variant_types); - // properties_map.insert(property.name.value(), property_variant_types); + properties_map.insert(property_value_name.to_string(), property_variant_types); } let mut block_state_enum_variants = quote! {}; @@ -312,14 +374,19 @@ pub fn make_block_states(input: TokenStream) -> TokenStream { let mut from_registry_block_to_blockstate_match = quote! {}; let mut from_registry_block_to_blockstates_match = quote! {}; - // a list of block state ids that are waterlogged - let mut waterlogged_state_ids: Vec = Vec::new(); + // { + // Waterlogged: [ + // [ vec of waterlogged = true state ids ], + // [ vec of waterlogged = false state ids ] + // } + // } + let mut properties_to_state_ids: HashMap> = HashMap::new(); for block in &input.block_definitions.blocks { let block_property_names = &block .properties_and_defaults .iter() - .map(|p| p.property_type.to_string()) + .map(|p| p.property_value_type.to_string()) .collect::>(); let mut block_properties_vec = Vec::new(); for property_name in block_property_names { @@ -373,6 +440,7 @@ pub fn make_block_states(input: TokenStream) -> TokenStream { properties_with_name.push(PropertyWithNameAndDefault { name: Ident::new(&property_name, proc_macro2::Span::call_site()), property_type: property.property_type.clone(), + property_value_type: property.property_value_type.clone(), is_enum: property.is_enum, default: property.default.clone(), }); @@ -388,7 +456,7 @@ pub fn make_block_states(input: TokenStream) -> TokenStream { // pub has_bottle_2: HasBottle, let mut block_struct_fields = quote! {}; for PropertyWithNameAndDefault { - property_type: struct_name, + property_value_type, name, is_enum, .. @@ -397,9 +465,9 @@ pub fn make_block_states(input: TokenStream) -> TokenStream { // let property_name_snake = // Ident::new(&property.to_string(), proc_macro2::Span::call_site()); block_struct_fields.extend(if *is_enum { - quote! { pub #name: properties::#struct_name, } + quote! { pub #name: properties::#property_value_type, } } else { - quote! { pub #name: #struct_name, } + quote! { pub #name: #property_value_type, } }); } @@ -435,15 +503,15 @@ pub fn make_block_states(input: TokenStream) -> TokenStream { for i in 0..properties_with_name.len() { let property = &properties_with_name[i]; let property_name = &property.name; - let property_struct_name_ident = &property.property_type; + let property_value_name_ident = &property.property_type; let variant = Ident::new(&combination[i].to_string(), proc_macro2::Span::call_site()); // this terrible code just gets the property default as a string - let property_default_as_string = if let TokenTree::Ident(i) = + let property_default_as_string = if let TokenTree::Ident(ident) = property.default.clone().into_iter().last().unwrap() { - i.to_string() + ident.to_string() } else { panic!() }; @@ -451,20 +519,30 @@ pub fn make_block_states(input: TokenStream) -> TokenStream { is_default = false; } - let property_value = if property.is_enum { - quote! {properties::#property_struct_name_ident::#variant} + let property_variant = if property.is_enum { + quote! {properties::#property_value_name_ident::#variant} } else { quote! {#variant} }; from_block_to_state_combination_match_inner.extend(quote! { - #property_name: #property_value, + #property_name: #property_variant, }); - // if "waterlogged" is a property and it's true for this state then add it to - // waterlogged_state_ids - if property_name == "waterlogged" && property_value.to_string() == "true" { - waterlogged_state_ids.push(state_id) + // add to properties_to_state_ids + let property_variants = properties_to_state_ids + .entry(property_value_name_ident.to_string()) + .or_default(); + let property_variant_data = + property_variants.iter_mut().find(|v| v.ident == variant); + if let Some(property_variant_data) = property_variant_data { + property_variant_data.block_state_ids.push(state_id); + } else { + property_variants.push(PropertyVariantData { + block_state_ids: vec![state_id], + ident: variant, + is_enum: property.is_enum, + }); } } @@ -509,13 +587,14 @@ pub fn make_block_states(input: TokenStream) -> TokenStream { let PropertyWithNameAndDefault { property_type: property_struct_name_ident, name: property_name, + property_value_type, .. } = &properties_with_name[i]; let property_variants = &block_properties_vec[i]; let property_variants_count = property_variants.len() as u32; let conversion_code = { - if &property_struct_name_ident.to_string() == "bool" { + if &property_value_type.to_string() == "bool" { assert_eq!(property_variants_count, 2); // this is not a mistake, it starts with true for some reason quote! {(b / #division) % #property_variants_count == 0} @@ -611,8 +690,6 @@ pub fn make_block_states(input: TokenStream) -> TokenStream { block_structs.extend(block_struct); } - let waterlogged_state_ids_match = quote! { #(#waterlogged_state_ids)|* }; - let last_state_id = state_id - 1; let mut generated = quote! { impl BlockState { @@ -622,21 +699,87 @@ pub fn make_block_states(input: TokenStream) -> TokenStream { #last_state_id } - /// Whether the given block state is waterlogged. This checks for - /// the `waterlogged` field, so it'll return false for water. - pub fn waterlogged(&self) -> bool { - matches!(self.id, #waterlogged_state_ids_match) + /// Get a property from this block state. Will be `None` if the block can't have the property. + /// + /// ``` + /// fn is_waterlogged(block_state: azalea_block::BlockState) -> bool { + /// block_state.property::().unwrap_or_default() + /// } + /// ``` + pub fn property(self) -> Option { + P::try_from_block_state(self) } } + }; + // now impl Property for every property + // ``` + // match state_id { + // // this is just an example of how it might look, these state ids are definitely not correct + // 0|3|6 => Some(Self::Axis::X), + // 1|4|7 => Some(Self::Axis::Y), + // 2|5|8 => Some(Self::Axis::Z), + // _ => None + // } + // ``` + let mut property_impls = quote! {}; + for (property_struct_name, property_values) in properties_to_state_ids { + let mut enum_inner_generated = quote! {}; + + let mut is_enum_ = false; + + for PropertyVariantData { + block_state_ids, + ident, + is_enum, + } in property_values + { + enum_inner_generated.extend(if is_enum { + quote! { + #(#block_state_ids)|* => Some(Self::#ident), + } + } else { + quote! { + #(#block_state_ids)|* => Some(#ident), + } + }); + is_enum_ = is_enum; + } + let is_enum = is_enum_; + + let property_struct_name = + Ident::new(&property_struct_name, proc_macro2::Span::call_site()); + + let value = if is_enum { + quote! { Self } + } else { + quote! { bool } + }; + + let property_impl = quote! { + impl Property for #property_struct_name { + type Value = #value; + + fn try_from_block_state(block_state: BlockState) -> Option { + match block_state.id { + #enum_inner_generated + _ => None + } + } + } + }; + property_impls.extend(property_impl); + } + + generated.extend(quote! { pub mod properties { use super::*; #property_enums + + #property_impls } - }; - generated.extend(quote! { pub mod blocks { use super::*; diff --git a/azalea-block/src/generated.rs b/azalea-block/src/generated.rs index e3f8fb9e3..97236b4ba 100755 --- a/azalea-block/src/generated.rs +++ b/azalea-block/src/generated.rs @@ -1,10 +1,10 @@ -use crate::{Block, BlockBehavior, BlockState, BlockStates}; +use crate::{Block, BlockBehavior, BlockState, BlockStates, Property}; use azalea_block_macros::make_block_states; use std::fmt::Debug; make_block_states! { Properties => { - "snowy" => bool, + "snowy" => Snowy(bool), "stage" => OakSaplingStage { _0, _1, @@ -40,12 +40,12 @@ make_block_states! { _3, _4, }, - "hanging" => bool, + "hanging" => Hanging(bool), "stage" => MangrovePropaguleStage { _0, _1, }, - "waterlogged" => bool, + "waterlogged" => Waterlogged(bool), "level" => WaterLevel { _0, _1, @@ -108,7 +108,7 @@ make_block_states! { _6, _7, }, - "persistent" => bool, + "persistent" => Persistent(bool), "distance" => SpruceLeavesDistance { _1, _2, @@ -198,7 +198,7 @@ make_block_states! { Up, Down, }, - "triggered" => bool, + "triggered" => Triggered(bool), "instrument" => Sound { Harp, Basedrum, @@ -251,14 +251,14 @@ make_block_states! { _23, _24, }, - "powered" => bool, + "powered" => Powered(bool), "facing" => FacingCardinal { North, South, West, East, }, - "occupied" => bool, + "occupied" => Occupied(bool), "part" => Part { Head, Foot, @@ -271,7 +271,7 @@ make_block_states! { AscendingNorth, AscendingSouth, }, - "extended" => bool, + "extended" => Extended(bool), "half" => Half { Upper, Lower, @@ -280,14 +280,9 @@ make_block_states! { Normal, Sticky, }, - "short" => bool, - "unstable" => bool, - "slot_0_occupied" => bool, - "slot_1_occupied" => bool, - "slot_2_occupied" => bool, - "slot_3_occupied" => bool, - "slot_4_occupied" => bool, - "slot_5_occupied" => bool, + "short" => Short(bool), + "unstable" => Unstable(bool), + "slot_5_occupied" => TrueFalse(bool), "age" => FireAge { _0, _1, @@ -306,11 +301,11 @@ make_block_states! { _14, _15, }, - "east" => bool, - "north" => bool, - "south" => bool, - "up" => bool, - "west" => bool, + "east" => East(bool), + "north" => North(bool), + "south" => South(bool), + "up" => Up(bool), + "west" => West(bool), "half" => TopBottom { Top, Bottom, @@ -385,7 +380,7 @@ make_block_states! { _6, _7, }, - "lit" => bool, + "lit" => Lit(bool), "rotation" => OakSignRotation { _0, _1, @@ -552,7 +547,7 @@ make_block_states! { Left, Right, }, - "open" => bool, + "open" => Open(bool), "shape" => Shape { NorthSouth, EastWest, @@ -565,7 +560,7 @@ make_block_states! { NorthWest, NorthEast, }, - "attached" => bool, + "attached" => Attached(bool), "rotation" => OakHangingSignRotation { _0, _1, @@ -815,7 +810,7 @@ make_block_states! { _14, _15, }, - "has_record" => bool, + "has_record" => HasRecord(bool), "axis" => AxisXZ { X, Z, @@ -835,8 +830,8 @@ make_block_states! { _3, _4, }, - "locked" => bool, - "down" => bool, + "locked" => Locked(bool), + "down" => Down(bool), "age" => PumpkinStemAge { _0, _1, @@ -857,19 +852,16 @@ make_block_states! { _6, _7, }, - "down" => bool, - "north" => bool, - "south" => bool, - "up" => bool, - "west" => bool, - "in_wall" => bool, + "in_wall" => InWall(bool), "age" => NetherWartAge { _0, _1, _2, _3, }, - "has_bottle" => bool, + "has_bottle" => HasBottle0(bool), + "has_bottle" => HasBottle1(bool), + "has_bottle" => HasBottle2(bool), "level" => WaterCauldronLevel { _1, _2, @@ -880,14 +872,14 @@ make_block_states! { _2, _3, }, - "eye" => bool, + "eye" => Eye(bool), "age" => CocoaAge { _0, _1, _2, }, - "disarmed" => bool, - "conditional" => bool, + "disarmed" => Disarmed(bool), + "conditional" => Conditional(bool), "east" => EastWall { None, Low, @@ -1094,7 +1086,7 @@ make_block_states! { Compare, Subtract, }, - "inverted" => bool, + "inverted" => Inverted(bool), "power" => DaylightDetectorPower { _0, _1, @@ -1113,7 +1105,7 @@ make_block_states! { _14, _15, }, - "enabled" => bool, + "enabled" => Enabled(bool), "facing" => Facing { Down, North, @@ -1530,8 +1522,8 @@ make_block_states! { _0, _1, }, - "drag" => bool, - "bottom" => bool, + "drag" => Drag(bool), + "bottom" => Bottom(bool), "distance" => ScaffoldingDistance { _0, _1, @@ -1542,14 +1534,14 @@ make_block_states! { _6, _7, }, - "has_book" => bool, + "has_book" => HasBook(bool), "attachment" => Attachment { Floor, Ceiling, SingleWall, DoubleWall, }, - "signal_fire" => bool, + "signal_fire" => SignalFire(bool), "age" => SweetBerryBushAge { _0, _1, @@ -1863,11 +1855,9 @@ make_block_states! { _14, _15, }, - "south" => bool, - "west" => bool, - "bloom" => bool, - "can_summon" => bool, - "shrieking" => bool, + "bloom" => Bloom(bool), + "can_summon" => CanSummon(bool), + "shrieking" => Shrieking(bool), "thickness" => Thickness { TipMerge, Tip, @@ -1907,7 +1897,7 @@ make_block_states! { _24, _25, }, - "berries" => bool, + "berries" => Berries(bool), "flower_amount" => PinkPetalsAmount { _1, _2, @@ -1920,12 +1910,21 @@ make_block_states! { Partial, Full, }, - "axis" => CacheSize { + "axis" => Falling { X, Y, Z, }, - "cracked" => bool, + "cracked" => Cracked(bool), + "crafting" => Crafting(bool), + "trial_spawner_state" => State { + Inactive, + WaitingForPlayers, + Active, + WaitingForRewardEjection, + EjectingReward, + Cooldown, + }, }, Blocks => { air => BlockBehavior::new(), {}, @@ -1937,12 +1936,12 @@ make_block_states! { andesite => BlockBehavior::new().requires_correct_tool_for_drops().strength(1.5, 6.0), {}, polished_andesite => BlockBehavior::new().requires_correct_tool_for_drops().strength(1.5, 6.0), {}, grass_block => BlockBehavior::new().strength(0.6, 0.6), { - snowy: false, + snowy: Snowy(false), }, dirt => BlockBehavior::new().strength(0.5, 0.5), {}, coarse_dirt => BlockBehavior::new().strength(0.5, 0.5), {}, podzol => BlockBehavior::new().strength(0.5, 0.5), { - snowy: false, + snowy: Snowy(false), }, cobblestone => BlockBehavior::new().requires_correct_tool_for_drops().strength(2.0, 6.0), {}, oak_planks => BlockBehavior::new().strength(2.0, 3.0), {}, @@ -1978,9 +1977,9 @@ make_block_states! { }, mangrove_propagule => BlockBehavior::new(), { age: MangrovePropaguleAge::_0, - hanging: false, + hanging: Hanging(false), stage: MangrovePropaguleStage::_0, - waterlogged: false, + waterlogged: Waterlogged(false), }, bedrock => BlockBehavior::new().strength(-1.0, 3600000.0), {}, water => BlockBehavior::new().strength(100.0, 100.0), { @@ -2030,7 +2029,7 @@ make_block_states! { axis: Axis::Y, }, mangrove_roots => BlockBehavior::new().strength(0.7, 0.7), { - waterlogged: false, + waterlogged: Waterlogged(false), }, muddy_mangrove_roots => BlockBehavior::new().strength(0.7, 0.7), { axis: Axis::Y, @@ -2115,53 +2114,53 @@ make_block_states! { }, oak_leaves => BlockBehavior::new().strength(0.2, 0.2), { distance: OakLeavesDistance::_7, - persistent: false, - waterlogged: false, + persistent: Persistent(false), + waterlogged: Waterlogged(false), }, spruce_leaves => BlockBehavior::new().strength(0.2, 0.2), { distance: SpruceLeavesDistance::_7, - persistent: false, - waterlogged: false, + persistent: Persistent(false), + waterlogged: Waterlogged(false), }, birch_leaves => BlockBehavior::new().strength(0.2, 0.2), { distance: BirchLeavesDistance::_7, - persistent: false, - waterlogged: false, + persistent: Persistent(false), + waterlogged: Waterlogged(false), }, jungle_leaves => BlockBehavior::new().strength(0.2, 0.2), { distance: JungleLeavesDistance::_7, - persistent: false, - waterlogged: false, + persistent: Persistent(false), + waterlogged: Waterlogged(false), }, acacia_leaves => BlockBehavior::new().strength(0.2, 0.2), { distance: AcaciaLeavesDistance::_7, - persistent: false, - waterlogged: false, + persistent: Persistent(false), + waterlogged: Waterlogged(false), }, cherry_leaves => BlockBehavior::new().strength(0.2, 0.2), { distance: CherryLeavesDistance::_7, - persistent: false, - waterlogged: false, + persistent: Persistent(false), + waterlogged: Waterlogged(false), }, dark_oak_leaves => BlockBehavior::new().strength(0.2, 0.2), { distance: DarkOakLeavesDistance::_7, - persistent: false, - waterlogged: false, + persistent: Persistent(false), + waterlogged: Waterlogged(false), }, mangrove_leaves => BlockBehavior::new().strength(0.2, 0.2), { distance: MangroveLeavesDistance::_7, - persistent: false, - waterlogged: false, + persistent: Persistent(false), + waterlogged: Waterlogged(false), }, azalea_leaves => BlockBehavior::new().strength(0.2, 0.2), { distance: AzaleaLeavesDistance::_7, - persistent: false, - waterlogged: false, + persistent: Persistent(false), + waterlogged: Waterlogged(false), }, flowering_azalea_leaves => BlockBehavior::new().strength(0.2, 0.2), { distance: FloweringAzaleaLeavesDistance::_7, - persistent: false, - waterlogged: false, + persistent: Persistent(false), + waterlogged: Waterlogged(false), }, sponge => BlockBehavior::new().strength(0.6, 0.6), {}, wet_sponge => BlockBehavior::new().strength(0.6, 0.6), {}, @@ -2171,7 +2170,7 @@ make_block_states! { lapis_block => BlockBehavior::new().requires_correct_tool_for_drops().strength(3.0, 3.0), {}, dispenser => BlockBehavior::new().requires_correct_tool_for_drops().strength(3.5, 3.5), { facing: FacingCubic::North, - triggered: false, + triggered: Triggered(false), }, sandstone => BlockBehavior::new().requires_correct_tool_for_drops().strength(0.8, 0.8), {}, chiseled_sandstone => BlockBehavior::new().requires_correct_tool_for_drops().strength(0.8, 0.8), {}, @@ -2179,104 +2178,104 @@ make_block_states! { note_block => BlockBehavior::new().strength(0.8, 0.8), { instrument: Sound::Harp, note: NoteBlockNote::_0, - powered: false, + powered: Powered(false), }, white_bed => BlockBehavior::new().strength(0.2, 0.2), { facing: FacingCardinal::North, - occupied: false, + occupied: Occupied(false), part: Part::Foot, }, orange_bed => BlockBehavior::new().strength(0.2, 0.2), { facing: FacingCardinal::North, - occupied: false, + occupied: Occupied(false), part: Part::Foot, }, magenta_bed => BlockBehavior::new().strength(0.2, 0.2), { facing: FacingCardinal::North, - occupied: false, + occupied: Occupied(false), part: Part::Foot, }, light_blue_bed => BlockBehavior::new().strength(0.2, 0.2), { facing: FacingCardinal::North, - occupied: false, + occupied: Occupied(false), part: Part::Foot, }, yellow_bed => BlockBehavior::new().strength(0.2, 0.2), { facing: FacingCardinal::North, - occupied: false, + occupied: Occupied(false), part: Part::Foot, }, lime_bed => BlockBehavior::new().strength(0.2, 0.2), { facing: FacingCardinal::North, - occupied: false, + occupied: Occupied(false), part: Part::Foot, }, pink_bed => BlockBehavior::new().strength(0.2, 0.2), { facing: FacingCardinal::North, - occupied: false, + occupied: Occupied(false), part: Part::Foot, }, gray_bed => BlockBehavior::new().strength(0.2, 0.2), { facing: FacingCardinal::North, - occupied: false, + occupied: Occupied(false), part: Part::Foot, }, light_gray_bed => BlockBehavior::new().strength(0.2, 0.2), { facing: FacingCardinal::North, - occupied: false, + occupied: Occupied(false), part: Part::Foot, }, cyan_bed => BlockBehavior::new().strength(0.2, 0.2), { facing: FacingCardinal::North, - occupied: false, + occupied: Occupied(false), part: Part::Foot, }, purple_bed => BlockBehavior::new().strength(0.2, 0.2), { facing: FacingCardinal::North, - occupied: false, + occupied: Occupied(false), part: Part::Foot, }, blue_bed => BlockBehavior::new().strength(0.2, 0.2), { facing: FacingCardinal::North, - occupied: false, + occupied: Occupied(false), part: Part::Foot, }, brown_bed => BlockBehavior::new().strength(0.2, 0.2), { facing: FacingCardinal::North, - occupied: false, + occupied: Occupied(false), part: Part::Foot, }, green_bed => BlockBehavior::new().strength(0.2, 0.2), { facing: FacingCardinal::North, - occupied: false, + occupied: Occupied(false), part: Part::Foot, }, red_bed => BlockBehavior::new().strength(0.2, 0.2), { facing: FacingCardinal::North, - occupied: false, + occupied: Occupied(false), part: Part::Foot, }, black_bed => BlockBehavior::new().strength(0.2, 0.2), { facing: FacingCardinal::North, - occupied: false, + occupied: Occupied(false), part: Part::Foot, }, powered_rail => BlockBehavior::new().strength(0.7, 0.7), { - powered: false, + powered: Powered(false), shape: RailShape::NorthSouth, - waterlogged: false, + waterlogged: Waterlogged(false), }, detector_rail => BlockBehavior::new().strength(0.7, 0.7), { - powered: false, + powered: Powered(false), shape: RailShape::NorthSouth, - waterlogged: false, + waterlogged: Waterlogged(false), }, sticky_piston => BlockBehavior::new().strength(1.5, 1.5), { - extended: false, + extended: Extended(false), facing: FacingCubic::North, }, cobweb => BlockBehavior::new().requires_correct_tool_for_drops().strength(4.0, 4.0), {}, - grass => BlockBehavior::new(), {}, + short_grass => BlockBehavior::new(), {}, fern => BlockBehavior::new(), {}, dead_bush => BlockBehavior::new(), {}, seagrass => BlockBehavior::new(), {}, @@ -2284,13 +2283,13 @@ make_block_states! { half: Half::Lower, }, piston => BlockBehavior::new().strength(1.5, 1.5), { - extended: false, + extended: Extended(false), facing: FacingCubic::North, }, piston_head => BlockBehavior::new().strength(1.5, 1.5), { kind: PistonType::Normal, facing: FacingCubic::North, - short: false, + short: Short(false), }, white_wool => BlockBehavior::new().strength(0.8, 0.8), {}, orange_wool => BlockBehavior::new().strength(0.8, 0.8), {}, @@ -2332,17 +2331,17 @@ make_block_states! { iron_block => BlockBehavior::new().requires_correct_tool_for_drops().strength(5.0, 6.0), {}, bricks => BlockBehavior::new().requires_correct_tool_for_drops().strength(2.0, 6.0), {}, tnt => BlockBehavior::new(), { - unstable: false, + unstable: Unstable(false), }, bookshelf => BlockBehavior::new().strength(1.5, 1.5), {}, chiseled_bookshelf => BlockBehavior::new().strength(1.5, 1.5), { facing: FacingCardinal::North, - slot_0_occupied: false, - slot_1_occupied: false, - slot_2_occupied: false, - slot_3_occupied: false, - slot_4_occupied: false, - slot_5_occupied: false, + slot_0_occupied: TrueFalse(false), + slot_1_occupied: TrueFalse(false), + slot_2_occupied: TrueFalse(false), + slot_3_occupied: TrueFalse(false), + slot_4_occupied: TrueFalse(false), + slot_5_occupied: TrueFalse(false), }, mossy_cobblestone => BlockBehavior::new().requires_correct_tool_for_drops().strength(2.0, 6.0), {}, obsidian => BlockBehavior::new().requires_correct_tool_for_drops().strength(50.0, 1200.0), {}, @@ -2352,11 +2351,11 @@ make_block_states! { }, fire => BlockBehavior::new(), { age: FireAge::_0, - east: false, - north: false, - south: false, - up: false, - west: false, + east: East(false), + north: North(false), + south: South(false), + up: Up(false), + west: West(false), }, soul_fire => BlockBehavior::new(), {}, spawner => BlockBehavior::new().requires_correct_tool_for_drops().strength(5.0, 5.0), {}, @@ -2364,12 +2363,12 @@ make_block_states! { facing: FacingCardinal::North, half: TopBottom::Bottom, shape: StairShape::Straight, - waterlogged: false, + waterlogged: Waterlogged(false), }, chest => BlockBehavior::new().strength(2.5, 2.5), { kind: ChestType::Single, facing: FacingCardinal::North, - waterlogged: false, + waterlogged: Waterlogged(false), }, redstone_wire => BlockBehavior::new(), { east: WireEast::None, @@ -2390,259 +2389,259 @@ make_block_states! { }, furnace => BlockBehavior::new().requires_correct_tool_for_drops().strength(3.5, 3.5), { facing: FacingCardinal::North, - lit: false, + lit: Lit(false), }, oak_sign => BlockBehavior::new().strength(1.0, 1.0), { rotation: OakSignRotation::_0, - waterlogged: false, + waterlogged: Waterlogged(false), }, spruce_sign => BlockBehavior::new().strength(1.0, 1.0), { rotation: SpruceSignRotation::_0, - waterlogged: false, + waterlogged: Waterlogged(false), }, birch_sign => BlockBehavior::new().strength(1.0, 1.0), { rotation: BirchSignRotation::_0, - waterlogged: false, + waterlogged: Waterlogged(false), }, acacia_sign => BlockBehavior::new().strength(1.0, 1.0), { rotation: AcaciaSignRotation::_0, - waterlogged: false, + waterlogged: Waterlogged(false), }, cherry_sign => BlockBehavior::new().strength(1.0, 1.0), { rotation: CherrySignRotation::_0, - waterlogged: false, + waterlogged: Waterlogged(false), }, jungle_sign => BlockBehavior::new().strength(1.0, 1.0), { rotation: JungleSignRotation::_0, - waterlogged: false, + waterlogged: Waterlogged(false), }, dark_oak_sign => BlockBehavior::new().strength(1.0, 1.0), { rotation: DarkOakSignRotation::_0, - waterlogged: false, + waterlogged: Waterlogged(false), }, mangrove_sign => BlockBehavior::new().strength(1.0, 1.0), { rotation: MangroveSignRotation::_0, - waterlogged: false, + waterlogged: Waterlogged(false), }, bamboo_sign => BlockBehavior::new().strength(1.0, 1.0), { rotation: BambooSignRotation::_0, - waterlogged: false, + waterlogged: Waterlogged(false), }, oak_door => BlockBehavior::new().strength(3.0, 3.0), { facing: FacingCardinal::North, half: Half::Lower, hinge: Hinge::Left, - open: false, - powered: false, + open: Open(false), + powered: Powered(false), }, ladder => BlockBehavior::new().strength(0.4, 0.4), { facing: FacingCardinal::North, - waterlogged: false, + waterlogged: Waterlogged(false), }, rail => BlockBehavior::new().strength(0.7, 0.7), { shape: Shape::NorthSouth, - waterlogged: false, + waterlogged: Waterlogged(false), }, cobblestone_stairs => BlockBehavior::new().requires_correct_tool_for_drops().strength(2.0, 6.0), { facing: FacingCardinal::North, half: TopBottom::Bottom, shape: StairShape::Straight, - waterlogged: false, + waterlogged: Waterlogged(false), }, oak_wall_sign => BlockBehavior::new().strength(1.0, 1.0), { facing: FacingCardinal::North, - waterlogged: false, + waterlogged: Waterlogged(false), }, spruce_wall_sign => BlockBehavior::new().strength(1.0, 1.0), { facing: FacingCardinal::North, - waterlogged: false, + waterlogged: Waterlogged(false), }, birch_wall_sign => BlockBehavior::new().strength(1.0, 1.0), { facing: FacingCardinal::North, - waterlogged: false, + waterlogged: Waterlogged(false), }, acacia_wall_sign => BlockBehavior::new().strength(1.0, 1.0), { facing: FacingCardinal::North, - waterlogged: false, + waterlogged: Waterlogged(false), }, cherry_wall_sign => BlockBehavior::new().strength(1.0, 1.0), { facing: FacingCardinal::North, - waterlogged: false, + waterlogged: Waterlogged(false), }, jungle_wall_sign => BlockBehavior::new().strength(1.0, 1.0), { facing: FacingCardinal::North, - waterlogged: false, + waterlogged: Waterlogged(false), }, dark_oak_wall_sign => BlockBehavior::new().strength(1.0, 1.0), { facing: FacingCardinal::North, - waterlogged: false, + waterlogged: Waterlogged(false), }, mangrove_wall_sign => BlockBehavior::new().strength(1.0, 1.0), { facing: FacingCardinal::North, - waterlogged: false, + waterlogged: Waterlogged(false), }, bamboo_wall_sign => BlockBehavior::new().strength(1.0, 1.0), { facing: FacingCardinal::North, - waterlogged: false, + waterlogged: Waterlogged(false), }, oak_hanging_sign => BlockBehavior::new().strength(1.0, 1.0), { - attached: false, + attached: Attached(false), rotation: OakHangingSignRotation::_0, - waterlogged: false, + waterlogged: Waterlogged(false), }, spruce_hanging_sign => BlockBehavior::new().strength(1.0, 1.0), { - attached: false, + attached: Attached(false), rotation: SpruceHangingSignRotation::_0, - waterlogged: false, + waterlogged: Waterlogged(false), }, birch_hanging_sign => BlockBehavior::new().strength(1.0, 1.0), { - attached: false, + attached: Attached(false), rotation: BirchHangingSignRotation::_0, - waterlogged: false, + waterlogged: Waterlogged(false), }, acacia_hanging_sign => BlockBehavior::new().strength(1.0, 1.0), { - attached: false, + attached: Attached(false), rotation: AcaciaHangingSignRotation::_0, - waterlogged: false, + waterlogged: Waterlogged(false), }, cherry_hanging_sign => BlockBehavior::new().strength(1.0, 1.0), { - attached: false, + attached: Attached(false), rotation: CherryHangingSignRotation::_0, - waterlogged: false, + waterlogged: Waterlogged(false), }, jungle_hanging_sign => BlockBehavior::new().strength(1.0, 1.0), { - attached: false, + attached: Attached(false), rotation: JungleHangingSignRotation::_0, - waterlogged: false, + waterlogged: Waterlogged(false), }, dark_oak_hanging_sign => BlockBehavior::new().strength(1.0, 1.0), { - attached: false, + attached: Attached(false), rotation: DarkOakHangingSignRotation::_0, - waterlogged: false, + waterlogged: Waterlogged(false), }, crimson_hanging_sign => BlockBehavior::new().strength(1.0, 1.0), { - attached: false, + attached: Attached(false), rotation: CrimsonHangingSignRotation::_0, - waterlogged: false, + waterlogged: Waterlogged(false), }, warped_hanging_sign => BlockBehavior::new().strength(1.0, 1.0), { - attached: false, + attached: Attached(false), rotation: WarpedHangingSignRotation::_0, - waterlogged: false, + waterlogged: Waterlogged(false), }, mangrove_hanging_sign => BlockBehavior::new().strength(1.0, 1.0), { - attached: false, + attached: Attached(false), rotation: MangroveHangingSignRotation::_0, - waterlogged: false, + waterlogged: Waterlogged(false), }, bamboo_hanging_sign => BlockBehavior::new().strength(1.0, 1.0), { - attached: false, + attached: Attached(false), rotation: BambooHangingSignRotation::_0, - waterlogged: false, + waterlogged: Waterlogged(false), }, oak_wall_hanging_sign => BlockBehavior::new().strength(1.0, 1.0), { facing: FacingCardinal::North, - waterlogged: false, + waterlogged: Waterlogged(false), }, spruce_wall_hanging_sign => BlockBehavior::new().strength(1.0, 1.0), { facing: FacingCardinal::North, - waterlogged: false, + waterlogged: Waterlogged(false), }, birch_wall_hanging_sign => BlockBehavior::new().strength(1.0, 1.0), { facing: FacingCardinal::North, - waterlogged: false, + waterlogged: Waterlogged(false), }, acacia_wall_hanging_sign => BlockBehavior::new().strength(1.0, 1.0), { facing: FacingCardinal::North, - waterlogged: false, + waterlogged: Waterlogged(false), }, cherry_wall_hanging_sign => BlockBehavior::new().strength(1.0, 1.0), { facing: FacingCardinal::North, - waterlogged: false, + waterlogged: Waterlogged(false), }, jungle_wall_hanging_sign => BlockBehavior::new().strength(1.0, 1.0), { facing: FacingCardinal::North, - waterlogged: false, + waterlogged: Waterlogged(false), }, dark_oak_wall_hanging_sign => BlockBehavior::new().strength(1.0, 1.0), { facing: FacingCardinal::North, - waterlogged: false, + waterlogged: Waterlogged(false), }, mangrove_wall_hanging_sign => BlockBehavior::new().strength(1.0, 1.0), { facing: FacingCardinal::North, - waterlogged: false, + waterlogged: Waterlogged(false), }, crimson_wall_hanging_sign => BlockBehavior::new().strength(1.0, 1.0), { facing: FacingCardinal::North, - waterlogged: false, + waterlogged: Waterlogged(false), }, warped_wall_hanging_sign => BlockBehavior::new().strength(1.0, 1.0), { facing: FacingCardinal::North, - waterlogged: false, + waterlogged: Waterlogged(false), }, bamboo_wall_hanging_sign => BlockBehavior::new().strength(1.0, 1.0), { facing: FacingCardinal::North, - waterlogged: false, + waterlogged: Waterlogged(false), }, lever => BlockBehavior::new().strength(0.5, 0.5), { face: Face::Wall, facing: FacingCardinal::North, - powered: false, + powered: Powered(false), }, stone_pressure_plate => BlockBehavior::new().requires_correct_tool_for_drops().strength(0.5, 0.5), { - powered: false, + powered: Powered(false), }, iron_door => BlockBehavior::new().requires_correct_tool_for_drops().strength(5.0, 5.0), { facing: FacingCardinal::North, half: Half::Lower, hinge: Hinge::Left, - open: false, - powered: false, + open: Open(false), + powered: Powered(false), }, oak_pressure_plate => BlockBehavior::new().strength(0.5, 0.5), { - powered: false, + powered: Powered(false), }, spruce_pressure_plate => BlockBehavior::new().strength(0.5, 0.5), { - powered: false, + powered: Powered(false), }, birch_pressure_plate => BlockBehavior::new().strength(0.5, 0.5), { - powered: false, + powered: Powered(false), }, jungle_pressure_plate => BlockBehavior::new().strength(0.5, 0.5), { - powered: false, + powered: Powered(false), }, acacia_pressure_plate => BlockBehavior::new().strength(0.5, 0.5), { - powered: false, + powered: Powered(false), }, cherry_pressure_plate => BlockBehavior::new().strength(0.5, 0.5), { - powered: false, + powered: Powered(false), }, dark_oak_pressure_plate => BlockBehavior::new().strength(0.5, 0.5), { - powered: false, + powered: Powered(false), }, mangrove_pressure_plate => BlockBehavior::new().strength(0.5, 0.5), { - powered: false, + powered: Powered(false), }, bamboo_pressure_plate => BlockBehavior::new().strength(0.5, 0.5), { - powered: false, + powered: Powered(false), }, redstone_ore => BlockBehavior::new().requires_correct_tool_for_drops().strength(3.0, 3.0), { - lit: false, + lit: Lit(false), }, deepslate_redstone_ore => BlockBehavior::new().requires_correct_tool_for_drops().strength(4.5, 3.0), { - lit: false, + lit: Lit(false), }, redstone_torch => BlockBehavior::new(), { - lit: true, + lit: Lit(true), }, redstone_wall_torch => BlockBehavior::new(), { facing: FacingCardinal::North, - lit: true, + lit: Lit(true), }, stone_button => BlockBehavior::new().strength(0.5, 0.5), { face: Face::Wall, facing: FacingCardinal::North, - powered: false, + powered: Powered(false), }, snow => BlockBehavior::new().requires_correct_tool_for_drops().strength(0.1, 0.1), { layers: SnowLayers::_1, @@ -2657,16 +2656,15 @@ make_block_states! { age: SugarCaneAge::_0, }, jukebox => BlockBehavior::new().strength(2.0, 6.0), { - has_record: false, + has_record: HasRecord(false), }, oak_fence => BlockBehavior::new().strength(2.0, 3.0), { - east: false, - north: false, - south: false, - waterlogged: false, - west: false, + east: East(false), + north: North(false), + south: South(false), + waterlogged: Waterlogged(false), + west: West(false), }, - pumpkin => BlockBehavior::new().strength(1.0, 1.0), {}, netherrack => BlockBehavior::new().requires_correct_tool_for_drops().strength(0.4, 0.4), {}, soul_sand => BlockBehavior::new().strength(0.5, 0.5), {}, soul_soil => BlockBehavior::new().strength(0.5, 0.5), {}, @@ -2696,8 +2694,8 @@ make_block_states! { repeater => BlockBehavior::new(), { delay: RepeaterDelay::_1, facing: FacingCardinal::North, - locked: false, - powered: false, + locked: Locked(false), + powered: Powered(false), }, white_stained_glass => BlockBehavior::new().strength(0.3, 0.3), {}, orange_stained_glass => BlockBehavior::new().strength(0.3, 0.3), {}, @@ -2718,65 +2716,65 @@ make_block_states! { oak_trapdoor => BlockBehavior::new().strength(3.0, 3.0), { facing: FacingCardinal::North, half: TopBottom::Bottom, - open: false, - powered: false, - waterlogged: false, + open: Open(false), + powered: Powered(false), + waterlogged: Waterlogged(false), }, spruce_trapdoor => BlockBehavior::new().strength(3.0, 3.0), { facing: FacingCardinal::North, half: TopBottom::Bottom, - open: false, - powered: false, - waterlogged: false, + open: Open(false), + powered: Powered(false), + waterlogged: Waterlogged(false), }, birch_trapdoor => BlockBehavior::new().strength(3.0, 3.0), { facing: FacingCardinal::North, half: TopBottom::Bottom, - open: false, - powered: false, - waterlogged: false, + open: Open(false), + powered: Powered(false), + waterlogged: Waterlogged(false), }, jungle_trapdoor => BlockBehavior::new().strength(3.0, 3.0), { facing: FacingCardinal::North, half: TopBottom::Bottom, - open: false, - powered: false, - waterlogged: false, + open: Open(false), + powered: Powered(false), + waterlogged: Waterlogged(false), }, acacia_trapdoor => BlockBehavior::new().strength(3.0, 3.0), { facing: FacingCardinal::North, half: TopBottom::Bottom, - open: false, - powered: false, - waterlogged: false, + open: Open(false), + powered: Powered(false), + waterlogged: Waterlogged(false), }, cherry_trapdoor => BlockBehavior::new().strength(3.0, 3.0), { facing: FacingCardinal::North, half: TopBottom::Bottom, - open: false, - powered: false, - waterlogged: false, + open: Open(false), + powered: Powered(false), + waterlogged: Waterlogged(false), }, dark_oak_trapdoor => BlockBehavior::new().strength(3.0, 3.0), { facing: FacingCardinal::North, half: TopBottom::Bottom, - open: false, - powered: false, - waterlogged: false, + open: Open(false), + powered: Powered(false), + waterlogged: Waterlogged(false), }, mangrove_trapdoor => BlockBehavior::new().strength(3.0, 3.0), { facing: FacingCardinal::North, half: TopBottom::Bottom, - open: false, - powered: false, - waterlogged: false, + open: Open(false), + powered: Powered(false), + waterlogged: Waterlogged(false), }, bamboo_trapdoor => BlockBehavior::new().strength(3.0, 3.0), { facing: FacingCardinal::North, half: TopBottom::Bottom, - open: false, - powered: false, - waterlogged: false, + open: Open(false), + powered: Powered(false), + waterlogged: Waterlogged(false), }, stone_bricks => BlockBehavior::new().requires_correct_tool_for_drops().strength(1.5, 6.0), {}, mossy_stone_bricks => BlockBehavior::new().requires_correct_tool_for_drops().strength(1.5, 6.0), {}, @@ -2791,47 +2789,48 @@ make_block_states! { infested_cracked_stone_bricks => BlockBehavior::new().strength(0.75, 0.75), {}, infested_chiseled_stone_bricks => BlockBehavior::new().strength(0.75, 0.75), {}, brown_mushroom_block => BlockBehavior::new().strength(0.2, 0.2), { - down: true, - east: true, - north: true, - south: true, - up: true, - west: true, + down: Down(true), + east: East(true), + north: North(true), + south: South(true), + up: Up(true), + west: West(true), }, red_mushroom_block => BlockBehavior::new().strength(0.2, 0.2), { - down: true, - east: true, - north: true, - south: true, - up: true, - west: true, + down: Down(true), + east: East(true), + north: North(true), + south: South(true), + up: Up(true), + west: West(true), }, mushroom_stem => BlockBehavior::new().strength(0.2, 0.2), { - down: true, - east: true, - north: true, - south: true, - up: true, - west: true, + down: Down(true), + east: East(true), + north: North(true), + south: South(true), + up: Up(true), + west: West(true), }, iron_bars => BlockBehavior::new().requires_correct_tool_for_drops().strength(5.0, 6.0), { - east: false, - north: false, - south: false, - waterlogged: false, - west: false, + east: East(false), + north: North(false), + south: South(false), + waterlogged: Waterlogged(false), + west: West(false), }, chain => BlockBehavior::new().requires_correct_tool_for_drops().strength(5.0, 6.0), { axis: Axis::Y, - waterlogged: false, + waterlogged: Waterlogged(false), }, glass_pane => BlockBehavior::new().strength(0.3, 0.3), { - east: false, - north: false, - south: false, - waterlogged: false, - west: false, + east: East(false), + north: North(false), + south: South(false), + waterlogged: Waterlogged(false), + west: West(false), }, + pumpkin => BlockBehavior::new().strength(1.0, 1.0), {}, melon => BlockBehavior::new().strength(1.0, 1.0), {}, attached_pumpkin_stem => BlockBehavior::new(), { facing: FacingCardinal::North, @@ -2846,71 +2845,71 @@ make_block_states! { age: MelonStemAge::_0, }, vine => BlockBehavior::new().strength(0.2, 0.2), { - east: false, - north: false, - south: false, - up: false, - west: false, + east: East(false), + north: North(false), + south: South(false), + up: Up(false), + west: West(false), }, glow_lichen => BlockBehavior::new().strength(0.2, 0.2), { - down: false, - east: false, - north: false, - south: false, - up: false, - waterlogged: false, - west: false, + down: Down(false), + east: East(false), + north: North(false), + south: South(false), + up: Up(false), + waterlogged: Waterlogged(false), + west: West(false), }, oak_fence_gate => BlockBehavior::new().strength(2.0, 3.0), { facing: FacingCardinal::North, - in_wall: false, - open: false, - powered: false, + in_wall: InWall(false), + open: Open(false), + powered: Powered(false), }, brick_stairs => BlockBehavior::new().requires_correct_tool_for_drops().strength(2.0, 6.0), { facing: FacingCardinal::North, half: TopBottom::Bottom, shape: StairShape::Straight, - waterlogged: false, + waterlogged: Waterlogged(false), }, stone_brick_stairs => BlockBehavior::new().requires_correct_tool_for_drops().strength(1.5, 6.0), { facing: FacingCardinal::North, half: TopBottom::Bottom, shape: StairShape::Straight, - waterlogged: false, + waterlogged: Waterlogged(false), }, mud_brick_stairs => BlockBehavior::new().requires_correct_tool_for_drops().strength(1.5, 3.0), { facing: FacingCardinal::North, half: TopBottom::Bottom, shape: StairShape::Straight, - waterlogged: false, + waterlogged: Waterlogged(false), }, mycelium => BlockBehavior::new().strength(0.6, 0.6), { - snowy: false, + snowy: Snowy(false), }, lily_pad => BlockBehavior::new(), {}, nether_bricks => BlockBehavior::new().requires_correct_tool_for_drops().strength(2.0, 6.0), {}, nether_brick_fence => BlockBehavior::new().requires_correct_tool_for_drops().strength(2.0, 6.0), { - east: false, - north: false, - south: false, - waterlogged: false, - west: false, + east: East(false), + north: North(false), + south: South(false), + waterlogged: Waterlogged(false), + west: West(false), }, nether_brick_stairs => BlockBehavior::new().requires_correct_tool_for_drops().strength(2.0, 6.0), { facing: FacingCardinal::North, half: TopBottom::Bottom, shape: StairShape::Straight, - waterlogged: false, + waterlogged: Waterlogged(false), }, nether_wart => BlockBehavior::new(), { age: NetherWartAge::_0, }, enchanting_table => BlockBehavior::new().requires_correct_tool_for_drops().strength(5.0, 1200.0), {}, brewing_stand => BlockBehavior::new().requires_correct_tool_for_drops().strength(0.5, 0.5), { - has_bottle: false, - has_bottle: false, - has_bottle: false, + has_bottle: HasBottle0(false), + has_bottle: HasBottle1(false), + has_bottle: HasBottle2(false), }, cauldron => BlockBehavior::new().requires_correct_tool_for_drops().strength(2.0, 2.0), {}, water_cauldron => BlockBehavior::new().requires_correct_tool_for_drops().strength(2.0, 2.0), { @@ -2922,13 +2921,13 @@ make_block_states! { }, end_portal => BlockBehavior::new().strength(-1.0, 3600000.0), {}, end_portal_frame => BlockBehavior::new().strength(-1.0, 3600000.0), { - eye: false, + eye: Eye(false), facing: FacingCardinal::North, }, end_stone => BlockBehavior::new().requires_correct_tool_for_drops().strength(3.0, 9.0), {}, dragon_egg => BlockBehavior::new().strength(3.0, 9.0), {}, redstone_lamp => BlockBehavior::new().strength(0.3, 0.3), { - lit: false, + lit: Lit(false), }, cocoa => BlockBehavior::new().strength(0.2, 3.0), { age: CocoaAge::_0, @@ -2938,49 +2937,49 @@ make_block_states! { facing: FacingCardinal::North, half: TopBottom::Bottom, shape: StairShape::Straight, - waterlogged: false, + waterlogged: Waterlogged(false), }, emerald_ore => BlockBehavior::new().requires_correct_tool_for_drops().strength(3.0, 3.0), {}, deepslate_emerald_ore => BlockBehavior::new().requires_correct_tool_for_drops().strength(4.5, 3.0), {}, ender_chest => BlockBehavior::new().requires_correct_tool_for_drops().strength(22.5, 600.0), { facing: FacingCardinal::North, - waterlogged: false, + waterlogged: Waterlogged(false), }, tripwire_hook => BlockBehavior::new(), { - attached: false, + attached: Attached(false), facing: FacingCardinal::North, - powered: false, + powered: Powered(false), }, tripwire => BlockBehavior::new(), { - attached: false, - disarmed: false, - east: false, - north: false, - powered: false, - south: false, - west: false, + attached: Attached(false), + disarmed: Disarmed(false), + east: East(false), + north: North(false), + powered: Powered(false), + south: South(false), + west: West(false), }, emerald_block => BlockBehavior::new().requires_correct_tool_for_drops().strength(5.0, 6.0), {}, spruce_stairs => BlockBehavior::new().strength(2.0, 3.0), { facing: FacingCardinal::North, half: TopBottom::Bottom, shape: StairShape::Straight, - waterlogged: false, + waterlogged: Waterlogged(false), }, birch_stairs => BlockBehavior::new().strength(2.0, 3.0), { facing: FacingCardinal::North, half: TopBottom::Bottom, shape: StairShape::Straight, - waterlogged: false, + waterlogged: Waterlogged(false), }, jungle_stairs => BlockBehavior::new().strength(2.0, 3.0), { facing: FacingCardinal::North, half: TopBottom::Bottom, shape: StairShape::Straight, - waterlogged: false, + waterlogged: Waterlogged(false), }, command_block => BlockBehavior::new().requires_correct_tool_for_drops().strength(-1.0, 3600000.0), { - conditional: false, + conditional: Conditional(false), facing: FacingCubic::North, }, beacon => BlockBehavior::new().strength(3.0, 3.0), {}, @@ -2988,16 +2987,16 @@ make_block_states! { east: EastWall::None, north: NorthWall::None, south: SouthWall::None, - up: true, - waterlogged: false, + up: Up(true), + waterlogged: Waterlogged(false), west: WestWall::None, }, mossy_cobblestone_wall => BlockBehavior::new().requires_correct_tool_for_drops().strength(2.0, 6.0), { east: EastWall::None, north: NorthWall::None, south: SouthWall::None, - up: true, - waterlogged: false, + up: Up(true), + waterlogged: Waterlogged(false), west: WestWall::None, }, flower_pot => BlockBehavior::new(), {}, @@ -3037,103 +3036,103 @@ make_block_states! { oak_button => BlockBehavior::new().strength(0.5, 0.5), { face: Face::Wall, facing: FacingCardinal::North, - powered: false, + powered: Powered(false), }, spruce_button => BlockBehavior::new().strength(0.5, 0.5), { face: Face::Wall, facing: FacingCardinal::North, - powered: false, + powered: Powered(false), }, birch_button => BlockBehavior::new().strength(0.5, 0.5), { face: Face::Wall, facing: FacingCardinal::North, - powered: false, + powered: Powered(false), }, jungle_button => BlockBehavior::new().strength(0.5, 0.5), { face: Face::Wall, facing: FacingCardinal::North, - powered: false, + powered: Powered(false), }, acacia_button => BlockBehavior::new().strength(0.5, 0.5), { face: Face::Wall, facing: FacingCardinal::North, - powered: false, + powered: Powered(false), }, cherry_button => BlockBehavior::new().strength(0.5, 0.5), { face: Face::Wall, facing: FacingCardinal::North, - powered: false, + powered: Powered(false), }, dark_oak_button => BlockBehavior::new().strength(0.5, 0.5), { face: Face::Wall, facing: FacingCardinal::North, - powered: false, + powered: Powered(false), }, mangrove_button => BlockBehavior::new().strength(0.5, 0.5), { face: Face::Wall, facing: FacingCardinal::North, - powered: false, + powered: Powered(false), }, bamboo_button => BlockBehavior::new().strength(0.5, 0.5), { face: Face::Wall, facing: FacingCardinal::North, - powered: false, + powered: Powered(false), }, skeleton_skull => BlockBehavior::new().strength(1.0, 1.0), { - powered: false, + powered: Powered(false), rotation: SkeletonSkullRotation::_0, }, skeleton_wall_skull => BlockBehavior::new().strength(1.0, 1.0), { facing: FacingCardinal::North, - powered: false, + powered: Powered(false), }, wither_skeleton_skull => BlockBehavior::new().strength(1.0, 1.0), { - powered: false, + powered: Powered(false), rotation: WitherSkeletonSkullRotation::_0, }, wither_skeleton_wall_skull => BlockBehavior::new().strength(1.0, 1.0), { facing: FacingCardinal::North, - powered: false, + powered: Powered(false), }, zombie_head => BlockBehavior::new().strength(1.0, 1.0), { - powered: false, + powered: Powered(false), rotation: ZombieHeadRotation::_0, }, zombie_wall_head => BlockBehavior::new().strength(1.0, 1.0), { facing: FacingCardinal::North, - powered: false, + powered: Powered(false), }, player_head => BlockBehavior::new().strength(1.0, 1.0), { - powered: false, + powered: Powered(false), rotation: PlayerHeadRotation::_0, }, player_wall_head => BlockBehavior::new().strength(1.0, 1.0), { facing: FacingCardinal::North, - powered: false, + powered: Powered(false), }, creeper_head => BlockBehavior::new().strength(1.0, 1.0), { - powered: false, + powered: Powered(false), rotation: CreeperHeadRotation::_0, }, creeper_wall_head => BlockBehavior::new().strength(1.0, 1.0), { facing: FacingCardinal::North, - powered: false, + powered: Powered(false), }, dragon_head => BlockBehavior::new().strength(1.0, 1.0), { - powered: false, + powered: Powered(false), rotation: DragonHeadRotation::_0, }, dragon_wall_head => BlockBehavior::new().strength(1.0, 1.0), { facing: FacingCardinal::North, - powered: false, + powered: Powered(false), }, piglin_head => BlockBehavior::new().strength(1.0, 1.0), { - powered: false, + powered: Powered(false), rotation: PiglinHeadRotation::_0, }, piglin_wall_head => BlockBehavior::new().strength(1.0, 1.0), { facing: FacingCardinal::North, - powered: false, + powered: Powered(false), }, anvil => BlockBehavior::new().requires_correct_tool_for_drops().strength(5.0, 1200.0), { facing: FacingCardinal::North, @@ -3147,7 +3146,7 @@ make_block_states! { trapped_chest => BlockBehavior::new().strength(2.5, 2.5), { kind: ChestType::Single, facing: FacingCardinal::North, - waterlogged: false, + waterlogged: Waterlogged(false), }, light_weighted_pressure_plate => BlockBehavior::new().requires_correct_tool_for_drops().strength(0.5, 0.5), { power: LightWeightedPressurePlatePower::_0, @@ -3158,16 +3157,16 @@ make_block_states! { comparator => BlockBehavior::new(), { facing: FacingCardinal::North, mode: ComparatorType::Compare, - powered: false, + powered: Powered(false), }, daylight_detector => BlockBehavior::new().strength(0.2, 0.2), { - inverted: false, + inverted: Inverted(false), power: DaylightDetectorPower::_0, }, redstone_block => BlockBehavior::new().requires_correct_tool_for_drops().strength(5.0, 6.0), {}, nether_quartz_ore => BlockBehavior::new().requires_correct_tool_for_drops().strength(3.0, 3.0), {}, hopper => BlockBehavior::new().requires_correct_tool_for_drops().strength(3.0, 4.8), { - enabled: true, + enabled: Enabled(true), facing: Facing::Down, }, quartz_block => BlockBehavior::new().requires_correct_tool_for_drops().strength(0.8, 0.8), {}, @@ -3179,16 +3178,16 @@ make_block_states! { facing: FacingCardinal::North, half: TopBottom::Bottom, shape: StairShape::Straight, - waterlogged: false, + waterlogged: Waterlogged(false), }, activator_rail => BlockBehavior::new().strength(0.7, 0.7), { - powered: false, + powered: Powered(false), shape: RailShape::NorthSouth, - waterlogged: false, + waterlogged: Waterlogged(false), }, dropper => BlockBehavior::new().requires_correct_tool_for_drops().strength(3.5, 3.5), { facing: FacingCubic::North, - triggered: false, + triggered: Triggered(false), }, white_terracotta => BlockBehavior::new().requires_correct_tool_for_drops().strength(1.25, 4.2), {}, orange_terracotta => BlockBehavior::new().requires_correct_tool_for_drops().strength(1.25, 4.2), {}, @@ -3207,167 +3206,167 @@ make_block_states! { red_terracotta => BlockBehavior::new().requires_correct_tool_for_drops().strength(1.25, 4.2), {}, black_terracotta => BlockBehavior::new().requires_correct_tool_for_drops().strength(1.25, 4.2), {}, white_stained_glass_pane => BlockBehavior::new().strength(0.3, 0.3), { - east: false, - north: false, - south: false, - waterlogged: false, - west: false, + east: East(false), + north: North(false), + south: South(false), + waterlogged: Waterlogged(false), + west: West(false), }, orange_stained_glass_pane => BlockBehavior::new().strength(0.3, 0.3), { - east: false, - north: false, - south: false, - waterlogged: false, - west: false, + east: East(false), + north: North(false), + south: South(false), + waterlogged: Waterlogged(false), + west: West(false), }, magenta_stained_glass_pane => BlockBehavior::new().strength(0.3, 0.3), { - east: false, - north: false, - south: false, - waterlogged: false, - west: false, + east: East(false), + north: North(false), + south: South(false), + waterlogged: Waterlogged(false), + west: West(false), }, light_blue_stained_glass_pane => BlockBehavior::new().strength(0.3, 0.3), { - east: false, - north: false, - south: false, - waterlogged: false, - west: false, + east: East(false), + north: North(false), + south: South(false), + waterlogged: Waterlogged(false), + west: West(false), }, yellow_stained_glass_pane => BlockBehavior::new().strength(0.3, 0.3), { - east: false, - north: false, - south: false, - waterlogged: false, - west: false, + east: East(false), + north: North(false), + south: South(false), + waterlogged: Waterlogged(false), + west: West(false), }, lime_stained_glass_pane => BlockBehavior::new().strength(0.3, 0.3), { - east: false, - north: false, - south: false, - waterlogged: false, - west: false, + east: East(false), + north: North(false), + south: South(false), + waterlogged: Waterlogged(false), + west: West(false), }, pink_stained_glass_pane => BlockBehavior::new().strength(0.3, 0.3), { - east: false, - north: false, - south: false, - waterlogged: false, - west: false, + east: East(false), + north: North(false), + south: South(false), + waterlogged: Waterlogged(false), + west: West(false), }, gray_stained_glass_pane => BlockBehavior::new().strength(0.3, 0.3), { - east: false, - north: false, - south: false, - waterlogged: false, - west: false, + east: East(false), + north: North(false), + south: South(false), + waterlogged: Waterlogged(false), + west: West(false), }, light_gray_stained_glass_pane => BlockBehavior::new().strength(0.3, 0.3), { - east: false, - north: false, - south: false, - waterlogged: false, - west: false, + east: East(false), + north: North(false), + south: South(false), + waterlogged: Waterlogged(false), + west: West(false), }, cyan_stained_glass_pane => BlockBehavior::new().strength(0.3, 0.3), { - east: false, - north: false, - south: false, - waterlogged: false, - west: false, + east: East(false), + north: North(false), + south: South(false), + waterlogged: Waterlogged(false), + west: West(false), }, purple_stained_glass_pane => BlockBehavior::new().strength(0.3, 0.3), { - east: false, - north: false, - south: false, - waterlogged: false, - west: false, + east: East(false), + north: North(false), + south: South(false), + waterlogged: Waterlogged(false), + west: West(false), }, blue_stained_glass_pane => BlockBehavior::new().strength(0.3, 0.3), { - east: false, - north: false, - south: false, - waterlogged: false, - west: false, + east: East(false), + north: North(false), + south: South(false), + waterlogged: Waterlogged(false), + west: West(false), }, brown_stained_glass_pane => BlockBehavior::new().strength(0.3, 0.3), { - east: false, - north: false, - south: false, - waterlogged: false, - west: false, + east: East(false), + north: North(false), + south: South(false), + waterlogged: Waterlogged(false), + west: West(false), }, green_stained_glass_pane => BlockBehavior::new().strength(0.3, 0.3), { - east: false, - north: false, - south: false, - waterlogged: false, - west: false, + east: East(false), + north: North(false), + south: South(false), + waterlogged: Waterlogged(false), + west: West(false), }, red_stained_glass_pane => BlockBehavior::new().strength(0.3, 0.3), { - east: false, - north: false, - south: false, - waterlogged: false, - west: false, + east: East(false), + north: North(false), + south: South(false), + waterlogged: Waterlogged(false), + west: West(false), }, black_stained_glass_pane => BlockBehavior::new().strength(0.3, 0.3), { - east: false, - north: false, - south: false, - waterlogged: false, - west: false, + east: East(false), + north: North(false), + south: South(false), + waterlogged: Waterlogged(false), + west: West(false), }, acacia_stairs => BlockBehavior::new().strength(2.0, 3.0), { facing: FacingCardinal::North, half: TopBottom::Bottom, shape: StairShape::Straight, - waterlogged: false, + waterlogged: Waterlogged(false), }, cherry_stairs => BlockBehavior::new().strength(2.0, 3.0), { facing: FacingCardinal::North, half: TopBottom::Bottom, shape: StairShape::Straight, - waterlogged: false, + waterlogged: Waterlogged(false), }, dark_oak_stairs => BlockBehavior::new().strength(2.0, 3.0), { facing: FacingCardinal::North, half: TopBottom::Bottom, shape: StairShape::Straight, - waterlogged: false, + waterlogged: Waterlogged(false), }, mangrove_stairs => BlockBehavior::new().strength(2.0, 3.0), { facing: FacingCardinal::North, half: TopBottom::Bottom, shape: StairShape::Straight, - waterlogged: false, + waterlogged: Waterlogged(false), }, bamboo_stairs => BlockBehavior::new().strength(2.0, 3.0), { facing: FacingCardinal::North, half: TopBottom::Bottom, shape: StairShape::Straight, - waterlogged: false, + waterlogged: Waterlogged(false), }, bamboo_mosaic_stairs => BlockBehavior::new().strength(2.0, 3.0), { facing: FacingCardinal::North, half: TopBottom::Bottom, shape: StairShape::Straight, - waterlogged: false, + waterlogged: Waterlogged(false), }, slime_block => BlockBehavior::new().friction(0.8), {}, barrier => BlockBehavior::new().strength(-1.0, 3600000.8), { - waterlogged: false, + waterlogged: Waterlogged(false), }, light => BlockBehavior::new().strength(-1.0, 3600000.8), { level: LightLevel::_15, - waterlogged: false, + waterlogged: Waterlogged(false), }, iron_trapdoor => BlockBehavior::new().requires_correct_tool_for_drops().strength(5.0, 5.0), { facing: FacingCardinal::North, half: TopBottom::Bottom, - open: false, - powered: false, - waterlogged: false, + open: Open(false), + powered: Powered(false), + waterlogged: Waterlogged(false), }, prismarine => BlockBehavior::new().requires_correct_tool_for_drops().strength(1.5, 6.0), {}, prismarine_bricks => BlockBehavior::new().requires_correct_tool_for_drops().strength(1.5, 6.0), {}, @@ -3376,31 +3375,31 @@ make_block_states! { facing: FacingCardinal::North, half: TopBottom::Bottom, shape: StairShape::Straight, - waterlogged: false, + waterlogged: Waterlogged(false), }, prismarine_brick_stairs => BlockBehavior::new().requires_correct_tool_for_drops().strength(1.5, 6.0), { facing: FacingCardinal::North, half: TopBottom::Bottom, shape: StairShape::Straight, - waterlogged: false, + waterlogged: Waterlogged(false), }, dark_prismarine_stairs => BlockBehavior::new().requires_correct_tool_for_drops().strength(1.5, 6.0), { facing: FacingCardinal::North, half: TopBottom::Bottom, shape: StairShape::Straight, - waterlogged: false, + waterlogged: Waterlogged(false), }, prismarine_slab => BlockBehavior::new().requires_correct_tool_for_drops().strength(1.5, 6.0), { kind: Type::Bottom, - waterlogged: false, + waterlogged: Waterlogged(false), }, prismarine_brick_slab => BlockBehavior::new().requires_correct_tool_for_drops().strength(1.5, 6.0), { kind: Type::Bottom, - waterlogged: false, + waterlogged: Waterlogged(false), }, dark_prismarine_slab => BlockBehavior::new().requires_correct_tool_for_drops().strength(1.5, 6.0), { kind: Type::Bottom, - waterlogged: false, + waterlogged: Waterlogged(false), }, sea_lantern => BlockBehavior::new().strength(0.3, 0.3), {}, hay_block => BlockBehavior::new().strength(0.5, 0.5), { @@ -3546,103 +3545,103 @@ make_block_states! { facing: FacingCardinal::North, half: TopBottom::Bottom, shape: StairShape::Straight, - waterlogged: false, + waterlogged: Waterlogged(false), }, oak_slab => BlockBehavior::new().strength(2.0, 3.0), { kind: Type::Bottom, - waterlogged: false, + waterlogged: Waterlogged(false), }, spruce_slab => BlockBehavior::new().strength(2.0, 3.0), { kind: Type::Bottom, - waterlogged: false, + waterlogged: Waterlogged(false), }, birch_slab => BlockBehavior::new().strength(2.0, 3.0), { kind: Type::Bottom, - waterlogged: false, + waterlogged: Waterlogged(false), }, jungle_slab => BlockBehavior::new().strength(2.0, 3.0), { kind: Type::Bottom, - waterlogged: false, + waterlogged: Waterlogged(false), }, acacia_slab => BlockBehavior::new().strength(2.0, 3.0), { kind: Type::Bottom, - waterlogged: false, + waterlogged: Waterlogged(false), }, cherry_slab => BlockBehavior::new().strength(2.0, 3.0), { kind: Type::Bottom, - waterlogged: false, + waterlogged: Waterlogged(false), }, dark_oak_slab => BlockBehavior::new().strength(2.0, 3.0), { kind: Type::Bottom, - waterlogged: false, + waterlogged: Waterlogged(false), }, mangrove_slab => BlockBehavior::new().strength(2.0, 3.0), { kind: Type::Bottom, - waterlogged: false, + waterlogged: Waterlogged(false), }, bamboo_slab => BlockBehavior::new().strength(2.0, 3.0), { kind: Type::Bottom, - waterlogged: false, + waterlogged: Waterlogged(false), }, bamboo_mosaic_slab => BlockBehavior::new().strength(2.0, 3.0), { kind: Type::Bottom, - waterlogged: false, + waterlogged: Waterlogged(false), }, stone_slab => BlockBehavior::new().requires_correct_tool_for_drops().strength(2.0, 6.0), { kind: Type::Bottom, - waterlogged: false, + waterlogged: Waterlogged(false), }, smooth_stone_slab => BlockBehavior::new().requires_correct_tool_for_drops().strength(2.0, 6.0), { kind: Type::Bottom, - waterlogged: false, + waterlogged: Waterlogged(false), }, sandstone_slab => BlockBehavior::new().requires_correct_tool_for_drops().strength(2.0, 6.0), { kind: Type::Bottom, - waterlogged: false, + waterlogged: Waterlogged(false), }, cut_sandstone_slab => BlockBehavior::new().requires_correct_tool_for_drops().strength(2.0, 6.0), { kind: Type::Bottom, - waterlogged: false, + waterlogged: Waterlogged(false), }, petrified_oak_slab => BlockBehavior::new().requires_correct_tool_for_drops().strength(2.0, 6.0), { kind: Type::Bottom, - waterlogged: false, + waterlogged: Waterlogged(false), }, cobblestone_slab => BlockBehavior::new().requires_correct_tool_for_drops().strength(2.0, 6.0), { kind: Type::Bottom, - waterlogged: false, + waterlogged: Waterlogged(false), }, brick_slab => BlockBehavior::new().requires_correct_tool_for_drops().strength(2.0, 6.0), { kind: Type::Bottom, - waterlogged: false, + waterlogged: Waterlogged(false), }, stone_brick_slab => BlockBehavior::new().requires_correct_tool_for_drops().strength(2.0, 6.0), { kind: Type::Bottom, - waterlogged: false, + waterlogged: Waterlogged(false), }, mud_brick_slab => BlockBehavior::new().requires_correct_tool_for_drops().strength(1.5, 3.0), { kind: Type::Bottom, - waterlogged: false, + waterlogged: Waterlogged(false), }, nether_brick_slab => BlockBehavior::new().requires_correct_tool_for_drops().strength(2.0, 6.0), { kind: Type::Bottom, - waterlogged: false, + waterlogged: Waterlogged(false), }, quartz_slab => BlockBehavior::new().requires_correct_tool_for_drops().strength(2.0, 6.0), { kind: Type::Bottom, - waterlogged: false, + waterlogged: Waterlogged(false), }, red_sandstone_slab => BlockBehavior::new().requires_correct_tool_for_drops().strength(2.0, 6.0), { kind: Type::Bottom, - waterlogged: false, + waterlogged: Waterlogged(false), }, cut_red_sandstone_slab => BlockBehavior::new().requires_correct_tool_for_drops().strength(2.0, 6.0), { kind: Type::Bottom, - waterlogged: false, + waterlogged: Waterlogged(false), }, purpur_slab => BlockBehavior::new().requires_correct_tool_for_drops().strength(2.0, 6.0), { kind: Type::Bottom, - waterlogged: false, + waterlogged: Waterlogged(false), }, smooth_stone => BlockBehavior::new().requires_correct_tool_for_drops().strength(2.0, 6.0), {}, smooth_sandstone => BlockBehavior::new().requires_correct_tool_for_drops().strength(2.0, 6.0), {}, @@ -3650,174 +3649,174 @@ make_block_states! { smooth_red_sandstone => BlockBehavior::new().requires_correct_tool_for_drops().strength(2.0, 6.0), {}, spruce_fence_gate => BlockBehavior::new().strength(2.0, 3.0), { facing: FacingCardinal::North, - in_wall: false, - open: false, - powered: false, + in_wall: InWall(false), + open: Open(false), + powered: Powered(false), }, birch_fence_gate => BlockBehavior::new().strength(2.0, 3.0), { facing: FacingCardinal::North, - in_wall: false, - open: false, - powered: false, + in_wall: InWall(false), + open: Open(false), + powered: Powered(false), }, jungle_fence_gate => BlockBehavior::new().strength(2.0, 3.0), { facing: FacingCardinal::North, - in_wall: false, - open: false, - powered: false, + in_wall: InWall(false), + open: Open(false), + powered: Powered(false), }, acacia_fence_gate => BlockBehavior::new().strength(2.0, 3.0), { facing: FacingCardinal::North, - in_wall: false, - open: false, - powered: false, + in_wall: InWall(false), + open: Open(false), + powered: Powered(false), }, cherry_fence_gate => BlockBehavior::new().strength(2.0, 3.0), { facing: FacingCardinal::North, - in_wall: false, - open: false, - powered: false, + in_wall: InWall(false), + open: Open(false), + powered: Powered(false), }, dark_oak_fence_gate => BlockBehavior::new().strength(2.0, 3.0), { facing: FacingCardinal::North, - in_wall: false, - open: false, - powered: false, + in_wall: InWall(false), + open: Open(false), + powered: Powered(false), }, mangrove_fence_gate => BlockBehavior::new().strength(2.0, 3.0), { facing: FacingCardinal::North, - in_wall: false, - open: false, - powered: false, + in_wall: InWall(false), + open: Open(false), + powered: Powered(false), }, bamboo_fence_gate => BlockBehavior::new().strength(2.0, 3.0), { facing: FacingCardinal::North, - in_wall: false, - open: false, - powered: false, + in_wall: InWall(false), + open: Open(false), + powered: Powered(false), }, spruce_fence => BlockBehavior::new().strength(2.0, 3.0), { - east: false, - north: false, - south: false, - waterlogged: false, - west: false, + east: East(false), + north: North(false), + south: South(false), + waterlogged: Waterlogged(false), + west: West(false), }, birch_fence => BlockBehavior::new().strength(2.0, 3.0), { - east: false, - north: false, - south: false, - waterlogged: false, - west: false, + east: East(false), + north: North(false), + south: South(false), + waterlogged: Waterlogged(false), + west: West(false), }, jungle_fence => BlockBehavior::new().strength(2.0, 3.0), { - east: false, - north: false, - south: false, - waterlogged: false, - west: false, + east: East(false), + north: North(false), + south: South(false), + waterlogged: Waterlogged(false), + west: West(false), }, acacia_fence => BlockBehavior::new().strength(2.0, 3.0), { - east: false, - north: false, - south: false, - waterlogged: false, - west: false, + east: East(false), + north: North(false), + south: South(false), + waterlogged: Waterlogged(false), + west: West(false), }, cherry_fence => BlockBehavior::new().strength(2.0, 3.0), { - east: false, - north: false, - south: false, - waterlogged: false, - west: false, + east: East(false), + north: North(false), + south: South(false), + waterlogged: Waterlogged(false), + west: West(false), }, dark_oak_fence => BlockBehavior::new().strength(2.0, 3.0), { - east: false, - north: false, - south: false, - waterlogged: false, - west: false, + east: East(false), + north: North(false), + south: South(false), + waterlogged: Waterlogged(false), + west: West(false), }, mangrove_fence => BlockBehavior::new().strength(2.0, 3.0), { - east: false, - north: false, - south: false, - waterlogged: false, - west: false, + east: East(false), + north: North(false), + south: South(false), + waterlogged: Waterlogged(false), + west: West(false), }, bamboo_fence => BlockBehavior::new().strength(2.0, 3.0), { - east: false, - north: false, - south: false, - waterlogged: false, - west: false, + east: East(false), + north: North(false), + south: South(false), + waterlogged: Waterlogged(false), + west: West(false), }, spruce_door => BlockBehavior::new().strength(3.0, 3.0), { facing: FacingCardinal::North, half: Half::Lower, hinge: Hinge::Left, - open: false, - powered: false, + open: Open(false), + powered: Powered(false), }, birch_door => BlockBehavior::new().strength(3.0, 3.0), { facing: FacingCardinal::North, half: Half::Lower, hinge: Hinge::Left, - open: false, - powered: false, + open: Open(false), + powered: Powered(false), }, jungle_door => BlockBehavior::new().strength(3.0, 3.0), { facing: FacingCardinal::North, half: Half::Lower, hinge: Hinge::Left, - open: false, - powered: false, + open: Open(false), + powered: Powered(false), }, acacia_door => BlockBehavior::new().strength(3.0, 3.0), { facing: FacingCardinal::North, half: Half::Lower, hinge: Hinge::Left, - open: false, - powered: false, + open: Open(false), + powered: Powered(false), }, cherry_door => BlockBehavior::new().strength(3.0, 3.0), { facing: FacingCardinal::North, half: Half::Lower, hinge: Hinge::Left, - open: false, - powered: false, + open: Open(false), + powered: Powered(false), }, dark_oak_door => BlockBehavior::new().strength(3.0, 3.0), { facing: FacingCardinal::North, half: Half::Lower, hinge: Hinge::Left, - open: false, - powered: false, + open: Open(false), + powered: Powered(false), }, mangrove_door => BlockBehavior::new().strength(3.0, 3.0), { facing: FacingCardinal::North, half: Half::Lower, hinge: Hinge::Left, - open: false, - powered: false, + open: Open(false), + powered: Powered(false), }, bamboo_door => BlockBehavior::new().strength(3.0, 3.0), { facing: FacingCardinal::North, half: Half::Lower, hinge: Hinge::Left, - open: false, - powered: false, + open: Open(false), + powered: Powered(false), }, end_rod => BlockBehavior::new(), { facing: FacingCubic::Up, }, chorus_plant => BlockBehavior::new().strength(0.4, 0.4), { - down: false, - east: false, - north: false, - south: false, - up: false, - west: false, + down: Down(false), + east: East(false), + north: North(false), + south: South(false), + up: Up(false), + west: West(false), }, chorus_flower => BlockBehavior::new().strength(0.4, 0.4), { age: ChorusFlowerAge::_0, @@ -3830,7 +3829,7 @@ make_block_states! { facing: FacingCardinal::North, half: TopBottom::Bottom, shape: StairShape::Straight, - waterlogged: false, + waterlogged: Waterlogged(false), }, end_stone_bricks => BlockBehavior::new().requires_correct_tool_for_drops().strength(3.0, 9.0), {}, torchflower_crop => BlockBehavior::new(), { @@ -3849,11 +3848,11 @@ make_block_states! { dirt_path => BlockBehavior::new().strength(0.65, 0.65), {}, end_gateway => BlockBehavior::new().strength(-1.0, 3600000.0), {}, repeating_command_block => BlockBehavior::new().requires_correct_tool_for_drops().strength(-1.0, 3600000.0), { - conditional: false, + conditional: Conditional(false), facing: FacingCubic::North, }, chain_command_block => BlockBehavior::new().requires_correct_tool_for_drops().strength(-1.0, 3600000.0), { - conditional: false, + conditional: Conditional(false), facing: FacingCubic::North, }, frosted_ice => BlockBehavior::new().strength(0.5, 0.5).friction(0.98), { @@ -3868,7 +3867,7 @@ make_block_states! { structure_void => BlockBehavior::new(), {}, observer => BlockBehavior::new().requires_correct_tool_for_drops().strength(3.0, 3.0), { facing: FacingCubic::South, - powered: false, + powered: Powered(false), }, shulker_box => BlockBehavior::new().strength(2.0, 2.0), { facing: FacingCubic::Up, @@ -4024,112 +4023,112 @@ make_block_states! { fire_coral_block => BlockBehavior::new().requires_correct_tool_for_drops().strength(1.5, 6.0), {}, horn_coral_block => BlockBehavior::new().requires_correct_tool_for_drops().strength(1.5, 6.0), {}, dead_tube_coral => BlockBehavior::new().requires_correct_tool_for_drops(), { - waterlogged: true, + waterlogged: Waterlogged(true), }, dead_brain_coral => BlockBehavior::new().requires_correct_tool_for_drops(), { - waterlogged: true, + waterlogged: Waterlogged(true), }, dead_bubble_coral => BlockBehavior::new().requires_correct_tool_for_drops(), { - waterlogged: true, + waterlogged: Waterlogged(true), }, dead_fire_coral => BlockBehavior::new().requires_correct_tool_for_drops(), { - waterlogged: true, + waterlogged: Waterlogged(true), }, dead_horn_coral => BlockBehavior::new().requires_correct_tool_for_drops(), { - waterlogged: true, + waterlogged: Waterlogged(true), }, tube_coral => BlockBehavior::new(), { - waterlogged: true, + waterlogged: Waterlogged(true), }, brain_coral => BlockBehavior::new(), { - waterlogged: true, + waterlogged: Waterlogged(true), }, bubble_coral => BlockBehavior::new(), { - waterlogged: true, + waterlogged: Waterlogged(true), }, fire_coral => BlockBehavior::new(), { - waterlogged: true, + waterlogged: Waterlogged(true), }, horn_coral => BlockBehavior::new(), { - waterlogged: true, + waterlogged: Waterlogged(true), }, dead_tube_coral_fan => BlockBehavior::new().requires_correct_tool_for_drops(), { - waterlogged: true, + waterlogged: Waterlogged(true), }, dead_brain_coral_fan => BlockBehavior::new().requires_correct_tool_for_drops(), { - waterlogged: true, + waterlogged: Waterlogged(true), }, dead_bubble_coral_fan => BlockBehavior::new().requires_correct_tool_for_drops(), { - waterlogged: true, + waterlogged: Waterlogged(true), }, dead_fire_coral_fan => BlockBehavior::new().requires_correct_tool_for_drops(), { - waterlogged: true, + waterlogged: Waterlogged(true), }, dead_horn_coral_fan => BlockBehavior::new().requires_correct_tool_for_drops(), { - waterlogged: true, + waterlogged: Waterlogged(true), }, tube_coral_fan => BlockBehavior::new(), { - waterlogged: true, + waterlogged: Waterlogged(true), }, brain_coral_fan => BlockBehavior::new(), { - waterlogged: true, + waterlogged: Waterlogged(true), }, bubble_coral_fan => BlockBehavior::new(), { - waterlogged: true, + waterlogged: Waterlogged(true), }, fire_coral_fan => BlockBehavior::new(), { - waterlogged: true, + waterlogged: Waterlogged(true), }, horn_coral_fan => BlockBehavior::new(), { - waterlogged: true, + waterlogged: Waterlogged(true), }, dead_tube_coral_wall_fan => BlockBehavior::new().requires_correct_tool_for_drops(), { facing: FacingCardinal::North, - waterlogged: true, + waterlogged: Waterlogged(true), }, dead_brain_coral_wall_fan => BlockBehavior::new().requires_correct_tool_for_drops(), { facing: FacingCardinal::North, - waterlogged: true, + waterlogged: Waterlogged(true), }, dead_bubble_coral_wall_fan => BlockBehavior::new().requires_correct_tool_for_drops(), { facing: FacingCardinal::North, - waterlogged: true, + waterlogged: Waterlogged(true), }, dead_fire_coral_wall_fan => BlockBehavior::new().requires_correct_tool_for_drops(), { facing: FacingCardinal::North, - waterlogged: true, + waterlogged: Waterlogged(true), }, dead_horn_coral_wall_fan => BlockBehavior::new().requires_correct_tool_for_drops(), { facing: FacingCardinal::North, - waterlogged: true, + waterlogged: Waterlogged(true), }, tube_coral_wall_fan => BlockBehavior::new(), { facing: FacingCardinal::North, - waterlogged: true, + waterlogged: Waterlogged(true), }, brain_coral_wall_fan => BlockBehavior::new(), { facing: FacingCardinal::North, - waterlogged: true, + waterlogged: Waterlogged(true), }, bubble_coral_wall_fan => BlockBehavior::new(), { facing: FacingCardinal::North, - waterlogged: true, + waterlogged: Waterlogged(true), }, fire_coral_wall_fan => BlockBehavior::new(), { facing: FacingCardinal::North, - waterlogged: true, + waterlogged: Waterlogged(true), }, horn_coral_wall_fan => BlockBehavior::new(), { facing: FacingCardinal::North, - waterlogged: true, + waterlogged: Waterlogged(true), }, sea_pickle => BlockBehavior::new(), { pickles: SeaPicklePickles::_1, - waterlogged: true, + waterlogged: Waterlogged(true), }, blue_ice => BlockBehavior::new().strength(2.8, 2.8).friction(0.989), {}, conduit => BlockBehavior::new().strength(3.0, 3.0), { - waterlogged: true, + waterlogged: Waterlogged(true), }, bamboo_sapling => BlockBehavior::new().strength(1.0, 1.0), {}, bamboo => BlockBehavior::new().strength(1.0, 1.0), { @@ -4141,267 +4140,267 @@ make_block_states! { void_air => BlockBehavior::new(), {}, cave_air => BlockBehavior::new(), {}, bubble_column => BlockBehavior::new(), { - drag: true, + drag: Drag(true), }, polished_granite_stairs => BlockBehavior::new().requires_correct_tool_for_drops().strength(1.5, 6.0), { facing: FacingCardinal::North, half: TopBottom::Bottom, shape: StairShape::Straight, - waterlogged: false, + waterlogged: Waterlogged(false), }, smooth_red_sandstone_stairs => BlockBehavior::new().requires_correct_tool_for_drops().strength(2.0, 6.0), { facing: FacingCardinal::North, half: TopBottom::Bottom, shape: StairShape::Straight, - waterlogged: false, + waterlogged: Waterlogged(false), }, mossy_stone_brick_stairs => BlockBehavior::new().requires_correct_tool_for_drops().strength(1.5, 6.0), { facing: FacingCardinal::North, half: TopBottom::Bottom, shape: StairShape::Straight, - waterlogged: false, + waterlogged: Waterlogged(false), }, polished_diorite_stairs => BlockBehavior::new().requires_correct_tool_for_drops().strength(1.5, 6.0), { facing: FacingCardinal::North, half: TopBottom::Bottom, shape: StairShape::Straight, - waterlogged: false, + waterlogged: Waterlogged(false), }, mossy_cobblestone_stairs => BlockBehavior::new().requires_correct_tool_for_drops().strength(2.0, 6.0), { facing: FacingCardinal::North, half: TopBottom::Bottom, shape: StairShape::Straight, - waterlogged: false, + waterlogged: Waterlogged(false), }, end_stone_brick_stairs => BlockBehavior::new().requires_correct_tool_for_drops().strength(3.0, 9.0), { facing: FacingCardinal::North, half: TopBottom::Bottom, shape: StairShape::Straight, - waterlogged: false, + waterlogged: Waterlogged(false), }, stone_stairs => BlockBehavior::new().requires_correct_tool_for_drops().strength(1.5, 6.0), { facing: FacingCardinal::North, half: TopBottom::Bottom, shape: StairShape::Straight, - waterlogged: false, + waterlogged: Waterlogged(false), }, smooth_sandstone_stairs => BlockBehavior::new().requires_correct_tool_for_drops().strength(2.0, 6.0), { facing: FacingCardinal::North, half: TopBottom::Bottom, shape: StairShape::Straight, - waterlogged: false, + waterlogged: Waterlogged(false), }, smooth_quartz_stairs => BlockBehavior::new().requires_correct_tool_for_drops().strength(2.0, 6.0), { facing: FacingCardinal::North, half: TopBottom::Bottom, shape: StairShape::Straight, - waterlogged: false, + waterlogged: Waterlogged(false), }, granite_stairs => BlockBehavior::new().requires_correct_tool_for_drops().strength(1.5, 6.0), { facing: FacingCardinal::North, half: TopBottom::Bottom, shape: StairShape::Straight, - waterlogged: false, + waterlogged: Waterlogged(false), }, andesite_stairs => BlockBehavior::new().requires_correct_tool_for_drops().strength(1.5, 6.0), { facing: FacingCardinal::North, half: TopBottom::Bottom, shape: StairShape::Straight, - waterlogged: false, + waterlogged: Waterlogged(false), }, red_nether_brick_stairs => BlockBehavior::new().requires_correct_tool_for_drops().strength(2.0, 6.0), { facing: FacingCardinal::North, half: TopBottom::Bottom, shape: StairShape::Straight, - waterlogged: false, + waterlogged: Waterlogged(false), }, polished_andesite_stairs => BlockBehavior::new().requires_correct_tool_for_drops().strength(1.5, 6.0), { facing: FacingCardinal::North, half: TopBottom::Bottom, shape: StairShape::Straight, - waterlogged: false, + waterlogged: Waterlogged(false), }, diorite_stairs => BlockBehavior::new().requires_correct_tool_for_drops().strength(1.5, 6.0), { facing: FacingCardinal::North, half: TopBottom::Bottom, shape: StairShape::Straight, - waterlogged: false, + waterlogged: Waterlogged(false), }, polished_granite_slab => BlockBehavior::new().requires_correct_tool_for_drops().strength(1.5, 6.0), { kind: Type::Bottom, - waterlogged: false, + waterlogged: Waterlogged(false), }, smooth_red_sandstone_slab => BlockBehavior::new().requires_correct_tool_for_drops().strength(2.0, 6.0), { kind: Type::Bottom, - waterlogged: false, + waterlogged: Waterlogged(false), }, mossy_stone_brick_slab => BlockBehavior::new().requires_correct_tool_for_drops().strength(1.5, 6.0), { kind: Type::Bottom, - waterlogged: false, + waterlogged: Waterlogged(false), }, polished_diorite_slab => BlockBehavior::new().requires_correct_tool_for_drops().strength(1.5, 6.0), { kind: Type::Bottom, - waterlogged: false, + waterlogged: Waterlogged(false), }, mossy_cobblestone_slab => BlockBehavior::new().requires_correct_tool_for_drops().strength(2.0, 6.0), { kind: Type::Bottom, - waterlogged: false, + waterlogged: Waterlogged(false), }, end_stone_brick_slab => BlockBehavior::new().requires_correct_tool_for_drops().strength(3.0, 9.0), { kind: Type::Bottom, - waterlogged: false, + waterlogged: Waterlogged(false), }, smooth_sandstone_slab => BlockBehavior::new().requires_correct_tool_for_drops().strength(2.0, 6.0), { kind: Type::Bottom, - waterlogged: false, + waterlogged: Waterlogged(false), }, smooth_quartz_slab => BlockBehavior::new().requires_correct_tool_for_drops().strength(2.0, 6.0), { kind: Type::Bottom, - waterlogged: false, + waterlogged: Waterlogged(false), }, granite_slab => BlockBehavior::new().requires_correct_tool_for_drops().strength(1.5, 6.0), { kind: Type::Bottom, - waterlogged: false, + waterlogged: Waterlogged(false), }, andesite_slab => BlockBehavior::new().requires_correct_tool_for_drops().strength(1.5, 6.0), { kind: Type::Bottom, - waterlogged: false, + waterlogged: Waterlogged(false), }, red_nether_brick_slab => BlockBehavior::new().requires_correct_tool_for_drops().strength(2.0, 6.0), { kind: Type::Bottom, - waterlogged: false, + waterlogged: Waterlogged(false), }, polished_andesite_slab => BlockBehavior::new().requires_correct_tool_for_drops().strength(1.5, 6.0), { kind: Type::Bottom, - waterlogged: false, + waterlogged: Waterlogged(false), }, diorite_slab => BlockBehavior::new().requires_correct_tool_for_drops().strength(1.5, 6.0), { kind: Type::Bottom, - waterlogged: false, + waterlogged: Waterlogged(false), }, brick_wall => BlockBehavior::new().requires_correct_tool_for_drops().strength(2.0, 6.0), { east: EastWall::None, north: NorthWall::None, south: SouthWall::None, - up: true, - waterlogged: false, + up: Up(true), + waterlogged: Waterlogged(false), west: WestWall::None, }, prismarine_wall => BlockBehavior::new().requires_correct_tool_for_drops().strength(1.5, 6.0), { east: EastWall::None, north: NorthWall::None, south: SouthWall::None, - up: true, - waterlogged: false, + up: Up(true), + waterlogged: Waterlogged(false), west: WestWall::None, }, red_sandstone_wall => BlockBehavior::new().requires_correct_tool_for_drops().strength(0.8, 0.8), { east: EastWall::None, north: NorthWall::None, south: SouthWall::None, - up: true, - waterlogged: false, + up: Up(true), + waterlogged: Waterlogged(false), west: WestWall::None, }, mossy_stone_brick_wall => BlockBehavior::new().requires_correct_tool_for_drops().strength(1.5, 6.0), { east: EastWall::None, north: NorthWall::None, south: SouthWall::None, - up: true, - waterlogged: false, + up: Up(true), + waterlogged: Waterlogged(false), west: WestWall::None, }, granite_wall => BlockBehavior::new().requires_correct_tool_for_drops().strength(1.5, 6.0), { east: EastWall::None, north: NorthWall::None, south: SouthWall::None, - up: true, - waterlogged: false, + up: Up(true), + waterlogged: Waterlogged(false), west: WestWall::None, }, stone_brick_wall => BlockBehavior::new().requires_correct_tool_for_drops().strength(1.5, 6.0), { east: EastWall::None, north: NorthWall::None, south: SouthWall::None, - up: true, - waterlogged: false, + up: Up(true), + waterlogged: Waterlogged(false), west: WestWall::None, }, mud_brick_wall => BlockBehavior::new().requires_correct_tool_for_drops().strength(1.5, 3.0), { east: EastWall::None, north: NorthWall::None, south: SouthWall::None, - up: true, - waterlogged: false, + up: Up(true), + waterlogged: Waterlogged(false), west: WestWall::None, }, nether_brick_wall => BlockBehavior::new().requires_correct_tool_for_drops().strength(2.0, 6.0), { east: EastWall::None, north: NorthWall::None, south: SouthWall::None, - up: true, - waterlogged: false, + up: Up(true), + waterlogged: Waterlogged(false), west: WestWall::None, }, andesite_wall => BlockBehavior::new().requires_correct_tool_for_drops().strength(1.5, 6.0), { east: EastWall::None, north: NorthWall::None, south: SouthWall::None, - up: true, - waterlogged: false, + up: Up(true), + waterlogged: Waterlogged(false), west: WestWall::None, }, red_nether_brick_wall => BlockBehavior::new().requires_correct_tool_for_drops().strength(2.0, 6.0), { east: EastWall::None, north: NorthWall::None, south: SouthWall::None, - up: true, - waterlogged: false, + up: Up(true), + waterlogged: Waterlogged(false), west: WestWall::None, }, sandstone_wall => BlockBehavior::new().requires_correct_tool_for_drops().strength(0.8, 0.8), { east: EastWall::None, north: NorthWall::None, south: SouthWall::None, - up: true, - waterlogged: false, + up: Up(true), + waterlogged: Waterlogged(false), west: WestWall::None, }, end_stone_brick_wall => BlockBehavior::new().requires_correct_tool_for_drops().strength(3.0, 9.0), { east: EastWall::None, north: NorthWall::None, south: SouthWall::None, - up: true, - waterlogged: false, + up: Up(true), + waterlogged: Waterlogged(false), west: WestWall::None, }, diorite_wall => BlockBehavior::new().requires_correct_tool_for_drops().strength(1.5, 6.0), { east: EastWall::None, north: NorthWall::None, south: SouthWall::None, - up: true, - waterlogged: false, + up: Up(true), + waterlogged: Waterlogged(false), west: WestWall::None, }, scaffolding => BlockBehavior::new(), { - bottom: false, + bottom: Bottom(false), distance: ScaffoldingDistance::_7, - waterlogged: false, + waterlogged: Waterlogged(false), }, loom => BlockBehavior::new().strength(2.5, 2.5), { facing: FacingCardinal::North, }, barrel => BlockBehavior::new().strength(2.5, 2.5), { facing: FacingCubic::North, - open: false, + open: Open(false), }, smoker => BlockBehavior::new().requires_correct_tool_for_drops().strength(3.5, 3.5), { facing: FacingCardinal::North, - lit: false, + lit: Lit(false), }, blast_furnace => BlockBehavior::new().requires_correct_tool_for_drops().strength(3.5, 3.5), { facing: FacingCardinal::North, - lit: false, + lit: Lit(false), }, cartography_table => BlockBehavior::new().strength(2.5, 2.5), {}, fletching_table => BlockBehavior::new().strength(2.5, 2.5), {}, @@ -4411,8 +4410,8 @@ make_block_states! { }, lectern => BlockBehavior::new().strength(2.5, 2.5), { facing: FacingCardinal::North, - has_book: false, - powered: false, + has_book: HasBook(false), + powered: Powered(false), }, smithing_table => BlockBehavior::new().strength(2.5, 2.5), {}, stonecutter => BlockBehavior::new().requires_correct_tool_for_drops().strength(3.5, 3.5), { @@ -4421,27 +4420,27 @@ make_block_states! { bell => BlockBehavior::new().requires_correct_tool_for_drops().strength(5.0, 5.0), { attachment: Attachment::Floor, facing: FacingCardinal::North, - powered: false, + powered: Powered(false), }, lantern => BlockBehavior::new().requires_correct_tool_for_drops().strength(3.5, 3.5), { - hanging: false, - waterlogged: false, + hanging: Hanging(false), + waterlogged: Waterlogged(false), }, soul_lantern => BlockBehavior::new().requires_correct_tool_for_drops().strength(3.5, 3.5), { - hanging: false, - waterlogged: false, + hanging: Hanging(false), + waterlogged: Waterlogged(false), }, campfire => BlockBehavior::new().strength(2.0, 2.0), { facing: FacingCardinal::North, - lit: true, - signal_fire: false, - waterlogged: false, + lit: Lit(true), + signal_fire: SignalFire(false), + waterlogged: Waterlogged(false), }, soul_campfire => BlockBehavior::new().strength(2.0, 2.0), { facing: FacingCardinal::North, - lit: true, - signal_fire: false, - waterlogged: false, + lit: Lit(true), + signal_fire: SignalFire(false), + waterlogged: Waterlogged(false), }, sweet_berry_bush => BlockBehavior::new(), { age: SweetBerryBushAge::_0, @@ -4491,109 +4490,109 @@ make_block_states! { warped_planks => BlockBehavior::new().strength(2.0, 3.0), {}, crimson_slab => BlockBehavior::new().strength(2.0, 3.0), { kind: Type::Bottom, - waterlogged: false, + waterlogged: Waterlogged(false), }, warped_slab => BlockBehavior::new().strength(2.0, 3.0), { kind: Type::Bottom, - waterlogged: false, + waterlogged: Waterlogged(false), }, crimson_pressure_plate => BlockBehavior::new().strength(0.5, 0.5), { - powered: false, + powered: Powered(false), }, warped_pressure_plate => BlockBehavior::new().strength(0.5, 0.5), { - powered: false, + powered: Powered(false), }, crimson_fence => BlockBehavior::new().strength(2.0, 3.0), { - east: false, - north: false, - south: false, - waterlogged: false, - west: false, + east: East(false), + north: North(false), + south: South(false), + waterlogged: Waterlogged(false), + west: West(false), }, warped_fence => BlockBehavior::new().strength(2.0, 3.0), { - east: false, - north: false, - south: false, - waterlogged: false, - west: false, + east: East(false), + north: North(false), + south: South(false), + waterlogged: Waterlogged(false), + west: West(false), }, crimson_trapdoor => BlockBehavior::new().strength(3.0, 3.0), { facing: FacingCardinal::North, half: TopBottom::Bottom, - open: false, - powered: false, - waterlogged: false, + open: Open(false), + powered: Powered(false), + waterlogged: Waterlogged(false), }, warped_trapdoor => BlockBehavior::new().strength(3.0, 3.0), { facing: FacingCardinal::North, half: TopBottom::Bottom, - open: false, - powered: false, - waterlogged: false, + open: Open(false), + powered: Powered(false), + waterlogged: Waterlogged(false), }, crimson_fence_gate => BlockBehavior::new().strength(2.0, 3.0), { facing: FacingCardinal::North, - in_wall: false, - open: false, - powered: false, + in_wall: InWall(false), + open: Open(false), + powered: Powered(false), }, warped_fence_gate => BlockBehavior::new().strength(2.0, 3.0), { facing: FacingCardinal::North, - in_wall: false, - open: false, - powered: false, + in_wall: InWall(false), + open: Open(false), + powered: Powered(false), }, crimson_stairs => BlockBehavior::new().strength(2.0, 3.0), { facing: FacingCardinal::North, half: TopBottom::Bottom, shape: StairShape::Straight, - waterlogged: false, + waterlogged: Waterlogged(false), }, warped_stairs => BlockBehavior::new().strength(2.0, 3.0), { facing: FacingCardinal::North, half: TopBottom::Bottom, shape: StairShape::Straight, - waterlogged: false, + waterlogged: Waterlogged(false), }, crimson_button => BlockBehavior::new().strength(0.5, 0.5), { face: Face::Wall, facing: FacingCardinal::North, - powered: false, + powered: Powered(false), }, warped_button => BlockBehavior::new().strength(0.5, 0.5), { face: Face::Wall, facing: FacingCardinal::North, - powered: false, + powered: Powered(false), }, crimson_door => BlockBehavior::new().strength(3.0, 3.0), { facing: FacingCardinal::North, half: Half::Lower, hinge: Hinge::Left, - open: false, - powered: false, + open: Open(false), + powered: Powered(false), }, warped_door => BlockBehavior::new().strength(3.0, 3.0), { facing: FacingCardinal::North, half: Half::Lower, hinge: Hinge::Left, - open: false, - powered: false, + open: Open(false), + powered: Powered(false), }, crimson_sign => BlockBehavior::new().strength(1.0, 1.0), { rotation: CrimsonSignRotation::_0, - waterlogged: false, + waterlogged: Waterlogged(false), }, warped_sign => BlockBehavior::new().strength(1.0, 1.0), { rotation: WarpedSignRotation::_0, - waterlogged: false, + waterlogged: Waterlogged(false), }, crimson_wall_sign => BlockBehavior::new().strength(1.0, 1.0), { facing: FacingCardinal::North, - waterlogged: false, + waterlogged: Waterlogged(false), }, warped_wall_sign => BlockBehavior::new().strength(1.0, 1.0), { facing: FacingCardinal::North, - waterlogged: false, + waterlogged: Waterlogged(false), }, structure_block => BlockBehavior::new().requires_correct_tool_for_drops().strength(-1.0, 3600000.0), { mode: Mode::Load, @@ -4633,19 +4632,19 @@ make_block_states! { facing: FacingCardinal::North, half: TopBottom::Bottom, shape: StairShape::Straight, - waterlogged: false, + waterlogged: Waterlogged(false), }, blackstone_wall => BlockBehavior::new().requires_correct_tool_for_drops().strength(1.5, 6.0), { east: EastWall::None, north: NorthWall::None, south: SouthWall::None, - up: true, - waterlogged: false, + up: Up(true), + waterlogged: Waterlogged(false), west: WestWall::None, }, blackstone_slab => BlockBehavior::new().requires_correct_tool_for_drops().strength(2.0, 6.0), { kind: Type::Bottom, - waterlogged: false, + waterlogged: Waterlogged(false), }, polished_blackstone => BlockBehavior::new().requires_correct_tool_for_drops().strength(2.0, 6.0), {}, polished_blackstone_bricks => BlockBehavior::new().requires_correct_tool_for_drops().strength(1.5, 6.0), {}, @@ -4653,20 +4652,20 @@ make_block_states! { chiseled_polished_blackstone => BlockBehavior::new().requires_correct_tool_for_drops().strength(1.5, 6.0), {}, polished_blackstone_brick_slab => BlockBehavior::new().requires_correct_tool_for_drops().strength(2.0, 6.0), { kind: Type::Bottom, - waterlogged: false, + waterlogged: Waterlogged(false), }, polished_blackstone_brick_stairs => BlockBehavior::new().requires_correct_tool_for_drops().strength(1.5, 6.0), { facing: FacingCardinal::North, half: TopBottom::Bottom, shape: StairShape::Straight, - waterlogged: false, + waterlogged: Waterlogged(false), }, polished_blackstone_brick_wall => BlockBehavior::new().requires_correct_tool_for_drops().strength(1.5, 6.0), { east: EastWall::None, north: NorthWall::None, south: SouthWall::None, - up: true, - waterlogged: false, + up: Up(true), + waterlogged: Waterlogged(false), west: WestWall::None, }, gilded_blackstone => BlockBehavior::new().requires_correct_tool_for_drops().strength(1.5, 6.0), {}, @@ -4674,26 +4673,26 @@ make_block_states! { facing: FacingCardinal::North, half: TopBottom::Bottom, shape: StairShape::Straight, - waterlogged: false, + waterlogged: Waterlogged(false), }, polished_blackstone_slab => BlockBehavior::new().requires_correct_tool_for_drops().strength(2.0, 6.0), { kind: Type::Bottom, - waterlogged: false, + waterlogged: Waterlogged(false), }, polished_blackstone_pressure_plate => BlockBehavior::new().requires_correct_tool_for_drops().strength(0.5, 0.5), { - powered: false, + powered: Powered(false), }, polished_blackstone_button => BlockBehavior::new().strength(0.5, 0.5), { face: Face::Wall, facing: FacingCardinal::North, - powered: false, + powered: Powered(false), }, polished_blackstone_wall => BlockBehavior::new().requires_correct_tool_for_drops().strength(2.0, 6.0), { east: EastWall::None, north: NorthWall::None, south: SouthWall::None, - up: true, - waterlogged: false, + up: Up(true), + waterlogged: Waterlogged(false), west: WestWall::None, }, chiseled_nether_bricks => BlockBehavior::new().requires_correct_tool_for_drops().strength(2.0, 6.0), {}, @@ -4701,240 +4700,306 @@ make_block_states! { quartz_bricks => BlockBehavior::new().requires_correct_tool_for_drops().strength(0.8, 0.8), {}, candle => BlockBehavior::new().strength(0.1, 0.1), { candles: CandleCandles::_1, - lit: false, - waterlogged: false, + lit: Lit(false), + waterlogged: Waterlogged(false), }, white_candle => BlockBehavior::new().strength(0.1, 0.1), { candles: WhiteCandleCandles::_1, - lit: false, - waterlogged: false, + lit: Lit(false), + waterlogged: Waterlogged(false), }, orange_candle => BlockBehavior::new().strength(0.1, 0.1), { candles: OrangeCandleCandles::_1, - lit: false, - waterlogged: false, + lit: Lit(false), + waterlogged: Waterlogged(false), }, magenta_candle => BlockBehavior::new().strength(0.1, 0.1), { candles: MagentaCandleCandles::_1, - lit: false, - waterlogged: false, + lit: Lit(false), + waterlogged: Waterlogged(false), }, light_blue_candle => BlockBehavior::new().strength(0.1, 0.1), { candles: LightBlueCandleCandles::_1, - lit: false, - waterlogged: false, + lit: Lit(false), + waterlogged: Waterlogged(false), }, yellow_candle => BlockBehavior::new().strength(0.1, 0.1), { candles: YellowCandleCandles::_1, - lit: false, - waterlogged: false, + lit: Lit(false), + waterlogged: Waterlogged(false), }, lime_candle => BlockBehavior::new().strength(0.1, 0.1), { candles: LimeCandleCandles::_1, - lit: false, - waterlogged: false, + lit: Lit(false), + waterlogged: Waterlogged(false), }, pink_candle => BlockBehavior::new().strength(0.1, 0.1), { candles: PinkCandleCandles::_1, - lit: false, - waterlogged: false, + lit: Lit(false), + waterlogged: Waterlogged(false), }, gray_candle => BlockBehavior::new().strength(0.1, 0.1), { candles: GrayCandleCandles::_1, - lit: false, - waterlogged: false, + lit: Lit(false), + waterlogged: Waterlogged(false), }, light_gray_candle => BlockBehavior::new().strength(0.1, 0.1), { candles: LightGrayCandleCandles::_1, - lit: false, - waterlogged: false, + lit: Lit(false), + waterlogged: Waterlogged(false), }, cyan_candle => BlockBehavior::new().strength(0.1, 0.1), { candles: CyanCandleCandles::_1, - lit: false, - waterlogged: false, + lit: Lit(false), + waterlogged: Waterlogged(false), }, purple_candle => BlockBehavior::new().strength(0.1, 0.1), { candles: PurpleCandleCandles::_1, - lit: false, - waterlogged: false, + lit: Lit(false), + waterlogged: Waterlogged(false), }, blue_candle => BlockBehavior::new().strength(0.1, 0.1), { candles: BlueCandleCandles::_1, - lit: false, - waterlogged: false, + lit: Lit(false), + waterlogged: Waterlogged(false), }, brown_candle => BlockBehavior::new().strength(0.1, 0.1), { candles: BrownCandleCandles::_1, - lit: false, - waterlogged: false, + lit: Lit(false), + waterlogged: Waterlogged(false), }, green_candle => BlockBehavior::new().strength(0.1, 0.1), { candles: GreenCandleCandles::_1, - lit: false, - waterlogged: false, + lit: Lit(false), + waterlogged: Waterlogged(false), }, red_candle => BlockBehavior::new().strength(0.1, 0.1), { candles: RedCandleCandles::_1, - lit: false, - waterlogged: false, + lit: Lit(false), + waterlogged: Waterlogged(false), }, black_candle => BlockBehavior::new().strength(0.1, 0.1), { candles: BlackCandleCandles::_1, - lit: false, - waterlogged: false, + lit: Lit(false), + waterlogged: Waterlogged(false), }, candle_cake => BlockBehavior::new().strength(0.5, 0.5), { - lit: false, + lit: Lit(false), }, white_candle_cake => BlockBehavior::new().strength(0.5, 0.5), { - lit: false, + lit: Lit(false), }, orange_candle_cake => BlockBehavior::new().strength(0.5, 0.5), { - lit: false, + lit: Lit(false), }, magenta_candle_cake => BlockBehavior::new().strength(0.5, 0.5), { - lit: false, + lit: Lit(false), }, light_blue_candle_cake => BlockBehavior::new().strength(0.5, 0.5), { - lit: false, + lit: Lit(false), }, yellow_candle_cake => BlockBehavior::new().strength(0.5, 0.5), { - lit: false, + lit: Lit(false), }, lime_candle_cake => BlockBehavior::new().strength(0.5, 0.5), { - lit: false, + lit: Lit(false), }, pink_candle_cake => BlockBehavior::new().strength(0.5, 0.5), { - lit: false, + lit: Lit(false), }, gray_candle_cake => BlockBehavior::new().strength(0.5, 0.5), { - lit: false, + lit: Lit(false), }, light_gray_candle_cake => BlockBehavior::new().strength(0.5, 0.5), { - lit: false, + lit: Lit(false), }, cyan_candle_cake => BlockBehavior::new().strength(0.5, 0.5), { - lit: false, + lit: Lit(false), }, purple_candle_cake => BlockBehavior::new().strength(0.5, 0.5), { - lit: false, + lit: Lit(false), }, blue_candle_cake => BlockBehavior::new().strength(0.5, 0.5), { - lit: false, + lit: Lit(false), }, brown_candle_cake => BlockBehavior::new().strength(0.5, 0.5), { - lit: false, + lit: Lit(false), }, green_candle_cake => BlockBehavior::new().strength(0.5, 0.5), { - lit: false, + lit: Lit(false), }, red_candle_cake => BlockBehavior::new().strength(0.5, 0.5), { - lit: false, + lit: Lit(false), }, black_candle_cake => BlockBehavior::new().strength(0.5, 0.5), { - lit: false, + lit: Lit(false), }, amethyst_block => BlockBehavior::new().requires_correct_tool_for_drops().strength(1.5, 1.5), {}, budding_amethyst => BlockBehavior::new().requires_correct_tool_for_drops().strength(1.5, 1.5), {}, amethyst_cluster => BlockBehavior::new().strength(1.5, 1.5), { facing: FacingCubic::Up, - waterlogged: false, + waterlogged: Waterlogged(false), }, large_amethyst_bud => BlockBehavior::new().strength(1.5, 1.5), { facing: FacingCubic::Up, - waterlogged: false, + waterlogged: Waterlogged(false), }, medium_amethyst_bud => BlockBehavior::new().strength(1.5, 1.5), { facing: FacingCubic::Up, - waterlogged: false, + waterlogged: Waterlogged(false), }, small_amethyst_bud => BlockBehavior::new().strength(1.5, 1.5), { facing: FacingCubic::Up, - waterlogged: false, + waterlogged: Waterlogged(false), }, tuff => BlockBehavior::new().requires_correct_tool_for_drops().strength(1.5, 6.0), {}, + tuff_slab => BlockBehavior::new().requires_correct_tool_for_drops().strength(1.5, 6.0), { + kind: Type::Bottom, + waterlogged: Waterlogged(false), + }, + tuff_stairs => BlockBehavior::new().requires_correct_tool_for_drops().strength(1.5, 6.0), { + facing: FacingCardinal::North, + half: TopBottom::Bottom, + shape: StairShape::Straight, + waterlogged: Waterlogged(false), + }, + tuff_wall => BlockBehavior::new().requires_correct_tool_for_drops().strength(1.5, 6.0), { + east: EastWall::None, + north: NorthWall::None, + south: SouthWall::None, + up: Up(true), + waterlogged: Waterlogged(false), + west: WestWall::None, + }, + polished_tuff => BlockBehavior::new().requires_correct_tool_for_drops().strength(1.5, 6.0), {}, + polished_tuff_slab => BlockBehavior::new().requires_correct_tool_for_drops().strength(1.5, 6.0), { + kind: Type::Bottom, + waterlogged: Waterlogged(false), + }, + polished_tuff_stairs => BlockBehavior::new().requires_correct_tool_for_drops().strength(1.5, 6.0), { + facing: FacingCardinal::North, + half: TopBottom::Bottom, + shape: StairShape::Straight, + waterlogged: Waterlogged(false), + }, + polished_tuff_wall => BlockBehavior::new().requires_correct_tool_for_drops().strength(1.5, 6.0), { + east: EastWall::None, + north: NorthWall::None, + south: SouthWall::None, + up: Up(true), + waterlogged: Waterlogged(false), + west: WestWall::None, + }, + chiseled_tuff => BlockBehavior::new().requires_correct_tool_for_drops().strength(1.5, 6.0), {}, + tuff_bricks => BlockBehavior::new().requires_correct_tool_for_drops().strength(1.5, 6.0), {}, + tuff_brick_slab => BlockBehavior::new().requires_correct_tool_for_drops().strength(1.5, 6.0), { + kind: Type::Bottom, + waterlogged: Waterlogged(false), + }, + tuff_brick_stairs => BlockBehavior::new().requires_correct_tool_for_drops().strength(1.5, 6.0), { + facing: FacingCardinal::North, + half: TopBottom::Bottom, + shape: StairShape::Straight, + waterlogged: Waterlogged(false), + }, + tuff_brick_wall => BlockBehavior::new().requires_correct_tool_for_drops().strength(1.5, 6.0), { + east: EastWall::None, + north: NorthWall::None, + south: SouthWall::None, + up: Up(true), + waterlogged: Waterlogged(false), + west: WestWall::None, + }, + chiseled_tuff_bricks => BlockBehavior::new().requires_correct_tool_for_drops().strength(1.5, 6.0), {}, calcite => BlockBehavior::new().requires_correct_tool_for_drops().strength(0.75, 0.75), {}, tinted_glass => BlockBehavior::new().strength(0.3, 0.3), {}, powder_snow => BlockBehavior::new().strength(0.25, 0.25), {}, sculk_sensor => BlockBehavior::new().strength(1.5, 1.5), { power: SculkSensorPower::_0, sculk_sensor_phase: Phase::Inactive, - waterlogged: false, + waterlogged: Waterlogged(false), }, calibrated_sculk_sensor => BlockBehavior::new().strength(1.5, 1.5), { facing: FacingCardinal::North, power: CalibratedSculkSensorPower::_0, sculk_sensor_phase: Phase::Inactive, - waterlogged: false, + waterlogged: Waterlogged(false), }, sculk => BlockBehavior::new().strength(0.2, 0.2), {}, sculk_vein => BlockBehavior::new().strength(0.2, 0.2), { - down: false, - east: false, - north: false, - south: false, - up: false, - waterlogged: false, - west: false, + down: Down(false), + east: East(false), + north: North(false), + south: South(false), + up: Up(false), + waterlogged: Waterlogged(false), + west: West(false), }, sculk_catalyst => BlockBehavior::new().strength(3.0, 3.0), { - bloom: false, + bloom: Bloom(false), }, sculk_shrieker => BlockBehavior::new().strength(3.0, 3.0), { - can_summon: false, - shrieking: false, - waterlogged: false, + can_summon: CanSummon(false), + shrieking: Shrieking(false), + waterlogged: Waterlogged(false), }, - oxidized_copper => BlockBehavior::new().requires_correct_tool_for_drops().strength(3.0, 6.0), {}, - weathered_copper => BlockBehavior::new().requires_correct_tool_for_drops().strength(3.0, 6.0), {}, - exposed_copper => BlockBehavior::new().requires_correct_tool_for_drops().strength(3.0, 6.0), {}, copper_block => BlockBehavior::new().requires_correct_tool_for_drops().strength(3.0, 6.0), {}, + exposed_copper => BlockBehavior::new().requires_correct_tool_for_drops().strength(3.0, 6.0), {}, + weathered_copper => BlockBehavior::new().requires_correct_tool_for_drops().strength(3.0, 6.0), {}, + oxidized_copper => BlockBehavior::new().requires_correct_tool_for_drops().strength(3.0, 6.0), {}, copper_ore => BlockBehavior::new().requires_correct_tool_for_drops().strength(3.0, 3.0), {}, deepslate_copper_ore => BlockBehavior::new().requires_correct_tool_for_drops().strength(4.5, 3.0), {}, oxidized_cut_copper => BlockBehavior::new().requires_correct_tool_for_drops().strength(3.0, 6.0), {}, weathered_cut_copper => BlockBehavior::new().requires_correct_tool_for_drops().strength(3.0, 6.0), {}, exposed_cut_copper => BlockBehavior::new().requires_correct_tool_for_drops().strength(3.0, 6.0), {}, cut_copper => BlockBehavior::new().requires_correct_tool_for_drops().strength(3.0, 6.0), {}, + oxidized_chiseled_copper => BlockBehavior::new().requires_correct_tool_for_drops().strength(3.0, 6.0), {}, + weathered_chiseled_copper => BlockBehavior::new().requires_correct_tool_for_drops().strength(3.0, 6.0), {}, + exposed_chiseled_copper => BlockBehavior::new().requires_correct_tool_for_drops().strength(3.0, 6.0), {}, + chiseled_copper => BlockBehavior::new().requires_correct_tool_for_drops().strength(3.0, 6.0), {}, + waxed_oxidized_chiseled_copper => BlockBehavior::new().requires_correct_tool_for_drops().strength(3.0, 6.0), {}, + waxed_weathered_chiseled_copper => BlockBehavior::new().requires_correct_tool_for_drops().strength(3.0, 6.0), {}, + waxed_exposed_chiseled_copper => BlockBehavior::new().requires_correct_tool_for_drops().strength(3.0, 6.0), {}, + waxed_chiseled_copper => BlockBehavior::new().requires_correct_tool_for_drops().strength(3.0, 6.0), {}, oxidized_cut_copper_stairs => BlockBehavior::new().requires_correct_tool_for_drops().strength(3.0, 6.0), { facing: FacingCardinal::North, half: TopBottom::Bottom, shape: StairShape::Straight, - waterlogged: false, + waterlogged: Waterlogged(false), }, weathered_cut_copper_stairs => BlockBehavior::new().requires_correct_tool_for_drops().strength(3.0, 6.0), { facing: FacingCardinal::North, half: TopBottom::Bottom, shape: StairShape::Straight, - waterlogged: false, + waterlogged: Waterlogged(false), }, exposed_cut_copper_stairs => BlockBehavior::new().requires_correct_tool_for_drops().strength(3.0, 6.0), { facing: FacingCardinal::North, half: TopBottom::Bottom, shape: StairShape::Straight, - waterlogged: false, + waterlogged: Waterlogged(false), }, cut_copper_stairs => BlockBehavior::new().requires_correct_tool_for_drops().strength(3.0, 6.0), { facing: FacingCardinal::North, half: TopBottom::Bottom, shape: StairShape::Straight, - waterlogged: false, + waterlogged: Waterlogged(false), }, oxidized_cut_copper_slab => BlockBehavior::new().requires_correct_tool_for_drops().strength(3.0, 6.0), { kind: Type::Bottom, - waterlogged: false, + waterlogged: Waterlogged(false), }, weathered_cut_copper_slab => BlockBehavior::new().requires_correct_tool_for_drops().strength(3.0, 6.0), { kind: Type::Bottom, - waterlogged: false, + waterlogged: Waterlogged(false), }, exposed_cut_copper_slab => BlockBehavior::new().requires_correct_tool_for_drops().strength(3.0, 6.0), { kind: Type::Bottom, - waterlogged: false, + waterlogged: Waterlogged(false), }, cut_copper_slab => BlockBehavior::new().requires_correct_tool_for_drops().strength(3.0, 6.0), { kind: Type::Bottom, - waterlogged: false, + waterlogged: Waterlogged(false), }, waxed_copper_block => BlockBehavior::new().requires_correct_tool_for_drops().strength(3.0, 6.0), {}, waxed_weathered_copper => BlockBehavior::new().requires_correct_tool_for_drops().strength(3.0, 6.0), {}, @@ -4948,59 +5013,227 @@ make_block_states! { facing: FacingCardinal::North, half: TopBottom::Bottom, shape: StairShape::Straight, - waterlogged: false, + waterlogged: Waterlogged(false), }, waxed_weathered_cut_copper_stairs => BlockBehavior::new().requires_correct_tool_for_drops().strength(3.0, 6.0), { facing: FacingCardinal::North, half: TopBottom::Bottom, shape: StairShape::Straight, - waterlogged: false, + waterlogged: Waterlogged(false), }, waxed_exposed_cut_copper_stairs => BlockBehavior::new().requires_correct_tool_for_drops().strength(3.0, 6.0), { facing: FacingCardinal::North, half: TopBottom::Bottom, shape: StairShape::Straight, - waterlogged: false, + waterlogged: Waterlogged(false), }, waxed_cut_copper_stairs => BlockBehavior::new().requires_correct_tool_for_drops().strength(3.0, 6.0), { facing: FacingCardinal::North, half: TopBottom::Bottom, shape: StairShape::Straight, - waterlogged: false, + waterlogged: Waterlogged(false), }, waxed_oxidized_cut_copper_slab => BlockBehavior::new().requires_correct_tool_for_drops().strength(3.0, 6.0), { kind: Type::Bottom, - waterlogged: false, + waterlogged: Waterlogged(false), }, waxed_weathered_cut_copper_slab => BlockBehavior::new().requires_correct_tool_for_drops().strength(3.0, 6.0), { kind: Type::Bottom, - waterlogged: false, + waterlogged: Waterlogged(false), }, waxed_exposed_cut_copper_slab => BlockBehavior::new().requires_correct_tool_for_drops().strength(3.0, 6.0), { kind: Type::Bottom, - waterlogged: false, + waterlogged: Waterlogged(false), }, waxed_cut_copper_slab => BlockBehavior::new().requires_correct_tool_for_drops().strength(3.0, 6.0), { kind: Type::Bottom, - waterlogged: false, + waterlogged: Waterlogged(false), + }, + copper_door => BlockBehavior::new().requires_correct_tool_for_drops().strength(3.0, 6.0), { + facing: FacingCardinal::North, + half: Half::Lower, + hinge: Hinge::Left, + open: Open(false), + powered: Powered(false), + }, + exposed_copper_door => BlockBehavior::new().requires_correct_tool_for_drops().strength(3.0, 6.0), { + facing: FacingCardinal::North, + half: Half::Lower, + hinge: Hinge::Left, + open: Open(false), + powered: Powered(false), + }, + oxidized_copper_door => BlockBehavior::new().requires_correct_tool_for_drops().strength(3.0, 6.0), { + facing: FacingCardinal::North, + half: Half::Lower, + hinge: Hinge::Left, + open: Open(false), + powered: Powered(false), + }, + weathered_copper_door => BlockBehavior::new().requires_correct_tool_for_drops().strength(3.0, 6.0), { + facing: FacingCardinal::North, + half: Half::Lower, + hinge: Hinge::Left, + open: Open(false), + powered: Powered(false), + }, + waxed_copper_door => BlockBehavior::new().requires_correct_tool_for_drops().strength(3.0, 6.0), { + facing: FacingCardinal::North, + half: Half::Lower, + hinge: Hinge::Left, + open: Open(false), + powered: Powered(false), + }, + waxed_exposed_copper_door => BlockBehavior::new().requires_correct_tool_for_drops().strength(3.0, 6.0), { + facing: FacingCardinal::North, + half: Half::Lower, + hinge: Hinge::Left, + open: Open(false), + powered: Powered(false), + }, + waxed_oxidized_copper_door => BlockBehavior::new().requires_correct_tool_for_drops().strength(3.0, 6.0), { + facing: FacingCardinal::North, + half: Half::Lower, + hinge: Hinge::Left, + open: Open(false), + powered: Powered(false), + }, + waxed_weathered_copper_door => BlockBehavior::new().requires_correct_tool_for_drops().strength(3.0, 6.0), { + facing: FacingCardinal::North, + half: Half::Lower, + hinge: Hinge::Left, + open: Open(false), + powered: Powered(false), + }, + copper_trapdoor => BlockBehavior::new().requires_correct_tool_for_drops().strength(3.0, 6.0), { + facing: FacingCardinal::North, + half: TopBottom::Bottom, + open: Open(false), + powered: Powered(false), + waterlogged: Waterlogged(false), + }, + exposed_copper_trapdoor => BlockBehavior::new().requires_correct_tool_for_drops().strength(3.0, 6.0), { + facing: FacingCardinal::North, + half: TopBottom::Bottom, + open: Open(false), + powered: Powered(false), + waterlogged: Waterlogged(false), + }, + oxidized_copper_trapdoor => BlockBehavior::new().requires_correct_tool_for_drops().strength(3.0, 6.0), { + facing: FacingCardinal::North, + half: TopBottom::Bottom, + open: Open(false), + powered: Powered(false), + waterlogged: Waterlogged(false), + }, + weathered_copper_trapdoor => BlockBehavior::new().requires_correct_tool_for_drops().strength(3.0, 6.0), { + facing: FacingCardinal::North, + half: TopBottom::Bottom, + open: Open(false), + powered: Powered(false), + waterlogged: Waterlogged(false), + }, + waxed_copper_trapdoor => BlockBehavior::new().requires_correct_tool_for_drops().strength(3.0, 6.0), { + facing: FacingCardinal::North, + half: TopBottom::Bottom, + open: Open(false), + powered: Powered(false), + waterlogged: Waterlogged(false), + }, + waxed_exposed_copper_trapdoor => BlockBehavior::new().requires_correct_tool_for_drops().strength(3.0, 6.0), { + facing: FacingCardinal::North, + half: TopBottom::Bottom, + open: Open(false), + powered: Powered(false), + waterlogged: Waterlogged(false), + }, + waxed_oxidized_copper_trapdoor => BlockBehavior::new().requires_correct_tool_for_drops().strength(3.0, 6.0), { + facing: FacingCardinal::North, + half: TopBottom::Bottom, + open: Open(false), + powered: Powered(false), + waterlogged: Waterlogged(false), + }, + waxed_weathered_copper_trapdoor => BlockBehavior::new().requires_correct_tool_for_drops().strength(3.0, 6.0), { + facing: FacingCardinal::North, + half: TopBottom::Bottom, + open: Open(false), + powered: Powered(false), + waterlogged: Waterlogged(false), + }, + copper_grate => BlockBehavior::new().requires_correct_tool_for_drops().strength(3.0, 6.0), { + waterlogged: Waterlogged(false), + }, + exposed_copper_grate => BlockBehavior::new().requires_correct_tool_for_drops().strength(3.0, 6.0), { + waterlogged: Waterlogged(false), + }, + weathered_copper_grate => BlockBehavior::new().requires_correct_tool_for_drops().strength(3.0, 6.0), { + waterlogged: Waterlogged(false), + }, + oxidized_copper_grate => BlockBehavior::new().requires_correct_tool_for_drops().strength(3.0, 6.0), { + waterlogged: Waterlogged(false), + }, + waxed_copper_grate => BlockBehavior::new().requires_correct_tool_for_drops().strength(3.0, 6.0), { + waterlogged: Waterlogged(false), + }, + waxed_exposed_copper_grate => BlockBehavior::new().requires_correct_tool_for_drops().strength(3.0, 6.0), { + waterlogged: Waterlogged(false), + }, + waxed_weathered_copper_grate => BlockBehavior::new().requires_correct_tool_for_drops().strength(3.0, 6.0), { + waterlogged: Waterlogged(false), + }, + waxed_oxidized_copper_grate => BlockBehavior::new().requires_correct_tool_for_drops().strength(3.0, 6.0), { + waterlogged: Waterlogged(false), + }, + copper_bulb => BlockBehavior::new().requires_correct_tool_for_drops().strength(3.0, 6.0), { + lit: Lit(false), + powered: Powered(false), + }, + exposed_copper_bulb => BlockBehavior::new().requires_correct_tool_for_drops().strength(3.0, 6.0), { + lit: Lit(false), + powered: Powered(false), + }, + weathered_copper_bulb => BlockBehavior::new().requires_correct_tool_for_drops().strength(3.0, 6.0), { + lit: Lit(false), + powered: Powered(false), + }, + oxidized_copper_bulb => BlockBehavior::new().requires_correct_tool_for_drops().strength(3.0, 6.0), { + lit: Lit(false), + powered: Powered(false), + }, + waxed_copper_bulb => BlockBehavior::new().requires_correct_tool_for_drops().strength(3.0, 6.0), { + lit: Lit(false), + powered: Powered(false), + }, + waxed_exposed_copper_bulb => BlockBehavior::new().requires_correct_tool_for_drops().strength(3.0, 6.0), { + lit: Lit(false), + powered: Powered(false), + }, + waxed_weathered_copper_bulb => BlockBehavior::new().requires_correct_tool_for_drops().strength(3.0, 6.0), { + lit: Lit(false), + powered: Powered(false), + }, + waxed_oxidized_copper_bulb => BlockBehavior::new().requires_correct_tool_for_drops().strength(3.0, 6.0), { + lit: Lit(false), + powered: Powered(false), }, lightning_rod => BlockBehavior::new().requires_correct_tool_for_drops().strength(3.0, 6.0), { facing: FacingCubic::Up, - powered: false, - waterlogged: false, + powered: Powered(false), + waterlogged: Waterlogged(false), }, pointed_dripstone => BlockBehavior::new().strength(1.5, 3.0), { thickness: Thickness::Tip, vertical_direction: TipDirection::Up, - waterlogged: false, + waterlogged: Waterlogged(false), }, dripstone_block => BlockBehavior::new().requires_correct_tool_for_drops().strength(1.5, 1.0), {}, cave_vines => BlockBehavior::new(), { age: CaveVinesAge::_0, - berries: false, + berries: Berries(false), }, cave_vines_plant => BlockBehavior::new(), { - berries: false, + berries: Berries(false), }, spore_blossom => BlockBehavior::new(), {}, azalea => BlockBehavior::new(), {}, @@ -5014,19 +5247,19 @@ make_block_states! { big_dripleaf => BlockBehavior::new().strength(0.1, 0.1), { facing: FacingCardinal::North, tilt: Tilt::None, - waterlogged: false, + waterlogged: Waterlogged(false), }, big_dripleaf_stem => BlockBehavior::new().strength(0.1, 0.1), { facing: FacingCardinal::North, - waterlogged: false, + waterlogged: Waterlogged(false), }, small_dripleaf => BlockBehavior::new(), { facing: FacingCardinal::North, half: Half::Lower, - waterlogged: false, + waterlogged: Waterlogged(false), }, hanging_roots => BlockBehavior::new(), { - waterlogged: false, + waterlogged: Waterlogged(false), }, rooted_dirt => BlockBehavior::new().strength(0.5, 0.5), {}, mud => BlockBehavior::new().strength(0.5, 0.5), {}, @@ -5038,18 +5271,18 @@ make_block_states! { facing: FacingCardinal::North, half: TopBottom::Bottom, shape: StairShape::Straight, - waterlogged: false, + waterlogged: Waterlogged(false), }, cobbled_deepslate_slab => BlockBehavior::new().requires_correct_tool_for_drops().strength(3.5, 6.0), { kind: Type::Bottom, - waterlogged: false, + waterlogged: Waterlogged(false), }, cobbled_deepslate_wall => BlockBehavior::new().requires_correct_tool_for_drops().strength(3.5, 6.0), { east: EastWall::None, north: NorthWall::None, south: SouthWall::None, - up: true, - waterlogged: false, + up: Up(true), + waterlogged: Waterlogged(false), west: WestWall::None, }, polished_deepslate => BlockBehavior::new().requires_correct_tool_for_drops().strength(3.5, 6.0), {}, @@ -5057,18 +5290,18 @@ make_block_states! { facing: FacingCardinal::North, half: TopBottom::Bottom, shape: StairShape::Straight, - waterlogged: false, + waterlogged: Waterlogged(false), }, polished_deepslate_slab => BlockBehavior::new().requires_correct_tool_for_drops().strength(3.5, 6.0), { kind: Type::Bottom, - waterlogged: false, + waterlogged: Waterlogged(false), }, polished_deepslate_wall => BlockBehavior::new().requires_correct_tool_for_drops().strength(3.5, 6.0), { east: EastWall::None, north: NorthWall::None, south: SouthWall::None, - up: true, - waterlogged: false, + up: Up(true), + waterlogged: Waterlogged(false), west: WestWall::None, }, deepslate_tiles => BlockBehavior::new().requires_correct_tool_for_drops().strength(3.5, 6.0), {}, @@ -5076,18 +5309,18 @@ make_block_states! { facing: FacingCardinal::North, half: TopBottom::Bottom, shape: StairShape::Straight, - waterlogged: false, + waterlogged: Waterlogged(false), }, deepslate_tile_slab => BlockBehavior::new().requires_correct_tool_for_drops().strength(3.5, 6.0), { kind: Type::Bottom, - waterlogged: false, + waterlogged: Waterlogged(false), }, deepslate_tile_wall => BlockBehavior::new().requires_correct_tool_for_drops().strength(3.5, 6.0), { east: EastWall::None, north: NorthWall::None, south: SouthWall::None, - up: true, - waterlogged: false, + up: Up(true), + waterlogged: Waterlogged(false), west: WestWall::None, }, deepslate_bricks => BlockBehavior::new().requires_correct_tool_for_drops().strength(3.5, 6.0), {}, @@ -5095,25 +5328,25 @@ make_block_states! { facing: FacingCardinal::North, half: TopBottom::Bottom, shape: StairShape::Straight, - waterlogged: false, + waterlogged: Waterlogged(false), }, deepslate_brick_slab => BlockBehavior::new().requires_correct_tool_for_drops().strength(3.5, 6.0), { kind: Type::Bottom, - waterlogged: false, + waterlogged: Waterlogged(false), }, deepslate_brick_wall => BlockBehavior::new().requires_correct_tool_for_drops().strength(3.5, 6.0), { east: EastWall::None, north: NorthWall::None, south: SouthWall::None, - up: true, - waterlogged: false, + up: Up(true), + waterlogged: Waterlogged(false), west: WestWall::None, }, chiseled_deepslate => BlockBehavior::new().requires_correct_tool_for_drops().strength(3.5, 6.0), {}, cracked_deepslate_bricks => BlockBehavior::new().requires_correct_tool_for_drops().strength(3.5, 6.0), {}, cracked_deepslate_tiles => BlockBehavior::new().requires_correct_tool_for_drops().strength(3.5, 6.0), {}, infested_deepslate => BlockBehavior::new().strength(1.5, 0.75), { - axis: CacheSize::Y, + axis: Falling::Y, }, smooth_basalt => BlockBehavior::new().requires_correct_tool_for_drops().strength(1.25, 4.2), {}, raw_iron_block => BlockBehavior::new().requires_correct_tool_for_drops().strength(5.0, 6.0), {}, @@ -5133,9 +5366,18 @@ make_block_states! { frogspawn => BlockBehavior::new(), {}, reinforced_deepslate => BlockBehavior::new().strength(55.0, 1200.0), {}, decorated_pot => BlockBehavior::new(), { - cracked: false, + cracked: Cracked(false), facing: FacingCardinal::North, - waterlogged: false, + waterlogged: Waterlogged(false), + }, + crafter => BlockBehavior::new().strength(1.5, 3.5), { + crafting: Crafting(false), + orientation: Orientation::NorthUp, + triggered: Triggered(false), + }, + trial_spawner => BlockBehavior::new().requires_correct_tool_for_drops().strength(50.0, 50.0), { + trial_spawner_state: State::Inactive, }, } } + diff --git a/azalea-block/src/lib.rs b/azalea-block/src/lib.rs index b67a6e763..983de5791 100755 --- a/azalea-block/src/lib.rs +++ b/azalea-block/src/lib.rs @@ -33,6 +33,12 @@ impl dyn Block { } } +pub trait Property { + type Value; + + fn try_from_block_state(state: BlockState) -> Option; +} + /// A representation of a state a block can be in. /// /// For example, a stone block only has one state but each possible stair @@ -113,7 +119,10 @@ impl Default for FluidState { impl From for FluidState { fn from(state: BlockState) -> Self { - if state.waterlogged() { + if state + .property::() + .unwrap_or_default() + { Self { fluid: azalea_registry::Fluid::Water, height: 15, @@ -158,6 +167,12 @@ impl From for BlockState { } } +impl From for azalea_registry::Block { + fn from(value: BlockState) -> Self { + Box::::from(value).as_registry_block() + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/azalea-block/src/range.rs b/azalea-block/src/range.rs index 6ccf4152e..9b520d496 100644 --- a/azalea-block/src/range.rs +++ b/azalea-block/src/range.rs @@ -1,4 +1,7 @@ -use std::{collections::HashSet, ops::RangeInclusive}; +use std::{ + collections::HashSet, + ops::{Add, RangeInclusive}, +}; use crate::BlockState; @@ -31,3 +34,13 @@ impl BlockStates { self.set.contains(state) } } + +impl Add for BlockStates { + type Output = Self; + + fn add(self, rhs: Self) -> Self::Output { + Self { + set: self.set.union(&rhs.set).copied().collect(), + } + } +} diff --git a/azalea-brigadier/Cargo.toml b/azalea-brigadier/Cargo.toml index c28f68a2a..d9bfa3637 100644 --- a/azalea-brigadier/Cargo.toml +++ b/azalea-brigadier/Cargo.toml @@ -4,13 +4,13 @@ edition = "2021" license = "MIT" name = "azalea-brigadier" repository = "https://github.com/azalea-rs/azalea/tree/main/azalea-brigadier" -version = "0.8.0" +version = "0.9.0" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -azalea-buf = { path = "../azalea-buf", version = "0.8.0", optional = true } -azalea-chat = { path = "../azalea-chat", version = "0.8.0", optional = true } +azalea-buf = { path = "../azalea-buf", version = "0.9.0", optional = true } +azalea-chat = { path = "../azalea-chat", version = "0.9.0", optional = true } parking_lot = "0.12.1" [features] diff --git a/azalea-brigadier/src/tree/mod.rs b/azalea-brigadier/src/tree/mod.rs index a2b1f38a2..a982e82b3 100755 --- a/azalea-brigadier/src/tree/mod.rs +++ b/azalea-brigadier/src/tree/mod.rs @@ -294,7 +294,7 @@ impl PartialEq for CommandNode { if let Some(selfexecutes) = &self.command { // idk how to do this better since we can't compare `dyn Fn`s if let Some(otherexecutes) = &other.command { - #[allow(clippy::vtable_address_comparisons)] + #[allow(ambiguous_wide_pointer_comparisons)] if !Arc::ptr_eq(selfexecutes, otherexecutes) { return false; } diff --git a/azalea-buf/Cargo.toml b/azalea-buf/Cargo.toml index dc25632b0..f15525b7a 100644 --- a/azalea-buf/Cargo.toml +++ b/azalea-buf/Cargo.toml @@ -4,18 +4,18 @@ edition = "2021" license = "MIT" name = "azalea-buf" repository = "https://github.com/azalea-rs/azalea/tree/main/azalea-buf" -version = "0.8.0" +version = "0.9.0" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -azalea-buf-macros = { path = "./azalea-buf-macros", version = "0.8.0" } +simdnbt = { version = "0.3", git = "https://github.com/azalea-rs/simdnbt" } +azalea-buf-macros = { path = "./azalea-buf-macros", version = "0.9.0" } byteorder = "^1.5.0" tracing = "0.1.40" serde_json = { version = "^1.0", optional = true } thiserror = "1.0.50" -uuid = "^1.5.0" -simdnbt = { version = "0.2.1" } +uuid = "^1.6.1" [features] serde_json = ["dep:serde_json"] diff --git a/azalea-buf/azalea-buf-macros/Cargo.toml b/azalea-buf/azalea-buf-macros/Cargo.toml index 1cb410647..facc04b1e 100644 --- a/azalea-buf/azalea-buf-macros/Cargo.toml +++ b/azalea-buf/azalea-buf-macros/Cargo.toml @@ -4,13 +4,13 @@ edition = "2021" license = "MIT" name = "azalea-buf-macros" repository = "https://github.com/azalea-rs/azalea/tree/main/azalea-buf" -version = "0.8.0" +version = "0.9.0" [lib] proc-macro = true # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -proc-macro2 = "^1.0.69" +proc-macro2 = "^1.0.70" quote = "^1.0.33" syn = { version = "^2.0.39", features = ["extra-traits"] } diff --git a/azalea-buf/src/write.rs b/azalea-buf/src/write.rs index 03d40d793..c8d1f9907 100755 --- a/azalea-buf/src/write.rs +++ b/azalea-buf/src/write.rs @@ -262,7 +262,7 @@ impl McBufWritable for simdnbt::owned::NbtTag { fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { let mut data = Vec::new(); self.write(&mut data); - data.write_into(buf) + buf.write_all(&data) } } @@ -270,7 +270,7 @@ impl McBufWritable for simdnbt::owned::NbtCompound { fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { let mut data = Vec::new(); simdnbt::owned::NbtTag::Compound(self.clone()).write(&mut data); - data.write_into(buf) + buf.write_all(&data) } } @@ -278,6 +278,6 @@ impl McBufWritable for simdnbt::owned::Nbt { fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { let mut data = Vec::new(); self.write_unnamed(&mut data); - data.write_into(buf) + buf.write_all(&data) } } diff --git a/azalea-chat/Cargo.toml b/azalea-chat/Cargo.toml index 96b3da9f1..247285c51 100644 --- a/azalea-chat/Cargo.toml +++ b/azalea-chat/Cargo.toml @@ -4,19 +4,24 @@ edition = "2021" license = "MIT" name = "azalea-chat" repository = "https://github.com/azalea-rs/azalea/tree/main/azalea-chat" -version = "0.8.0" +version = "0.9.0" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [features] -default = ["azalea-buf"] +default = [] +azalea-buf = ["dep:azalea-buf", "simdnbt"] +numbers = ["dep:azalea-registry", "dep:simdnbt"] +simdnbt = ["dep:simdnbt"] [dependencies] azalea-buf = { path = "../azalea-buf", features = [ "serde_json", -], version = "^0.8.0", optional = true } -azalea-language = { path = "../azalea-language", version = "0.8.0" } +], version = "0.9.0", optional = true } +azalea-language = { path = "../azalea-language", version = "0.9.0" } +simdnbt = { version = "0.3", optional = true, git = "https://github.com/azalea-rs/simdnbt" } tracing = "0.1.40" once_cell = "1.18.0" serde = { version = "^1.0", features = ["derive"] } serde_json = "^1.0.108" +azalea-registry = { path = "../azalea-registry", version = "0.9.0", optional = true } diff --git a/azalea-chat/src/base_component.rs b/azalea-chat/src/base_component.rs index dcc28ecc7..8f70ecb7f 100755 --- a/azalea-chat/src/base_component.rs +++ b/azalea-chat/src/base_component.rs @@ -19,6 +19,26 @@ impl BaseComponent { } } +#[cfg(feature = "simdnbt")] +impl simdnbt::Serialize for BaseComponent { + fn to_compound(self) -> simdnbt::owned::NbtCompound { + let mut compound = simdnbt::owned::NbtCompound::new(); + if !self.siblings.is_empty() { + compound.insert( + "extra", + simdnbt::owned::NbtList::from( + self.siblings + .into_iter() + .map(|component| component.to_compound()) + .collect::>(), + ), + ); + } + compound.extend(self.style.to_compound()); + compound + } +} + impl Default for BaseComponent { fn default() -> Self { Self::new() diff --git a/azalea-chat/src/component.rs b/azalea-chat/src/component.rs index e80e7e4bc..807d0b1aa 100755 --- a/azalea-chat/src/component.rs +++ b/azalea-chat/src/component.rs @@ -8,11 +8,9 @@ use crate::{ use azalea_buf::{BufReadError, McBufReadable, McBufWritable}; use once_cell::sync::Lazy; use serde::{de, Deserialize, Deserializer, Serialize}; -use std::{ - fmt::Display, - io::{Cursor, Write}, -}; -use tracing::debug; +#[cfg(feature = "simdnbt")] +use simdnbt::{Deserialize as _, FromNbtTag as _, Serialize as _}; +use std::fmt::Display; /// A chat component, basically anything you can see in chat. #[derive(Clone, Debug, PartialEq, Eq, Serialize, Hash)] @@ -52,20 +50,27 @@ impl FormattedText { fn parse_separator( json: &serde_json::Value, ) -> Result, serde_json::Error> { - if json.get("separator").is_some() { - return Ok(Some(FormattedText::deserialize( - json.get("separator").unwrap(), - )?)); + if let Some(separator) = json.get("separator") { + return Ok(Some(FormattedText::deserialize(separator)?)); } Ok(None) } + #[cfg(feature = "simdnbt")] + fn parse_separator_nbt(nbt: &simdnbt::borrow::NbtCompound) -> Option { + if let Some(separator) = nbt.get("separator") { + FormattedText::from_nbt_tag(separator) + } else { + None + } + } + /// Convert this component into an /// [ANSI string](https://en.wikipedia.org/wiki/ANSI_escape_code), so you /// can print it to your terminal and get styling. /// /// This is technically a shortcut for - /// [`FormattedText::to_ansi_custom_style`] with a default [`Style`] + /// [`FormattedText::to_ansi_with_custom_style`] with a default [`Style`] /// colored white. /// /// # Examples @@ -83,7 +88,7 @@ impl FormattedText { /// ``` pub fn to_ansi(&self) -> String { // default the default_style to white if it's not set - self.to_ansi_custom_style(&DEFAULT_STYLE) + self.to_ansi_with_custom_style(&DEFAULT_STYLE) } /// Convert this component into an @@ -91,7 +96,7 @@ impl FormattedText { /// /// This is the same as [`FormattedText::to_ansi`], but you can specify a /// default [`Style`] to use. - pub fn to_ansi_custom_style(&self, default_style: &Style) -> String { + pub fn to_ansi_with_custom_style(&self, default_style: &Style) -> String { // this contains the final string will all the ansi escape codes let mut built_string = String::new(); // this style will update as we visit components @@ -273,23 +278,155 @@ impl<'de> Deserialize<'de> for FormattedText { } } +#[cfg(feature = "simdnbt")] +impl simdnbt::Serialize for FormattedText { + fn to_compound(self) -> simdnbt::owned::NbtCompound { + match self { + FormattedText::Text(c) => c.to_compound(), + FormattedText::Translatable(c) => c.to_compound(), + } + } +} + +#[cfg(feature = "simdnbt")] +impl simdnbt::FromNbtTag for FormattedText { + fn from_nbt_tag(tag: &simdnbt::borrow::NbtTag) -> Option { + // we create a component that we might add siblings to + let mut component: FormattedText; + + match tag { + // if it's a string, return a text component with that string + simdnbt::borrow::NbtTag::String(string) => { + Some(FormattedText::Text(TextComponent::new(string.to_string()))) + } + // if it's a compound, make it do things with { text } and stuff + simdnbt::borrow::NbtTag::Compound(compound) => { + if let Some(text) = compound.get("text") { + let text = text.string().unwrap_or_default().to_string(); + component = FormattedText::Text(TextComponent::new(text)); + } else if let Some(translate) = compound.get("translate") { + let translate = translate.string()?.into(); + if let Some(with) = compound.get("with") { + let with = with.list()?.compounds()?; + let mut with_array = Vec::with_capacity(with.len()); + for item in with { + // if it's a string component with no styling and no siblings, just add + // a string to with_array otherwise add the + // component to the array + let c = FormattedText::from_nbt_tag( + &simdnbt::borrow::NbtTag::Compound(item.clone()), + )?; + if let FormattedText::Text(text_component) = c { + if text_component.base.siblings.is_empty() + && text_component.base.style.is_empty() + { + with_array.push(StringOrComponent::String(text_component.text)); + continue; + } + } + with_array.push(StringOrComponent::FormattedText( + FormattedText::from_nbt_tag(&simdnbt::borrow::NbtTag::Compound( + item.clone(), + ))?, + )); + } + component = FormattedText::Translatable(TranslatableComponent::new( + translate, with_array, + )); + } else { + // if it doesn't have a "with", just have the with_array be empty + component = FormattedText::Translatable(TranslatableComponent::new( + translate, + Vec::new(), + )); + } + } else if let Some(score) = compound.compound("score") { + // object = GsonHelper.getAsJsonObject(jsonObject, "score"); + if score.get("name").is_none() || score.get("objective").is_none() { + // A score component needs at least a name and an objective + tracing::trace!("A score component needs at least a name and an objective"); + return None; + } + // TODO, score text components aren't yet supported + return None; + } else if compound.get("selector").is_some() { + // selector text components aren't yet supported + tracing::trace!("selector text components aren't yet supported"); + return None; + } else if compound.get("keybind").is_some() { + // keybind text components aren't yet supported + tracing::trace!("keybind text components aren't yet supported"); + return None; + } else { + let Some(_nbt) = compound.get("nbt") else { + // Don't know how to turn 'nbt' into a FormattedText + return None; + }; + let _separator = FormattedText::parse_separator_nbt(compound)?; + + let _interpret = match compound.get("interpret") { + Some(v) => v.byte().unwrap_or_default() != 0, + None => false, + }; + if let Some(_block) = compound.get("block") {} + // nbt text components aren't yet supported + return None; + } + if let Some(extra) = compound.get("extra") { + let extra = extra.list()?.as_nbt_tags(); + if extra.is_empty() { + // Unexpected empty array of components + return None; + } + for extra_component in extra { + let sibling = FormattedText::from_nbt_tag(&extra_component)?; + component.append(sibling); + } + } + + let style = Style::from_compound(compound).ok()?; + component.get_base_mut().style = style; + + Some(component) + } + // ok so it's not a compound, if it's a list deserialize every item + simdnbt::borrow::NbtTag::List(list) => { + let list = list.compounds()?; + let mut component = FormattedText::from_nbt_tag( + &simdnbt::borrow::NbtTag::Compound(list.first()?.clone()), + )?; + for i in 1..list.len() { + component.append(FormattedText::from_nbt_tag( + &simdnbt::borrow::NbtTag::Compound(list.get(i)?.clone()), + )?); + } + Some(component) + } + _ => Some(FormattedText::Text(TextComponent::new("".to_owned()))), + } + } +} + #[cfg(feature = "azalea-buf")] impl McBufReadable for FormattedText { - fn read_from(buf: &mut Cursor<&[u8]>) -> Result { - let string = String::read_from(buf)?; - debug!("FormattedText string: {}", string); - let json: serde_json::Value = serde_json::from_str(string.as_str())?; - let component = FormattedText::deserialize(json)?; - Ok(component) + fn read_from(buf: &mut std::io::Cursor<&[u8]>) -> Result { + let nbt = simdnbt::borrow::NbtTag::read_optional(buf)?; + if let Some(nbt) = nbt { + FormattedText::from_nbt_tag(&nbt) + .ok_or(BufReadError::Custom("couldn't read nbt".to_owned())) + } else { + Ok(FormattedText::default()) + } } } #[cfg(feature = "azalea-buf")] +#[cfg(feature = "simdnbt")] impl McBufWritable for FormattedText { - fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { - let json = serde_json::to_string(self).unwrap(); - json.write_into(buf)?; - Ok(()) + fn write_into(&self, buf: &mut impl std::io::Write) -> Result<(), std::io::Error> { + let mut out = Vec::new(); + simdnbt::owned::BaseNbt::write_unnamed(&(self.clone().to_compound().into()), &mut out); + buf.write_all(&out) } } diff --git a/azalea-chat/src/lib.rs b/azalea-chat/src/lib.rs index d6ff72855..782839ae7 100755 --- a/azalea-chat/src/lib.rs +++ b/azalea-chat/src/lib.rs @@ -1,7 +1,10 @@ +#![feature(cursor_remaining)] #![doc = include_str!("../README.md")] pub mod base_component; mod component; +#[cfg(feature = "numbers")] +pub mod numbers; pub mod style; pub mod text_component; pub mod translatable_component; diff --git a/azalea-chat/src/numbers.rs b/azalea-chat/src/numbers.rs new file mode 100644 index 000000000..21c305911 --- /dev/null +++ b/azalea-chat/src/numbers.rs @@ -0,0 +1,52 @@ +//! Contains a few ways to style numbers. At the time of writing, Minecraft only +//! uses this for rendering scoreboard objectives. + +use std::io::{Cursor, Write}; + +#[cfg(feature = "azalea-buf")] +use azalea_buf::{McBufReadable, McBufWritable}; +use azalea_registry::NumberFormatKind; +use simdnbt::owned::Nbt; + +use crate::FormattedText; + +#[derive(Clone, Debug)] +pub enum NumberFormat { + Blank, + Styled { style: Nbt }, + Fixed { value: FormattedText }, +} + +#[cfg(feature = "azalea-buf")] +impl McBufReadable for NumberFormat { + fn read_from(buf: &mut Cursor<&[u8]>) -> Result { + let kind = NumberFormatKind::read_from(buf)?; + match kind { + NumberFormatKind::Blank => Ok(NumberFormat::Blank), + NumberFormatKind::Styled => Ok(NumberFormat::Styled { + style: Nbt::read(buf)?, + }), + NumberFormatKind::Fixed => Ok(NumberFormat::Fixed { + value: FormattedText::read_from(buf)?, + }), + } + } +} + +#[cfg(feature = "azalea-buf")] +impl McBufWritable for NumberFormat { + fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { + match self { + NumberFormat::Blank => NumberFormatKind::Blank.write_into(buf)?, + NumberFormat::Styled { style } => { + NumberFormatKind::Styled.write_into(buf)?; + style.write_into(buf)?; + } + NumberFormat::Fixed { value } => { + NumberFormatKind::Fixed.write_into(buf)?; + value.write_into(buf)?; + } + } + Ok(()) + } +} diff --git a/azalea-chat/src/style.rs b/azalea-chat/src/style.rs index ba4d6e724..e04c19254 100755 --- a/azalea-chat/src/style.rs +++ b/azalea-chat/src/style.rs @@ -5,6 +5,8 @@ use azalea_buf::McBuf; use once_cell::sync::Lazy; use serde::{ser::SerializeStruct, Serialize, Serializer}; use serde_json::Value; +#[cfg(feature = "simdnbt")] +use simdnbt::owned::{NbtCompound, NbtTag}; #[derive(Clone, PartialEq, Eq, Debug, Hash)] pub struct TextColor { @@ -17,11 +19,22 @@ impl Serialize for TextColor { where S: Serializer, { - if self.name.is_some() { - serializer.serialize_str(&self.name.as_ref().unwrap().to_ascii_lowercase()) - } else { - serializer.serialize_str(&self.format()) - } + serializer.serialize_str( + &self + .name + .as_ref() + .map(|n| n.to_ascii_lowercase()) + .unwrap_or_else(|| self.format()), + ) + } +} + +#[cfg(feature = "simdnbt")] +impl simdnbt::ToNbtTag for TextColor { + fn to_nbt_tag(self) -> simdnbt::owned::NbtTag { + self.name + .map(|n| NbtTag::String(n.to_ascii_lowercase().into())) + .unwrap_or_else(|| NbtTag::Int(self.value as i32)) } } @@ -303,6 +316,36 @@ pub struct Style { pub reset: bool, } +fn serde_serialize_field( + state: &mut S, + name: &'static str, + value: &Option, + default: &(impl serde::Serialize + ?Sized), + reset: bool, +) -> Result<(), S::Error> { + if let Some(value) = value { + state.serialize_field(name, value)?; + } else if reset { + state.serialize_field(name, default)?; + } + Ok(()) +} + +#[cfg(feature = "simdnbt")] +fn simdnbt_serialize_field( + compound: &mut simdnbt::owned::NbtCompound, + name: &'static str, + value: Option, + default: impl simdnbt::ToNbtTag, + reset: bool, +) { + if let Some(value) = value { + compound.insert(name, value); + } else if reset { + compound.insert(name, default); + } +} + impl Serialize for Style { fn serialize(&self, serializer: S) -> Result where @@ -319,69 +362,97 @@ impl Serialize for Style { + usize::from(self.obfuscated.is_some()) }; let mut state = serializer.serialize_struct("Style", len)?; - if let Some(color) = &self.color { - state.serialize_field("color", color)?; - } else if self.reset { - state.serialize_field("color", "white")?; - } - if let Some(bold) = &self.bold { - state.serialize_field("bold", bold)?; - } else if self.reset { - state.serialize_field("bold", &false)?; - } - if let Some(italic) = &self.italic { - state.serialize_field("italic", italic)?; - } else if self.reset { - state.serialize_field("italic", &false)?; - } - if let Some(underlined) = &self.underlined { - state.serialize_field("underlined", underlined)?; - } else if self.reset { - state.serialize_field("underlined", &false)?; - } - if let Some(strikethrough) = &self.strikethrough { - state.serialize_field("strikethrough", strikethrough)?; - } else if self.reset { - state.serialize_field("strikethrough", &false)?; - } - if let Some(obfuscated) = &self.obfuscated { - state.serialize_field("obfuscated", obfuscated)?; - } else if self.reset { - state.serialize_field("obfuscated", &false)?; - } + + serde_serialize_field(&mut state, "color", &self.color, "white", self.reset)?; + serde_serialize_field(&mut state, "bold", &self.bold, &false, self.reset)?; + serde_serialize_field(&mut state, "italic", &self.italic, &false, self.reset)?; + serde_serialize_field( + &mut state, + "underlined", + &self.underlined, + &false, + self.reset, + )?; + serde_serialize_field( + &mut state, + "strikethrough", + &self.strikethrough, + &false, + self.reset, + )?; + serde_serialize_field( + &mut state, + "obfuscated", + &self.obfuscated, + &false, + self.reset, + )?; + state.end() } } +#[cfg(feature = "simdnbt")] +impl simdnbt::Serialize for Style { + fn to_compound(self) -> NbtCompound { + let mut compound = NbtCompound::new(); + + simdnbt_serialize_field(&mut compound, "color", self.color, "white", self.reset); + simdnbt_serialize_field(&mut compound, "bold", self.bold, false, self.reset); + simdnbt_serialize_field(&mut compound, "italic", self.italic, false, self.reset); + simdnbt_serialize_field( + &mut compound, + "underlined", + self.underlined, + false, + self.reset, + ); + simdnbt_serialize_field( + &mut compound, + "strikethrough", + self.strikethrough, + false, + self.reset, + ); + simdnbt_serialize_field( + &mut compound, + "obfuscated", + self.obfuscated, + false, + self.reset, + ); + + compound + } +} + impl Style { pub fn empty() -> Self { Self::default() } pub fn deserialize(json: &Value) -> Style { - return if json.is_object() { - let json_object = json.as_object().unwrap(); - let bold = json_object.get("bold").and_then(|v| v.as_bool()); - let italic = json_object.get("italic").and_then(|v| v.as_bool()); - let underlined = json_object.get("underlined").and_then(|v| v.as_bool()); - let strikethrough = json_object.get("strikethrough").and_then(|v| v.as_bool()); - let obfuscated = json_object.get("obfuscated").and_then(|v| v.as_bool()); - let color: Option = json_object - .get("color") - .and_then(|v| v.as_str()) - .and_then(|v| TextColor::parse(v.to_string())); - Style { - color, - bold, - italic, - underlined, - strikethrough, - obfuscated, - ..Style::default() - } - } else { - Style::default() + let Some(json_object) = json.as_object() else { + return Style::default(); }; + let bold = json_object.get("bold").and_then(|v| v.as_bool()); + let italic = json_object.get("italic").and_then(|v| v.as_bool()); + let underlined = json_object.get("underlined").and_then(|v| v.as_bool()); + let strikethrough = json_object.get("strikethrough").and_then(|v| v.as_bool()); + let obfuscated = json_object.get("obfuscated").and_then(|v| v.as_bool()); + let color: Option = json_object + .get("color") + .and_then(|v| v.as_str()) + .and_then(|v| TextColor::parse(v.to_string())); + Style { + color, + bold, + italic, + underlined, + strikethrough, + obfuscated, + ..Style::default() + } } /// Check if a style has no attributes set @@ -507,6 +578,31 @@ impl Style { } } +#[cfg(feature = "simdnbt")] +impl simdnbt::Deserialize for Style { + fn from_compound( + compound: &simdnbt::borrow::NbtCompound, + ) -> Result { + let bold = compound.byte("bold").map(|v| v != 0); + let italic = compound.byte("italic").map(|v| v != 0); + let underlined = compound.byte("underlined").map(|v| v != 0); + let strikethrough = compound.byte("strikethrough").map(|v| v != 0); + let obfuscated = compound.byte("obfuscated").map(|v| v != 0); + let color: Option = compound + .string("color") + .and_then(|v| TextColor::parse(v.to_string())); + Ok(Style { + color, + bold, + italic, + underlined, + strikethrough, + obfuscated, + ..Style::default() + }) + } +} + #[cfg(test)] mod tests { use crate::component::DEFAULT_STYLE; diff --git a/azalea-chat/src/text_component.rs b/azalea-chat/src/text_component.rs index fefd2cb8e..6f95840d9 100755 --- a/azalea-chat/src/text_component.rs +++ b/azalea-chat/src/text_component.rs @@ -24,6 +24,28 @@ impl Serialize for TextComponent { } } +#[cfg(feature = "simdnbt")] +impl simdnbt::Serialize for TextComponent { + fn to_compound(self) -> simdnbt::owned::NbtCompound { + let mut compound = simdnbt::owned::NbtCompound::new(); + compound.insert("text", self.text); + compound.extend(self.base.style.to_compound()); + if !self.base.siblings.is_empty() { + compound.insert( + "extra", + simdnbt::owned::NbtList::from( + self.base + .siblings + .into_iter() + .map(|component| component.to_compound()) + .collect::>(), + ), + ); + } + compound + } +} + const LEGACY_FORMATTING_CODE_SYMBOL: char = '§'; /// Convert a legacy color code string into a FormattedText diff --git a/azalea-chat/src/translatable_component.rs b/azalea-chat/src/translatable_component.rs index 56c6507e5..912271ae4 100755 --- a/azalea-chat/src/translatable_component.rs +++ b/azalea-chat/src/translatable_component.rs @@ -4,6 +4,8 @@ use crate::{ base_component::BaseComponent, style::Style, text_component::TextComponent, FormattedText, }; use serde::{ser::SerializeMap, Serialize, Serializer, __private::ser::FlatMapSerializer}; +#[cfg(feature = "simdnbt")] +use simdnbt::Serialize as _; #[derive(Clone, Debug, PartialEq, Serialize, Eq, Hash)] #[serde(untagged)] @@ -12,6 +14,16 @@ pub enum StringOrComponent { FormattedText(FormattedText), } +#[cfg(feature = "simdnbt")] +impl simdnbt::ToNbtTag for StringOrComponent { + fn to_nbt_tag(self) -> simdnbt::owned::NbtTag { + match self { + StringOrComponent::String(s) => s.to_nbt_tag(), + StringOrComponent::FormattedText(c) => c.to_nbt_tag(), + } + } +} + /// A message whose content depends on the client's language. #[derive(Clone, Debug, PartialEq, Eq, Hash)] pub struct TranslatableComponent { @@ -33,6 +45,54 @@ impl Serialize for TranslatableComponent { } } +#[cfg(feature = "simdnbt")] +fn serialize_args_as_nbt(args: &[StringOrComponent]) -> simdnbt::owned::NbtList { + // if it's all strings then make it a string list + // if it's all components then make it a compound list + // if it's a mix then return an error + + let mut string_list = Vec::new(); + let mut compound_list = Vec::new(); + + for arg in args { + match arg { + StringOrComponent::String(s) => { + string_list.push(s.clone()); + } + StringOrComponent::FormattedText(c) => { + compound_list.push(c.clone().to_compound()); + } + } + } + + if !string_list.is_empty() && !compound_list.is_empty() { + // i'm actually not sure what vanilla does here, so i just made it return the + // string list + tracing::debug!( + "Tried to serialize a TranslatableComponent with a mix of strings and components." + ); + return string_list.into(); + } + + if !string_list.is_empty() { + return string_list.into(); + } + + compound_list.into() +} + +#[cfg(feature = "simdnbt")] +impl simdnbt::Serialize for TranslatableComponent { + fn to_compound(self) -> simdnbt::owned::NbtCompound { + let mut compound = simdnbt::owned::NbtCompound::new(); + compound.insert("translate", self.key); + compound.extend(self.base.style.to_compound()); + + compound.insert("with", serialize_args_as_nbt(&self.args)); + compound + } +} + impl TranslatableComponent { pub fn new(key: String, args: Vec) -> Self { Self { diff --git a/azalea-client/Cargo.toml b/azalea-client/Cargo.toml index e644c0e84..b18d1762d 100644 --- a/azalea-client/Cargo.toml +++ b/azalea-client/Cargo.toml @@ -4,31 +4,31 @@ edition = "2021" license = "MIT" name = "azalea-client" repository = "https://github.com/azalea-rs/azalea/tree/main/azalea-client" -version = "0.8.0" +version = "0.9.0" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +simdnbt = { version = "0.3", git = "https://github.com/azalea-rs/simdnbt" } reqwest = { version = "0.11.22", default-features = false } anyhow = "1.0.75" async-trait = "0.1.74" -azalea-auth = { path = "../azalea-auth", version = "0.8.0" } -azalea-block = { path = "../azalea-block", version = "0.8.0" } -simdnbt = { version = "0.2.1" } -azalea-chat = { path = "../azalea-chat", version = "0.8.0" } -azalea-core = { path = "../azalea-core", version = "0.8.0" } -azalea-crypto = { path = "../azalea-crypto", version = "0.8.0" } -azalea-physics = { path = "../azalea-physics", version = "0.8.0" } -azalea-buf = { path = "../azalea-buf", version = "0.8.0" } -azalea-protocol = { path = "../azalea-protocol", version = "0.8.0" } -azalea-registry = { path = "../azalea-registry", version = "0.8.0" } -azalea-world = { path = "../azalea-world", version = "0.8.0" } -bevy_app = "0.12.0" -bevy_ecs = "0.12.0" -bevy_log = { version = "0.12.0", optional = true } -bevy_tasks = "0.12.0" -bevy_time = "0.12.0" -azalea-inventory = { path = "../azalea-inventory", version = "0.8.0" } +azalea-auth = { path = "../azalea-auth", version = "0.9.0" } +azalea-block = { path = "../azalea-block", version = "0.9.0" } +azalea-chat = { path = "../azalea-chat", version = "0.9.0" } +azalea-core = { path = "../azalea-core", version = "0.9.0" } +azalea-crypto = { path = "../azalea-crypto", version = "0.9.0" } +azalea-physics = { path = "../azalea-physics", version = "0.9.0" } +azalea-buf = { path = "../azalea-buf", version = "0.9.0" } +azalea-protocol = { path = "../azalea-protocol", version = "0.9.0" } +azalea-registry = { path = "../azalea-registry", version = "0.9.0" } +azalea-world = { path = "../azalea-world", version = "0.9.0" } +bevy_app = "0.12.1" +bevy_ecs = "0.12.1" +bevy_log = { version = "0.12.1", optional = true } +bevy_tasks = "0.12.1" +bevy_time = "0.12.1" +azalea-inventory = { path = "../azalea-inventory", version = "0.9.0" } derive_more = { version = "0.99.17", features = ["deref", "deref_mut"] } futures = "0.3.29" tracing = "0.1.40" @@ -38,11 +38,12 @@ parking_lot = { version = "^0.12.1", features = ["deadlock_detection"] } regex = "1.10.2" thiserror = "^1.0.50" tokio = { version = "^1.34.0", features = ["sync"] } -uuid = "^1.5.0" -azalea-entity = { version = "0.8.0", path = "../azalea-entity" } +uuid = "^1.6.1" +azalea-entity = { version = "0.9.0", path = "../azalea-entity" } serde_json = "1.0.108" serde = "1.0.192" socks5-impl = "0.5.6" +minecraft_folder_path = "0.1.1" [features] default = ["log"] diff --git a/azalea-client/src/account.rs b/azalea-client/src/account.rs index 0be4146be..741a07d45 100755 --- a/azalea-client/src/account.rs +++ b/azalea-client/src/account.rs @@ -2,7 +2,6 @@ use std::sync::Arc; -use crate::get_mc_dir; use azalea_auth::certs::{Certificates, FetchCertificatesError}; use azalea_auth::AccessTokenResponse; use bevy_ecs::component::Component; @@ -91,10 +90,10 @@ impl Account { /// a key for the cache, but it's recommended to use the real email to /// avoid confusion. pub async fn microsoft(email: &str) -> Result { - let minecraft_dir = get_mc_dir::minecraft_dir().unwrap_or_else(|| { + let minecraft_dir = minecraft_folder_path::minecraft_dir().unwrap_or_else(|| { panic!( "No {} environment variable found", - get_mc_dir::home_env_var() + minecraft_folder_path::home_env_var() ) }); let auth_result = azalea_auth::auth( @@ -200,6 +199,13 @@ impl Account { } } } + + /// Get the UUID of this account. This will generate an offline-mode UUID + /// by making a hash with the username if the `uuid` field is None. + pub fn uuid_or_offline(&self) -> Uuid { + self.uuid + .unwrap_or_else(|| azalea_auth::offline::generate_uuid(&self.username)) + } } #[derive(Error, Debug)] diff --git a/azalea-client/src/attack.rs b/azalea-client/src/attack.rs index 287a2ddef..a28f800db 100644 --- a/azalea-client/src/attack.rs +++ b/azalea-client/src/attack.rs @@ -1,4 +1,4 @@ -use azalea_core::game_type::GameMode; +use azalea_core::{game_type::GameMode, tick::GameTick}; use azalea_entity::{ metadata::{ShiftKeyDown, Sprinting}, update_bounding_box, Attributes, Physics, @@ -8,16 +8,13 @@ use azalea_protocol::packets::game::serverbound_interact_packet::{ self, ServerboundInteractPacket, }; use azalea_world::MinecraftEntityId; -use bevy_app::{App, FixedUpdate, Plugin, Update}; +use bevy_app::{App, Plugin, Update}; use bevy_ecs::prelude::*; use derive_more::{Deref, DerefMut}; use crate::{ - interact::SwingArmEvent, - local_player::{LocalGameMode, SendPacketEvent}, - movement::MoveEventsSet, - respawn::perform_respawn, - Client, + interact::SwingArmEvent, local_player::LocalGameMode, movement::MoveEventsSet, + packet_handling::game::SendPacketEvent, respawn::perform_respawn, Client, }; pub struct AttackPlugin; @@ -32,7 +29,7 @@ impl Plugin for AttackPlugin { .after(perform_respawn), ) .add_systems( - FixedUpdate, + GameTick, ( increment_ticks_since_last_attack, update_attack_strength_scale.after(PhysicsSet), diff --git a/azalea-client/src/chat.rs b/azalea-client/src/chat.rs index dbc2843ca..0dad54bbd 100755 --- a/azalea-client/src/chat.rs +++ b/azalea-client/src/chat.rs @@ -2,6 +2,7 @@ use azalea_chat::FormattedText; use azalea_protocol::packets::game::{ + clientbound_disguised_chat_packet::ClientboundDisguisedChatPacket, clientbound_player_chat_packet::ClientboundPlayerChatPacket, clientbound_system_chat_packet::ClientboundSystemChatPacket, serverbound_chat_command_packet::ServerboundChatCommandPacket, @@ -22,7 +23,7 @@ use uuid::Uuid; use crate::{ client::Client, - local_player::{handle_send_packet_event, SendPacketEvent}, + packet_handling::game::{handle_send_packet_event, SendPacketEvent}, }; /// A chat packet, either a system message or a chat message. @@ -30,6 +31,7 @@ use crate::{ pub enum ChatPacket { System(Arc), Player(Arc), + Disguised(Arc), } macro_rules! regex { @@ -45,6 +47,7 @@ impl ChatPacket { match self { ChatPacket::System(p) => p.content.clone(), ChatPacket::Player(p) => p.message(), + ChatPacket::Disguised(p) => p.message(), } } @@ -54,12 +57,6 @@ impl ChatPacket { /// None. pub fn split_sender_and_content(&self) -> (Option, String) { match self { - ChatPacket::Player(p) => ( - // If it's a player chat packet, then the sender and content - // are already split for us. - Some(p.chat_type.name.to_string()), - p.body.content.clone(), - ), ChatPacket::System(p) => { let message = p.content.to_string(); // Overlay messages aren't in chat @@ -74,6 +71,18 @@ impl ChatPacket { (None, message) } + ChatPacket::Player(p) => ( + // If it's a player chat packet, then the sender and content + // are already split for us. + Some(p.chat_type.name.to_string()), + p.body.content.clone(), + ), + ChatPacket::Disguised(p) => ( + // disguised chat packets are basically the same as player chat packets but without + // the chat signing things + Some(p.chat_type.name.to_string()), + p.message.to_string(), + ), } } @@ -91,6 +100,7 @@ impl ChatPacket { match self { ChatPacket::System(_) => None, ChatPacket::Player(m) => Some(m.sender), + ChatPacket::Disguised(_) => None, } } diff --git a/azalea-client/src/chunks.rs b/azalea-client/src/chunks.rs index e91e6b019..072fbd313 100644 --- a/azalea-client/src/chunks.rs +++ b/azalea-client/src/chunks.rs @@ -21,7 +21,7 @@ use tracing::{error, trace}; use crate::{ interact::handle_block_interact_event, inventory::InventorySet, - local_player::{handle_send_packet_event, SendPacketEvent}, + packet_handling::game::{handle_send_packet_event, SendPacketEvent}, respawn::perform_respawn, InstanceHolder, }; diff --git a/azalea-client/src/client.rs b/azalea-client/src/client.rs index fd3189125..911b70a75 100644 --- a/azalea-client/src/client.rs +++ b/azalea-client/src/client.rs @@ -7,12 +7,16 @@ use crate::{ interact::{CurrentSequenceNumber, InteractPlugin}, inventory::{InventoryComponent, InventoryPlugin}, local_player::{ - death_event, handle_send_packet_event, GameProfileComponent, Hunger, InstanceHolder, - PermissionLevel, PlayerAbilities, SendPacketEvent, TabList, + death_event, GameProfileComponent, Hunger, InstanceHolder, PermissionLevel, + PlayerAbilities, TabList, }, mining::{self, MinePlugin}, movement::{LastSentLookDirection, PhysicsState, PlayerMovePlugin}, - packet_handling::PacketHandlerPlugin, + packet_handling::{ + game::{handle_send_packet_event, SendPacketEvent}, + login::{self, LoginSendPacketQueue}, + PacketHandlerPlugin, + }, player::retroactively_add_game_profile_component, raw_connection::RawConnection, respawn::RespawnPlugin, @@ -23,7 +27,7 @@ use crate::{ use azalea_auth::{game_profile::GameProfile, sessionserver::ClientSessionServerError}; use azalea_buf::McBufWritable; use azalea_chat::FormattedText; -use azalea_core::{position::Vec3, resource_location::ResourceLocation}; +use azalea_core::{position::Vec3, resource_location::ResourceLocation, tick::GameTick}; use azalea_entity::{ indexing::{EntityIdIndex, EntityUuidIndex}, metadata::Health, @@ -35,6 +39,7 @@ use azalea_protocol::{ packets::{ configuration::{ serverbound_client_information_packet::ClientInformation, + serverbound_custom_payload_packet::ServerboundCustomPayloadPacket, ClientboundConfigurationPacket, ServerboundConfigurationPacket, }, game::ServerboundGamePacket, @@ -43,7 +48,6 @@ use azalea_protocol::{ ServerboundHandshakePacket, }, login::{ - serverbound_custom_query_answer_packet::ServerboundCustomQueryAnswerPacket, serverbound_hello_packet::ServerboundHelloPacket, serverbound_key_packet::ServerboundKeyPacket, serverbound_login_acknowledged_packet::ServerboundLoginAcknowledgedPacket, @@ -54,7 +58,7 @@ use azalea_protocol::{ resolver, ServerAddress, }; use azalea_world::{Instance, InstanceContainer, InstanceName, PartialInstance}; -use bevy_app::{App, FixedUpdate, Plugin, PluginGroup, PluginGroupBuilder, Update}; +use bevy_app::{App, Plugin, PluginGroup, PluginGroupBuilder, Update}; use bevy_ecs::{ bundle::Bundle, component::Component, @@ -63,11 +67,17 @@ use bevy_ecs::{ system::{ResMut, Resource}, world::World, }; -use bevy_time::{Fixed, Time, TimePlugin}; +use bevy_time::TimePlugin; use derive_more::Deref; use parking_lot::{Mutex, RwLock}; use std::{ - collections::HashMap, fmt::Debug, io, net::SocketAddr, ops::Deref, sync::Arc, time::Duration, + collections::HashMap, + fmt::Debug, + io, + net::SocketAddr, + ops::Deref, + sync::Arc, + time::{Duration, Instant}, }; use thiserror::Error; use tokio::{ @@ -209,8 +219,34 @@ impl Client { proxy: Option, run_schedule_sender: mpsc::UnboundedSender<()>, ) -> Result<(Self, mpsc::UnboundedReceiver), JoinError> { + // check if an entity with our uuid already exists in the ecs and if so then + // just use that + let entity = { + let mut ecs = ecs_lock.lock(); + + let entity_uuid_index = ecs.resource::(); + let uuid = account.uuid_or_offline(); + let entity = if let Some(entity) = entity_uuid_index.get(&account.uuid_or_offline()) { + debug!("Reusing entity {entity:?} for client"); + entity + } else { + let entity = ecs.spawn_empty().id(); + debug!("Created new entity {entity:?} for client"); + // add to the uuid index + let mut entity_uuid_index = ecs.resource_mut::(); + entity_uuid_index.insert(uuid, entity); + entity + }; + + // add the Account to the entity now so plugins can access it earlier + ecs.entity_mut(entity).insert(account.to_owned()); + + entity + }; + let conn = Connection::new(resolved_address, proxy).await?; - let (mut conn, game_profile) = Self::handshake(conn, account, address).await?; + let (mut conn, game_profile) = + Self::handshake(ecs_lock.clone(), entity, conn, account, address).await?; { // quickly send the brand here @@ -218,12 +254,13 @@ impl Client { // they don't have to know :) "vanilla".write_into(&mut brand_data).unwrap(); conn.write( - azalea_protocol::packets::configuration::serverbound_custom_payload_packet::ServerboundCustomPayloadPacket { + ServerboundCustomPayloadPacket { identifier: ResourceLocation::new("brand"), data: brand_data.into(), } .get(), - ).await?; + ) + .await?; } let (read_conn, write_conn) = conn.into_split(); @@ -235,22 +272,6 @@ impl Client { let mut ecs = ecs_lock.lock(); - // check if an entity with our uuid already exists in the ecs and if so then - // just use that - let entity = { - let entity_uuid_index = ecs.resource::(); - if let Some(entity) = entity_uuid_index.get(&game_profile.uuid) { - debug!("Reusing entity {entity:?} for client"); - entity - } else { - let entity = ecs.spawn_empty().id(); - debug!("Created new entity {entity:?} for client"); - // add to the uuid index - let mut entity_uuid_index = ecs.resource_mut::(); - entity_uuid_index.insert(game_profile.uuid, entity); - entity - } - }; // we got the ConfigurationConnection, so the client is now connected :) let client = Client::new( game_profile.clone(), @@ -271,7 +292,6 @@ impl Client { local_player_events: LocalPlayerEvents(tx), game_profile: GameProfileComponent(game_profile), client_information: crate::ClientInformation::default(), - account: account.to_owned(), }, InConfigurationState, )); @@ -285,6 +305,8 @@ impl Client { /// This will also automatically refresh the account's access token if /// it's expired. pub async fn handshake( + ecs_lock: Arc>, + entity: Entity, mut conn: Connection, account: &Account, address: &ServerAddress, @@ -308,6 +330,14 @@ impl Client { .await?; let mut conn = conn.login(); + // this makes it so plugins can send an `SendLoginPacketEvent` event to the ecs + // and we'll send it to the server + let (ecs_packets_tx, mut ecs_packets_rx) = mpsc::unbounded_channel(); + ecs_lock.lock().entity_mut(entity).insert(( + LoginSendPacketQueue { tx: ecs_packets_tx }, + login::IgnoreQueryIds::default(), + )); + // login conn.write( ServerboundHelloPacket { @@ -321,7 +351,20 @@ impl Client { .await?; let (conn, profile) = loop { - let packet = conn.read().await?; + let packet = tokio::select! { + packet = conn.read() => packet?, + Some(packet) = ecs_packets_rx.recv() => { + // write this packet to the server + conn.write(packet).await?; + continue; + } + }; + + ecs_lock.lock().send_event(login::LoginPacketEvent { + entity, + packet: Arc::new(packet.clone()), + }); + match packet { ClientboundLoginPacket::Hello(p) => { debug!("Got encryption request"); @@ -394,18 +437,18 @@ impl Client { } ClientboundLoginPacket::CustomQuery(p) => { debug!("Got custom query {:?}", p); - conn.write( - ServerboundCustomQueryAnswerPacket { - transaction_id: p.transaction_id, - data: None, - } - .get(), - ) - .await?; + // replying to custom query is done in + // packet_handling::login::process_packet_events } } }; + ecs_lock + .lock() + .entity_mut(entity) + .remove::() + .remove::(); + Ok((conn, profile)) } @@ -597,7 +640,6 @@ pub struct LocalPlayerBundle { pub local_player_events: LocalPlayerEvents, pub game_profile: GameProfileComponent, pub client_information: ClientInformation, - pub account: Account, } /// A bundle for the components that are present on a local player that is @@ -632,21 +674,19 @@ pub struct InConfigurationState; pub struct AzaleaPlugin; impl Plugin for AzaleaPlugin { fn build(&self, app: &mut App) { - // Minecraft ticks happen every 50ms - app.insert_resource(Time::::from_duration(Duration::from_millis(50))) - .add_systems( - Update, - ( - // fire the Death event when the player dies. - death_event, - // add GameProfileComponent when we get an AddPlayerEvent - retroactively_add_game_profile_component.after(EntityUpdateSet::Index), - handle_send_packet_event, - ), - ) - .add_event::() - .init_resource::() - .init_resource::(); + app.add_systems( + Update, + ( + // fire the Death event when the player dies. + death_event, + // add GameProfileComponent when we get an AddPlayerEvent + retroactively_add_game_profile_component.after(EntityUpdateSet::Index), + handle_send_packet_event, + ), + ) + .add_event::() + .init_resource::() + .init_resource::(); } } @@ -679,6 +719,7 @@ async fn run_schedule_loop( outer_schedule_label: InternedScheduleLabel, mut run_schedule_receiver: mpsc::UnboundedReceiver<()>, ) { + let mut last_tick: Option = None; loop { // get rid of any queued events while let Ok(()) = run_schedule_receiver.try_recv() {} @@ -687,7 +728,22 @@ async fn run_schedule_loop( run_schedule_receiver.recv().await; let mut ecs = ecs.lock(); + + // if last tick is None or more than 50ms ago, run the GameTick schedule + if last_tick + .map(|last_tick| last_tick.elapsed() > Duration::from_millis(50)) + .unwrap_or(true) + { + if let Some(last_tick) = &mut last_tick { + *last_tick += Duration::from_millis(50); + } else { + last_tick = Some(Instant::now()); + } + ecs.run_schedule(GameTick); + } + ecs.run_schedule(outer_schedule_label); + ecs.clear_trackers(); } } @@ -738,7 +794,7 @@ pub struct TickBroadcastPlugin; impl Plugin for TickBroadcastPlugin { fn build(&self, app: &mut App) { app.insert_resource(TickBroadcast(broadcast::channel(1).0)) - .add_systems(FixedUpdate, send_tick_broadcast); + .add_systems(GameTick, send_tick_broadcast); } } @@ -751,7 +807,7 @@ impl Plugin for AmbiguityLoggerPlugin { ..Default::default() }); }); - app.edit_schedule(FixedUpdate, |schedule| { + app.edit_schedule(GameTick, |schedule| { schedule.set_build_settings(ScheduleBuildSettings { ambiguity_detection: LogLevel::Warn, ..Default::default() diff --git a/azalea-client/src/disconnect.rs b/azalea-client/src/disconnect.rs index ac663a662..ea8479d39 100644 --- a/azalea-client/src/disconnect.rs +++ b/azalea-client/src/disconnect.rs @@ -1,18 +1,20 @@ //! Disconnect a client from the server. +use azalea_chat::FormattedText; +use azalea_entity::LocalEntity; use bevy_app::{App, Plugin, PostUpdate}; use bevy_ecs::{ component::Component, entity::Entity, event::{EventReader, EventWriter}, prelude::Event, - query::Changed, + query::{Changed, With}, schedule::IntoSystemConfigs, system::{Commands, Query}, }; use derive_more::Deref; -use crate::{client::JoinedClientBundle, raw_connection::RawConnection}; +use crate::{client::JoinedClientBundle, events::LocalPlayerEvents, raw_connection::RawConnection}; pub struct DisconnectPlugin; impl Plugin for DisconnectPlugin { @@ -33,7 +35,7 @@ impl Plugin for DisconnectPlugin { #[derive(Event)] pub struct DisconnectEvent { pub entity: Entity, - pub reason: Option, + pub reason: Option, } /// System that removes the [`JoinedClientBundle`] from the entity when it @@ -43,7 +45,11 @@ pub fn remove_components_from_disconnected_players( mut events: EventReader, ) { for DisconnectEvent { entity, .. } in events.read() { - commands.entity(*entity).remove::(); + commands + .entity(*entity) + .remove::() + // swarm detects when this tx gets dropped to fire SwarmEvent::Disconnect + .remove::(); } } @@ -59,13 +65,18 @@ fn update_read_packets_task_running_component( commands.entity(entity).insert(IsConnectionAlive(running)); } } + +#[allow(clippy::type_complexity)] fn disconnect_on_connection_dead( - query: Query<(Entity, &IsConnectionAlive), Changed>, + query: Query<(Entity, &IsConnectionAlive), (Changed, With)>, mut disconnect_events: EventWriter, ) { for (entity, &is_connection_alive) in &query { if !*is_connection_alive { - disconnect_events.send(DisconnectEvent { entity, reason: None }); + disconnect_events.send(DisconnectEvent { + entity, + reason: None, + }); } } } diff --git a/azalea-client/src/events.rs b/azalea-client/src/events.rs index 045f1e7ea..3feed4918 100644 --- a/azalea-client/src/events.rs +++ b/azalea-client/src/events.rs @@ -3,11 +3,13 @@ use std::sync::Arc; +use azalea_chat::FormattedText; +use azalea_core::tick::GameTick; use azalea_protocol::packets::game::{ clientbound_player_combat_kill_packet::ClientboundPlayerCombatKillPacket, ClientboundGamePacket, }; use azalea_world::{InstanceName, MinecraftEntityId}; -use bevy_app::{App, FixedUpdate, Plugin, PreUpdate, Update}; +use bevy_app::{App, Plugin, PreUpdate, Update}; use bevy_ecs::{ component::Component, event::EventReader, @@ -20,6 +22,7 @@ use tokio::sync::mpsc; use crate::{ chat::{ChatPacket, ChatReceivedEvent}, + disconnect::DisconnectEvent, packet_handling::game::{ AddPlayerEvent, DeathEvent, KeepAliveEvent, PacketEvent, RemovePlayerEvent, UpdatePlayerEvent, @@ -94,7 +97,7 @@ pub enum Event { /// A `KeepAlive` packet was sent by the server. KeepAlive(u64), /// The client disconnected from the server. - Disconnect(Option), + Disconnect(Option), } /// A component that contains an event sender for events that are only @@ -126,7 +129,7 @@ impl Plugin for EventPlugin { PreUpdate, init_listener.before(crate::packet_handling::game::process_packet_events), ) - .add_systems(FixedUpdate, tick_listener); + .add_systems(GameTick, tick_listener); } } @@ -230,3 +233,11 @@ fn disconnect_listener(query: Query<&LocalPlayerEvents>, mut events: EventReader } } } + +fn disconnect_listener(query: Query<&LocalPlayerEvents>, mut events: EventReader) { + for event in events.read() { + if let Ok(local_player_events) = query.get(event.entity) { + let _ = local_player_events.send(Event::Disconnect(event.reason.clone())); + } + } +} diff --git a/azalea-client/src/get_mc_dir.rs b/azalea-client/src/get_mc_dir.rs deleted file mode 100755 index df2a81aa3..000000000 --- a/azalea-client/src/get_mc_dir.rs +++ /dev/null @@ -1,61 +0,0 @@ -//! Find out where the user's .minecraft directory is. -//! -//! Used for the auth cache. - -use std::path::PathBuf; - -/// Return the location of the user's .minecraft directory. -/// -/// Windows: `%appdata%\.minecraft`\ -/// Mac: `$HOME/Library/Application Support/minecraft`\ -/// Linux: `$HOME/.minecraft` -/// -/// If the environment variable is not set, this will return `None`. -pub fn minecraft_dir() -> Option { - let env_var = home_env_var(); - let home = std::env::var(env_var).ok()?; - let path = PathBuf::from(home).join(minecraft_dir_relative()); - Some(path) -} - -/// Return the name of the environment variable that's used for the home folder -/// on the user's operating system. -pub fn home_env_var() -> &'static str { - #[cfg(target_os = "windows")] - { - "APPDATA" - } - #[cfg(target_os = "macos")] - { - "HOME" - } - #[cfg(target_os = "linux")] - { - "HOME" - } - #[cfg(not(any(target_os = "windows", target_os = "macos", target_os = "linux")))] - { - "HOME" - } -} - -/// Return the path relative to the home folder where we expect to find the -/// .minecraft directory. -pub fn minecraft_dir_relative() -> &'static str { - #[cfg(target_os = "windows")] - { - ".minecraft" - } - #[cfg(target_os = "macos")] - { - "Library/Application Support/minecraft" - } - #[cfg(target_os = "linux")] - { - ".minecraft" - } - #[cfg(not(any(target_os = "windows", target_os = "macos", target_os = "linux")))] - { - ".minecraft" - } -} diff --git a/azalea-client/src/interact.rs b/azalea-client/src/interact.rs index bdb178272..026e94caa 100644 --- a/azalea-client/src/interact.rs +++ b/azalea-client/src/interact.rs @@ -34,10 +34,9 @@ use tracing::warn; use crate::{ attack::handle_attack_event, inventory::{InventoryComponent, InventorySet}, - local_player::{ - handle_send_packet_event, LocalGameMode, PermissionLevel, PlayerAbilities, SendPacketEvent, - }, + local_player::{LocalGameMode, PermissionLevel, PlayerAbilities}, movement::MoveEventsSet, + packet_handling::game::{handle_send_packet_event, SendPacketEvent}, respawn::perform_respawn, Client, }; @@ -194,7 +193,7 @@ pub fn update_hit_result_component( }; let instance = instance_lock.read(); - let hit_result = pick(look_direction, &eye_position, &instance, pick_range); + let hit_result = pick(look_direction, &eye_position, &instance.chunks, pick_range); if let Some(mut hit_result_ref) = hit_result_ref { **hit_result_ref = hit_result; } else { @@ -213,13 +212,13 @@ pub fn update_hit_result_component( pub fn pick( look_direction: &LookDirection, eye_position: &Vec3, - instance: &Instance, + chunks: &azalea_world::ChunkStorage, pick_range: f64, ) -> BlockHitResult { let view_vector = view_vector(look_direction); let end_position = eye_position + &(view_vector * pick_range); azalea_physics::clip::clip( - &instance.chunks, + chunks, ClipContext { from: *eye_position, to: end_position, diff --git a/azalea-client/src/inventory.rs b/azalea-client/src/inventory.rs index e1ac9dd41..527feae78 100644 --- a/azalea-client/src/inventory.rs +++ b/azalea-client/src/inventory.rs @@ -12,6 +12,7 @@ use azalea_inventory::{ use azalea_protocol::packets::game::{ serverbound_container_click_packet::ServerboundContainerClickPacket, serverbound_container_close_packet::ServerboundContainerClosePacket, + serverbound_set_carried_item_packet::ServerboundSetCarriedItemPacket, }; use azalea_registry::MenuKind; use bevy_app::{App, Plugin, Update}; @@ -26,7 +27,8 @@ use bevy_ecs::{ use tracing::warn; use crate::{ - local_player::{handle_send_packet_event, PlayerAbilities, SendPacketEvent}, + local_player::PlayerAbilities, + packet_handling::game::{handle_send_packet_event, SendPacketEvent}, respawn::perform_respawn, Client, }; @@ -39,9 +41,11 @@ impl Plugin for InventoryPlugin { .add_event::() .add_event::() .add_event::() + .add_event::() .add_systems( Update, ( + handle_set_selected_hotbar_slot_event, handle_menu_opened_event, handle_set_container_content_event, handle_container_click_event, @@ -733,3 +737,33 @@ fn handle_set_container_content_event( } } } + +#[derive(Event)] +pub struct SetSelectedHotbarSlotEvent { + pub entity: Entity, + /// The hotbar slot to select. This should be in the range 0..=8. + pub slot: u8, +} +fn handle_set_selected_hotbar_slot_event( + mut events: EventReader, + mut send_packet_events: EventWriter, + mut query: Query<&mut InventoryComponent>, +) { + for event in events.read() { + let mut inventory = query.get_mut(event.entity).unwrap(); + + // if the slot is already selected, don't send a packet + if inventory.selected_hotbar_slot == event.slot { + continue; + } + + inventory.selected_hotbar_slot = event.slot; + send_packet_events.send(SendPacketEvent { + entity: event.entity, + packet: ServerboundSetCarriedItemPacket { + slot: event.slot as u16, + } + .get(), + }); + } +} diff --git a/azalea-client/src/lib.rs b/azalea-client/src/lib.rs index a391798d1..1eaf46057 100644 --- a/azalea-client/src/lib.rs +++ b/azalea-client/src/lib.rs @@ -16,7 +16,6 @@ mod client; pub mod disconnect; mod entity_query; mod events; -mod get_mc_dir; pub mod interact; pub mod inventory; mod local_player; @@ -35,7 +34,7 @@ pub use client::{ start_ecs_runner, Client, DefaultPlugins, JoinError, JoinedClientBundle, TickBroadcast, }; pub use events::Event; -pub use local_player::{GameProfileComponent, InstanceHolder, SendPacketEvent, TabList}; +pub use local_player::{GameProfileComponent, InstanceHolder, TabList}; pub use movement::{ PhysicsState, SprintDirection, StartSprintEvent, StartWalkEvent, WalkDirection, }; diff --git a/azalea-client/src/local_player.rs b/azalea-client/src/local_player.rs index bebe841ea..3b3ae89b3 100644 --- a/azalea-client/src/local_player.rs +++ b/azalea-client/src/local_player.rs @@ -3,14 +3,9 @@ use std::{collections::HashMap, io, sync::Arc}; use azalea_auth::game_profile::GameProfile; use azalea_core::game_type::GameMode; use azalea_entity::Dead; -use azalea_protocol::packets::game::{ - clientbound_player_abilities_packet::ClientboundPlayerAbilitiesPacket, ServerboundGamePacket, -}; +use azalea_protocol::packets::game::clientbound_player_abilities_packet::ClientboundPlayerAbilitiesPacket; use azalea_world::{Instance, PartialInstance}; -use bevy_ecs::{ - component::Component, entity::Entity, event::EventReader, prelude::*, query::Added, - system::Query, -}; +use bevy_ecs::{component::Component, entity::Entity, prelude::*, query::Added, system::Query}; use derive_more::{Deref, DerefMut}; use parking_lot::RwLock; use thiserror::Error; @@ -20,7 +15,6 @@ use uuid::Uuid; use crate::{ events::{Event as AzaleaEvent, LocalPlayerEvents}, - raw_connection::RawConnection, ClientInformation, PlayerInfo, }; @@ -160,24 +154,3 @@ impl From> for HandlePacketError { HandlePacketError::Poison(e.to_string()) } } - -/// Event for sending a packet to the server. -#[derive(Event)] -pub struct SendPacketEvent { - pub entity: Entity, - pub packet: ServerboundGamePacket, -} - -pub fn handle_send_packet_event( - mut send_packet_events: EventReader, - mut query: Query<&mut RawConnection>, -) { - for event in send_packet_events.read() { - if let Ok(raw_connection) = query.get_mut(event.entity) { - // debug!("Sending packet: {:?}", event.packet); - if let Err(e) = raw_connection.write_packet(event.packet.clone()) { - error!("Failed to send packet: {e}"); - } - } - } -} diff --git a/azalea-client/src/mining.rs b/azalea-client/src/mining.rs index 840351432..d2e66ca84 100644 --- a/azalea-client/src/mining.rs +++ b/azalea-client/src/mining.rs @@ -1,5 +1,5 @@ use azalea_block::{Block, BlockState, FluidState}; -use azalea_core::{direction::Direction, game_type::GameMode, position::BlockPos}; +use azalea_core::{direction::Direction, game_type::GameMode, position::BlockPos, tick::GameTick}; use azalea_entity::{mining::get_mine_progress, FluidOnEyes, Physics}; use azalea_inventory::ItemSlot; use azalea_physics::PhysicsSet; @@ -7,7 +7,7 @@ use azalea_protocol::packets::game::serverbound_player_action_packet::{ self, ServerboundPlayerActionPacket, }; use azalea_world::{InstanceContainer, InstanceName}; -use bevy_app::{App, FixedUpdate, Plugin, Update}; +use bevy_app::{App, Plugin, Update}; use bevy_ecs::prelude::*; use derive_more::{Deref, DerefMut}; @@ -17,8 +17,9 @@ use crate::{ HitResultComponent, SwingArmEvent, }, inventory::{InventoryComponent, InventorySet}, - local_player::{LocalGameMode, PermissionLevel, PlayerAbilities, SendPacketEvent}, + local_player::{LocalGameMode, PermissionLevel, PlayerAbilities}, movement::MoveEventsSet, + packet_handling::game::SendPacketEvent, Client, }; @@ -32,7 +33,7 @@ impl Plugin for MinePlugin { .add_event::() .add_event::() .add_event::() - .add_systems(FixedUpdate, continue_mining_block.before(PhysicsSet)) + .add_systems(GameTick, continue_mining_block.before(PhysicsSet)) .add_systems( Update, ( @@ -199,7 +200,20 @@ fn handle_start_mining_block_with_direction_event( .get_block_state(&event.position) .unwrap_or_default(); *sequence_number += 1; - let block_is_solid = !target_block_state.is_air(); + let target_registry_block = azalea_registry::Block::from(target_block_state); + + // we can't break blocks if they don't have a bounding box + + // TODO: So right now azalea doesn't differenciate between different types of + // bounding boxes. See ClipContext::block_shape for more info. Ideally this + // should just call ClipContext::block_shape and check if it's empty. + let block_is_solid = !target_block_state.is_air() + // this is a hack to make sure we can't break water or lava + && !matches!( + target_registry_block, + azalea_registry::Block::Water | azalea_registry::Block::Lava + ); + if block_is_solid && **mine_progress == 0. { // interact with the block (like note block left click) here attack_block_events.send(AttackBlockEvent { diff --git a/azalea-client/src/movement.rs b/azalea-client/src/movement.rs index b636fbcb7..2f70a40ee 100644 --- a/azalea-client/src/movement.rs +++ b/azalea-client/src/movement.rs @@ -1,6 +1,7 @@ use crate::client::Client; -use crate::local_player::SendPacketEvent; +use crate::packet_handling::game::SendPacketEvent; use azalea_core::position::Vec3; +use azalea_core::tick::GameTick; use azalea_entity::{metadata::Sprinting, Attributes, Jumping}; use azalea_entity::{InLoadedChunk, LastSentPosition, LookDirection, Physics, Position}; use azalea_physics::{ai_step, PhysicsSet}; @@ -12,7 +13,7 @@ use azalea_protocol::packets::game::{ serverbound_move_player_status_only_packet::ServerboundMovePlayerStatusOnlyPacket, }; use azalea_world::{MinecraftEntityId, MoveEntityError}; -use bevy_app::{App, FixedUpdate, Plugin, Update}; +use bevy_app::{App, Plugin, Update}; use bevy_ecs::prelude::{Event, EventWriter}; use bevy_ecs::schedule::SystemSet; use bevy_ecs::{ @@ -54,7 +55,7 @@ impl Plugin for PlayerMovePlugin { .in_set(MoveEventsSet), ) .add_systems( - FixedUpdate, + GameTick, ( (tick_controls, local_player_ai_step) .chain() @@ -266,7 +267,7 @@ fn send_sprinting_if_needed( } } -/// Update the impulse from self.move_direction. The multipler is used for +/// Update the impulse from self.move_direction. The multiplier is used for /// sneaking. pub(crate) fn tick_controls(mut query: Query<&mut PhysicsState>) { for mut physics_state in query.iter_mut() { diff --git a/azalea-client/src/packet_handling/configuration.rs b/azalea-client/src/packet_handling/configuration.rs index 1c476b2c5..df39c4482 100644 --- a/azalea-client/src/packet_handling/configuration.rs +++ b/azalea-client/src/packet_handling/configuration.rs @@ -22,7 +22,7 @@ use crate::packet_handling::game::KeepAliveEvent; use crate::raw_connection::RawConnection; #[derive(Event, Debug, Clone)] -pub struct PacketEvent { +pub struct ConfigurationPacketEvent { /// The client entity that received the packet. pub entity: Entity, /// The packet that was actually received. @@ -31,7 +31,7 @@ pub struct PacketEvent { pub fn send_packet_events( query: Query<(Entity, &RawConnection), With>, - mut packet_events: ResMut>, + mut packet_events: ResMut>, ) { // we manually clear and send the events at the beginning of each update // since otherwise it'd cause issues with events in process_packet_events @@ -51,7 +51,7 @@ pub fn send_packet_events( continue; } }; - packet_events.send(PacketEvent { + packet_events.send(ConfigurationPacketEvent { entity: player_entity, packet, }); @@ -64,9 +64,10 @@ pub fn send_packet_events( pub fn process_packet_events(ecs: &mut World) { let mut events_owned = Vec::new(); - let mut system_state: SystemState> = SystemState::new(ecs); + let mut system_state: SystemState> = + SystemState::new(ecs); let mut events = system_state.get_mut(ecs); - for PacketEvent { + for ConfigurationPacketEvent { entity: player_entity, packet, } in events.read() @@ -125,7 +126,7 @@ pub fn process_packet_events(ecs: &mut World) { let mut disconnect_events = system_state.get_mut(ecs); disconnect_events.send(DisconnectEvent { entity: player_entity, - reason: Some(p.reason.to_ansi()), + reason: Some(p.reason.clone()), }); } ClientboundConfigurationPacket::FinishConfiguration(p) => { @@ -194,7 +195,7 @@ pub fn process_packet_events(ecs: &mut World) { .write_packet(ServerboundPongPacket { id: p.id }.get()) .unwrap(); } - ClientboundConfigurationPacket::ResourcePack(p) => { + ClientboundConfigurationPacket::ResourcePackPush(p) => { debug!("Got resource pack packet {p:?}"); let mut system_state: SystemState> = SystemState::new(ecs); @@ -203,9 +204,15 @@ pub fn process_packet_events(ecs: &mut World) { // always accept resource pack raw_connection.write_packet( - ServerboundResourcePackPacket { action: azalea_protocol::packets::configuration::serverbound_resource_pack_packet::Action::Accepted }.get() + ServerboundResourcePackPacket { + id: p.id, + action: azalea_protocol::packets::configuration::serverbound_resource_pack_packet::Action::Accepted + }.get() ).unwrap(); } + ClientboundConfigurationPacket::ResourcePackPop(_) => { + // we can ignore this + } ClientboundConfigurationPacket::UpdateEnabledFeatures(p) => { debug!("Got update enabled features packet {p:?}"); } diff --git a/azalea-client/src/packet_handling/game.rs b/azalea-client/src/packet_handling/game.rs index db8da69af..ec14f4004 100644 --- a/azalea-client/src/packet_handling/game.rs +++ b/azalea-client/src/packet_handling/game.rs @@ -20,9 +20,11 @@ use azalea_protocol::{ packets::game::{ clientbound_player_combat_kill_packet::ClientboundPlayerCombatKillPacket, serverbound_accept_teleportation_packet::ServerboundAcceptTeleportationPacket, + serverbound_configuration_acknowledged_packet::ServerboundConfigurationAcknowledgedPacket, serverbound_keep_alive_packet::ServerboundKeepAlivePacket, serverbound_move_player_pos_rot_packet::ServerboundMovePlayerPosRotPacket, serverbound_pong_packet::ServerboundPongPacket, ClientboundGamePacket, + ServerboundGamePacket, }, read::deserialize_packet, }; @@ -30,6 +32,7 @@ use azalea_world::{Instance, InstanceContainer, InstanceName, MinecraftEntityId, use bevy_ecs::{prelude::*, system::SystemState}; use parking_lot::RwLock; use tracing::{debug, error, trace, warn}; +use uuid::Uuid; use crate::{ chat::{ChatPacket, ChatReceivedEvent}, @@ -40,8 +43,7 @@ use crate::{ SetContainerContentEvent, }, local_player::{ - GameProfileComponent, Hunger, InstanceHolder, LocalGameMode, PlayerAbilities, - SendPacketEvent, TabList, + GameProfileComponent, Hunger, InstanceHolder, LocalGameMode, PlayerAbilities, TabList, }, movement::{KnockbackEvent, KnockbackType}, raw_connection::RawConnection, @@ -123,6 +125,9 @@ pub struct KeepAliveEvent { #[derive(Event, Debug, Clone)] pub struct ResourcePackEvent { pub entity: Entity, + /// The random ID for this request to download the resource pack. The packet + /// for replying to a resource pack push must contain the same ID. + pub id: Uuid, pub url: String, pub hash: String, pub required: bool, @@ -401,7 +406,11 @@ pub fn process_packet_events(ecs: &mut World) { let mut disconnect_events = system_state.get_mut(ecs); disconnect_events.send(DisconnectEvent { entity: player_entity, +<<<<<<< HEAD reason: Some(p.reason.to_ansi()), +======= + reason: Some(p.reason.clone()), +>>>>>>> socks5 }); } ClientboundGamePacket::UpdateRecipes(_p) => { @@ -737,9 +746,9 @@ pub fn process_packet_events(ecs: &mut World) { entity.world_scope(|world| { let mut commands_system_state = SystemState::::new(world); let mut commands = commands_system_state.get_mut(world); - let mut entity_comands = commands.entity(entity_id); + let mut entity_commands = commands.entity(entity_id); if let Err(e) = - apply_metadata(&mut entity_comands, *entity_kind, packed_items) + apply_metadata(&mut entity_commands, *entity_kind, packed_items) { warn!("{e}"); } @@ -997,6 +1006,18 @@ pub fn process_packet_events(ecs: &mut World) { packet: ChatPacket::System(Arc::new(p.clone())), }); } + ClientboundGamePacket::DisguisedChat(p) => { + debug!("Got disguised chat packet {p:?}"); + + let mut system_state: SystemState> = + SystemState::new(ecs); + let mut chat_events = system_state.get_mut(ecs); + + chat_events.send(ChatReceivedEvent { + entity: player_entity, + packet: ChatPacket::Disguised(Arc::new(p.clone())), + }); + } ClientboundGamePacket::Sound(_p) => { // debug!("Got sound packet {p:?}"); } @@ -1253,7 +1274,7 @@ pub fn process_packet_events(ecs: &mut World) { } ClientboundGamePacket::PlayerLookAt(_) => {} ClientboundGamePacket::RemoveMobEffect(_) => {} - ClientboundGamePacket::ResourcePack(p) => { + ClientboundGamePacket::ResourcePackPush(p) => { debug!("Got resource pack packet {p:?}"); let mut system_state: SystemState> = @@ -1262,6 +1283,7 @@ pub fn process_packet_events(ecs: &mut World) { resource_pack_events.send(ResourcePackEvent { entity: player_entity, + id: p.id, url: p.url.to_owned(), hash: p.hash.to_owned(), required: p.required, @@ -1270,6 +1292,7 @@ pub fn process_packet_events(ecs: &mut World) { system_state.apply(ecs); } + ClientboundGamePacket::ResourcePackPop(_) => {} ClientboundGamePacket::Respawn(p) => { debug!("Got respawn packet {p:?}"); @@ -1359,6 +1382,24 @@ pub fn process_packet_events(ecs: &mut World) { system_state.apply(ecs); } + ClientboundGamePacket::StartConfiguration(_) => { + let mut system_state: SystemState<(Commands, EventWriter)> = + SystemState::new(ecs); + let (mut commands, mut packet_events) = system_state.get_mut(ecs); + + packet_events.send(SendPacketEvent { + entity: player_entity, + packet: ServerboundConfigurationAcknowledgedPacket {}.get(), + }); + + commands + .entity(player_entity) + .insert(crate::client::InConfigurationState) + .remove::(); + + system_state.apply(ecs); + } + ClientboundGamePacket::SelectAdvancementsTab(_) => {} ClientboundGamePacket::SetActionBarText(_) => {} ClientboundGamePacket::SetBorderCenter(_) => {} @@ -1382,12 +1423,35 @@ pub fn process_packet_events(ecs: &mut World) { ClientboundGamePacket::TabList(_) => {} ClientboundGamePacket::TagQuery(_) => {} ClientboundGamePacket::TakeItemEntity(_) => {} - ClientboundGamePacket::DisguisedChat(_) => {} ClientboundGamePacket::Bundle(_) => {} ClientboundGamePacket::DamageEvent(_) => {} ClientboundGamePacket::HurtAnimation(_) => {} - ClientboundGamePacket::StartConfiguration(_) => todo!(), + ClientboundGamePacket::TickingState(_) => {} + ClientboundGamePacket::TickingStep(_) => {} + + ClientboundGamePacket::ResetScore(_) => {} + } + } +} + +/// An event for sending a packet to the server while we're in the `game` state. +#[derive(Event)] +pub struct SendPacketEvent { + pub entity: Entity, + pub packet: ServerboundGamePacket, +} + +pub fn handle_send_packet_event( + mut send_packet_events: EventReader, + mut query: Query<&mut RawConnection>, +) { + for event in send_packet_events.read() { + if let Ok(raw_connection) = query.get_mut(event.entity) { + // debug!("Sending packet: {:?}", event.packet); + if let Err(e) = raw_connection.write_packet(event.packet.clone()) { + error!("Failed to send packet: {e}"); + } } } } diff --git a/azalea-client/src/packet_handling/login.rs b/azalea-client/src/packet_handling/login.rs new file mode 100644 index 000000000..7d71b440a --- /dev/null +++ b/azalea-client/src/packet_handling/login.rs @@ -0,0 +1,101 @@ +// login packets aren't actually handled here because compression/encryption +// would make packet handling a lot messier + +use std::{collections::HashSet, sync::Arc}; + +use azalea_protocol::packets::login::{ + serverbound_custom_query_answer_packet::ServerboundCustomQueryAnswerPacket, + ClientboundLoginPacket, ServerboundLoginPacket, +}; +use bevy_ecs::{prelude::*, system::SystemState}; +use derive_more::{Deref, DerefMut}; +use tokio::sync::mpsc; +use tracing::error; + +// this struct is defined here anyways though so it's consistent with the other +// ones + +/// An event that's sent when we receive a login packet from the server. Note +/// that if you want to handle this in a system, you must add +/// `.before(azalea::packet_handling::login::process_packet_events)` to it +/// because that system clears the events. +#[derive(Event, Debug, Clone)] +pub struct LoginPacketEvent { + /// The client entity that received the packet. + pub entity: Entity, + /// The packet that was actually received. + pub packet: Arc, +} + +/// Event for sending a login packet to the server. +#[derive(Event)] +pub struct SendLoginPacketEvent { + pub entity: Entity, + pub packet: ServerboundLoginPacket, +} + +#[derive(Component)] +pub struct LoginSendPacketQueue { + pub tx: mpsc::UnboundedSender, +} + +pub fn handle_send_packet_event( + mut send_packet_events: EventReader, + mut query: Query<&mut LoginSendPacketQueue>, +) { + for event in send_packet_events.read() { + if let Ok(queue) = query.get_mut(event.entity) { + let _ = queue.tx.send(event.packet.clone()); + } else { + error!("Sent SendPacketEvent for entity that doesn't have a LoginSendPacketQueue"); + } + } +} + +/// Plugins can add to this set if they want to handle a custom query packet +/// themselves. This component removed after the login state ends. +#[derive(Component, Default, Debug, Deref, DerefMut)] +pub struct IgnoreQueryIds(HashSet); + +pub fn process_packet_events(ecs: &mut World) { + let mut events_owned = Vec::new(); + let mut system_state: SystemState>> = SystemState::new(ecs); + let mut events = system_state.get_mut(ecs); + for LoginPacketEvent { + entity: player_entity, + packet, + } in events.drain() + { + // we do this so `ecs` isn't borrowed for the whole loop + events_owned.push((player_entity, packet)); + } + for (player_entity, packet) in events_owned { + #[allow(clippy::single_match)] + match packet.as_ref() { + ClientboundLoginPacket::CustomQuery(p) => { + let mut system_state: SystemState<( + EventWriter, + Query<&IgnoreQueryIds>, + )> = SystemState::new(ecs); + let (mut send_packet_events, query) = system_state.get_mut(ecs); + + let ignore_query_ids = query.get(player_entity).ok().map(|x| x.0.clone()); + if let Some(ignore_query_ids) = ignore_query_ids { + if ignore_query_ids.contains(&p.transaction_id) { + continue; + } + } + + send_packet_events.send(SendLoginPacketEvent { + entity: player_entity, + packet: ServerboundCustomQueryAnswerPacket { + transaction_id: p.transaction_id, + data: None, + } + .get(), + }); + } + _ => {} + } + } +} diff --git a/azalea-client/src/packet_handling/mod.rs b/azalea-client/src/packet_handling/mod.rs index 35bdfc043..9823035d3 100644 --- a/azalea-client/src/packet_handling/mod.rs +++ b/azalea-client/src/packet_handling/mod.rs @@ -4,13 +4,17 @@ use bevy_ecs::prelude::*; use crate::{chat::ChatReceivedEvent, events::death_listener}; -use self::game::{ - AddPlayerEvent, DeathEvent, InstanceLoadedEvent, KeepAliveEvent, RemovePlayerEvent, - ResourcePackEvent, UpdatePlayerEvent, +use self::{ + game::{ + AddPlayerEvent, DeathEvent, InstanceLoadedEvent, KeepAliveEvent, RemovePlayerEvent, + ResourcePackEvent, UpdatePlayerEvent, + }, + login::{LoginPacketEvent, SendLoginPacketEvent}, }; pub mod configuration; pub mod game; +pub mod login; pub struct PacketHandlerPlugin; @@ -37,16 +41,18 @@ impl Plugin for PacketHandlerPlugin { .add_systems( PreUpdate, ( - game::process_packet_events, + game::process_packet_events + // we want to index and deindex right after + .before(EntityUpdateSet::Deindex), configuration::process_packet_events, - ) - // we want to index and deindex right after - .before(EntityUpdateSet::Deindex), + login::handle_send_packet_event, + login::process_packet_events, + ), ) .add_systems(Update, death_event_on_0_health.before(death_listener)) // we do this instead of add_event so we can handle the events ourselves .init_resource::>() - .init_resource::>() + .init_resource::>() .add_event::() .add_event::() .add_event::() @@ -54,6 +60,8 @@ impl Plugin for PacketHandlerPlugin { .add_event::() .add_event::() .add_event::() - .add_event::(); + .add_event::() + .add_event::() + .add_event::(); } } diff --git a/azalea-client/src/raw_connection.rs b/azalea-client/src/raw_connection.rs index e2daaba2a..bcb1ada04 100644 --- a/azalea-client/src/raw_connection.rs +++ b/azalea-client/src/raw_connection.rs @@ -133,7 +133,10 @@ impl RawConnectionReader { Ok(raw_packet) => { self.incoming_packet_queue.lock().push(raw_packet); // tell the client to run all the systems - self.run_schedule_sender.send(()).unwrap(); + if self.run_schedule_sender.send(()).is_err() { + // the client was dropped + break; + } } Err(error) => { if !matches!(*error, ReadPacketError::ConnectionClosed) { diff --git a/azalea-client/src/respawn.rs b/azalea-client/src/respawn.rs index cd9b434e8..150b35913 100644 --- a/azalea-client/src/respawn.rs +++ b/azalea-client/src/respawn.rs @@ -4,7 +4,7 @@ use azalea_protocol::packets::game::serverbound_client_command_packet::{ use bevy_app::{App, Plugin, Update}; use bevy_ecs::prelude::*; -use crate::local_player::{handle_send_packet_event, SendPacketEvent}; +use crate::packet_handling::game::{handle_send_packet_event, SendPacketEvent}; /// Tell the server that we're respawning. #[derive(Event, Debug, Clone)] diff --git a/azalea-core/Cargo.toml b/azalea-core/Cargo.toml index 957eaaf5f..2b862d3ce 100644 --- a/azalea-core/Cargo.toml +++ b/azalea-core/Cargo.toml @@ -4,20 +4,20 @@ edition = "2021" license = "MIT" name = "azalea-core" repository = "https://github.com/azalea-rs/azalea/tree/main/azalea-core" -version = "0.8.0" +version = "0.9.0" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -azalea-buf = { path = "../azalea-buf", version = "0.8.0" } -azalea-inventory = { version = "0.8.0", path = "../azalea-inventory" } -simdnbt = { version = "0.2.1" } -azalea-registry = { path = "../azalea-registry", version = "0.8.0" } -bevy_ecs = { version = "0.12.0", default-features = false, optional = true } +simdnbt = { version = "0.3", git = "https://github.com/azalea-rs/simdnbt" } +azalea-buf = { path = "../azalea-buf", version = "0.9.0" } +azalea-inventory = { version = "0.9.0", path = "../azalea-inventory" } +azalea-registry = { path = "../azalea-registry", version = "0.9.0" } +bevy_ecs = { version = "0.12.1", default-features = false, optional = true } nohash-hasher = "0.2.0" num-traits = "0.2.17" serde = { version = "^1.0", optional = true } -uuid = "^1.5.0" +uuid = "^1.6.1" serde_json = "^1.0.108" tracing = "0.1.40" diff --git a/azalea-core/src/game_type.rs b/azalea-core/src/game_type.rs index 8a17ef498..99f0c0fea 100644 --- a/azalea-core/src/game_type.rs +++ b/azalea-core/src/game_type.rs @@ -1,5 +1,6 @@ -use azalea_buf::{BufReadError, McBufReadable, McBufWritable}; +use azalea_buf::{BufReadError, McBufReadable, McBufVarReadable, McBufWritable}; use std::io::{Cursor, Write}; +use tracing::warn; /// A Minecraft gamemode, like survival or creative. #[derive(Hash, Copy, Clone, Debug, Default, Eq, PartialEq)] @@ -93,8 +94,15 @@ impl GameMode { impl McBufReadable for GameMode { fn read_from(buf: &mut Cursor<&[u8]>) -> Result { - let id = u8::read_from(buf)?; - GameMode::from_id(id).ok_or(BufReadError::UnexpectedEnumVariant { id: id as i32 }) + let id = u32::var_read_from(buf)?; + let id = id.try_into().unwrap_or_else(|_| { + warn!("Unknown game mode id {id}, defaulting to survival"); + 0 + }); + Ok(GameMode::from_id(id).unwrap_or_else(|| { + warn!("Unknown game mode id {id}, defaulting to survival"); + GameMode::Survival + })) } } diff --git a/azalea-core/src/lib.rs b/azalea-core/src/lib.rs index cb67da586..5471120b5 100755 --- a/azalea-core/src/lib.rs +++ b/azalea-core/src/lib.rs @@ -12,8 +12,11 @@ pub mod difficulty; pub mod direction; pub mod game_type; pub mod math; +pub mod objectives; pub mod particle; pub mod position; pub mod registry_holder; pub mod resource_location; +#[cfg(feature = "bevy_ecs")] +pub mod tick; pub mod tier; diff --git a/azalea-core/src/math.rs b/azalea-core/src/math.rs index 83e6020eb..aa9d88c8d 100644 --- a/azalea-core/src/math.rs +++ b/azalea-core/src/math.rs @@ -13,15 +13,15 @@ pub static SIN: LazyLock<[f32; 65536]> = LazyLock::new(|| { /// A sine function that uses a lookup table. pub fn sin(x: f32) -> f32 { let x = x * 10430.378; - let x = x as usize; - SIN[x & 65535] + let x = x as i32 as usize & 65535; + SIN[x] } /// A cosine function that uses a lookup table. pub fn cos(x: f32) -> f32 { let x = x * 10430.378 + 16384.0; - let x = x as usize; - SIN[x & 65535] + let x = x as i32 as usize & 65535; + SIN[x] } // TODO: make this generic @@ -83,4 +83,24 @@ mod tests { assert_eq!(gcd(12, 7), 1); assert_eq!(gcd(7, 12), 1); } + + #[test] + fn test_sin() { + const PI: f32 = std::f32::consts::PI; + // check that they're close enough + fn assert_sin_eq_enough(number: f32) { + let a = sin(number); + let b = f32::sin(number); + assert!((a - b).abs() < 0.01, "sin({number}) failed, {a} != {b}"); + } + assert_sin_eq_enough(0.0); + assert_sin_eq_enough(PI / 2.0); + assert_sin_eq_enough(PI); + assert_sin_eq_enough(PI * 2.0); + assert_sin_eq_enough(PI * 3.0 / 2.0); + assert_sin_eq_enough(-PI / 2.0); + assert_sin_eq_enough(-PI); + assert_sin_eq_enough(-PI * 2.0); + assert_sin_eq_enough(-PI * 3.0 / 2.0); + } } diff --git a/azalea-core/src/objectives.rs b/azalea-core/src/objectives.rs new file mode 100644 index 000000000..dd1534f20 --- /dev/null +++ b/azalea-core/src/objectives.rs @@ -0,0 +1,33 @@ +use std::{ + fmt::{self, Display, Formatter}, + str::FromStr, +}; + +use azalea_buf::McBuf; + +#[derive(Clone, Copy, Debug, McBuf)] +pub enum ObjectiveCriteria { + Integer, + Hearts, +} + +impl Display for ObjectiveCriteria { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + match self { + ObjectiveCriteria::Integer => write!(f, "integer"), + ObjectiveCriteria::Hearts => write!(f, "hearts"), + } + } +} + +impl FromStr for ObjectiveCriteria { + type Err = (); + + fn from_str(s: &str) -> Result { + match s { + "integer" => Ok(ObjectiveCriteria::Integer), + "hearts" => Ok(ObjectiveCriteria::Hearts), + _ => Err(()), + } + } +} diff --git a/azalea-core/src/particle.rs b/azalea-core/src/particle.rs index 8e48255f7..4137d8677 100755 --- a/azalea-core/src/particle.rs +++ b/azalea-core/src/particle.rs @@ -1,6 +1,7 @@ use crate::position::BlockPos; use azalea_buf::McBuf; use azalea_inventory::ItemSlot; +use azalea_registry::ParticleKind; #[cfg_attr(feature = "bevy_ecs", derive(bevy_ecs::component::Component))] #[derive(Debug, Clone, McBuf, Default)] @@ -37,10 +38,17 @@ pub enum ParticleData { EntityEffect, ExplosionEmitter, Explosion, + Gust, + GustEmitter, + SonicBoom, FallingDust(BlockParticle), Firework, Fishing, Flame, + CherryLeaves, + SculkSoul, + SculkCharge(SculkChargeParticle), + SculkChargePop, SoulFireFlame, Soul, Flash, @@ -60,6 +68,7 @@ pub enum ParticleData { Portal, Rain, Smoke, + WhiteSmoke, Sneeze, Spit, SquidInk, @@ -101,14 +110,133 @@ pub enum ParticleData { WaxOff, ElectricSpark, Scrape, + Shriek(ShriekParticle), + EggCrack, + DustPlume, + GustDust, + TrialSpawnerDetection, } -#[derive(Debug, Clone, McBuf)] +impl From for ParticleData { + /// Convert a particle kind into particle data. If the particle has data + /// attached (like block particles), then it's set to the default. + fn from(kind: ParticleKind) -> Self { + // this is mostly just here so it fails to compile when a new particle is added + // to ParticleKind, since ParticleData has to be updated manually + match kind { + ParticleKind::AmbientEntityEffect => Self::AmbientEntityEffect, + ParticleKind::AngryVillager => Self::AngryVillager, + ParticleKind::Block => Self::Block(BlockParticle::default()), + ParticleKind::BlockMarker => Self::BlockMarker(BlockParticle::default()), + ParticleKind::Bubble => Self::Bubble, + ParticleKind::Cloud => Self::Cloud, + ParticleKind::Crit => Self::Crit, + ParticleKind::DamageIndicator => Self::DamageIndicator, + ParticleKind::DragonBreath => Self::DragonBreath, + ParticleKind::DrippingLava => Self::DrippingLava, + ParticleKind::FallingLava => Self::FallingLava, + ParticleKind::LandingLava => Self::LandingLava, + ParticleKind::DrippingWater => Self::DrippingWater, + ParticleKind::FallingWater => Self::FallingWater, + ParticleKind::Dust => Self::Dust(DustParticle::default()), + ParticleKind::DustColorTransition => { + Self::DustColorTransition(DustColorTransitionParticle::default()) + } + ParticleKind::Effect => Self::Effect, + ParticleKind::ElderGuardian => Self::ElderGuardian, + ParticleKind::EnchantedHit => Self::EnchantedHit, + ParticleKind::Enchant => Self::Enchant, + ParticleKind::EndRod => Self::EndRod, + ParticleKind::EntityEffect => Self::EntityEffect, + ParticleKind::ExplosionEmitter => Self::ExplosionEmitter, + ParticleKind::Explosion => Self::Explosion, + ParticleKind::Gust => Self::Gust, + ParticleKind::GustEmitter => Self::GustEmitter, + ParticleKind::SonicBoom => Self::SonicBoom, + ParticleKind::FallingDust => Self::FallingDust(BlockParticle::default()), + ParticleKind::Firework => Self::Firework, + ParticleKind::Fishing => Self::Fishing, + ParticleKind::Flame => Self::Flame, + ParticleKind::CherryLeaves => Self::CherryLeaves, + ParticleKind::SculkSoul => Self::SculkSoul, + ParticleKind::SculkCharge => Self::SculkCharge(SculkChargeParticle::default()), + ParticleKind::SculkChargePop => Self::SculkChargePop, + ParticleKind::SoulFireFlame => Self::SoulFireFlame, + ParticleKind::Soul => Self::Soul, + ParticleKind::Flash => Self::Flash, + ParticleKind::HappyVillager => Self::HappyVillager, + ParticleKind::Composter => Self::Composter, + ParticleKind::Heart => Self::Heart, + ParticleKind::InstantEffect => Self::InstantEffect, + ParticleKind::Item => Self::Item(ItemParticle::default()), + ParticleKind::Vibration => Self::Vibration(VibrationParticle::default()), + ParticleKind::ItemSlime => Self::ItemSlime, + ParticleKind::ItemSnowball => Self::ItemSnowball, + ParticleKind::LargeSmoke => Self::LargeSmoke, + ParticleKind::Lava => Self::Lava, + ParticleKind::Mycelium => Self::Mycelium, + ParticleKind::Note => Self::Note, + ParticleKind::Poof => Self::Poof, + ParticleKind::Portal => Self::Portal, + ParticleKind::Rain => Self::Rain, + ParticleKind::Smoke => Self::Smoke, + ParticleKind::WhiteSmoke => Self::WhiteSmoke, + ParticleKind::Sneeze => Self::Sneeze, + ParticleKind::Spit => Self::Spit, + ParticleKind::SquidInk => Self::SquidInk, + ParticleKind::SweepAttack => Self::SweepAttack, + ParticleKind::TotemOfUndying => Self::TotemOfUndying, + ParticleKind::Underwater => Self::Underwater, + ParticleKind::Splash => Self::Splash, + ParticleKind::Witch => Self::Witch, + ParticleKind::BubblePop => Self::BubblePop, + ParticleKind::CurrentDown => Self::CurrentDown, + ParticleKind::BubbleColumnUp => Self::BubbleColumnUp, + ParticleKind::Nautilus => Self::Nautilus, + ParticleKind::Dolphin => Self::Dolphin, + ParticleKind::CampfireCosySmoke => Self::CampfireCozySmoke, + ParticleKind::CampfireSignalSmoke => Self::CampfireSignalSmoke, + ParticleKind::DrippingHoney => Self::DrippingHoney, + ParticleKind::FallingHoney => Self::FallingHoney, + ParticleKind::LandingHoney => Self::LandingHoney, + ParticleKind::FallingNectar => Self::FallingNectar, + ParticleKind::FallingSporeBlossom => Self::FallingSporeBlossom, + ParticleKind::Ash => Self::Ash, + ParticleKind::CrimsonSpore => Self::CrimsonSpore, + ParticleKind::WarpedSpore => Self::WarpedSpore, + ParticleKind::SporeBlossomAir => Self::SporeBlossomAir, + ParticleKind::DrippingObsidianTear => Self::DrippingObsidianTear, + ParticleKind::FallingObsidianTear => Self::FallingObsidianTear, + ParticleKind::LandingObsidianTear => Self::LandingObsidianTear, + ParticleKind::ReversePortal => Self::ReversePortal, + ParticleKind::WhiteAsh => Self::WhiteAsh, + ParticleKind::SmallFlame => Self::SmallFlame, + ParticleKind::Snowflake => Self::Snowflake, + ParticleKind::DrippingDripstoneLava => Self::DrippingDripstoneLava, + ParticleKind::FallingDripstoneLava => Self::FallingDripstoneLava, + ParticleKind::DrippingDripstoneWater => Self::DrippingDripstoneWater, + ParticleKind::FallingDripstoneWater => Self::FallingDripstoneWater, + ParticleKind::GlowSquidInk => Self::GlowSquidInk, + ParticleKind::Glow => Self::Glow, + ParticleKind::WaxOn => Self::WaxOn, + ParticleKind::WaxOff => Self::WaxOff, + ParticleKind::ElectricSpark => Self::ElectricSpark, + ParticleKind::Scrape => Self::Scrape, + ParticleKind::Shriek => Self::Shriek(ShriekParticle::default()), + ParticleKind::EggCrack => Self::EggCrack, + ParticleKind::DustPlume => Self::DustPlume, + ParticleKind::GustDust => Self::GustDust, + ParticleKind::TrialSpawnerDetection => Self::TrialSpawnerDetection, + } + } +} + +#[derive(Debug, Clone, McBuf, Default)] pub struct BlockParticle { #[var] pub block_state: i32, } -#[derive(Debug, Clone, McBuf)] +#[derive(Debug, Clone, McBuf, Default)] pub struct DustParticle { /// Red value, 0-1 pub red: f32, @@ -120,7 +248,7 @@ pub struct DustParticle { pub scale: f32, } -#[derive(Debug, Clone, McBuf)] +#[derive(Debug, Clone, McBuf, Default)] pub struct DustColorTransitionParticle { /// Red value, 0-1 pub from_red: f32, @@ -138,12 +266,12 @@ pub struct DustColorTransitionParticle { pub to_blue: f32, } -#[derive(Debug, Clone, McBuf)] +#[derive(Debug, Clone, McBuf, Default)] pub struct ItemParticle { pub item: ItemSlot, } -#[derive(Debug, Clone, McBuf)] +#[derive(Debug, Clone, McBuf, Default)] pub struct VibrationParticle { pub origin: BlockPos, pub position_type: String, @@ -153,3 +281,14 @@ pub struct VibrationParticle { #[var] pub ticks: u32, } + +#[derive(Debug, Clone, McBuf, Default)] +pub struct SculkChargeParticle { + pub roll: f32, +} + +#[derive(Debug, Clone, McBuf, Default)] +pub struct ShriekParticle { + #[var] + pub delay: i32, // The time in ticks before the particle is displayed +} diff --git a/azalea-core/src/position.rs b/azalea-core/src/position.rs index 196a70d5c..e98640357 100755 --- a/azalea-core/src/position.rs +++ b/azalea-core/src/position.rs @@ -5,6 +5,7 @@ use azalea_buf::{BufReadError, McBuf, McBufReadable, McBufWritable}; use std::{ + fmt, hash::Hash, io::{Cursor, Write}, ops::{Add, AddAssign, Mul, Rem, Sub}, @@ -65,6 +66,43 @@ macro_rules! vec3_impl { } } + /// Return a new instance of this position with the z coordinate subtracted + /// by the given number. + pub fn north(&self, z: $type) -> Self { + Self { + x: self.x, + y: self.y, + z: self.z - z, + } + } + /// Return a new instance of this position with the x coordinate increased + /// by the given number. + pub fn east(&self, x: $type) -> Self { + Self { + x: self.x + x, + y: self.y, + z: self.z, + } + } + /// Return a new instance of this position with the z coordinate increased + /// by the given number. + pub fn south(&self, z: $type) -> Self { + Self { + x: self.x, + y: self.y, + z: self.z + z, + } + } + /// Return a new instance of this position with the x coordinate subtracted + /// by the given number. + pub fn west(&self, x: $type) -> Self { + Self { + x: self.x - x, + y: self.y, + z: self.z, + } + } + #[inline] pub fn dot(&self, other: Self) -> $type { self.x * other.x + self.y * other.y + self.z * other.z @@ -213,7 +251,7 @@ impl BlockPos { /// Chunk coordinates are used to represent where a chunk is in the world. You /// can convert the x and z to block coordinates by multiplying them by 16. -#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, McBuf)] +#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] pub struct ChunkPos { pub x: i32, pub z: i32, @@ -233,12 +271,38 @@ impl Add for ChunkPos { } } } + +// reading ChunkPos is done in reverse, so z first and then x +// ........ +// mojang why impl From for u64 { #[inline] fn from(pos: ChunkPos) -> Self { - ((pos.x as u64) << 32) | (pos.z as u64) + (pos.x as u64) | ((pos.z as u64) << 32) + } +} +impl From for ChunkPos { + #[inline] + fn from(pos: u64) -> Self { + ChunkPos { + x: (pos) as i32, + z: (pos >> 32) as i32, + } + } +} +impl McBufReadable for ChunkPos { + fn read_from(buf: &mut Cursor<&[u8]>) -> Result { + let long = u64::read_from(buf)?; + Ok(ChunkPos::from(long)) + } +} +impl McBufWritable for ChunkPos { + fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { + u64::from(*self).write_into(buf)?; + Ok(()) } } + impl Hash for ChunkPos { #[inline] fn hash(&self, state: &mut H) { @@ -475,6 +539,13 @@ impl From for ChunkBlockPos { } } +impl fmt::Display for BlockPos { + /// Display a block position as `x y z`. + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{} {} {}", self.x, self.y, self.z) + } +} + const PACKED_X_LENGTH: u64 = 1 + 25; // minecraft does something a bit more complicated to get this 25 const PACKED_Z_LENGTH: u64 = PACKED_X_LENGTH; const PACKED_Y_LENGTH: u64 = 64 - PACKED_X_LENGTH - PACKED_Z_LENGTH; @@ -600,4 +671,13 @@ mod tests { ChunkSectionBlockPos::new(0, 4, 0) ); } + + #[test] + fn test_read_chunk_pos_from() { + let mut buf = Vec::new(); + ChunkPos::new(2, -1).write_into(&mut buf).unwrap(); + let mut buf = Cursor::new(&buf[..]); + let chunk_pos = ChunkPos::from(u64::read_from(&mut buf).unwrap()); + assert_eq!(chunk_pos, ChunkPos::new(2, -1)); + } } diff --git a/azalea-core/src/registry_holder.rs b/azalea-core/src/registry_holder.rs index 6d58f77a3..1c21c890c 100644 --- a/azalea-core/src/registry_holder.rs +++ b/azalea-core/src/registry_holder.rs @@ -28,7 +28,15 @@ impl RegistryHolder { &self, name: &ResourceLocation, ) -> Option> { - self.map.get(name).map(|nbt| T::from_compound(nbt.clone())) + // this is suboptimal, ideally simdnbt should just have a way to get the + // owned::NbtCompound as a borrow::NbtCompound + + let nbt_owned_compound = self.map.get(name)?; + let mut nbt_bytes = Vec::new(); + nbt_owned_compound.write(&mut nbt_bytes); + let nbt_borrow_compound = + simdnbt::borrow::NbtCompound::read(&mut Cursor::new(&nbt_bytes)).ok()?; + Some(T::from_compound(&nbt_borrow_compound)) } /// Get the dimension type registry, or `None` if it doesn't exist. You @@ -51,7 +59,10 @@ impl RegistryHolder { impl McBufReadable for RegistryHolder { fn read_from(buf: &mut Cursor<&[u8]>) -> Result { - let nbt_compound = NbtCompound::read_from(buf)?; + let nbt_tag = simdnbt::borrow::NbtTag::read(buf)?; + let nbt_compound = nbt_tag + .compound() + .ok_or_else(|| BufReadError::Custom("RegistryHolder must be a compound".to_string()))?; Ok(RegistryHolder { map: simdnbt::Deserialize::from_compound(nbt_compound)?, }) @@ -193,12 +204,12 @@ pub enum MonsterSpawnLightLevel { } impl FromNbtTag for MonsterSpawnLightLevel { - fn from_nbt_tag(tag: simdnbt::owned::NbtTag) -> Option { + fn from_nbt_tag(tag: &simdnbt::borrow::NbtTag) -> Option { if let Some(value) = tag.int() { Some(Self::Simple(value as u32)) } else if let Some(value) = tag.compound() { - let kind = ResourceLocation::from_nbt_tag(value.get("type")?.clone())?; - let value = MonsterSpawnLightLevelValues::from_nbt_tag(value.get("value")?.clone())?; + let kind = ResourceLocation::from_nbt_tag(value.get("type")?)?; + let value = MonsterSpawnLightLevelValues::from_nbt_tag(value.get("value")?)?; Some(Self::Complex { kind, value }) } else { None @@ -251,7 +262,7 @@ pub enum BiomePrecipitation { Snow, } impl FromNbtTag for BiomePrecipitation { - fn from_nbt_tag(tag: NbtTag) -> Option { + fn from_nbt_tag(tag: &simdnbt::borrow::NbtTag) -> Option { match tag.string()?.to_str().as_ref() { "none" => Some(Self::None), "rain" => Some(Self::Rain), diff --git a/azalea-core/src/resource_location.rs b/azalea-core/src/resource_location.rs index e6a702478..f9f950041 100755 --- a/azalea-core/src/resource_location.rs +++ b/azalea-core/src/resource_location.rs @@ -100,7 +100,7 @@ impl<'de> Deserialize<'de> for ResourceLocation { } impl FromNbtTag for ResourceLocation { - fn from_nbt_tag(tag: NbtTag) -> Option { + fn from_nbt_tag(tag: &simdnbt::borrow::NbtTag) -> Option { tag.string().and_then(|s| s.to_str().parse().ok()) } } diff --git a/azalea-core/src/tick.rs b/azalea-core/src/tick.rs new file mode 100644 index 000000000..db10d80ad --- /dev/null +++ b/azalea-core/src/tick.rs @@ -0,0 +1,4 @@ +use bevy_ecs::schedule::ScheduleLabel; + +#[derive(ScheduleLabel, Hash, Copy, Clone, Debug, Default, Eq, PartialEq)] +pub struct GameTick; diff --git a/azalea-crypto/Cargo.toml b/azalea-crypto/Cargo.toml index 65a18920d..e15374f97 100644 --- a/azalea-crypto/Cargo.toml +++ b/azalea-crypto/Cargo.toml @@ -3,22 +3,22 @@ description = "Cryptography features used in Minecraft." edition = "2021" license = "MIT" name = "azalea-crypto" -version = "0.8.0" +version = "0.9.0" repository = "https://github.com/azalea-rs/azalea/tree/main/azalea-crypto" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] aes = "0.8.3" -azalea-buf = { path = "../azalea-buf", version = "0.8.0" } +azalea-buf = { path = "../azalea-buf", version = "0.9.0" } cfb8 = "0.8.1" num-bigint = "^0.4.4" rand = { version = "^0.8.5", features = ["getrandom"] } -rsa = { version = "0.9.3", features = ["sha2"] } +rsa = { version = "0.9.6", features = ["sha2"] } rsa_public_encrypt_pkcs1 = "0.4.0" sha-1 = "^0.10.1" sha2 = "0.10.8" -uuid = "^1.5.0" +uuid = "^1.6.1" [dev-dependencies] criterion = { version = "^0.5.1", features = ["html_reports"] } diff --git a/azalea-entity/Cargo.toml b/azalea-entity/Cargo.toml index 7cdf53431..b3762da83 100644 --- a/azalea-entity/Cargo.toml +++ b/azalea-entity/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "azalea-entity" -version = "0.8.0" +version = "0.9.0" edition = "2021" description = "Things related to Minecraft entities used by Azalea" repository = "https://github.com/azalea-rs/azalea/tree/main/azalea-entity" @@ -9,20 +9,22 @@ license = "MIT" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -azalea-block = { version = "0.8.0", path = "../azalea-block" } -azalea-buf = { version = "0.8.0", path = "../azalea-buf" } -azalea-chat = { version = "0.8.0", path = "../azalea-chat" } -azalea-core = { version = "0.8.0", path = "../azalea-core" } -azalea-inventory = { version = "0.8.0", path = "../azalea-inventory" } -simdnbt = { version = "0.2.1" } -azalea-registry = { version = "0.8.0", path = "../azalea-registry" } -azalea-world = { version = "0.8.0", path = "../azalea-world" } -bevy_app = "0.12.0" -bevy_ecs = "0.12.0" +simdnbt = { version = "0.3", git = "https://github.com/azalea-rs/simdnbt" } +azalea-block = { version = "0.9.0", path = "../azalea-block" } +azalea-buf = { version = "0.9.0", path = "../azalea-buf" } +azalea-chat = { version = "0.9.0", path = "../azalea-chat", features = [ + "azalea-buf", +] } +azalea-core = { version = "0.9.0", path = "../azalea-core" } +azalea-inventory = { version = "0.9.0", path = "../azalea-inventory" } +azalea-registry = { version = "0.9.0", path = "../azalea-registry" } +azalea-world = { version = "0.9.0", path = "../azalea-world" } +bevy_app = "0.12.1" +bevy_ecs = "0.12.1" derive_more = "0.99.17" enum-as-inner = "0.6.0" tracing = "0.1.40" nohash-hasher = "0.2.0" parking_lot = "0.12.1" thiserror = "1.0.50" -uuid = "1.5.0" +uuid = "1.6.1" diff --git a/azalea-entity/src/data.rs b/azalea-entity/src/data.rs index 83779b217..b0a05e74d 100755 --- a/azalea-entity/src/data.rs +++ b/azalea-entity/src/data.rs @@ -134,7 +134,7 @@ pub struct Rotations { pub z: f32, } -#[derive(Clone, Debug, Copy, McBuf, Default, Component)] +#[derive(Clone, Debug, Copy, McBuf, Default, Component, Eq, PartialEq)] pub enum Pose { #[default] Standing = 0, diff --git a/azalea-entity/src/lib.rs b/azalea-entity/src/lib.rs index bf3dfc823..eb5b5b252 100644 --- a/azalea-entity/src/lib.rs +++ b/azalea-entity/src/lib.rs @@ -116,8 +116,13 @@ pub fn on_pos(offset: f32, chunk_storage: &ChunkStorage, pos: &Position) -> Bloc /// The Minecraft UUID of the entity. For players, this is their actual player /// UUID, and for other entities it's just random. -#[derive(Component, Deref, DerefMut, Clone, Copy)] +#[derive(Component, Deref, DerefMut, Clone, Copy, Default)] pub struct EntityUuid(Uuid); +impl EntityUuid { + pub fn new(uuid: Uuid) -> Self { + Self(uuid) + } +} impl Debug for EntityUuid { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { (self.0).fmt(f) @@ -228,6 +233,10 @@ pub struct Physics { pub bounding_box: AABB, pub has_impulse: bool, + + pub horizontal_collision: bool, + // pub minor_horizontal_collision: bool, + pub vertical_collision: bool, } impl Physics { @@ -246,6 +255,9 @@ impl Physics { dimensions, has_impulse: false, + + horizontal_collision: false, + vertical_collision: false, } } } @@ -311,6 +323,7 @@ pub struct EntityBundle { pub attributes: Attributes, pub jumping: Jumping, pub fluid_on_eyes: FluidOnEyes, + pub on_climbable: OnClimbable, } impl EntityBundle { @@ -346,6 +359,7 @@ impl EntityBundle { jumping: Jumping(false), fluid_on_eyes: FluidOnEyes(azalea_registry::Fluid::Empty), + on_climbable: OnClimbable(false), } } } @@ -373,6 +387,9 @@ impl FluidOnEyes { } } +#[derive(Component, Clone, Debug, PartialEq, Deref, DerefMut)] +pub struct OnClimbable(bool); + // #[cfg(test)] // mod tests { // use super::*; diff --git a/azalea-entity/src/metadata.rs b/azalea-entity/src/metadata.rs index 006020d1e..c73136b3b 100644 --- a/azalea-entity/src/metadata.rs +++ b/azalea-entity/src/metadata.rs @@ -795,7 +795,7 @@ pub struct BlockDisplayHeight(pub f32); #[derive(Component, Deref, DerefMut, Clone)] pub struct BlockDisplayGlowColorOverride(pub i32); #[derive(Component, Deref, DerefMut, Clone)] -pub struct BlockState(pub azalea_block::BlockState); +pub struct BlockDisplayBlockState(pub azalea_block::BlockState); #[derive(Component)] pub struct BlockDisplay; impl BlockDisplay { @@ -855,7 +855,7 @@ impl BlockDisplay { entity.insert(BlockDisplayGlowColorOverride(d.value.into_int()?)); } 23 => { - entity.insert(BlockState(d.value.into_block_state()?)); + entity.insert(BlockDisplayBlockState(d.value.into_block_state()?)); } _ => {} } @@ -884,7 +884,7 @@ pub struct BlockDisplayMetadataBundle { block_display_width: BlockDisplayWidth, block_display_height: BlockDisplayHeight, block_display_glow_color_override: BlockDisplayGlowColorOverride, - block_state: BlockState, + block_display_block_state: BlockDisplayBlockState, } impl Default for BlockDisplayMetadataBundle { fn default() -> Self { @@ -946,7 +946,7 @@ impl Default for BlockDisplayMetadataBundle { block_display_width: BlockDisplayWidth(0.0), block_display_height: BlockDisplayHeight(0.0), block_display_glow_color_override: BlockDisplayGlowColorOverride(-1), - block_state: BlockState(Default::default()), + block_display_block_state: BlockDisplayBlockState(Default::default()), } } } @@ -1045,6 +1045,74 @@ impl Default for BoatMetadataBundle { } } +#[derive(Component)] +pub struct Breeze; +impl Breeze { + pub fn apply_metadata( + entity: &mut bevy_ecs::system::EntityCommands, + d: EntityDataItem, + ) -> Result<(), UpdateMetadataError> { + match d.index { + 0..=15 => AbstractMonster::apply_metadata(entity, d)?, + _ => {} + } + Ok(()) + } +} + +#[derive(Bundle)] +pub struct BreezeMetadataBundle { + _marker: Breeze, + parent: AbstractMonsterMetadataBundle, +} +impl Default for BreezeMetadataBundle { + fn default() -> Self { + Self { + _marker: Breeze, + parent: AbstractMonsterMetadataBundle { + _marker: AbstractMonster, + parent: AbstractCreatureMetadataBundle { + _marker: AbstractCreature, + parent: AbstractInsentientMetadataBundle { + _marker: AbstractInsentient, + parent: AbstractLivingMetadataBundle { + _marker: AbstractLiving, + parent: AbstractEntityMetadataBundle { + _marker: AbstractEntity, + on_fire: OnFire(false), + shift_key_down: ShiftKeyDown(false), + sprinting: Sprinting(false), + swimming: Swimming(false), + currently_glowing: CurrentlyGlowing(false), + invisible: Invisible(false), + fall_flying: FallFlying(false), + air_supply: AirSupply(Default::default()), + custom_name: CustomName(None), + custom_name_visible: CustomNameVisible(false), + silent: Silent(false), + no_gravity: NoGravity(false), + pose: Pose::default(), + ticks_frozen: TicksFrozen(0), + }, + auto_spin_attack: AutoSpinAttack(false), + abstract_living_using_item: AbstractLivingUsingItem(false), + health: Health(1.0), + abstract_living_effect_color: AbstractLivingEffectColor(0), + effect_ambience: EffectAmbience(false), + arrow_count: ArrowCount(0), + stinger_count: StingerCount(0), + sleeping_pos: SleepingPos(None), + }, + no_ai: NoAi(false), + left_handed: LeftHanded(false), + aggressive: Aggressive(false), + }, + }, + }, + } + } +} + #[derive(Component, Deref, DerefMut, Clone, Copy)] pub struct CamelTamed(pub bool); #[derive(Component, Deref, DerefMut, Clone, Copy)] @@ -8074,6 +8142,8 @@ impl Default for TextDisplayMetadataBundle { #[derive(Component, Deref, DerefMut, Clone)] pub struct Fuse(pub i32); +#[derive(Component, Deref, DerefMut, Clone)] +pub struct TntBlockState(pub azalea_block::BlockState); #[derive(Component)] pub struct Tnt; impl Tnt { @@ -8086,6 +8156,9 @@ impl Tnt { 8 => { entity.insert(Fuse(d.value.into_int()?)); } + 9 => { + entity.insert(TntBlockState(d.value.into_block_state()?)); + } _ => {} } Ok(()) @@ -8097,6 +8170,7 @@ pub struct TntMetadataBundle { _marker: Tnt, parent: AbstractEntityMetadataBundle, fuse: Fuse, + tnt_block_state: TntBlockState, } impl Default for TntMetadataBundle { fn default() -> Self { @@ -8120,6 +8194,7 @@ impl Default for TntMetadataBundle { ticks_frozen: TicksFrozen(0), }, fuse: Fuse(80), + tnt_block_state: TntBlockState(Default::default()), } } } @@ -8927,6 +9002,51 @@ impl Default for WardenMetadataBundle { } } +#[derive(Component)] +pub struct WindCharge; +impl WindCharge { + pub fn apply_metadata( + entity: &mut bevy_ecs::system::EntityCommands, + d: EntityDataItem, + ) -> Result<(), UpdateMetadataError> { + match d.index { + 0..=7 => AbstractEntity::apply_metadata(entity, d)?, + _ => {} + } + Ok(()) + } +} + +#[derive(Bundle)] +pub struct WindChargeMetadataBundle { + _marker: WindCharge, + parent: AbstractEntityMetadataBundle, +} +impl Default for WindChargeMetadataBundle { + fn default() -> Self { + Self { + _marker: WindCharge, + parent: AbstractEntityMetadataBundle { + _marker: AbstractEntity, + on_fire: OnFire(false), + shift_key_down: ShiftKeyDown(false), + sprinting: Sprinting(false), + swimming: Swimming(false), + currently_glowing: CurrentlyGlowing(false), + invisible: Invisible(false), + fall_flying: FallFlying(false), + air_supply: AirSupply(Default::default()), + custom_name: CustomName(None), + custom_name_visible: CustomNameVisible(false), + silent: Silent(false), + no_gravity: NoGravity(false), + pose: Pose::default(), + ticks_frozen: TicksFrozen(0), + }, + } + } +} + #[derive(Component, Deref, DerefMut, Clone)] pub struct WitchIsCelebrating(pub bool); #[derive(Component, Deref, DerefMut, Clone)] @@ -10468,6 +10588,11 @@ pub fn apply_metadata( Boat::apply_metadata(entity, d)?; } } + azalea_registry::EntityKind::Breeze => { + for d in items { + Breeze::apply_metadata(entity, d)?; + } + } azalea_registry::EntityKind::Camel => { for d in items { Camel::apply_metadata(entity, d)?; @@ -10988,6 +11113,11 @@ pub fn apply_metadata( Warden::apply_metadata(entity, d)?; } } + azalea_registry::EntityKind::WindCharge => { + for d in items { + WindCharge::apply_metadata(entity, d)?; + } + } azalea_registry::EntityKind::Witch => { for d in items { Witch::apply_metadata(entity, d)?; @@ -11077,6 +11207,9 @@ pub fn apply_default_metadata( azalea_registry::EntityKind::Boat => { entity.insert(BoatMetadataBundle::default()); } + azalea_registry::EntityKind::Breeze => { + entity.insert(BreezeMetadataBundle::default()); + } azalea_registry::EntityKind::Camel => { entity.insert(CamelMetadataBundle::default()); } @@ -11389,6 +11522,9 @@ pub fn apply_default_metadata( azalea_registry::EntityKind::Warden => { entity.insert(WardenMetadataBundle::default()); } + azalea_registry::EntityKind::WindCharge => { + entity.insert(WindChargeMetadataBundle::default()); + } azalea_registry::EntityKind::Witch => { entity.insert(WitchMetadataBundle::default()); } diff --git a/azalea-entity/src/plugin/mod.rs b/azalea-entity/src/plugin/mod.rs index 9950e6ba3..4b6d99793 100644 --- a/azalea-entity/src/plugin/mod.rs +++ b/azalea-entity/src/plugin/mod.rs @@ -3,6 +3,7 @@ mod relative_updates; use std::collections::HashSet; +use azalea_block::BlockState; use azalea_core::position::{BlockPos, ChunkPos, Vec3}; use azalea_world::{InstanceContainer, InstanceName, MinecraftEntityId}; use bevy_app::{App, Plugin, PreUpdate, Update}; @@ -11,7 +12,8 @@ use derive_more::{Deref, DerefMut}; use tracing::debug; use crate::{ - metadata::Health, Dead, EyeHeight, FluidOnEyes, LocalEntity, LookDirection, Physics, Position, + metadata::Health, Dead, EyeHeight, FluidOnEyes, LocalEntity, LookDirection, OnClimbable, + Physics, Position, }; use indexing::EntityUuidIndex; @@ -48,6 +50,7 @@ impl Plugin for EntityPlugin { add_dead, clamp_look_direction, update_fluid_on_eyes, + update_on_climbable, ), ), ) @@ -106,6 +109,72 @@ pub fn update_fluid_on_eyes( } } +pub fn update_on_climbable( + mut query: Query<(&mut OnClimbable, &Position, &InstanceName)>, + instance_container: Res, +) { + for (mut on_climbable, position, instance_name) in query.iter_mut() { + // TODO: there's currently no gamemode component that can be accessed from here, + // maybe LocalGameMode should be replaced with two components, maybe called + // EntityGameMode and PreviousGameMode? + + // if game_mode == GameMode::Spectator { + // continue; + // } + + let Some(instance) = instance_container.get(instance_name) else { + continue; + }; + + let instance = instance.read(); + + let block_pos = BlockPos::from(position); + let block_state_at_feet = instance.get_block_state(&block_pos).unwrap_or_default(); + let block_at_feet = Box::::from(block_state_at_feet); + let registry_block_at_feet = block_at_feet.as_registry_block(); + + **on_climbable = azalea_registry::tags::blocks::CLIMBABLE.contains(®istry_block_at_feet) + || (azalea_registry::tags::blocks::TRAPDOORS.contains(®istry_block_at_feet) + && is_trapdoor_useable_as_ladder(block_state_at_feet, block_pos, &instance)); + } +} + +fn is_trapdoor_useable_as_ladder( + block_state: BlockState, + block_pos: BlockPos, + instance: &azalea_world::Instance, +) -> bool { + // trapdoor must be open + if !block_state + .property::() + .unwrap_or_default() + { + return false; + } + + // block below must be a ladder + let block_below = instance + .get_block_state(&block_pos.down(1)) + .unwrap_or_default(); + let registry_block_below = + Box::::from(block_below).as_registry_block(); + if registry_block_below != azalea_registry::Block::Ladder { + return false; + } + // and the ladder must be facing the same direction as the trapdoor + let ladder_facing = block_below + .property::() + .expect("ladder block must have facing property"); + let trapdoor_facing = block_state + .property::() + .expect("trapdoor block must have facing property"); + if ladder_facing != trapdoor_facing { + return false; + } + + true +} + /// A component that lists all the local player entities that have this entity /// loaded. If this is empty, the entity will be removed from the ECS. #[derive(Component, Clone, Deref, DerefMut)] diff --git a/azalea-inventory/Cargo.toml b/azalea-inventory/Cargo.toml index 994dbb2bf..b3c66eceb 100644 --- a/azalea-inventory/Cargo.toml +++ b/azalea-inventory/Cargo.toml @@ -4,12 +4,12 @@ edition = "2021" license = "MIT" name = "azalea-inventory" repository = "https://github.com/azalea-rs/azalea/tree/main/azalea-inventory-macros" -version = "0.8.0" +version = "0.9.0" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -azalea-buf = { version = "0.8.0", path = "../azalea-buf" } -azalea-inventory-macros = { version = "0.8.0", path = "./azalea-inventory-macros" } -simdnbt = { version = "0.2.1" } -azalea-registry = { version = "0.8.0", path = "../azalea-registry" } +simdnbt = { version = "0.3", git = "https://github.com/azalea-rs/simdnbt" } +azalea-buf = { version = "0.9.0", path = "../azalea-buf" } +azalea-inventory-macros = { version = "0.9.0", path = "./azalea-inventory-macros" } +azalea-registry = { version = "0.9.0", path = "../azalea-registry" } diff --git a/azalea-inventory/azalea-inventory-macros/Cargo.toml b/azalea-inventory/azalea-inventory-macros/Cargo.toml index 0e8190789..a18e2769c 100644 --- a/azalea-inventory/azalea-inventory-macros/Cargo.toml +++ b/azalea-inventory/azalea-inventory-macros/Cargo.toml @@ -4,7 +4,7 @@ edition = "2021" license = "MIT" name = "azalea-inventory-macros" repository = "https://github.com/azalea-rs/azalea/tree/main/azalea-inventory/azalea-inventory-macros" -version = "0.8.0" +version = "0.9.0" [lib] proc-macro = true @@ -12,6 +12,6 @@ proc-macro = true # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -proc-macro2 = "1.0.69" +proc-macro2 = "1.0.70" quote = "1.0.33" syn = "2.0.39" diff --git a/azalea-inventory/src/lib.rs b/azalea-inventory/src/lib.rs index dbbf1f5c7..b2ea215a1 100644 --- a/azalea-inventory/src/lib.rs +++ b/azalea-inventory/src/lib.rs @@ -89,6 +89,9 @@ declare_menus! { Generic3x3 { contents: 9, }, + Crafter3x3 { + contents: 9, + }, Anvil { first: 1, second: 1, diff --git a/azalea-inventory/src/operations.rs b/azalea-inventory/src/operations.rs index 29d61bd83..ca21a3e78 100644 --- a/azalea-inventory/src/operations.rs +++ b/azalea-inventory/src/operations.rs @@ -4,13 +4,13 @@ use azalea_buf::McBuf; use crate::{ item::MaxStackSizeExt, AnvilMenuLocation, BeaconMenuLocation, BlastFurnaceMenuLocation, - BrewingStandMenuLocation, CartographyTableMenuLocation, CraftingMenuLocation, - EnchantmentMenuLocation, FurnaceMenuLocation, Generic3x3MenuLocation, Generic9x1MenuLocation, - Generic9x2MenuLocation, Generic9x3MenuLocation, Generic9x4MenuLocation, Generic9x5MenuLocation, - Generic9x6MenuLocation, GrindstoneMenuLocation, HopperMenuLocation, ItemSlot, ItemSlotData, - LecternMenuLocation, LoomMenuLocation, Menu, MenuLocation, MerchantMenuLocation, Player, - PlayerMenuLocation, ShulkerBoxMenuLocation, SmithingMenuLocation, SmokerMenuLocation, - StonecutterMenuLocation, + BrewingStandMenuLocation, CartographyTableMenuLocation, Crafter3x3MenuLocation, + CraftingMenuLocation, EnchantmentMenuLocation, FurnaceMenuLocation, Generic3x3MenuLocation, + Generic9x1MenuLocation, Generic9x2MenuLocation, Generic9x3MenuLocation, Generic9x4MenuLocation, + Generic9x5MenuLocation, Generic9x6MenuLocation, GrindstoneMenuLocation, HopperMenuLocation, + ItemSlot, ItemSlotData, LecternMenuLocation, LoomMenuLocation, Menu, MenuLocation, + MerchantMenuLocation, Player, PlayerMenuLocation, ShulkerBoxMenuLocation, SmithingMenuLocation, + SmokerMenuLocation, StonecutterMenuLocation, }; #[derive(Debug, Clone)] @@ -386,6 +386,17 @@ impl Menu { ); } }, + MenuLocation::Crafter3x3(l) => match l { + Crafter3x3MenuLocation::Contents => { + self.try_move_item_to_slots(slot_index, self.player_slots_range()); + } + Crafter3x3MenuLocation::Player => { + self.try_move_item_to_slots_or_toggle_hotbar( + slot_index, + Menu::GENERIC3X3_CONTENTS_SLOTS, + ); + } + }, MenuLocation::Anvil(l) => match l { AnvilMenuLocation::Player => { self.try_move_item_to_slots_or_toggle_hotbar( diff --git a/azalea-language/Cargo.toml b/azalea-language/Cargo.toml index b4102ed43..57bfe1453 100644 --- a/azalea-language/Cargo.toml +++ b/azalea-language/Cargo.toml @@ -4,7 +4,7 @@ edition = "2021" license = "MIT" name = "azalea-language" repository = "https://github.com/azalea-rs/azalea/tree/main/azalea-language" -version = "0.8.0" +version = "0.9.0" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/azalea-language/src/en_us.json b/azalea-language/src/en_us.json index ee0df16b6..85ae19902 100755 --- a/azalea-language/src/en_us.json +++ b/azalea-language/src/en_us.json @@ -1,4 +1,5 @@ { + "accessibility.onboarding.accessibility.button": "Accessibility Settings...", "accessibility.onboarding.screen.narrator": "Press enter to enable the narrator", "accessibility.onboarding.screen.title": "Welcome to Minecraft!\n\nWould you like to enable the Narrator or visit the Accessibility Settings?", "addServer.add": "Done", @@ -358,6 +359,7 @@ "argument.rotation.incomplete": "Incomplete (expected 2 coordinates)", "argument.scoreboardDisplaySlot.invalid": "Unknown display slot '%s'", "argument.scoreHolder.empty": "No relevant score holders could be found", + "argument.style.invalid": "Invalid style: %s", "argument.time.invalid_tick_count": "Tick count must be non-negative", "argument.time.invalid_unit": "Invalid unit", "argument.time.tick_count_too_low": "Tick count must not be less than %s, found %s", @@ -1307,6 +1309,7 @@ "block.minecraft.chest": "Chest", "block.minecraft.chipped_anvil": "Chipped Anvil", "block.minecraft.chiseled_bookshelf": "Chiseled Bookshelf", + "block.minecraft.chiseled_copper": "Chiseled Copper", "block.minecraft.chiseled_deepslate": "Chiseled Deepslate", "block.minecraft.chiseled_nether_bricks": "Chiseled Nether Bricks", "block.minecraft.chiseled_polished_blackstone": "Chiseled Polished Blackstone", @@ -1314,6 +1317,8 @@ "block.minecraft.chiseled_red_sandstone": "Chiseled Red Sandstone", "block.minecraft.chiseled_sandstone": "Chiseled Sandstone", "block.minecraft.chiseled_stone_bricks": "Chiseled Stone Bricks", + "block.minecraft.chiseled_tuff": "Chiseled Tuff", + "block.minecraft.chiseled_tuff_bricks": "Chiseled Tuff Bricks", "block.minecraft.chorus_flower": "Chorus Flower", "block.minecraft.chorus_plant": "Chorus Plant", "block.minecraft.clay": "Clay", @@ -1335,13 +1340,18 @@ "block.minecraft.composter": "Composter", "block.minecraft.conduit": "Conduit", "block.minecraft.copper_block": "Block of Copper", + "block.minecraft.copper_bulb": "Copper Bulb", + "block.minecraft.copper_door": "Copper Door", + "block.minecraft.copper_grate": "Copper Grate", "block.minecraft.copper_ore": "Copper Ore", + "block.minecraft.copper_trapdoor": "Copper Trapdoor", "block.minecraft.cornflower": "Cornflower", "block.minecraft.cracked_deepslate_bricks": "Cracked Deepslate Bricks", "block.minecraft.cracked_deepslate_tiles": "Cracked Deepslate Tiles", "block.minecraft.cracked_nether_bricks": "Cracked Nether Bricks", "block.minecraft.cracked_polished_blackstone_bricks": "Cracked Polished Blackstone Bricks", "block.minecraft.cracked_stone_bricks": "Cracked Stone Bricks", + "block.minecraft.crafter": "Crafter", "block.minecraft.crafting_table": "Crafting Table", "block.minecraft.creeper_head": "Creeper Head", "block.minecraft.creeper_wall_head": "Creeper Wall Head", @@ -1475,7 +1485,12 @@ "block.minecraft.end_stone_brick_wall": "End Stone Brick Wall", "block.minecraft.end_stone_bricks": "End Stone Bricks", "block.minecraft.ender_chest": "Ender Chest", + "block.minecraft.exposed_chiseled_copper": "Exposed Chiseled Copper", "block.minecraft.exposed_copper": "Exposed Copper", + "block.minecraft.exposed_copper_bulb": "Exposed Copper Bulb", + "block.minecraft.exposed_copper_door": "Exposed Copper Door", + "block.minecraft.exposed_copper_grate": "Exposed Copper Grate", + "block.minecraft.exposed_copper_trapdoor": "Exposed Copper Trapdoor", "block.minecraft.exposed_cut_copper": "Exposed Cut Copper", "block.minecraft.exposed_cut_copper_slab": "Exposed Cut Copper Slab", "block.minecraft.exposed_cut_copper_stairs": "Exposed Cut Copper Stairs", @@ -1740,7 +1755,12 @@ "block.minecraft.orange_tulip": "Orange Tulip", "block.minecraft.orange_wool": "Orange Wool", "block.minecraft.oxeye_daisy": "Oxeye Daisy", + "block.minecraft.oxidized_chiseled_copper": "Oxidized Chiseled Copper", "block.minecraft.oxidized_copper": "Oxidized Copper", + "block.minecraft.oxidized_copper_bulb": "Oxidized Copper Bulb", + "block.minecraft.oxidized_copper_door": "Oxidized Copper Door", + "block.minecraft.oxidized_copper_grate": "Oxidized Copper Grate", + "block.minecraft.oxidized_copper_trapdoor": "Oxidized Copper Trapdoor", "block.minecraft.oxidized_cut_copper": "Oxidized Cut Copper", "block.minecraft.oxidized_cut_copper_slab": "Oxidized Cut Copper Slab", "block.minecraft.oxidized_cut_copper_stairs": "Oxidized Cut Copper Stairs", @@ -1799,6 +1819,10 @@ "block.minecraft.polished_granite": "Polished Granite", "block.minecraft.polished_granite_slab": "Polished Granite Slab", "block.minecraft.polished_granite_stairs": "Polished Granite Stairs", + "block.minecraft.polished_tuff": "Polished Tuff", + "block.minecraft.polished_tuff_slab": "Polished Tuff Slab", + "block.minecraft.polished_tuff_stairs": "Polished Tuff Stairs", + "block.minecraft.polished_tuff_wall": "Polished Tuff Wall", "block.minecraft.poppy": "Poppy", "block.minecraft.potatoes": "Potatoes", "block.minecraft.potted_acacia_sapling": "Potted Acacia Sapling", @@ -1925,6 +1949,7 @@ "block.minecraft.sea_pickle": "Sea Pickle", "block.minecraft.seagrass": "Seagrass", "block.minecraft.set_spawn": "Respawn point set", + "block.minecraft.short_grass": "Short Grass", "block.minecraft.shroomlight": "Shroomlight", "block.minecraft.shulker_box": "Shulker Box", "block.minecraft.skeleton_skull": "Skeleton Skull", @@ -2028,6 +2053,7 @@ "block.minecraft.torchflower": "Torchflower", "block.minecraft.torchflower_crop": "Torchflower Crop", "block.minecraft.trapped_chest": "Trapped Chest", + "block.minecraft.trial_spawner": "Trial Spawner", "block.minecraft.tripwire": "Tripwire", "block.minecraft.tripwire_hook": "Tripwire Hook", "block.minecraft.tube_coral": "Tube Coral", @@ -2035,6 +2061,13 @@ "block.minecraft.tube_coral_fan": "Tube Coral Fan", "block.minecraft.tube_coral_wall_fan": "Tube Coral Wall Fan", "block.minecraft.tuff": "Tuff", + "block.minecraft.tuff_brick_slab": "Tuff Brick Slab", + "block.minecraft.tuff_brick_stairs": "Tuff Brick Stairs", + "block.minecraft.tuff_brick_wall": "Tuff Brick Wall", + "block.minecraft.tuff_bricks": "Tuff Bricks", + "block.minecraft.tuff_slab": "Tuff Slab", + "block.minecraft.tuff_stairs": "Tuff Stairs", + "block.minecraft.tuff_wall": "Tuff Wall", "block.minecraft.turtle_egg": "Turtle Egg", "block.minecraft.twisting_vines": "Twisting Vines", "block.minecraft.twisting_vines_plant": "Twisting Vines Plant", @@ -2063,23 +2096,48 @@ "block.minecraft.warped_wart_block": "Warped Wart Block", "block.minecraft.water": "Water", "block.minecraft.water_cauldron": "Water Cauldron", + "block.minecraft.waxed_chiseled_copper": "Waxed Chiseled Copper", "block.minecraft.waxed_copper_block": "Waxed Block of Copper", + "block.minecraft.waxed_copper_bulb": "Waxed Copper Bulb", + "block.minecraft.waxed_copper_door": "Waxed Copper Door", + "block.minecraft.waxed_copper_grate": "Waxed Copper Grate", + "block.minecraft.waxed_copper_trapdoor": "Waxed Copper Trapdoor", "block.minecraft.waxed_cut_copper": "Waxed Cut Copper", "block.minecraft.waxed_cut_copper_slab": "Waxed Cut Copper Slab", "block.minecraft.waxed_cut_copper_stairs": "Waxed Cut Copper Stairs", + "block.minecraft.waxed_exposed_chiseled_copper": "Waxed Exposed Chiseled Copper", "block.minecraft.waxed_exposed_copper": "Waxed Exposed Copper", + "block.minecraft.waxed_exposed_copper_bulb": "Waxed Exposed Copper Bulb", + "block.minecraft.waxed_exposed_copper_door": "Waxed Exposed Copper Door", + "block.minecraft.waxed_exposed_copper_grate": "Waxed Exposed Copper Grate", + "block.minecraft.waxed_exposed_copper_trapdoor": "Waxed Exposed Copper Trapdoor", "block.minecraft.waxed_exposed_cut_copper": "Waxed Exposed Cut Copper", "block.minecraft.waxed_exposed_cut_copper_slab": "Waxed Exposed Cut Copper Slab", "block.minecraft.waxed_exposed_cut_copper_stairs": "Waxed Exposed Cut Copper Stairs", + "block.minecraft.waxed_oxidized_chiseled_copper": "Waxed Oxidized Chiseled Copper", "block.minecraft.waxed_oxidized_copper": "Waxed Oxidized Copper", + "block.minecraft.waxed_oxidized_copper_bulb": "Waxed Oxidized Copper Bulb", + "block.minecraft.waxed_oxidized_copper_door": "Waxed Oxidized Copper Door", + "block.minecraft.waxed_oxidized_copper_grate": "Waxed Oxidized Copper Grate", + "block.minecraft.waxed_oxidized_copper_trapdoor": "Waxed Oxidized Copper Trapdoor", "block.minecraft.waxed_oxidized_cut_copper": "Waxed Oxidized Cut Copper", "block.minecraft.waxed_oxidized_cut_copper_slab": "Waxed Oxidized Cut Copper Slab", "block.minecraft.waxed_oxidized_cut_copper_stairs": "Waxed Oxidized Cut Copper Stairs", + "block.minecraft.waxed_weathered_chiseled_copper": "Waxed Weathered Chiseled Copper", "block.minecraft.waxed_weathered_copper": "Waxed Weathered Copper", + "block.minecraft.waxed_weathered_copper_bulb": "Waxed Weathered Copper Bulb", + "block.minecraft.waxed_weathered_copper_door": "Waxed Weathered Copper Door", + "block.minecraft.waxed_weathered_copper_grate": "Waxed Weathered Copper Grate", + "block.minecraft.waxed_weathered_copper_trapdoor": "Waxed Weathered Copper Trapdoor", "block.minecraft.waxed_weathered_cut_copper": "Waxed Weathered Cut Copper", "block.minecraft.waxed_weathered_cut_copper_slab": "Waxed Weathered Cut Copper Slab", "block.minecraft.waxed_weathered_cut_copper_stairs": "Waxed Weathered Cut Copper Stairs", + "block.minecraft.weathered_chiseled_copper": "Weathered Chiseled Copper", "block.minecraft.weathered_copper": "Weathered Copper", + "block.minecraft.weathered_copper_bulb": "Weathered Copper Bulb", + "block.minecraft.weathered_copper_door": "Weathered Copper Door", + "block.minecraft.weathered_copper_grate": "Weathered Copper Grate", + "block.minecraft.weathered_copper_trapdoor": "Weathered Copper Trapdoor", "block.minecraft.weathered_cut_copper": "Weathered Cut Copper", "block.minecraft.weathered_cut_copper_slab": "Weathered Cut Copper Slab", "block.minecraft.weathered_cut_copper_stairs": "Weathered Cut Copper Stairs", @@ -2196,6 +2254,7 @@ "command.exception": "Could not parse command: %s", "command.expected.separator": "Expected whitespace to end one argument, but found trailing data", "command.failed": "An unexpected error occurred trying to execute that command", + "command.forkLimit": "Maximum number of contexts (%s) reached", "command.unknown.argument": "Incorrect argument for command", "command.unknown.command": "Unknown or incomplete command, see below for error", "commands.advancement.advancementNotFound": "No advancement was found by the name '%1$s'", @@ -2315,6 +2374,7 @@ "commands.datapack.unknown": "Unknown data pack '%s'", "commands.debug.alreadyRunning": "The tick profiler is already started", "commands.debug.function.noRecursion": "Can't trace from inside of function", + "commands.debug.function.noReturnRun": "Tracing can't be used with return run", "commands.debug.function.success.multiple": "Traced %s command(s) from %s functions to output file %s", "commands.debug.function.success.single": "Traced %s command(s) from function '%s' to output file %s", "commands.debug.function.traceFailed": "Failed to trace function", @@ -2354,6 +2414,7 @@ "commands.execute.conditional.fail_count": "Test failed, count: %s", "commands.execute.conditional.pass": "Test passed", "commands.execute.conditional.pass_count": "Test passed, count: %s", + "commands.execute.function.instantiationFailure": "Failed to instantiate function %s: %s", "commands.experience.add.levels.success.multiple": "Gave %s experience levels to %s players", "commands.experience.add.levels.success.single": "Gave %s experience levels to %s", "commands.experience.add.points.success.multiple": "Gave %s experience points to %s players", @@ -2388,6 +2449,11 @@ "commands.function.error.missing_argument": "Missing argument %2$s to function %1$s", "commands.function.error.missing_arguments": "Missing arguments to function %s", "commands.function.error.parse": "While instantiating macro %s: Command '%s' caused error: %s", + "commands.function.instantiationFailure": "Failed to instantiate function %s: %s", + "commands.function.result": "Function %s returned %s", + "commands.function.scheduled.multiple": "Running functions %s", + "commands.function.scheduled.no_functions": "Can't find any functions for name %s", + "commands.function.scheduled.single": "Running function %s", "commands.function.success.multiple": "Executed %s command(s) from %s functions", "commands.function.success.multiple.result": "Executed %s functions", "commands.function.success.single": "Executed %s command(s) from function '%s'", @@ -2413,6 +2479,8 @@ "commands.jfr.start.failed": "Failed to start JFR profiling", "commands.jfr.started": "JFR profiling started", "commands.jfr.stopped": "JFR profiling stopped and dumped to %s", + "commands.kick.owner.failed": "Cannot kick server owner in LAN game", + "commands.kick.singleplayer.failed": "Cannot kick in an offline singleplayer game", "commands.kick.success": "Kicked %s: %s", "commands.kill.success.multiple": "Killed %s entities", "commands.kill.success.single": "Killed %s", @@ -2503,11 +2571,23 @@ "commands.scoreboard.objectives.display.set": "Set display slot %s to show objective %s", "commands.scoreboard.objectives.list.empty": "There are no objectives", "commands.scoreboard.objectives.list.success": "There are %s objective(s): %s", + "commands.scoreboard.objectives.modify.displayAutoUpdate.disable": "Disabled display auto-update for objective %s", + "commands.scoreboard.objectives.modify.displayAutoUpdate.enable": "Enabled display auto-update for objective %s", "commands.scoreboard.objectives.modify.displayname": "Changed the display name of %s to %s", + "commands.scoreboard.objectives.modify.objectiveFormat.clear": "Cleared default number format of objective %s", + "commands.scoreboard.objectives.modify.objectiveFormat.set": "Changed default number format of objective %s", "commands.scoreboard.objectives.modify.rendertype": "Changed the render type of objective %s", "commands.scoreboard.objectives.remove.success": "Removed objective %s", "commands.scoreboard.players.add.success.multiple": "Added %s to %s for %s entities", "commands.scoreboard.players.add.success.single": "Added %s to %s for %s (now %s)", + "commands.scoreboard.players.display.name.clear.success.multiple": "Cleared display name for %s entities in %s", + "commands.scoreboard.players.display.name.clear.success.single": "Cleared display name for %s in %s", + "commands.scoreboard.players.display.name.set.success.multiple": "Changed display name to %s for %s entities in %s", + "commands.scoreboard.players.display.name.set.success.single": "Changed display name to %s for %s in %s", + "commands.scoreboard.players.display.numberFormat.clear.success.multiple": "Cleared number format for %s entities in %s", + "commands.scoreboard.players.display.numberFormat.clear.success.single": "Cleared number format for %s in %s", + "commands.scoreboard.players.display.numberFormat.set.success.multiple": "Changed number format for %s entities in %s", + "commands.scoreboard.players.display.numberFormat.set.success.single": "Changed number format for %s in %s", "commands.scoreboard.players.enable.failed": "Nothing changed. That trigger is already enabled", "commands.scoreboard.players.enable.invalid": "Enable only works on trigger-objectives", "commands.scoreboard.players.enable.success.multiple": "Enabled trigger %s for %s entities", @@ -2603,6 +2683,21 @@ "commands.teleport.success.entity.single": "Teleported %s to %s", "commands.teleport.success.location.multiple": "Teleported %s entities to %s, %s, %s", "commands.teleport.success.location.single": "Teleported %s to %s, %s, %s", + "commands.tick.query.percentiles": "Percentiles: P50: %sms P95: %sms P99: %sms, sample: %s", + "commands.tick.query.rate.running": "Target tick rate: %s per second.\nAverage time per tick: %sms (Target: %sms)", + "commands.tick.query.rate.sprinting": "Target tick rate: %s per second (ignored, reference only).\nAverage time per tick: %sms", + "commands.tick.rate.success": "Set the target tick rate to %s per second", + "commands.tick.sprint.report": "Sprint completed with %s ticks per second, or %s ms per tick", + "commands.tick.sprint.stop.fail": "No tick sprint in progress", + "commands.tick.sprint.stop.success": "A tick sprint interrupted", + "commands.tick.status.frozen": "The game is frozen", + "commands.tick.status.lagging": "The game is running, but can't keep up with the target tick rate", + "commands.tick.status.running": "The game runs normally", + "commands.tick.status.sprinting": "The game is sprinting", + "commands.tick.step.fail": "Unable to step the game - the game must be frozen first", + "commands.tick.step.stop.fail": "No tick step in progress", + "commands.tick.step.stop.success": "A tick step interrupted", + "commands.tick.step.success": "Stepping %s tick(s)", "commands.time.query": "The time is %s", "commands.time.set": "Set the time to %s", "commands.title.cleared.multiple": "Cleared titles for %s players", @@ -2673,6 +2768,7 @@ "container.cartography_table": "Cartography Table", "container.chest": "Chest", "container.chestDouble": "Large Chest", + "container.crafter": "Crafter", "container.crafting": "Crafting", "container.creative": "Item Selection", "container.dispenser": "Dispenser", @@ -2802,6 +2898,8 @@ "dataPack.trade_rebalance.name": "Villager Trade Rebalance", "dataPack.update_1_20.description": "New features and content for Minecraft 1.20", "dataPack.update_1_20.name": "Update 1.20", + "dataPack.update_1_21.description": "New features and content for Minecraft 1.21", + "dataPack.update_1_21.name": "Update 1.21", "dataPack.validation.back": "Go Back", "dataPack.validation.failed": "Data pack validation failed!", "dataPack.validation.reset": "Reset to Default", @@ -3000,6 +3098,10 @@ "disconnect.spam": "Kicked for spamming", "disconnect.timeout": "Timed out", "disconnect.unknownHost": "Unknown host", + "download.pack.failed": "%s out of %s packs failed to download", + "download.pack.progress.bytes": "Progress: %s (total size unknown)", + "download.pack.progress.percent": "Progress: %s%%", + "download.pack.title": "Downloading resource pack %s/%s", "editGamerule.default": "Default: %s", "editGamerule.title": "Edit Game Rules", "effect.duration.infinite": "\u221e", @@ -3096,6 +3198,7 @@ "entity.minecraft.blaze": "Blaze", "entity.minecraft.block_display": "Block Display", "entity.minecraft.boat": "Boat", + "entity.minecraft.breeze": "Breeze", "entity.minecraft.camel": "Camel", "entity.minecraft.cat": "Cat", "entity.minecraft.cave_spider": "Cave Spider", @@ -3251,6 +3354,7 @@ "entity.minecraft.vindicator": "Vindicator", "entity.minecraft.wandering_trader": "Wandering Trader", "entity.minecraft.warden": "Warden", + "entity.minecraft.wind_charge": "Wind Charge", "entity.minecraft.witch": "Witch", "entity.minecraft.wither": "Wither", "entity.minecraft.wither_skeleton": "Wither Skeleton", @@ -3310,7 +3414,7 @@ "gamerule.category.spawning": "Spawning", "gamerule.category.updates": "World Updates", "gamerule.commandBlockOutput": "Broadcast command block output", - "gamerule.commandModificationBlockLimit": "Command Modification Block Limit", + "gamerule.commandModificationBlockLimit": "Command modification block limit", "gamerule.commandModificationBlockLimit.description": "Number of blocks that can be changed at once by one command, such as fill or clone.", "gamerule.disableElytraMovementCheck": "Disable elytra movement check", "gamerule.disableRaids": "Disable raids", @@ -3350,13 +3454,21 @@ "gamerule.logAdminCommands": "Broadcast admin commands", "gamerule.maxCommandChainLength": "Command chain size limit", "gamerule.maxCommandChainLength.description": "Applies to command block chains and functions.", + "gamerule.maxCommandForkCount": "Command context limit", + "gamerule.maxCommandForkCount.description": "Maximum number of contexts that can be used by commands like 'execute as'.", "gamerule.maxEntityCramming": "Entity cramming threshold", "gamerule.mobExplosionDropDecay": "In mob explosions, some blocks won't drop their loot", "gamerule.mobExplosionDropDecay.description": "Some of the drops from blocks destroyed by explosions caused by mobs are lost in the explosion.", "gamerule.mobGriefing": "Allow destructive mob actions", "gamerule.naturalRegeneration": "Regenerate health", + "gamerule.playersNetherPortalCreativeDelay": "Player's Nether portal delay in creative mode", + "gamerule.playersNetherPortalCreativeDelay.description": "Time (in ticks) that a creative mode player needs to stand in a Nether portal before changing dimensions.", + "gamerule.playersNetherPortalDefaultDelay": "Player's Nether portal delay in non-creative mode", + "gamerule.playersNetherPortalDefaultDelay.description": "Time (in ticks) that a non-creative mode player needs to stand in a Nether portal before changing dimensions.", "gamerule.playersSleepingPercentage": "Sleep percentage", "gamerule.playersSleepingPercentage.description": "The percentage of players who must be sleeping to skip the night.", + "gamerule.projectilesCanBreakBlocks": "Projectiles can break blocks", + "gamerule.projectilesCanBreakBlocks.description": "Controls whether impact projectiles will destroy blocks that are destructible by them.", "gamerule.randomTickSpeed": "Random tick speed rate", "gamerule.reducedDebugInfo": "Reduce debug info", "gamerule.reducedDebugInfo.description": "Limits contents of debug screen.", @@ -3516,6 +3628,7 @@ "gui.down": "Down", "gui.entity_tooltip.type": "Type: %s", "gui.hours": "%s hour(s)", + "gui.loadingMinecraft": "Loading Minecraft", "gui.minutes": "%s minute(s)", "gui.multiLineEditBox.character_limit": "%s/%s", "gui.narrate.button": "%s button", @@ -3565,6 +3678,7 @@ "gui.socialInteractions.tooltip.report.not_reportable": "This player can't be reported, because their chat messages can't be verified on this server", "gui.socialInteractions.tooltip.show": "Show messages", "gui.stats": "Statistics", + "gui.togglable_slot": "Click to disable slot", "gui.toMenu": "Back to Server List", "gui.toRealms": "Back to Realms List", "gui.toTitle": "Back to Title Screen", @@ -3629,6 +3743,7 @@ "item.minecraft.bow": "Bow", "item.minecraft.bowl": "Bowl", "item.minecraft.bread": "Bread", + "item.minecraft.breeze_spawn_egg": "Breeze Spawn Egg", "item.minecraft.brewer_pottery_shard": "Brewer Pottery Shard", "item.minecraft.brewer_pottery_sherd": "Brewer Pottery Sherd", "item.minecraft.brewing_stand": "Brewing Stand", @@ -4155,6 +4270,7 @@ "item.minecraft.torchflower_seeds": "Torchflower Seeds", "item.minecraft.totem_of_undying": "Totem of Undying", "item.minecraft.trader_llama_spawn_egg": "Trader Llama Spawn Egg", + "item.minecraft.trial_key": "Trial Key", "item.minecraft.trident": "Trident", "item.minecraft.tropical_fish": "Tropical Fish", "item.minecraft.tropical_fish_bucket": "Bucket of Tropical Fish", @@ -4224,7 +4340,11 @@ "jigsaw_block.keep_jigsaws": "Keep Jigsaws", "jigsaw_block.levels": "Levels: %s", "jigsaw_block.name": "Name:", + "jigsaw_block.placement_priority": "Placement Priority:", + "jigsaw_block.placement_priority.tooltip": "When this Jigsaw block connects to a piece, this is the order in which that piece is processed for connections in the wider structure.\n\nPieces will be processed in descending priority with insertion order breaking ties.", "jigsaw_block.pool": "Target Pool:", + "jigsaw_block.selection_priority": "Selection Priority:", + "jigsaw_block.selection_priority.tooltip": "When the parent piece is being processed for connections, this is the order in which this Jigsaw block attempts to connect to its target piece.\n\nJigsaws will be processed in descending priority with random ordering breaking ties.", "jigsaw_block.target": "Target Name:", "key.advancements": "Advancements", "key.attack": "Attack/Destroy", @@ -4371,18 +4491,20 @@ "lectern.take_book": "Take Book", "loading.progress": "%s%%", "mco.account.privacy.info": "Read more about Mojang and privacy laws", + "mco.account.privacy.info.button": "Read more about GDPR", + "mco.account.privacy.information": "Mojang implements certain procedures to help protect children and their privacy including complying with the Children\u2019s Online Privacy Protection Act (COPPA) and General Data Protection Regulation (GDPR).\n\nYou may need to obtain parental consent before accessing your Realms account.", "mco.account.privacyinfo": "Mojang implements certain procedures to help protect children and their privacy including complying with the Children\u2019s Online Privacy Protection Act (COPPA) and General Data Protection Regulation (GDPR).\n\nYou may need to obtain parental consent before accessing your Realms account.\n\nIf you have an older Minecraft account (you log in with your username), you need to migrate the account to a Mojang account in order to access Realms.", "mco.account.update": "Update account", "mco.activity.noactivity": "No activity for the past %s day(s)", "mco.activity.title": "Player activity", - "mco.backup.button.download": "Download latest", - "mco.backup.button.reset": "Reset world", + "mco.backup.button.download": "Download Latest", + "mco.backup.button.reset": "Reset World", "mco.backup.button.restore": "Restore", - "mco.backup.button.upload": "Upload world", + "mco.backup.button.upload": "Upload World", "mco.backup.changes.tooltip": "Changes", "mco.backup.entry": "Backup (%s)", "mco.backup.entry.description": "Description", - "mco.backup.entry.enabledPack": "Enabled Pack", + "mco.backup.entry.enabledPack": "Enabled Pack(s)", "mco.backup.entry.gameDifficulty": "Game Difficulty", "mco.backup.entry.gameMode": "Game Mode", "mco.backup.entry.gameServerVersion": "Game Server Version", @@ -4393,7 +4515,7 @@ "mco.backup.entry.uploaded": "Uploaded", "mco.backup.entry.worldType": "World Type", "mco.backup.generate.world": "Generate world", - "mco.backup.info.title": "Changes from last backup", + "mco.backup.info.title": "Changes From Last Backup", "mco.backup.nobackups": "This realm doesn't have any backups currently.", "mco.backup.restoring": "Restoring your realm", "mco.backup.unknown": "UNKNOWN", @@ -4411,36 +4533,43 @@ "mco.client.incompatible.msg.line2": "Please use the most recent version of Minecraft.", "mco.client.incompatible.msg.line3": "Realms is not compatible with snapshot versions.", "mco.client.incompatible.title": "Client incompatible!", + "mco.compatibility.downgrade": "Downgrade", + "mco.compatibility.downgrade.description": "This world was last played in version %s; you are on version %s. Downgrading a world could cause corruption - we cannot guarantee that it will load or work.\n\nA backup of your world will be saved under \"World backups\". Please restore your world if needed.", + "mco.compatibility.unverifiable.message": "The version this world was last played in could not be verified. If the world gets upgraded or downgraded, a backup will be automatically created and saved under \"World backups\".", + "mco.compatibility.unverifiable.title": "Compatibility not verifiable", + "mco.compatibility.upgrade": "Upgrade", + "mco.compatibility.upgrade.description": "This world was last played in version %s; you are on version %s.\n\nA backup of your world will be saved under \"World backups\". Please restore your world if needed.", + "mco.compatibility.upgrade.title": "Do you really want to upgrade your world?", "mco.configure.current.minigame": "Current", "mco.configure.world.activityfeed.disabled": "Player feed temporarily disabled", - "mco.configure.world.backup": "World backups", + "mco.configure.world.backup": "World Backups", "mco.configure.world.buttons.activity": "Player activity", - "mco.configure.world.buttons.close": "Close realm", + "mco.configure.world.buttons.close": "Close Realm", "mco.configure.world.buttons.delete": "Delete", "mco.configure.world.buttons.done": "Done", "mco.configure.world.buttons.edit": "Settings", - "mco.configure.world.buttons.invite": "Invite player", + "mco.configure.world.buttons.invite": "Invite Player", "mco.configure.world.buttons.moreoptions": "More options", - "mco.configure.world.buttons.open": "Open realm", - "mco.configure.world.buttons.options": "World options", + "mco.configure.world.buttons.open": "Open Realm", + "mco.configure.world.buttons.options": "World Options", "mco.configure.world.buttons.players": "Players", - "mco.configure.world.buttons.resetworld": "Reset world", + "mco.configure.world.buttons.resetworld": "Reset World", "mco.configure.world.buttons.settings": "Settings", "mco.configure.world.buttons.subscription": "Subscription", "mco.configure.world.buttons.switchminigame": "Switch minigame", "mco.configure.world.close.question.line1": "Your realm will become unavailable.", "mco.configure.world.close.question.line2": "Are you sure you want to continue?", "mco.configure.world.closing": "Closing the realm...", - "mco.configure.world.commandBlocks": "Command blocks", - "mco.configure.world.delete.button": "Delete realm", + "mco.configure.world.commandBlocks": "Command Blocks", + "mco.configure.world.delete.button": "Delete Realm", "mco.configure.world.delete.question.line1": "Your realm will be permanently deleted", "mco.configure.world.delete.question.line2": "Are you sure you want to continue?", - "mco.configure.world.description": "Realm description", - "mco.configure.world.edit.slot.name": "World name", + "mco.configure.world.description": "Realm Description", + "mco.configure.world.edit.slot.name": "World Name", "mco.configure.world.edit.subscreen.adventuremap": "Some settings are disabled since your current world is an adventure", "mco.configure.world.edit.subscreen.experience": "Some settings are disabled since your current world is an experience", "mco.configure.world.edit.subscreen.inspiration": "Some settings are disabled since your current world is an inspiration", - "mco.configure.world.forceGameMode": "Force game mode", + "mco.configure.world.forceGameMode": "Force Game Mode", "mco.configure.world.invite.narration": "You have %s new invite(s)", "mco.configure.world.invite.profile.name": "Name", "mco.configure.world.invited": "Invited", @@ -4452,7 +4581,7 @@ "mco.configure.world.leave.question.line2": "Are you sure you want to continue?", "mco.configure.world.location": "Location", "mco.configure.world.minigame": "Current: %s", - "mco.configure.world.name": "Realm name", + "mco.configure.world.name": "Realm Name", "mco.configure.world.opening": "Opening the realm...", "mco.configure.world.players.error": "A player with the provided name does not exist", "mco.configure.world.players.inviting": "Inviting player...", @@ -4477,15 +4606,15 @@ "mco.configure.world.spawn_toggle.message": "Turning this option off will REMOVE ALL existing entities of that type", "mco.configure.world.spawn_toggle.message.npc": "Turning this option off will REMOVE ALL existing entities of that type, like Villagers", "mco.configure.world.spawn_toggle.title": "Warning!", - "mco.configure.world.spawnAnimals": "Spawn animals", - "mco.configure.world.spawnMonsters": "Spawn monsters", + "mco.configure.world.spawnAnimals": "Spawn Animals", + "mco.configure.world.spawnMonsters": "Spawn Monsters", "mco.configure.world.spawnNPCs": "Spawn NPCs", - "mco.configure.world.spawnProtection": "Spawn protection", + "mco.configure.world.spawnProtection": "Spawn Protection", "mco.configure.world.status": "Status", "mco.configure.world.subscription.day": "day", "mco.configure.world.subscription.days": "days", "mco.configure.world.subscription.expired": "Expired", - "mco.configure.world.subscription.extend": "Extend subscription", + "mco.configure.world.subscription.extend": "Extend Subscription", "mco.configure.world.subscription.less_than_a_day": "Less than a day", "mco.configure.world.subscription.month": "month", "mco.configure.world.subscription.months": "months", @@ -4494,13 +4623,13 @@ "mco.configure.world.subscription.remaining.days": "%1$s day(s)", "mco.configure.world.subscription.remaining.months": "%1$s month(s)", "mco.configure.world.subscription.remaining.months.days": "%1$s month(s), %2$s day(s)", - "mco.configure.world.subscription.start": "Start date", - "mco.configure.world.subscription.timeleft": "Time left", - "mco.configure.world.subscription.title": "Your subscription", + "mco.configure.world.subscription.start": "Start Date", + "mco.configure.world.subscription.timeleft": "Time Left", + "mco.configure.world.subscription.title": "Your Subscription", "mco.configure.world.subscription.unknown": "Unknown", - "mco.configure.world.switch.slot": "Create world", + "mco.configure.world.switch.slot": "Create World", "mco.configure.world.switch.slot.subtitle": "This world is empty, choose how to create your world", - "mco.configure.world.title": "Configure realm:", + "mco.configure.world.title": "Configure Realm:", "mco.configure.world.uninvite.player": "Are you sure that you want to uninvite '%s'?", "mco.configure.world.uninvite.question": "Are you sure that you want to uninvite", "mco.configure.worlds.title": "Worlds", @@ -4526,9 +4655,9 @@ "mco.download.resourcePack.fail": "Failed to download resource pack!", "mco.download.speed": "(%s/s)", "mco.download.speed.narration": "%s/s", - "mco.download.title": "Downloading latest world", + "mco.download.title": "Downloading Latest World", "mco.error.invalid.session.message": "Please try restarting Minecraft", - "mco.error.invalid.session.title": "Invalid session", + "mco.error.invalid.session.title": "Invalid Session", "mco.errorMessage.6001": "Client outdated", "mco.errorMessage.6002": "Terms of service not accepted", "mco.errorMessage.6003": "Download limit reached", @@ -4572,26 +4701,28 @@ "mco.minigame.world.title": "Switch realm to minigame", "mco.news": "Realms news", "mco.notification.dismiss": "Dismiss", + "mco.notification.transferSubscription.buttonText": "Transfer Now", + "mco.notification.transferSubscription.message": "Java Realms subscriptions are moving to the Microsoft Store. Do not let your subscription expire!\nTransfer now and get 30 days of Realms for free.\nGo to Profile on minecraft.net to transfer your subscription.", "mco.notification.visitUrl.buttonText.default": "Open link", "mco.notification.visitUrl.message.default": "Please visit the link below", "mco.question": "Question", "mco.reset.world.adventure": "Adventures", "mco.reset.world.experience": "Experiences", - "mco.reset.world.generate": "New world", + "mco.reset.world.generate": "New World", "mco.reset.world.inspiration": "Inspiration", "mco.reset.world.resetting.screen.title": "Resetting world...", "mco.reset.world.seed": "Seed (Optional)", - "mco.reset.world.template": "World templates", - "mco.reset.world.title": "Reset world", + "mco.reset.world.template": "World Templates", + "mco.reset.world.title": "Reset World", "mco.reset.world.upload": "Upload world", "mco.reset.world.warning": "This will replace the current world of your realm", - "mco.selectServer.buy": "Buy a realm!", + "mco.selectServer.buy": "Buy a Realm!", "mco.selectServer.close": "Close", "mco.selectServer.closed": "Closed realm", "mco.selectServer.closeserver": "Close realm", "mco.selectServer.configure": "Configure", "mco.selectServer.configureRealm": "Configure realm", - "mco.selectServer.create": "Create realm", + "mco.selectServer.create": "Create Realm", "mco.selectServer.create.subtitle": "Select what world to put on your new realm", "mco.selectServer.expired": "Expired realm", "mco.selectServer.expiredList": "Your subscription has expired", @@ -4601,10 +4732,11 @@ "mco.selectServer.expires.day": "Expires in a day", "mco.selectServer.expires.days": "Expires in %s days", "mco.selectServer.expires.soon": "Expires soon", - "mco.selectServer.leave": "Leave realm", + "mco.selectServer.leave": "Leave Realm", "mco.selectServer.loading": "Loading Realms List", "mco.selectServer.mapOnlySupportedForVersion": "This map is unsupported in %s", "mco.selectServer.minigame": "Minigame:", + "mco.selectServer.minigameName": "Minigame: %s", "mco.selectServer.minigameNotSupportedInVersion": "Can't play this minigame in %s", "mco.selectServer.noRealms": "You don't seem to have a Realm. Add a Realm to play together with your friends.", "mco.selectServer.note": "Note:", @@ -4613,8 +4745,22 @@ "mco.selectServer.play": "Play", "mco.selectServer.popup": "Realms is a safe, simple way to enjoy an online Minecraft world with up to ten friends at a time. It supports loads of minigames and plenty of custom worlds! Only the owner of the realm needs to pay.", "mco.selectServer.purchase": "Add Realm", - "mco.selectServer.trial": "Get a trial!", + "mco.selectServer.trial": "Get a Trial!", "mco.selectServer.uninitialized": "Click to start your new realm!", + "mco.snapshot.createSnapshotPopup.text": "You are about to create a free Snapshot Realm that will be paired with your paid Realms subscription. This new Snapshot Realm will be accessible for as long as the paid subscription is active. Your paid Realm will not be affected.", + "mco.snapshot.createSnapshotPopup.title": "Create Snapshot Realm?", + "mco.snapshot.creating": "Creating Snapshot Realm...", + "mco.snapshot.description": "Paired with \"%s\"", + "mco.snapshot.friendsRealm.downgrade": "You need to be on version %s to join this Realm", + "mco.snapshot.friendsRealm.upgrade": "%s needs to upgrade their Realm before you can play from this version", + "mco.snapshot.paired": "This Snapshot Realm is paired with \"%s\"", + "mco.snapshot.parent.tooltip": "Use the latest release of Minecraft to play on this Realm", + "mco.snapshot.start": "Start free Snapshot Realm", + "mco.snapshot.subscription.info": "This is a Snapshot Realm that is paired to the subscription of your Realm '%s'. It will stay active for as long as its paired Realm is.", + "mco.snapshot.tooltip": "Use Snapshot Realms to get a sneak peek at upcoming versions of Minecraft, which might include new features and other changes.\n\nYou can find your normal Realms in the release version of the game.", + "mco.snapshotRealmsPopup.message": "Realms are now available in Snapshots starting with Snapshot 23w41a. Every Realms subscription comes with a free Snapshot Realm that is separate from your normal Java Realm!", + "mco.snapshotRealmsPopup.title": "Realms now available in Snapshots", + "mco.snapshotRealmsPopup.urlText": "Learn More", "mco.template.button.publisher": "Publisher", "mco.template.button.select": "Select", "mco.template.button.trailer": "Trailer", @@ -4653,11 +4799,12 @@ "mco.upload.preparing": "Preparing your world", "mco.upload.select.world.none": "No singleplayer worlds found!", "mco.upload.select.world.subtitle": "Please select a singleplayer world to upload", - "mco.upload.select.world.title": "Upload world", + "mco.upload.select.world.title": "Upload World", "mco.upload.size.failure.line1": "'%s' is too big!", "mco.upload.size.failure.line2": "It is %s. The maximum allowed size is %s.", "mco.upload.uploading": "Uploading '%s'", "mco.upload.verifying": "Verifying your world", + "mco.version": "Version: %s", "mco.warning": "Warning!", "mco.worldSlot.minigame": "Minigame", "menu.convertingLevel": "Converting world", @@ -4847,6 +4994,7 @@ "options.accessibility.high_contrast.tooltip": "Enhances the contrast of UI elements", "options.accessibility.link": "Accessibility Guide", "options.accessibility.narrator_hotkey": "Narrator Hotkey", + "options.accessibility.narrator_hotkey.mac.tooltip": "Allows the Narrator to be toggled on and off with 'Cmd+B'", "options.accessibility.narrator_hotkey.tooltip": "Allows the Narrator to be toggled on and off with 'Ctrl+B'", "options.accessibility.panorama_speed": "Panorama Scroll Speed", "options.accessibility.text_background": "Text Background", @@ -4967,6 +5115,8 @@ "options.hideLightningFlashes.tooltip": "Prevents Lightning Bolts from making the sky flash. The bolts themselves will still be visible.", "options.hideMatchedNames": "Hide Matched Names", "options.hideMatchedNames.tooltip": "3rd-party Servers may send chat messages in non-standard formats.\nWith this option on, hidden players will be matched based on chat sender names.", + "options.hideSplashTexts": "Hide Splash Texts", + "options.hideSplashTexts.tooltip": "Hides the yellow splash text in the main menu.", "options.invertMouse": "Invert Mouse", "options.key.hold": "Hold", "options.key.toggle": "Toggle", @@ -5173,6 +5323,21 @@ "recipe.toast.description": "Check your recipe book", "recipe.toast.title": "New Recipes Unlocked!", "record.nowPlaying": "Now Playing: %s", + "recover_world.bug_tracker": "Report a Bug", + "recover_world.button": "Attempt to Recover", + "recover_world.done.failed": "Failed to recover from previous state.", + "recover_world.done.success": "Recovery was successful!", + "recover_world.done.title": "Recovery done", + "recover_world.issue.missing_file": "Missing file", + "recover_world.issue.none": "No issues", + "recover_world.message": "The following issues occurred while trying to read world folder \"%s\".\nIt might be possible to restore the world from an older state or you can report this issue on the bug tracker.", + "recover_world.no_fallback": "No state to recover from available", + "recover_world.restore": "Attempt to Restore", + "recover_world.restoring": "Attempting to restore world...", + "recover_world.state_entry": "State from %s: ", + "recover_world.state_entry.unknown": "unknown", + "recover_world.title": "Failed to load world", + "recover_world.warning": "Failed to load world summary", "resourcePack.broken_assets": "BROKEN ASSETS DETECTED", "resourcepack.downloading": "Downloading Resource Pack", "resourcePack.high_contrast.name": "High Contrast", @@ -5209,9 +5374,9 @@ "selectWorld.backupQuestion.experimental": "Worlds using Experimental Settings are not supported", "selectWorld.backupQuestion.snapshot": "Do you really want to load this world?", "selectWorld.backupWarning.customized": "Unfortunately, we do not support customized worlds in this version of Minecraft. We can still load this world and keep everything the way it was, but any newly generated terrain will no longer be customized. We're sorry for the inconvenience!", - "selectWorld.backupWarning.downgrade": "This world was last played in version %s; you are on version %s. Downgrading a world could cause corruption - we cannot guarantee that it will load or work. If you still want to continue, please make a backup!", + "selectWorld.backupWarning.downgrade": "This world was last played in version %s; you are on version %s. Downgrading a world could cause corruption - we cannot guarantee that it will load or work. If you still want to continue, please make a backup.", "selectWorld.backupWarning.experimental": "This world uses experimental settings that could stop working at any time. We cannot guarantee it will load or work. Here be dragons!", - "selectWorld.backupWarning.snapshot": "This world was last played in version %s; you are on version %s. Please make a backup in case you experience world corruptions!", + "selectWorld.backupWarning.snapshot": "This world was last played in version %s; you are on version %s. Please make a backup in case you experience world corruptions.", "selectWorld.bonusItems": "Bonus Chest", "selectWorld.cheats": "Cheats", "selectWorld.conversion": "Must be converted!", @@ -5278,6 +5443,10 @@ "selectWorld.import_worldgen_settings.failure": "Error importing settings", "selectWorld.import_worldgen_settings.select_file": "Select settings file (.json)", "selectWorld.incompatible_series": "Created by an incompatible version", + "selectWorld.incompatible.description": "This world cannot be opened in this version.\nIt was last played in version %s.", + "selectWorld.incompatible.info": "Incompatible version: %s", + "selectWorld.incompatible.title": "Incompatible version", + "selectWorld.incompatible.tooltip": "This world cannot be opened because it was created by an incompatible version.", "selectWorld.load_folder_access": "Unable to read or access folder where game worlds are saved!", "selectWorld.loading_list": "Loading World List", "selectWorld.locked": "Locked by another running instance of Minecraft", @@ -5292,6 +5461,7 @@ "selectWorld.recreate.customized.title": "Customized worlds are no longer supported", "selectWorld.recreate.error.text": "Something went wrong while trying to recreate a world.", "selectWorld.recreate.error.title": "An error occurred!", + "selectWorld.resource_load": "Preparing Resources...", "selectWorld.resultFolder": "Will be saved in:", "selectWorld.search": "search for worlds", "selectWorld.seedInfo": "Leave blank for a random seed", @@ -5514,7 +5684,15 @@ "subtitles.block.conduit.ambient": "Conduit pulses", "subtitles.block.conduit.attack.target": "Conduit attacks", "subtitles.block.conduit.deactivate": "Conduit deactivates", - "subtitles.block.decorated_pot.shatter": "Pot shatters", + "subtitles.block.copper_bulb.turn_off": "Copper Bulb turns off", + "subtitles.block.copper_bulb.turn_on": "Copper Bulb turns on", + "subtitles.block.copper_trapdoor.close": "Trapdoor closes", + "subtitles.block.copper_trapdoor.open": "Trapdoor opens", + "subtitles.block.crafter.craft": "Crafter crafts", + "subtitles.block.crafter.fail": "Crafter fails crafting", + "subtitles.block.decorated_pot.insert": "Decorated Pot fills", + "subtitles.block.decorated_pot.insert_fail": "Decorated Pot wobbles", + "subtitles.block.decorated_pot.shatter": "Decorated Pot shatters", "subtitles.block.dispenser.dispense": "Dispensed item", "subtitles.block.dispenser.fail": "Dispenser failed", "subtitles.block.door.toggle": "Door creaks", @@ -5532,6 +5710,7 @@ "subtitles.block.generic.place": "Block placed", "subtitles.block.grindstone.use": "Grindstone used", "subtitles.block.growing_plant.crop": "Plant cropped", + "subtitles.block.hanging_sign.waxed_interact_fail": "Sign wobbles", "subtitles.block.honey_block.slide": "Sliding down a honey block", "subtitles.block.iron_trapdoor.close": "Trapdoor closes", "subtitles.block.iron_trapdoor.open": "Trapdoor opens", @@ -5572,6 +5751,12 @@ "subtitles.block.sponge.absorb": "Sponge sucks", "subtitles.block.sweet_berry_bush.pick_berries": "Berries pop", "subtitles.block.trapdoor.toggle": "Trapdoor creaks", + "subtitles.block.trial_spawner.ambient": "Trial Spawner crackles", + "subtitles.block.trial_spawner.close_shutter": "Trial Spawner closes", + "subtitles.block.trial_spawner.detect_player": "Trial Spawner charges up", + "subtitles.block.trial_spawner.eject_item": "Trial Spawner ejects items", + "subtitles.block.trial_spawner.open_shutter": "Trial Spawner opens", + "subtitles.block.trial_spawner.spawn_mob": "Mob spawns", "subtitles.block.tripwire.attach": "Tripwire attaches", "subtitles.block.tripwire.click": "Tripwire clicks", "subtitles.block.tripwire.detach": "Tripwire detaches", @@ -5617,6 +5802,15 @@ "subtitles.entity.blaze.shoot": "Blaze shoots", "subtitles.entity.boat.paddle_land": "Rowing", "subtitles.entity.boat.paddle_water": "Rowing", + "subtitles.entity.breeze.death": "Breeze dies", + "subtitles.entity.breeze.hurt": "Breeze hurts", + "subtitles.entity.breeze.idle_air": "Breeze flies", + "subtitles.entity.breeze.idle_ground": "Breeze whirs", + "subtitles.entity.breeze.inhale": "Breeze inhales", + "subtitles.entity.breeze.jump": "Breeze jumps", + "subtitles.entity.breeze.land": "Breeze lands", + "subtitles.entity.breeze.shoot": "Breeze shoots", + "subtitles.entity.breeze.slide": "Breeze slides", "subtitles.entity.camel.ambient": "Camel grunts", "subtitles.entity.camel.dash": "Camel yeets", "subtitles.entity.camel.dash_ready": "Camel recovers", @@ -5741,6 +5935,7 @@ "subtitles.entity.generic.small_fall": "Something trips", "subtitles.entity.generic.splash": "Splashing", "subtitles.entity.generic.swim": "Swimming", + "subtitles.entity.generic.wind_burst": "Wind Charge bursts", "subtitles.entity.ghast.ambient": "Ghast cries", "subtitles.entity.ghast.death": "Ghast dies", "subtitles.entity.ghast.hurt": "Ghast hurts", @@ -5858,6 +6053,7 @@ "subtitles.entity.parrot.fly": "Parrot flutters", "subtitles.entity.parrot.hurts": "Parrot hurts", "subtitles.entity.parrot.imitate.blaze": "Parrot breathes", + "subtitles.entity.parrot.imitate.breeze": "Parrot whirs", "subtitles.entity.parrot.imitate.creeper": "Parrot hisses", "subtitles.entity.parrot.imitate.drowned": "Parrot gurgles", "subtitles.entity.parrot.imitate.elder_guardian": "Parrot moans", @@ -5932,6 +6128,7 @@ "subtitles.entity.player.hurt_drown": "Player drowning", "subtitles.entity.player.hurt_on_fire": "Player burns", "subtitles.entity.player.levelup": "Player dings", + "subtitles.entity.player.teleport": "Player teleports", "subtitles.entity.polar_bear.ambient": "Polar Bear groans", "subtitles.entity.polar_bear.ambient_baby": "Polar Bear hums", "subtitles.entity.polar_bear.death": "Polar Bear dies", @@ -6213,6 +6410,7 @@ "symlink_warning.message": "Loading worlds from folders with symbolic links can be unsafe if you don't know exactly what you are doing. Please visit %s to learn more.", "symlink_warning.message.pack": "Loading packs with symbolic links can be unsafe if you don't know exactly what you are doing. Please visit %s to learn more.", "symlink_warning.message.world": "Loading worlds from folders with symbolic links can be unsafe if you don't know exactly what you are doing. Please visit %s to learn more.", + "symlink_warning.more_info": "More Information", "symlink_warning.title": "World folder contains symbolic links", "symlink_warning.title.pack": "Added pack(s) contain(s) symbolic links", "symlink_warning.title.world": "The world folder contains symbolic links", @@ -6227,7 +6425,8 @@ "team.visibility.never": "Never", "telemetry_info.button.give_feedback": "Give Feedback", "telemetry_info.button.privacy_statement": "Privacy Statement", - "telemetry_info.button.show_data": "Open My Data", + "telemetry_info.button.show_data": "View My Data", + "telemetry_info.opt_in.description": "I consent to sending optional telemetry data", "telemetry_info.property_title": "Included Data", "telemetry_info.screen.description": "Collecting this data helps us improve Minecraft by guiding us in directions that are relevant to our players.\nYou can also send in additional feedback to help us keep improving Minecraft.", "telemetry_info.screen.title": "Telemetry Data Collection", @@ -6236,6 +6435,7 @@ "telemetry.event.game_load_times.description": "This event can help us figure out where startup performance improvements are needed by measuring the execution times of the startup phases.", "telemetry.event.game_load_times.title": "Game Load Times", "telemetry.event.optional": "%s (Optional)", + "telemetry.event.optional.disabled": "%s (Optional) - Disabled", "telemetry.event.performance_metrics.description": "Knowing the overall performance profile of Minecraft helps us tune and optimize the game for a wide range of machine specifications and operating systems. \nGame version is included to help us compare the performance profile for new versions of Minecraft.", "telemetry.event.performance_metrics.title": "Performance Metrics", "telemetry.event.required": "%s (Required)", diff --git a/azalea-physics/Cargo.toml b/azalea-physics/Cargo.toml index f79ddf43d..9652fdb22 100644 --- a/azalea-physics/Cargo.toml +++ b/azalea-physics/Cargo.toml @@ -4,23 +4,23 @@ edition = "2021" license = "MIT" name = "azalea-physics" repository = "https://github.com/azalea-rs/azalea/tree/main/azalea-physics" -version = "0.8.0" +version = "0.9.0" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -azalea-block = { path = "../azalea-block", version = "0.8.0" } -azalea-core = { path = "../azalea-core", version = "0.8.0" } -azalea-entity = { version = "0.8.0", path = "../azalea-entity" } -azalea-inventory = { version = "0.8.0", path = "../azalea-inventory" } -azalea-registry = { path = "../azalea-registry", version = "0.8.0" } -azalea-world = { path = "../azalea-world", version = "0.8.0" } -bevy_app = "0.12.0" -bevy_ecs = "0.12.0" +azalea-block = { path = "../azalea-block", version = "0.9.0" } +azalea-core = { path = "../azalea-core", version = "0.9.0" } +azalea-entity = { version = "0.9.0", path = "../azalea-entity" } +azalea-inventory = { version = "0.9.0", path = "../azalea-inventory" } +azalea-registry = { path = "../azalea-registry", version = "0.9.0" } +azalea-world = { path = "../azalea-world", version = "0.9.0" } +bevy_app = "0.12.1" +bevy_ecs = "0.12.1" tracing = "0.1.40" once_cell = "1.18.0" parking_lot = "^0.12.1" [dev-dependencies] -bevy_time = "0.12.0" -uuid = "^1.5.0" +bevy_time = "0.12.1" +uuid = "^1.6.1" diff --git a/azalea-physics/src/collision/blocks.rs b/azalea-physics/src/collision/blocks.rs index f6baea9f0..aec40f37d 100644 --- a/azalea-physics/src/collision/blocks.rs +++ b/azalea-physics/src/collision/blocks.rs @@ -1752,8 +1752,8 @@ impl BlockWithShape for BlockState { | 5716..=5733 | 5738..=5772 | 5799..=5814 - | 5859..=5863 - | 5865..=5866 + | 5858..=5862 + | 5864..=5865 | 6813..=6998 | 7001..=7002 | 7005..=7006 @@ -1907,23 +1907,29 @@ impl BlockWithShape for BlockState { | 20372..=20397 | 20404 | 20407 - | 21084 - | 21566..=21693 - | 22455..=22509 - | 22513..=22528 - | 22536..=22537 - | 22544..=22545 - | 22552..=22553 - | 22560..=22587 - | 22686 - | 22689 - | 23097 - | 23100 - | 23508 - | 23511 - | 23919 - | 23922 - | 24258 => &SHAPE0, + | 21174 + | 21177 + | 21585 + | 21588 + | 21997 + | 22000 + | 22318 + | 22800..=22927 + | 24769..=24823 + | 24827..=24842 + | 24850..=24851 + | 24858..=24859 + | 24866..=24867 + | 24874..=24901 + | 25000 + | 25003 + | 25411 + | 25414 + | 25822 + | 25825 + | 26233 + | 26236 + | 26572 => &SHAPE0, 1688 | 1690 | 1693 | 1695 | 1704 | 1706 | 1709 | 1711 | 1720 | 1722 | 1725 | 1727 | 1736 | 1738 | 1741 | 1743 | 1752 | 1754 | 1757 | 1759 | 1768 | 1770 | 1773 | 1775 | 1784 | 1786 | 1789 | 1791 | 1800 | 1802 | 1805 | 1807 | 1816 | 1818 | 1821 | 1823 @@ -2091,18 +2097,21 @@ impl BlockWithShape for BlockState { | 19461..=19462 | 19881..=19882 | 20286..=20287 - | 21714..=21715 - | 21794..=21795 - | 21874..=21875 - | 21954..=21955 - | 22066..=22067 - | 22146..=22147 - | 22226..=22227 - | 22306..=22307 - | 22594..=22595 - | 23005..=23006 - | 23416..=23417 - | 23827..=23828 => &SHAPE29, + | 21088..=21089 + | 21499..=21500 + | 21911..=21912 + | 22956..=22957 + | 23036..=23037 + | 23116..=23117 + | 23196..=23197 + | 23308..=23309 + | 23388..=23389 + | 23468..=23469 + | 23548..=23549 + | 24908..=24909 + | 25319..=25320 + | 25730..=25731 + | 26141..=26142 => &SHAPE29, 2876..=2877 | 2918..=2919 | 4684..=4685 @@ -2185,30 +2194,36 @@ impl BlockWithShape for BlockState { | 19925..=19926 | 20288..=20289 | 20330..=20331 - | 21716..=21717 - | 21758..=21759 - | 21796..=21797 - | 21838..=21839 - | 21876..=21877 - | 21918..=21919 - | 21956..=21957 - | 21998..=21999 - | 22068..=22069 - | 22110..=22111 - | 22148..=22149 - | 22190..=22191 - | 22228..=22229 - | 22270..=22271 - | 22308..=22309 - | 22350..=22351 - | 22596..=22597 - | 22638..=22639 - | 23007..=23008 - | 23049..=23050 - | 23418..=23419 - | 23460..=23461 - | 23829..=23830 - | 23871..=23872 => &SHAPE30, + | 21090..=21091 + | 21132..=21133 + | 21501..=21502 + | 21543..=21544 + | 21913..=21914 + | 21955..=21956 + | 22958..=22959 + | 23000..=23001 + | 23038..=23039 + | 23080..=23081 + | 23118..=23119 + | 23160..=23161 + | 23198..=23199 + | 23240..=23241 + | 23310..=23311 + | 23352..=23353 + | 23390..=23391 + | 23432..=23433 + | 23470..=23471 + | 23512..=23513 + | 23550..=23551 + | 23592..=23593 + | 24910..=24911 + | 24952..=24953 + | 25321..=25322 + | 25363..=25364 + | 25732..=25733 + | 25774..=25775 + | 26143..=26144 + | 26185..=26186 => &SHAPE30, 2878..=2879 | 2936..=2937 | 4686..=4687 @@ -2291,30 +2306,36 @@ impl BlockWithShape for BlockState { | 19943..=19944 | 20290..=20291 | 20348..=20349 - | 21718..=21719 - | 21776..=21777 - | 21798..=21799 - | 21856..=21857 - | 21878..=21879 - | 21936..=21937 - | 21958..=21959 - | 22016..=22017 - | 22070..=22071 - | 22128..=22129 - | 22150..=22151 - | 22208..=22209 - | 22230..=22231 - | 22288..=22289 - | 22310..=22311 - | 22368..=22369 - | 22598..=22599 - | 22656..=22657 - | 23009..=23010 - | 23067..=23068 - | 23420..=23421 - | 23478..=23479 - | 23831..=23832 - | 23889..=23890 => &SHAPE31, + | 21092..=21093 + | 21150..=21151 + | 21503..=21504 + | 21561..=21562 + | 21915..=21916 + | 21973..=21974 + | 22960..=22961 + | 23018..=23019 + | 23040..=23041 + | 23098..=23099 + | 23120..=23121 + | 23178..=23179 + | 23200..=23201 + | 23258..=23259 + | 23312..=23313 + | 23370..=23371 + | 23392..=23393 + | 23450..=23451 + | 23472..=23473 + | 23530..=23531 + | 23552..=23553 + | 23610..=23611 + | 24912..=24913 + | 24970..=24971 + | 25323..=25324 + | 25381..=25382 + | 25734..=25735 + | 25792..=25793 + | 26145..=26146 + | 26203..=26204 => &SHAPE31, 2880..=2881 | 2922..=2923 | 4688..=4689 @@ -2397,30 +2418,36 @@ impl BlockWithShape for BlockState { | 19929..=19930 | 20292..=20293 | 20334..=20335 - | 21720..=21721 - | 21762..=21763 - | 21800..=21801 - | 21842..=21843 - | 21880..=21881 - | 21922..=21923 - | 21960..=21961 - | 22002..=22003 - | 22072..=22073 - | 22114..=22115 - | 22152..=22153 - | 22194..=22195 - | 22232..=22233 - | 22274..=22275 - | 22312..=22313 - | 22354..=22355 - | 22600..=22601 - | 22642..=22643 - | 23011..=23012 - | 23053..=23054 - | 23422..=23423 - | 23464..=23465 - | 23833..=23834 - | 23875..=23876 => &SHAPE32, + | 21094..=21095 + | 21136..=21137 + | 21505..=21506 + | 21547..=21548 + | 21917..=21918 + | 21959..=21960 + | 22962..=22963 + | 23004..=23005 + | 23042..=23043 + | 23084..=23085 + | 23122..=23123 + | 23164..=23165 + | 23202..=23203 + | 23244..=23245 + | 23314..=23315 + | 23356..=23357 + | 23394..=23395 + | 23436..=23437 + | 23474..=23475 + | 23516..=23517 + | 23554..=23555 + | 23596..=23597 + | 24914..=24915 + | 24956..=24957 + | 25325..=25326 + | 25367..=25368 + | 25736..=25737 + | 25778..=25779 + | 26147..=26148 + | 26189..=26190 => &SHAPE32, 2882..=2883 | 2940..=2941 | 4690..=4691 @@ -2503,30 +2530,36 @@ impl BlockWithShape for BlockState { | 19947..=19948 | 20294..=20295 | 20352..=20353 - | 21722..=21723 - | 21780..=21781 - | 21802..=21803 - | 21860..=21861 - | 21882..=21883 - | 21940..=21941 - | 21962..=21963 - | 22020..=22021 - | 22074..=22075 - | 22132..=22133 - | 22154..=22155 - | 22212..=22213 - | 22234..=22235 - | 22292..=22293 - | 22314..=22315 - | 22372..=22373 - | 22602..=22603 - | 22660..=22661 - | 23013..=23014 - | 23071..=23072 - | 23424..=23425 - | 23482..=23483 - | 23835..=23836 - | 23893..=23894 => &SHAPE33, + | 21096..=21097 + | 21154..=21155 + | 21507..=21508 + | 21565..=21566 + | 21919..=21920 + | 21977..=21978 + | 22964..=22965 + | 23022..=23023 + | 23044..=23045 + | 23102..=23103 + | 23124..=23125 + | 23182..=23183 + | 23204..=23205 + | 23262..=23263 + | 23316..=23317 + | 23374..=23375 + | 23396..=23397 + | 23454..=23455 + | 23476..=23477 + | 23534..=23535 + | 23556..=23557 + | 23614..=23615 + | 24916..=24917 + | 24974..=24975 + | 25327..=25328 + | 25385..=25386 + | 25738..=25739 + | 25796..=25797 + | 26149..=26150 + | 26207..=26208 => &SHAPE33, 2884..=2885 | 4692..=4693 | 7039..=7040 @@ -2568,18 +2601,21 @@ impl BlockWithShape for BlockState { | 19471..=19472 | 19891..=19892 | 20296..=20297 - | 21724..=21725 - | 21804..=21805 - | 21884..=21885 - | 21964..=21965 - | 22076..=22077 - | 22156..=22157 - | 22236..=22237 - | 22316..=22317 - | 22604..=22605 - | 23015..=23016 - | 23426..=23427 - | 23837..=23838 => &SHAPE34, + | 21098..=21099 + | 21509..=21510 + | 21921..=21922 + | 22966..=22967 + | 23046..=23047 + | 23126..=23127 + | 23206..=23207 + | 23318..=23319 + | 23398..=23399 + | 23478..=23479 + | 23558..=23559 + | 24918..=24919 + | 25329..=25330 + | 25740..=25741 + | 26151..=26152 => &SHAPE34, 2886..=2887 | 2928..=2929 | 4694..=4695 @@ -2662,30 +2698,36 @@ impl BlockWithShape for BlockState { | 19935..=19936 | 20298..=20299 | 20340..=20341 - | 21726..=21727 - | 21768..=21769 - | 21806..=21807 - | 21848..=21849 - | 21886..=21887 - | 21928..=21929 - | 21966..=21967 - | 22008..=22009 - | 22078..=22079 - | 22120..=22121 - | 22158..=22159 - | 22200..=22201 - | 22238..=22239 - | 22280..=22281 - | 22318..=22319 - | 22360..=22361 - | 22606..=22607 - | 22648..=22649 - | 23017..=23018 - | 23059..=23060 - | 23428..=23429 - | 23470..=23471 - | 23839..=23840 - | 23881..=23882 => &SHAPE35, + | 21100..=21101 + | 21142..=21143 + | 21511..=21512 + | 21553..=21554 + | 21923..=21924 + | 21965..=21966 + | 22968..=22969 + | 23010..=23011 + | 23048..=23049 + | 23090..=23091 + | 23128..=23129 + | 23170..=23171 + | 23208..=23209 + | 23250..=23251 + | 23320..=23321 + | 23362..=23363 + | 23400..=23401 + | 23442..=23443 + | 23480..=23481 + | 23522..=23523 + | 23560..=23561 + | 23602..=23603 + | 24920..=24921 + | 24962..=24963 + | 25331..=25332 + | 25373..=25374 + | 25742..=25743 + | 25784..=25785 + | 26153..=26154 + | 26195..=26196 => &SHAPE35, 2888..=2889 | 2946..=2947 | 4696..=4697 @@ -2768,30 +2810,36 @@ impl BlockWithShape for BlockState { | 19953..=19954 | 20300..=20301 | 20358..=20359 - | 21728..=21729 - | 21786..=21787 - | 21808..=21809 - | 21866..=21867 - | 21888..=21889 - | 21946..=21947 - | 21968..=21969 - | 22026..=22027 - | 22080..=22081 - | 22138..=22139 - | 22160..=22161 - | 22218..=22219 - | 22240..=22241 - | 22298..=22299 - | 22320..=22321 - | 22378..=22379 - | 22608..=22609 - | 22666..=22667 - | 23019..=23020 - | 23077..=23078 - | 23430..=23431 - | 23488..=23489 - | 23841..=23842 - | 23899..=23900 => &SHAPE36, + | 21102..=21103 + | 21160..=21161 + | 21513..=21514 + | 21571..=21572 + | 21925..=21926 + | 21983..=21984 + | 22970..=22971 + | 23028..=23029 + | 23050..=23051 + | 23108..=23109 + | 23130..=23131 + | 23188..=23189 + | 23210..=23211 + | 23268..=23269 + | 23322..=23323 + | 23380..=23381 + | 23402..=23403 + | 23460..=23461 + | 23482..=23483 + | 23540..=23541 + | 23562..=23563 + | 23620..=23621 + | 24922..=24923 + | 24980..=24981 + | 25333..=25334 + | 25391..=25392 + | 25744..=25745 + | 25802..=25803 + | 26155..=26156 + | 26213..=26214 => &SHAPE36, 2890..=2891 | 2932..=2933 | 4698..=4699 @@ -2874,30 +2922,36 @@ impl BlockWithShape for BlockState { | 19939..=19940 | 20302..=20303 | 20344..=20345 - | 21730..=21731 - | 21772..=21773 - | 21810..=21811 - | 21852..=21853 - | 21890..=21891 - | 21932..=21933 - | 21970..=21971 - | 22012..=22013 - | 22082..=22083 - | 22124..=22125 - | 22162..=22163 - | 22204..=22205 - | 22242..=22243 - | 22284..=22285 - | 22322..=22323 - | 22364..=22365 - | 22610..=22611 - | 22652..=22653 - | 23021..=23022 - | 23063..=23064 - | 23432..=23433 - | 23474..=23475 - | 23843..=23844 - | 23885..=23886 => &SHAPE37, + | 21104..=21105 + | 21146..=21147 + | 21515..=21516 + | 21557..=21558 + | 21927..=21928 + | 21969..=21970 + | 22972..=22973 + | 23014..=23015 + | 23052..=23053 + | 23094..=23095 + | 23132..=23133 + | 23174..=23175 + | 23212..=23213 + | 23254..=23255 + | 23324..=23325 + | 23366..=23367 + | 23404..=23405 + | 23446..=23447 + | 23484..=23485 + | 23526..=23527 + | 23564..=23565 + | 23606..=23607 + | 24924..=24925 + | 24966..=24967 + | 25335..=25336 + | 25377..=25378 + | 25746..=25747 + | 25788..=25789 + | 26157..=26158 + | 26199..=26200 => &SHAPE37, 2892..=2893 | 2950..=2951 | 4700..=4701 @@ -2980,30 +3034,36 @@ impl BlockWithShape for BlockState { | 19957..=19958 | 20304..=20305 | 20362..=20363 - | 21732..=21733 - | 21790..=21791 - | 21812..=21813 - | 21870..=21871 - | 21892..=21893 - | 21950..=21951 - | 21972..=21973 - | 22030..=22031 - | 22084..=22085 - | 22142..=22143 - | 22164..=22165 - | 22222..=22223 - | 22244..=22245 - | 22302..=22303 - | 22324..=22325 - | 22382..=22383 - | 22612..=22613 - | 22670..=22671 - | 23023..=23024 - | 23081..=23082 - | 23434..=23435 - | 23492..=23493 - | 23845..=23846 - | 23903..=23904 => &SHAPE38, + | 21106..=21107 + | 21164..=21165 + | 21517..=21518 + | 21575..=21576 + | 21929..=21930 + | 21987..=21988 + | 22974..=22975 + | 23032..=23033 + | 23054..=23055 + | 23112..=23113 + | 23134..=23135 + | 23192..=23193 + | 23214..=23215 + | 23272..=23273 + | 23326..=23327 + | 23384..=23385 + | 23406..=23407 + | 23464..=23465 + | 23486..=23487 + | 23544..=23545 + | 23566..=23567 + | 23624..=23625 + | 24926..=24927 + | 24984..=24985 + | 25337..=25338 + | 25395..=25396 + | 25748..=25749 + | 25806..=25807 + | 26159..=26160 + | 26217..=26218 => &SHAPE38, 2894..=2895 | 4702..=4703 | 7049..=7050 @@ -3045,18 +3105,21 @@ impl BlockWithShape for BlockState { | 19481..=19482 | 19901..=19902 | 20306..=20307 - | 21734..=21735 - | 21814..=21815 - | 21894..=21895 - | 21974..=21975 - | 22086..=22087 - | 22166..=22167 - | 22246..=22247 - | 22326..=22327 - | 22614..=22615 - | 23025..=23026 - | 23436..=23437 - | 23847..=23848 => &SHAPE39, + | 21108..=21109 + | 21519..=21520 + | 21931..=21932 + | 22976..=22977 + | 23056..=23057 + | 23136..=23137 + | 23216..=23217 + | 23328..=23329 + | 23408..=23409 + | 23488..=23489 + | 23568..=23569 + | 24928..=24929 + | 25339..=25340 + | 25750..=25751 + | 26161..=26162 => &SHAPE39, 2896..=2897 | 2938..=2939 | 4704..=4705 @@ -3139,30 +3202,36 @@ impl BlockWithShape for BlockState { | 19945..=19946 | 20308..=20309 | 20350..=20351 - | 21736..=21737 - | 21778..=21779 - | 21816..=21817 - | 21858..=21859 - | 21896..=21897 - | 21938..=21939 - | 21976..=21977 - | 22018..=22019 - | 22088..=22089 - | 22130..=22131 - | 22168..=22169 - | 22210..=22211 - | 22248..=22249 - | 22290..=22291 - | 22328..=22329 - | 22370..=22371 - | 22616..=22617 - | 22658..=22659 - | 23027..=23028 - | 23069..=23070 - | 23438..=23439 - | 23480..=23481 - | 23849..=23850 - | 23891..=23892 => &SHAPE40, + | 21110..=21111 + | 21152..=21153 + | 21521..=21522 + | 21563..=21564 + | 21933..=21934 + | 21975..=21976 + | 22978..=22979 + | 23020..=23021 + | 23058..=23059 + | 23100..=23101 + | 23138..=23139 + | 23180..=23181 + | 23218..=23219 + | 23260..=23261 + | 23330..=23331 + | 23372..=23373 + | 23410..=23411 + | 23452..=23453 + | 23490..=23491 + | 23532..=23533 + | 23570..=23571 + | 23612..=23613 + | 24930..=24931 + | 24972..=24973 + | 25341..=25342 + | 25383..=25384 + | 25752..=25753 + | 25794..=25795 + | 26163..=26164 + | 26205..=26206 => &SHAPE40, 2898..=2899 | 2916..=2917 | 4706..=4707 @@ -3245,30 +3314,36 @@ impl BlockWithShape for BlockState { | 19923..=19924 | 20310..=20311 | 20328..=20329 - | 21738..=21739 - | 21756..=21757 - | 21818..=21819 - | 21836..=21837 - | 21898..=21899 - | 21916..=21917 - | 21978..=21979 - | 21996..=21997 - | 22090..=22091 - | 22108..=22109 - | 22170..=22171 - | 22188..=22189 - | 22250..=22251 - | 22268..=22269 - | 22330..=22331 - | 22348..=22349 - | 22618..=22619 - | 22636..=22637 - | 23029..=23030 - | 23047..=23048 - | 23440..=23441 - | 23458..=23459 - | 23851..=23852 - | 23869..=23870 => &SHAPE41, + | 21112..=21113 + | 21130..=21131 + | 21523..=21524 + | 21541..=21542 + | 21935..=21936 + | 21953..=21954 + | 22980..=22981 + | 22998..=22999 + | 23060..=23061 + | 23078..=23079 + | 23140..=23141 + | 23158..=23159 + | 23220..=23221 + | 23238..=23239 + | 23332..=23333 + | 23350..=23351 + | 23412..=23413 + | 23430..=23431 + | 23492..=23493 + | 23510..=23511 + | 23572..=23573 + | 23590..=23591 + | 24932..=24933 + | 24950..=24951 + | 25343..=25344 + | 25361..=25362 + | 25754..=25755 + | 25772..=25773 + | 26165..=26166 + | 26183..=26184 => &SHAPE41, 2900..=2901 | 2942..=2943 | 4708..=4709 @@ -3351,30 +3426,36 @@ impl BlockWithShape for BlockState { | 19949..=19950 | 20312..=20313 | 20354..=20355 - | 21740..=21741 - | 21782..=21783 - | 21820..=21821 - | 21862..=21863 - | 21900..=21901 - | 21942..=21943 - | 21980..=21981 - | 22022..=22023 - | 22092..=22093 - | 22134..=22135 - | 22172..=22173 - | 22214..=22215 - | 22252..=22253 - | 22294..=22295 - | 22332..=22333 - | 22374..=22375 - | 22620..=22621 - | 22662..=22663 - | 23031..=23032 - | 23073..=23074 - | 23442..=23443 - | 23484..=23485 - | 23853..=23854 - | 23895..=23896 => &SHAPE42, + | 21114..=21115 + | 21156..=21157 + | 21525..=21526 + | 21567..=21568 + | 21937..=21938 + | 21979..=21980 + | 22982..=22983 + | 23024..=23025 + | 23062..=23063 + | 23104..=23105 + | 23142..=23143 + | 23184..=23185 + | 23222..=23223 + | 23264..=23265 + | 23334..=23335 + | 23376..=23377 + | 23414..=23415 + | 23456..=23457 + | 23494..=23495 + | 23536..=23537 + | 23574..=23575 + | 23616..=23617 + | 24934..=24935 + | 24976..=24977 + | 25345..=25346 + | 25387..=25388 + | 25756..=25757 + | 25798..=25799 + | 26167..=26168 + | 26209..=26210 => &SHAPE42, 2902..=2903 | 2920..=2921 | 4710..=4711 @@ -3457,30 +3538,36 @@ impl BlockWithShape for BlockState { | 19927..=19928 | 20314..=20315 | 20332..=20333 - | 21742..=21743 - | 21760..=21761 - | 21822..=21823 - | 21840..=21841 - | 21902..=21903 - | 21920..=21921 - | 21982..=21983 - | 22000..=22001 - | 22094..=22095 - | 22112..=22113 - | 22174..=22175 - | 22192..=22193 - | 22254..=22255 - | 22272..=22273 - | 22334..=22335 - | 22352..=22353 - | 22622..=22623 - | 22640..=22641 - | 23033..=23034 - | 23051..=23052 - | 23444..=23445 - | 23462..=23463 - | 23855..=23856 - | 23873..=23874 => &SHAPE43, + | 21116..=21117 + | 21134..=21135 + | 21527..=21528 + | 21545..=21546 + | 21939..=21940 + | 21957..=21958 + | 22984..=22985 + | 23002..=23003 + | 23064..=23065 + | 23082..=23083 + | 23144..=23145 + | 23162..=23163 + | 23224..=23225 + | 23242..=23243 + | 23336..=23337 + | 23354..=23355 + | 23416..=23417 + | 23434..=23435 + | 23496..=23497 + | 23514..=23515 + | 23576..=23577 + | 23594..=23595 + | 24936..=24937 + | 24954..=24955 + | 25347..=25348 + | 25365..=25366 + | 25758..=25759 + | 25776..=25777 + | 26169..=26170 + | 26187..=26188 => &SHAPE43, 2904..=2905 | 4712..=4713 | 7059..=7060 @@ -3522,18 +3609,21 @@ impl BlockWithShape for BlockState { | 19491..=19492 | 19911..=19912 | 20316..=20317 - | 21744..=21745 - | 21824..=21825 - | 21904..=21905 - | 21984..=21985 - | 22096..=22097 - | 22176..=22177 - | 22256..=22257 - | 22336..=22337 - | 22624..=22625 - | 23035..=23036 - | 23446..=23447 - | 23857..=23858 => &SHAPE44, + | 21118..=21119 + | 21529..=21530 + | 21941..=21942 + | 22986..=22987 + | 23066..=23067 + | 23146..=23147 + | 23226..=23227 + | 23338..=23339 + | 23418..=23419 + | 23498..=23499 + | 23578..=23579 + | 24938..=24939 + | 25349..=25350 + | 25760..=25761 + | 26171..=26172 => &SHAPE44, 2906..=2907 | 2948..=2949 | 4714..=4715 @@ -3616,30 +3706,36 @@ impl BlockWithShape for BlockState { | 19955..=19956 | 20318..=20319 | 20360..=20361 - | 21746..=21747 - | 21788..=21789 - | 21826..=21827 - | 21868..=21869 - | 21906..=21907 - | 21948..=21949 - | 21986..=21987 - | 22028..=22029 - | 22098..=22099 - | 22140..=22141 - | 22178..=22179 - | 22220..=22221 - | 22258..=22259 - | 22300..=22301 - | 22338..=22339 - | 22380..=22381 - | 22626..=22627 - | 22668..=22669 - | 23037..=23038 - | 23079..=23080 - | 23448..=23449 - | 23490..=23491 - | 23859..=23860 - | 23901..=23902 => &SHAPE45, + | 21120..=21121 + | 21162..=21163 + | 21531..=21532 + | 21573..=21574 + | 21943..=21944 + | 21985..=21986 + | 22988..=22989 + | 23030..=23031 + | 23068..=23069 + | 23110..=23111 + | 23148..=23149 + | 23190..=23191 + | 23228..=23229 + | 23270..=23271 + | 23340..=23341 + | 23382..=23383 + | 23420..=23421 + | 23462..=23463 + | 23500..=23501 + | 23542..=23543 + | 23580..=23581 + | 23622..=23623 + | 24940..=24941 + | 24982..=24983 + | 25351..=25352 + | 25393..=25394 + | 25762..=25763 + | 25804..=25805 + | 26173..=26174 + | 26215..=26216 => &SHAPE45, 2908..=2909 | 2926..=2927 | 4716..=4717 @@ -3722,30 +3818,36 @@ impl BlockWithShape for BlockState { | 19933..=19934 | 20320..=20321 | 20338..=20339 - | 21748..=21749 - | 21766..=21767 - | 21828..=21829 - | 21846..=21847 - | 21908..=21909 - | 21926..=21927 - | 21988..=21989 - | 22006..=22007 - | 22100..=22101 - | 22118..=22119 - | 22180..=22181 - | 22198..=22199 - | 22260..=22261 - | 22278..=22279 - | 22340..=22341 - | 22358..=22359 - | 22628..=22629 - | 22646..=22647 - | 23039..=23040 - | 23057..=23058 - | 23450..=23451 - | 23468..=23469 - | 23861..=23862 - | 23879..=23880 => &SHAPE46, + | 21122..=21123 + | 21140..=21141 + | 21533..=21534 + | 21551..=21552 + | 21945..=21946 + | 21963..=21964 + | 22990..=22991 + | 23008..=23009 + | 23070..=23071 + | 23088..=23089 + | 23150..=23151 + | 23168..=23169 + | 23230..=23231 + | 23248..=23249 + | 23342..=23343 + | 23360..=23361 + | 23422..=23423 + | 23440..=23441 + | 23502..=23503 + | 23520..=23521 + | 23582..=23583 + | 23600..=23601 + | 24942..=24943 + | 24960..=24961 + | 25353..=25354 + | 25371..=25372 + | 25764..=25765 + | 25782..=25783 + | 26175..=26176 + | 26193..=26194 => &SHAPE46, 2910..=2911 | 2952..=2953 | 4718..=4719 @@ -3828,30 +3930,36 @@ impl BlockWithShape for BlockState { | 19959..=19960 | 20322..=20323 | 20364..=20365 - | 21750..=21751 - | 21792..=21793 - | 21830..=21831 - | 21872..=21873 - | 21910..=21911 - | 21952..=21953 - | 21990..=21991 - | 22032..=22033 - | 22102..=22103 - | 22144..=22145 - | 22182..=22183 - | 22224..=22225 - | 22262..=22263 - | 22304..=22305 - | 22342..=22343 - | 22384..=22385 - | 22630..=22631 - | 22672..=22673 - | 23041..=23042 - | 23083..=23084 - | 23452..=23453 - | 23494..=23495 - | 23863..=23864 - | 23905..=23906 => &SHAPE47, + | 21124..=21125 + | 21166..=21167 + | 21535..=21536 + | 21577..=21578 + | 21947..=21948 + | 21989..=21990 + | 22992..=22993 + | 23034..=23035 + | 23072..=23073 + | 23114..=23115 + | 23152..=23153 + | 23194..=23195 + | 23232..=23233 + | 23274..=23275 + | 23344..=23345 + | 23386..=23387 + | 23424..=23425 + | 23466..=23467 + | 23504..=23505 + | 23546..=23547 + | 23584..=23585 + | 23626..=23627 + | 24944..=24945 + | 24986..=24987 + | 25355..=25356 + | 25397..=25398 + | 25766..=25767 + | 25808..=25809 + | 26177..=26178 + | 26219..=26220 => &SHAPE47, 2912..=2913 | 2930..=2931 | 4720..=4721 @@ -3934,30 +4042,36 @@ impl BlockWithShape for BlockState { | 19937..=19938 | 20324..=20325 | 20342..=20343 - | 21752..=21753 - | 21770..=21771 - | 21832..=21833 - | 21850..=21851 - | 21912..=21913 - | 21930..=21931 - | 21992..=21993 - | 22010..=22011 - | 22104..=22105 - | 22122..=22123 - | 22184..=22185 - | 22202..=22203 - | 22264..=22265 - | 22282..=22283 - | 22344..=22345 - | 22362..=22363 - | 22632..=22633 - | 22650..=22651 - | 23043..=23044 - | 23061..=23062 - | 23454..=23455 - | 23472..=23473 - | 23865..=23866 - | 23883..=23884 => &SHAPE48, + | 21126..=21127 + | 21144..=21145 + | 21537..=21538 + | 21555..=21556 + | 21949..=21950 + | 21967..=21968 + | 22994..=22995 + | 23012..=23013 + | 23074..=23075 + | 23092..=23093 + | 23154..=23155 + | 23172..=23173 + | 23234..=23235 + | 23252..=23253 + | 23346..=23347 + | 23364..=23365 + | 23426..=23427 + | 23444..=23445 + | 23506..=23507 + | 23524..=23525 + | 23586..=23587 + | 23604..=23605 + | 24946..=24947 + | 24964..=24965 + | 25357..=25358 + | 25375..=25376 + | 25768..=25769 + | 25786..=25787 + | 26179..=26180 + | 26197..=26198 => &SHAPE48, 2914..=2915 | 4722..=4723 | 7069..=7070 @@ -3999,18 +4113,21 @@ impl BlockWithShape for BlockState { | 19501..=19502 | 19921..=19922 | 20326..=20327 - | 21754..=21755 - | 21834..=21835 - | 21914..=21915 - | 21994..=21995 - | 22106..=22107 - | 22186..=22187 - | 22266..=22267 - | 22346..=22347 - | 22634..=22635 - | 23045..=23046 - | 23456..=23457 - | 23867..=23868 => &SHAPE49, + | 21128..=21129 + | 21539..=21540 + | 21951..=21952 + | 22996..=22997 + | 23076..=23077 + | 23156..=23157 + | 23236..=23237 + | 23348..=23349 + | 23428..=23429 + | 23508..=23509 + | 23588..=23589 + | 24948..=24949 + | 25359..=25360 + | 25770..=25771 + | 26181..=26182 => &SHAPE49, 2924..=2925 | 4732..=4733 | 7079..=7080 @@ -4052,18 +4169,21 @@ impl BlockWithShape for BlockState { | 19511..=19512 | 19931..=19932 | 20336..=20337 - | 21764..=21765 - | 21844..=21845 - | 21924..=21925 - | 22004..=22005 - | 22116..=22117 - | 22196..=22197 - | 22276..=22277 - | 22356..=22357 - | 22644..=22645 - | 23055..=23056 - | 23466..=23467 - | 23877..=23878 => &SHAPE50, + | 21138..=21139 + | 21549..=21550 + | 21961..=21962 + | 23006..=23007 + | 23086..=23087 + | 23166..=23167 + | 23246..=23247 + | 23358..=23359 + | 23438..=23439 + | 23518..=23519 + | 23598..=23599 + | 24958..=24959 + | 25369..=25370 + | 25780..=25781 + | 26191..=26192 => &SHAPE50, 2934..=2935 | 4742..=4743 | 7089..=7090 @@ -4105,18 +4225,21 @@ impl BlockWithShape for BlockState { | 19521..=19522 | 19941..=19942 | 20346..=20347 - | 21774..=21775 - | 21854..=21855 - | 21934..=21935 - | 22014..=22015 - | 22126..=22127 - | 22206..=22207 - | 22286..=22287 - | 22366..=22367 - | 22654..=22655 - | 23065..=23066 - | 23476..=23477 - | 23887..=23888 => &SHAPE51, + | 21148..=21149 + | 21559..=21560 + | 21971..=21972 + | 23016..=23017 + | 23096..=23097 + | 23176..=23177 + | 23256..=23257 + | 23368..=23369 + | 23448..=23449 + | 23528..=23529 + | 23608..=23609 + | 24968..=24969 + | 25379..=25380 + | 25790..=25791 + | 26201..=26202 => &SHAPE51, 2944..=2945 | 4752..=4753 | 7099..=7100 @@ -4158,18 +4281,21 @@ impl BlockWithShape for BlockState { | 19531..=19532 | 19951..=19952 | 20356..=20357 - | 21784..=21785 - | 21864..=21865 - | 21944..=21945 - | 22024..=22025 - | 22136..=22137 - | 22216..=22217 - | 22296..=22297 - | 22376..=22377 - | 22664..=22665 - | 23075..=23076 - | 23486..=23487 - | 23897..=23898 => &SHAPE52, + | 21158..=21159 + | 21569..=21570 + | 21981..=21982 + | 23026..=23027 + | 23106..=23107 + | 23186..=23187 + | 23266..=23267 + | 23378..=23379 + | 23458..=23459 + | 23538..=23539 + | 23618..=23619 + | 24978..=24979 + | 25389..=25390 + | 25800..=25801 + | 26211..=26212 => &SHAPE52, 2954..=2955 | 2960..=2961 | 2966..=2967 @@ -4201,24 +4327,24 @@ impl BlockWithShape for BlockState { | 5706..=5707 | 5710..=5711 | 5714..=5715 - | 6010..=6013 - | 6018..=6021 - | 6074..=6077 - | 6082..=6085 - | 6138..=6141 - | 6146..=6149 - | 6202..=6205 - | 6210..=6213 - | 6266..=6269 - | 6274..=6277 - | 6330..=6333 - | 6338..=6341 - | 6394..=6397 - | 6402..=6405 - | 6458..=6461 - | 6466..=6469 - | 6522..=6525 - | 6530..=6533 + | 6009..=6012 + | 6017..=6020 + | 6073..=6076 + | 6081..=6084 + | 6137..=6140 + | 6145..=6148 + | 6201..=6204 + | 6209..=6212 + | 6265..=6268 + | 6273..=6276 + | 6329..=6332 + | 6337..=6340 + | 6393..=6396 + | 6401..=6404 + | 6457..=6460 + | 6465..=6468 + | 6521..=6524 + | 6529..=6532 | 10447..=10450 | 10455..=10458 | 11822..=11823 @@ -4296,7 +4422,80 @@ impl BlockWithShape for BlockState { | 19262..=19263 | 19266..=19267 | 19270..=19271 - | 19274..=19275 => &SHAPE64, + | 19274..=19275 + | 23652..=23653 + | 23660..=23661 + | 23672..=23673 + | 23680..=23681 + | 23702..=23703 + | 23706..=23707 + | 23710..=23711 + | 23714..=23717 + | 23724..=23725 + | 23736..=23737 + | 23744..=23745 + | 23766..=23767 + | 23770..=23771 + | 23774..=23775 + | 23778..=23781 + | 23788..=23789 + | 23800..=23801 + | 23808..=23809 + | 23830..=23831 + | 23834..=23835 + | 23838..=23839 + | 23842..=23845 + | 23852..=23853 + | 23864..=23865 + | 23872..=23873 + | 23894..=23895 + | 23898..=23899 + | 23902..=23903 + | 23906..=23909 + | 23916..=23917 + | 23928..=23929 + | 23936..=23937 + | 23958..=23959 + | 23962..=23963 + | 23966..=23967 + | 23970..=23973 + | 23980..=23981 + | 23992..=23993 + | 24000..=24001 + | 24022..=24023 + | 24026..=24027 + | 24030..=24031 + | 24034..=24037 + | 24044..=24045 + | 24056..=24057 + | 24064..=24065 + | 24086..=24087 + | 24090..=24091 + | 24094..=24095 + | 24098..=24101 + | 24108..=24109 + | 24120..=24121 + | 24128..=24129 + | 24150..=24151 + | 24154..=24155 + | 24158..=24159 + | 24162..=24163 + | 24212..=24215 + | 24220..=24223 + | 24276..=24279 + | 24284..=24287 + | 24340..=24343 + | 24348..=24351 + | 24404..=24407 + | 24412..=24415 + | 24468..=24471 + | 24476..=24479 + | 24532..=24535 + | 24540..=24543 + | 24596..=24599 + | 24604..=24607 + | 24660..=24663 + | 24668..=24671 => &SHAPE64, 4592..=4593 | 4596..=4597 | 4600..=4601 @@ -4314,24 +4513,24 @@ impl BlockWithShape for BlockState { | 5692..=5693 | 5704..=5705 | 5712..=5713 - | 5962..=5965 - | 5970..=5973 - | 6026..=6029 - | 6034..=6037 - | 6090..=6093 - | 6098..=6101 - | 6154..=6157 - | 6162..=6165 - | 6218..=6221 - | 6226..=6229 - | 6282..=6285 - | 6290..=6293 - | 6346..=6349 - | 6354..=6357 - | 6410..=6413 - | 6418..=6421 - | 6474..=6477 - | 6482..=6485 + | 5961..=5964 + | 5969..=5972 + | 6025..=6028 + | 6033..=6036 + | 6089..=6092 + | 6097..=6100 + | 6153..=6156 + | 6161..=6164 + | 6217..=6220 + | 6225..=6228 + | 6281..=6284 + | 6289..=6292 + | 6345..=6348 + | 6353..=6356 + | 6409..=6412 + | 6417..=6420 + | 6473..=6476 + | 6481..=6484 | 10399..=10402 | 10407..=10410 | 11824..=11825 @@ -4417,7 +4616,87 @@ impl BlockWithShape for BlockState { | 19244..=19245 | 19252..=19253 | 19264..=19265 - | 19272..=19273 => &SHAPE65, + | 19272..=19273 + | 23654..=23655 + | 23658..=23659 + | 23662..=23663 + | 23666..=23667 + | 23684..=23685 + | 23692..=23693 + | 23704..=23705 + | 23712..=23713 + | 23718..=23719 + | 23722..=23723 + | 23726..=23727 + | 23730..=23731 + | 23748..=23749 + | 23756..=23757 + | 23768..=23769 + | 23776..=23777 + | 23782..=23783 + | 23786..=23787 + | 23790..=23791 + | 23794..=23795 + | 23812..=23813 + | 23820..=23821 + | 23832..=23833 + | 23840..=23841 + | 23846..=23847 + | 23850..=23851 + | 23854..=23855 + | 23858..=23859 + | 23876..=23877 + | 23884..=23885 + | 23896..=23897 + | 23904..=23905 + | 23910..=23911 + | 23914..=23915 + | 23918..=23919 + | 23922..=23923 + | 23940..=23941 + | 23948..=23949 + | 23960..=23961 + | 23968..=23969 + | 23974..=23975 + | 23978..=23979 + | 23982..=23983 + | 23986..=23987 + | 24004..=24005 + | 24012..=24013 + | 24024..=24025 + | 24032..=24033 + | 24038..=24039 + | 24042..=24043 + | 24046..=24047 + | 24050..=24051 + | 24068..=24069 + | 24076..=24077 + | 24088..=24089 + | 24096..=24097 + | 24102..=24103 + | 24106..=24107 + | 24110..=24111 + | 24114..=24115 + | 24132..=24133 + | 24140..=24141 + | 24152..=24153 + | 24160..=24161 + | 24164..=24167 + | 24172..=24175 + | 24228..=24231 + | 24236..=24239 + | 24292..=24295 + | 24300..=24303 + | 24356..=24359 + | 24364..=24367 + | 24420..=24423 + | 24428..=24431 + | 24484..=24487 + | 24492..=24495 + | 24548..=24551 + | 24556..=24559 + | 24612..=24615 + | 24620..=24623 => &SHAPE65, 4594..=4595 | 4602..=4603 | 4606..=4607 @@ -4435,24 +4714,24 @@ impl BlockWithShape for BlockState { | 5690..=5691 | 5694..=5695 | 5698..=5699 - | 5994..=5997 - | 6002..=6005 - | 6058..=6061 - | 6066..=6069 - | 6122..=6125 - | 6130..=6133 - | 6186..=6189 - | 6194..=6197 - | 6250..=6253 - | 6258..=6261 - | 6314..=6317 - | 6322..=6325 - | 6378..=6381 - | 6386..=6389 - | 6442..=6445 - | 6450..=6453 - | 6506..=6509 - | 6514..=6517 + | 5993..=5996 + | 6001..=6004 + | 6057..=6060 + | 6065..=6068 + | 6121..=6124 + | 6129..=6132 + | 6185..=6188 + | 6193..=6196 + | 6249..=6252 + | 6257..=6260 + | 6313..=6316 + | 6321..=6324 + | 6377..=6380 + | 6385..=6388 + | 6441..=6444 + | 6449..=6452 + | 6505..=6508 + | 6513..=6516 | 10431..=10434 | 10439..=10442 | 11826..=11827 @@ -4538,7 +4817,87 @@ impl BlockWithShape for BlockState { | 19246..=19247 | 19250..=19251 | 19254..=19255 - | 19258..=19259 => &SHAPE66, + | 19258..=19259 + | 23656..=23657 + | 23664..=23665 + | 23668..=23669 + | 23676..=23677 + | 23686..=23687 + | 23690..=23691 + | 23694..=23695 + | 23698..=23699 + | 23720..=23721 + | 23728..=23729 + | 23732..=23733 + | 23740..=23741 + | 23750..=23751 + | 23754..=23755 + | 23758..=23759 + | 23762..=23763 + | 23784..=23785 + | 23792..=23793 + | 23796..=23797 + | 23804..=23805 + | 23814..=23815 + | 23818..=23819 + | 23822..=23823 + | 23826..=23827 + | 23848..=23849 + | 23856..=23857 + | 23860..=23861 + | 23868..=23869 + | 23878..=23879 + | 23882..=23883 + | 23886..=23887 + | 23890..=23891 + | 23912..=23913 + | 23920..=23921 + | 23924..=23925 + | 23932..=23933 + | 23942..=23943 + | 23946..=23947 + | 23950..=23951 + | 23954..=23955 + | 23976..=23977 + | 23984..=23985 + | 23988..=23989 + | 23996..=23997 + | 24006..=24007 + | 24010..=24011 + | 24014..=24015 + | 24018..=24019 + | 24040..=24041 + | 24048..=24049 + | 24052..=24053 + | 24060..=24061 + | 24070..=24071 + | 24074..=24075 + | 24078..=24079 + | 24082..=24083 + | 24104..=24105 + | 24112..=24113 + | 24116..=24117 + | 24124..=24125 + | 24134..=24135 + | 24138..=24139 + | 24142..=24143 + | 24146..=24147 + | 24196..=24199 + | 24204..=24207 + | 24260..=24263 + | 24268..=24271 + | 24324..=24327 + | 24332..=24335 + | 24388..=24391 + | 24396..=24399 + | 24452..=24455 + | 24460..=24463 + | 24516..=24519 + | 24524..=24527 + | 24580..=24583 + | 24588..=24591 + | 24644..=24647 + | 24652..=24655 => &SHAPE66, 4608..=4609 | 4612..=4613 | 4616..=4617 @@ -4556,24 +4915,24 @@ impl BlockWithShape for BlockState { | 5696..=5697 | 5700..=5701 | 5708..=5709 - | 5978..=5981 - | 5986..=5989 - | 6042..=6045 - | 6050..=6053 - | 6106..=6109 - | 6114..=6117 - | 6170..=6173 - | 6178..=6181 - | 6234..=6237 - | 6242..=6245 - | 6298..=6301 - | 6306..=6309 - | 6362..=6365 - | 6370..=6373 - | 6426..=6429 - | 6434..=6437 - | 6490..=6493 - | 6498..=6501 + | 5977..=5980 + | 5985..=5988 + | 6041..=6044 + | 6049..=6052 + | 6105..=6108 + | 6113..=6116 + | 6169..=6172 + | 6177..=6180 + | 6233..=6236 + | 6241..=6244 + | 6297..=6300 + | 6305..=6308 + | 6361..=6364 + | 6369..=6372 + | 6425..=6428 + | 6433..=6436 + | 6489..=6492 + | 6497..=6500 | 10415..=10418 | 10423..=10426 | 11840..=11841 @@ -4659,7 +5018,87 @@ impl BlockWithShape for BlockState { | 19248..=19249 | 19256..=19257 | 19260..=19261 - | 19268..=19269 => &SHAPE67, + | 19268..=19269 + | 23670..=23671 + | 23674..=23675 + | 23678..=23679 + | 23682..=23683 + | 23688..=23689 + | 23696..=23697 + | 23700..=23701 + | 23708..=23709 + | 23734..=23735 + | 23738..=23739 + | 23742..=23743 + | 23746..=23747 + | 23752..=23753 + | 23760..=23761 + | 23764..=23765 + | 23772..=23773 + | 23798..=23799 + | 23802..=23803 + | 23806..=23807 + | 23810..=23811 + | 23816..=23817 + | 23824..=23825 + | 23828..=23829 + | 23836..=23837 + | 23862..=23863 + | 23866..=23867 + | 23870..=23871 + | 23874..=23875 + | 23880..=23881 + | 23888..=23889 + | 23892..=23893 + | 23900..=23901 + | 23926..=23927 + | 23930..=23931 + | 23934..=23935 + | 23938..=23939 + | 23944..=23945 + | 23952..=23953 + | 23956..=23957 + | 23964..=23965 + | 23990..=23991 + | 23994..=23995 + | 23998..=23999 + | 24002..=24003 + | 24008..=24009 + | 24016..=24017 + | 24020..=24021 + | 24028..=24029 + | 24054..=24055 + | 24058..=24059 + | 24062..=24063 + | 24066..=24067 + | 24072..=24073 + | 24080..=24081 + | 24084..=24085 + | 24092..=24093 + | 24118..=24119 + | 24122..=24123 + | 24126..=24127 + | 24130..=24131 + | 24136..=24137 + | 24144..=24145 + | 24148..=24149 + | 24156..=24157 + | 24180..=24183 + | 24188..=24191 + | 24244..=24247 + | 24252..=24255 + | 24308..=24311 + | 24316..=24319 + | 24372..=24375 + | 24380..=24383 + | 24436..=24439 + | 24444..=24447 + | 24500..=24503 + | 24508..=24511 + | 24564..=24567 + | 24572..=24575 + | 24628..=24631 + | 24636..=24639 => &SHAPE67, 5538..=5541 | 5546..=5549 | 5554..=5557 @@ -4682,7 +5121,7 @@ impl BlockWithShape for BlockState { | 5606..=5609 | 5614..=5617 | 5622..=5625 => &SHAPE70, - 5773 | 5882..=5945 | 9175..=9190 => &SHAPE7, + 5773 | 5881..=5944 | 9175..=9190 => &SHAPE7, 5774 => &SHAPE58, 5775 | 9191..=9222 => &SHAPE59, 5776 @@ -4731,22 +5170,25 @@ impl BlockWithShape for BlockState { | 19867..=19868 | 19877..=19878 | 20368..=20369 - | 21085..=21564 - | 21696..=21703 - | 22036..=22037 - | 22042..=22043 - | 22048..=22049 - | 22054..=22055 - | 22388..=22389 - | 22394..=22395 - | 22400..=22401 - | 22406..=22407 - | 22676..=22677 - | 23087..=23088 - | 23498..=23499 - | 23909..=23910 => &SHAPE8, + | 21084..=21085 + | 21495..=21496 + | 21907..=21908 + | 22319..=22798 + | 22930..=22937 + | 23278..=23279 + | 23284..=23285 + | 23290..=23291 + | 23296..=23297 + | 23630..=23631 + | 23636..=23637 + | 23642..=23643 + | 23648..=23649 + | 24990..=24991 + | 25401..=25402 + | 25812..=25813 + | 26223..=26224 => &SHAPE8, 5777 => &SHAPE60, - 5779 | 5851 | 22589 => &SHAPE61, + 5779 | 5850 | 24903 => &SHAPE61, 5782..=5797 | 19445 => &SHAPE71, 5817 | 5819 | 7273 | 7275 | 11566 | 11568 | 11598 | 11600 | 11630 | 11632 | 11662 | 11664 | 11694 | 11696 | 11726 | 11728 | 11758 | 11760 | 11790 | 11792 | 18684 @@ -4926,49 +5368,49 @@ impl BlockWithShape for BlockState { 5846 | 5848 | 7302 | 7304 | 11595 | 11597 | 11627 | 11629 | 11659 | 11661 | 11691 | 11693 | 11723 | 11725 | 11755 | 11757 | 11787 | 11789 | 11819 | 11821 | 18713 | 18715 | 18745 | 18747 => &SHAPE89, - 5875 => &SHAPE91, - 5876 => &SHAPE92, - 5877 => &SHAPE93, - 5878 => &SHAPE94, - 5879 => &SHAPE95, - 5880 => &SHAPE96, - 5881 => &SHAPE97, - 5966..=5969 - | 5982..=5985 - | 5998..=6001 - | 6014..=6017 - | 6030..=6033 - | 6046..=6049 - | 6062..=6065 - | 6078..=6081 - | 6094..=6097 - | 6110..=6113 - | 6126..=6129 - | 6142..=6145 - | 6158..=6161 - | 6174..=6177 - | 6190..=6193 - | 6206..=6209 - | 6222..=6225 - | 6238..=6241 - | 6254..=6257 - | 6270..=6273 - | 6286..=6289 - | 6302..=6305 - | 6318..=6321 - | 6334..=6337 - | 6350..=6353 - | 6366..=6369 - | 6382..=6385 - | 6398..=6401 - | 6414..=6417 - | 6430..=6433 - | 6446..=6449 - | 6462..=6465 - | 6478..=6481 - | 6494..=6497 - | 6510..=6513 - | 6526..=6529 + 5874 => &SHAPE91, + 5875 => &SHAPE92, + 5876 => &SHAPE93, + 5877 => &SHAPE94, + 5878 => &SHAPE95, + 5879 => &SHAPE96, + 5880 => &SHAPE97, + 5965..=5968 + | 5981..=5984 + | 5997..=6000 + | 6013..=6016 + | 6029..=6032 + | 6045..=6048 + | 6061..=6064 + | 6077..=6080 + | 6093..=6096 + | 6109..=6112 + | 6125..=6128 + | 6141..=6144 + | 6157..=6160 + | 6173..=6176 + | 6189..=6192 + | 6205..=6208 + | 6221..=6224 + | 6237..=6240 + | 6253..=6256 + | 6269..=6272 + | 6285..=6288 + | 6301..=6304 + | 6317..=6320 + | 6333..=6336 + | 6349..=6352 + | 6365..=6368 + | 6381..=6384 + | 6397..=6400 + | 6413..=6416 + | 6429..=6432 + | 6445..=6448 + | 6461..=6464 + | 6477..=6480 + | 6493..=6496 + | 6509..=6512 + | 6525..=6528 | 10403..=10406 | 10419..=10422 | 10435..=10438 @@ -4980,43 +5422,75 @@ impl BlockWithShape for BlockState { | 18816..=18819 | 18832..=18835 | 18848..=18851 - | 18864..=18867 => &SHAPE98, - 5974..=5977 - | 5990..=5993 - | 6006..=6009 - | 6022..=6025 - | 6038..=6041 - | 6054..=6057 - | 6070..=6073 - | 6086..=6089 - | 6102..=6105 - | 6118..=6121 - | 6134..=6137 - | 6150..=6153 - | 6166..=6169 - | 6182..=6185 - | 6198..=6201 - | 6214..=6217 - | 6230..=6233 - | 6246..=6249 - | 6262..=6265 - | 6278..=6281 - | 6294..=6297 - | 6310..=6313 - | 6326..=6329 - | 6342..=6345 - | 6358..=6361 - | 6374..=6377 - | 6390..=6393 - | 6406..=6409 - | 6422..=6425 - | 6438..=6441 - | 6454..=6457 - | 6470..=6473 - | 6486..=6489 - | 6502..=6505 - | 6518..=6521 - | 6534..=6537 + | 18864..=18867 + | 24168..=24171 + | 24184..=24187 + | 24200..=24203 + | 24216..=24219 + | 24232..=24235 + | 24248..=24251 + | 24264..=24267 + | 24280..=24283 + | 24296..=24299 + | 24312..=24315 + | 24328..=24331 + | 24344..=24347 + | 24360..=24363 + | 24376..=24379 + | 24392..=24395 + | 24408..=24411 + | 24424..=24427 + | 24440..=24443 + | 24456..=24459 + | 24472..=24475 + | 24488..=24491 + | 24504..=24507 + | 24520..=24523 + | 24536..=24539 + | 24552..=24555 + | 24568..=24571 + | 24584..=24587 + | 24600..=24603 + | 24616..=24619 + | 24632..=24635 + | 24648..=24651 + | 24664..=24667 => &SHAPE98, + 5973..=5976 + | 5989..=5992 + | 6005..=6008 + | 6021..=6024 + | 6037..=6040 + | 6053..=6056 + | 6069..=6072 + | 6085..=6088 + | 6101..=6104 + | 6117..=6120 + | 6133..=6136 + | 6149..=6152 + | 6165..=6168 + | 6181..=6184 + | 6197..=6200 + | 6213..=6216 + | 6229..=6232 + | 6245..=6248 + | 6261..=6264 + | 6277..=6280 + | 6293..=6296 + | 6309..=6312 + | 6325..=6328 + | 6341..=6344 + | 6357..=6360 + | 6373..=6376 + | 6389..=6392 + | 6405..=6408 + | 6421..=6424 + | 6437..=6440 + | 6453..=6456 + | 6469..=6472 + | 6485..=6488 + | 6501..=6504 + | 6517..=6520 + | 6533..=6536 | 10411..=10414 | 10427..=10430 | 10443..=10446 @@ -5028,96 +5502,128 @@ impl BlockWithShape for BlockState { | 18824..=18827 | 18840..=18843 | 18856..=18859 - | 18872..=18875 => &SHAPE99, - 6742 | 6744 | 6780 | 6782 | 9372 | 9374 | 9404 | 9406 | 9436 | 9438 | 9468 | 9470 + | 18872..=18875 + | 24176..=24179 + | 24192..=24195 + | 24208..=24211 + | 24224..=24227 + | 24240..=24243 + | 24256..=24259 + | 24272..=24275 + | 24288..=24291 + | 24304..=24307 + | 24320..=24323 + | 24336..=24339 + | 24352..=24355 + | 24368..=24371 + | 24384..=24387 + | 24400..=24403 + | 24416..=24419 + | 24432..=24435 + | 24448..=24451 + | 24464..=24467 + | 24480..=24483 + | 24496..=24499 + | 24512..=24515 + | 24528..=24531 + | 24544..=24547 + | 24560..=24563 + | 24576..=24579 + | 24592..=24595 + | 24608..=24611 + | 24624..=24627 + | 24640..=24643 + | 24656..=24659 + | 24672..=24675 => &SHAPE99, + 6741 | 6743 | 6779 | 6781 | 9372 | 9374 | 9404 | 9406 | 9436 | 9438 | 9468 | 9470 | 9500 | 9502 | 9532 | 9534 | 9564 | 9566 | 9596 | 9598 | 9628 | 9630 | 9660 | 9662 | 9692 | 9694 | 9724 | 9726 | 9756 | 9758 | 9788 | 9790 | 9820 | 9822 | 9852 | 9854 => { &SHAPE100 } - 6743 | 6745 | 6781 | 6783 | 9373 | 9375 | 9405 | 9407 | 9437 | 9439 | 9469 | 9471 + 6742 | 6744 | 6780 | 6782 | 9373 | 9375 | 9405 | 9407 | 9437 | 9439 | 9469 | 9471 | 9501 | 9503 | 9533 | 9535 | 9565 | 9567 | 9597 | 9599 | 9629 | 9631 | 9661 | 9663 | 9693 | 9695 | 9725 | 9727 | 9757 | 9759 | 9789 | 9791 | 9821 | 9823 | 9853 | 9855 => { &SHAPE101 } - 6746 | 6748 | 6784 | 6786 | 9376 | 9378 | 9408 | 9410 | 9440 | 9442 | 9472 | 9474 + 6745 | 6747 | 6783 | 6785 | 9376 | 9378 | 9408 | 9410 | 9440 | 9442 | 9472 | 9474 | 9504 | 9506 | 9536 | 9538 | 9568 | 9570 | 9600 | 9602 | 9632 | 9634 | 9664 | 9666 | 9696 | 9698 | 9728 | 9730 | 9760 | 9762 | 9792 | 9794 | 9824 | 9826 | 9856 | 9858 => { &SHAPE102 } - 6747 | 6749 | 6785 | 6787 | 9377 | 9379 | 9409 | 9411 | 9441 | 9443 | 9473 | 9475 + 6746 | 6748 | 6784 | 6786 | 9377 | 9379 | 9409 | 9411 | 9441 | 9443 | 9473 | 9475 | 9505 | 9507 | 9537 | 9539 | 9569 | 9571 | 9601 | 9603 | 9633 | 9635 | 9665 | 9667 | 9697 | 9699 | 9729 | 9731 | 9761 | 9763 | 9793 | 9795 | 9825 | 9827 | 9857 | 9859 => { &SHAPE103 } - 6750 | 6752 | 6788 | 6790 | 9380 | 9382 | 9412 | 9414 | 9444 | 9446 | 9476 | 9478 + 6749 | 6751 | 6787 | 6789 | 9380 | 9382 | 9412 | 9414 | 9444 | 9446 | 9476 | 9478 | 9508 | 9510 | 9540 | 9542 | 9572 | 9574 | 9604 | 9606 | 9636 | 9638 | 9668 | 9670 | 9700 | 9702 | 9732 | 9734 | 9764 | 9766 | 9796 | 9798 | 9828 | 9830 | 9860 | 9862 => { &SHAPE104 } - 6751 | 6753 | 6789 | 6791 | 9381 | 9383 | 9413 | 9415 | 9445 | 9447 | 9477 | 9479 + 6750 | 6752 | 6788 | 6790 | 9381 | 9383 | 9413 | 9415 | 9445 | 9447 | 9477 | 9479 | 9509 | 9511 | 9541 | 9543 | 9573 | 9575 | 9605 | 9607 | 9637 | 9639 | 9669 | 9671 | 9701 | 9703 | 9733 | 9735 | 9765 | 9767 | 9797 | 9799 | 9829 | 9831 | 9861 | 9863 => { &SHAPE105 } - 6754 | 6756 | 6792 | 6794 | 9384 | 9386 | 9416 | 9418 | 9448 | 9450 | 9480 | 9482 + 6753 | 6755 | 6791 | 6793 | 9384 | 9386 | 9416 | 9418 | 9448 | 9450 | 9480 | 9482 | 9512 | 9514 | 9544 | 9546 | 9576 | 9578 | 9608 | 9610 | 9640 | 9642 | 9672 | 9674 | 9704 | 9706 | 9736 | 9738 | 9768 | 9770 | 9800 | 9802 | 9832 | 9834 | 9864 | 9866 => { &SHAPE106 } - 6755 | 6757 | 6793 | 6795 | 9385 | 9387 | 9417 | 9419 | 9449 | 9451 | 9481 | 9483 + 6754 | 6756 | 6792 | 6794 | 9385 | 9387 | 9417 | 9419 | 9449 | 9451 | 9481 | 9483 | 9513 | 9515 | 9545 | 9547 | 9577 | 9579 | 9609 | 9611 | 9641 | 9643 | 9673 | 9675 | 9705 | 9707 | 9737 | 9739 | 9769 | 9771 | 9801 | 9803 | 9833 | 9835 | 9865 | 9867 => { &SHAPE107 } - 6758 | 6760 | 6796 | 6798 | 9388 | 9390 | 9420 | 9422 | 9452 | 9454 | 9484 | 9486 + 6757 | 6759 | 6795 | 6797 | 9388 | 9390 | 9420 | 9422 | 9452 | 9454 | 9484 | 9486 | 9516 | 9518 | 9548 | 9550 | 9580 | 9582 | 9612 | 9614 | 9644 | 9646 | 9676 | 9678 | 9708 | 9710 | 9740 | 9742 | 9772 | 9774 | 9804 | 9806 | 9836 | 9838 | 9868 | 9870 => { &SHAPE108 } - 6759 | 6761 | 6797 | 6799 | 9389 | 9391 | 9421 | 9423 | 9453 | 9455 | 9485 | 9487 + 6758 | 6760 | 6796 | 6798 | 9389 | 9391 | 9421 | 9423 | 9453 | 9455 | 9485 | 9487 | 9517 | 9519 | 9549 | 9551 | 9581 | 9583 | 9613 | 9615 | 9645 | 9647 | 9677 | 9679 | 9709 | 9711 | 9741 | 9743 | 9773 | 9775 | 9805 | 9807 | 9837 | 9839 | 9869 | 9871 => { &SHAPE109 } - 6762 | 6764 | 6800 | 6802 | 9392 | 9394 | 9424 | 9426 | 9456 | 9458 | 9488 | 9490 + 6761 | 6763 | 6799 | 6801 | 9392 | 9394 | 9424 | 9426 | 9456 | 9458 | 9488 | 9490 | 9520 | 9522 | 9552 | 9554 | 9584 | 9586 | 9616 | 9618 | 9648 | 9650 | 9680 | 9682 | 9712 | 9714 | 9744 | 9746 | 9776 | 9778 | 9808 | 9810 | 9840 | 9842 | 9872 | 9874 => { &SHAPE110 } - 6763 | 6765 | 6801 | 6803 | 9393 | 9395 | 9425 | 9427 | 9457 | 9459 | 9489 | 9491 + 6762 | 6764 | 6800 | 6802 | 9393 | 9395 | 9425 | 9427 | 9457 | 9459 | 9489 | 9491 | 9521 | 9523 | 9553 | 9555 | 9585 | 9587 | 9617 | 9619 | 9649 | 9651 | 9681 | 9683 | 9713 | 9715 | 9745 | 9747 | 9777 | 9779 | 9809 | 9811 | 9841 | 9843 | 9873 | 9875 => { &SHAPE111 } - 6766 | 6768 | 6804 | 6806 | 9396 | 9398 | 9428 | 9430 | 9460 | 9462 | 9492 | 9494 + 6765 | 6767 | 6803 | 6805 | 9396 | 9398 | 9428 | 9430 | 9460 | 9462 | 9492 | 9494 | 9524 | 9526 | 9556 | 9558 | 9588 | 9590 | 9620 | 9622 | 9652 | 9654 | 9684 | 9686 | 9716 | 9718 | 9748 | 9750 | 9780 | 9782 | 9812 | 9814 | 9844 | 9846 | 9876 | 9878 => { &SHAPE112 } - 6767 | 6769 | 6805 | 6807 | 9397 | 9399 | 9429 | 9431 | 9461 | 9463 | 9493 | 9495 + 6766 | 6768 | 6804 | 6806 | 9397 | 9399 | 9429 | 9431 | 9461 | 9463 | 9493 | 9495 | 9525 | 9527 | 9557 | 9559 | 9589 | 9591 | 9621 | 9623 | 9653 | 9655 | 9685 | 9687 | 9717 | 9719 | 9749 | 9751 | 9781 | 9783 | 9813 | 9815 | 9845 | 9847 | 9877 | 9879 => { &SHAPE113 } - 6770 | 6772 | 6808 | 6810 | 9400 | 9402 | 9432 | 9434 | 9464 | 9466 | 9496 | 9498 + 6769 | 6771 | 6807 | 6809 | 9400 | 9402 | 9432 | 9434 | 9464 | 9466 | 9496 | 9498 | 9528 | 9530 | 9560 | 9562 | 9592 | 9594 | 9624 | 9626 | 9656 | 9658 | 9688 | 9690 | 9720 | 9722 | 9752 | 9754 | 9784 | 9786 | 9816 | 9818 | 9848 | 9850 | 9880 | 9882 => { &SHAPE114 } - 6771 | 6773 | 6809 | 6811 | 9401 | 9403 | 9433 | 9435 | 9465 | 9467 | 9497 | 9499 + 6770 | 6772 | 6808 | 6810 | 9401 | 9403 | 9433 | 9435 | 9465 | 9467 | 9497 | 9499 | 9529 | 9531 | 9561 | 9563 | 9593 | 9595 | 9625 | 9627 | 9657 | 9659 | 9689 | 9691 | 9721 | 9723 | 9753 | 9755 | 9785 | 9787 | 9817 | 9819 | 9849 | 9851 | 9881 | 9883 => { &SHAPE2 } - 6774..=6775 => &SHAPE115, - 6776..=6777 | 12945..=12956 => &SHAPE116, - 6778..=6779 => &SHAPE117, + 6773..=6774 => &SHAPE115, + 6775..=6776 | 12945..=12956 => &SHAPE116, + 6777..=6778 => &SHAPE117, 7271 => &SHAPE119, 7390..=7397 => &SHAPE120, 7398..=7405 => &SHAPE121, 7407..=7410 => &SHAPE122, 7411..=7414 => &SHAPE123, - 7416 | 24260..=24275 => &SHAPE72, + 7416 | 26574..=26589 => &SHAPE72, 7419 => &SHAPE124, 7420 => &SHAPE125, 7421 => &SHAPE126, @@ -5133,8 +5639,8 @@ impl BlockWithShape for BlockState { 7919 | 7922 | 8243 | 8246 | 14160 | 14163 | 14484 | 14487 | 14808 | 14811 | 15132 | 15135 | 15456 | 15459 | 15780 | 15783 | 16104 | 16107 | 16428 | 16431 | 16752 | 16755 | 17076 | 17079 | 17400 | 17403 | 17724 | 17727 | 18048 | 18051 | 19541 - | 19544 | 19961 | 19964 | 20398 | 20401 | 22680 | 22683 | 23091 | 23094 | 23502 - | 23505 | 23913 | 23916 => &SHAPE136, + | 19544 | 19961 | 19964 | 20398 | 20401 | 21168 | 21171 | 21579 | 21582 | 21991 + | 21994 | 24994 | 24997 | 25405 | 25408 | 25816 | 25819 | 26227 | 26230 => &SHAPE136, 7920..=7921 | 7923..=7924 | 8244..=8245 @@ -5171,14 +5677,20 @@ impl BlockWithShape for BlockState { | 19965..=19966 | 20399..=20400 | 20402..=20403 - | 22681..=22682 - | 22684..=22685 - | 23092..=23093 - | 23095..=23096 - | 23503..=23504 - | 23506..=23507 - | 23914..=23915 - | 23917..=23918 => &SHAPE137, + | 21169..=21170 + | 21172..=21173 + | 21580..=21581 + | 21583..=21584 + | 21992..=21993 + | 21995..=21996 + | 24995..=24996 + | 24998..=24999 + | 25406..=25407 + | 25409..=25410 + | 25817..=25818 + | 25820..=25821 + | 26228..=26229 + | 26231..=26232 => &SHAPE137, 7926..=7927 | 7929..=7930 | 8250..=8251 @@ -5215,14 +5727,20 @@ impl BlockWithShape for BlockState { | 19971..=19972 | 20405..=20406 | 20408..=20409 - | 22687..=22688 - | 22690..=22691 - | 23098..=23099 - | 23101..=23102 - | 23509..=23510 - | 23512..=23513 - | 23920..=23921 - | 23923..=23924 => &SHAPE138, + | 21175..=21176 + | 21178..=21179 + | 21586..=21587 + | 21589..=21590 + | 21998..=21999 + | 22001..=22002 + | 25001..=25002 + | 25004..=25005 + | 25412..=25413 + | 25415..=25416 + | 25823..=25824 + | 25826..=25827 + | 26234..=26235 + | 26237..=26238 => &SHAPE138, 7931 | 7934 | 7943 | 7946 | 8255 | 8258 | 8267 | 8270 | 14172 | 14175 | 14184 | 14187 | 14496 | 14499 | 14508 | 14511 | 14820 | 14823 | 14832 | 14835 | 15144 | 15147 | 15156 | 15159 | 15468 | 15471 | 15480 | 15483 | 15792 | 15795 | 15804 @@ -5230,8 +5748,9 @@ impl BlockWithShape for BlockState { | 16767 | 16776 | 16779 | 17088 | 17091 | 17100 | 17103 | 17412 | 17415 | 17424 | 17427 | 17736 | 17739 | 17748 | 17751 | 18060 | 18063 | 18072 | 18075 | 19553 | 19556 | 19565 | 19568 | 19973 | 19976 | 19985 | 19988 | 20410 | 20413 | 20422 - | 20425 | 22692 | 22695 | 22704 | 22707 | 23103 | 23106 | 23115 | 23118 | 23514 - | 23517 | 23526 | 23529 | 23925 | 23928 | 23937 | 23940 => &SHAPE139, + | 20425 | 21180 | 21183 | 21192 | 21195 | 21591 | 21594 | 21603 | 21606 | 22003 + | 22006 | 22015 | 22018 | 25006 | 25009 | 25018 | 25021 | 25417 | 25420 | 25429 + | 25432 | 25828 | 25831 | 25840 | 25843 | 26239 | 26242 | 26251 | 26254 => &SHAPE139, 7932..=7933 | 7935..=7936 | 7944..=7945 @@ -5304,22 +5823,34 @@ impl BlockWithShape for BlockState { | 20414..=20415 | 20423..=20424 | 20426..=20427 - | 22693..=22694 - | 22696..=22697 - | 22705..=22706 - | 22708..=22709 - | 23104..=23105 - | 23107..=23108 - | 23116..=23117 - | 23119..=23120 - | 23515..=23516 - | 23518..=23519 - | 23527..=23528 - | 23530..=23531 - | 23926..=23927 - | 23929..=23930 - | 23938..=23939 - | 23941..=23942 => &SHAPE140, + | 21181..=21182 + | 21184..=21185 + | 21193..=21194 + | 21196..=21197 + | 21592..=21593 + | 21595..=21596 + | 21604..=21605 + | 21607..=21608 + | 22004..=22005 + | 22007..=22008 + | 22016..=22017 + | 22019..=22020 + | 25007..=25008 + | 25010..=25011 + | 25019..=25020 + | 25022..=25023 + | 25418..=25419 + | 25421..=25422 + | 25430..=25431 + | 25433..=25434 + | 25829..=25830 + | 25832..=25833 + | 25841..=25842 + | 25844..=25845 + | 26240..=26241 + | 26243..=26244 + | 26252..=26253 + | 26255..=26256 => &SHAPE140, 7937 | 7940 | 7949 | 7952 | 8261 | 8264 | 8273 | 8276 | 14178 | 14181 | 14190 | 14193 | 14502 | 14505 | 14514 | 14517 | 14826 | 14829 | 14838 | 14841 | 15150 | 15153 | 15162 | 15165 | 15474 | 15477 | 15486 | 15489 | 15798 | 15801 | 15810 @@ -5327,8 +5858,9 @@ impl BlockWithShape for BlockState { | 16773 | 16782 | 16785 | 17094 | 17097 | 17106 | 17109 | 17418 | 17421 | 17430 | 17433 | 17742 | 17745 | 17754 | 17757 | 18066 | 18069 | 18078 | 18081 | 19559 | 19562 | 19571 | 19574 | 19979 | 19982 | 19991 | 19994 | 20416 | 20419 | 20428 - | 20431 | 22698 | 22701 | 22710 | 22713 | 23109 | 23112 | 23121 | 23124 | 23520 - | 23523 | 23532 | 23535 | 23931 | 23934 | 23943 | 23946 => &SHAPE141, + | 20431 | 21186 | 21189 | 21198 | 21201 | 21597 | 21600 | 21609 | 21612 | 22009 + | 22012 | 22021 | 22024 | 25012 | 25015 | 25024 | 25027 | 25423 | 25426 | 25435 + | 25438 | 25834 | 25837 | 25846 | 25849 | 26245 | 26248 | 26257 | 26260 => &SHAPE141, 7938..=7939 | 7941..=7942 | 7950..=7951 @@ -5401,22 +5933,34 @@ impl BlockWithShape for BlockState { | 20420..=20421 | 20429..=20430 | 20432..=20433 - | 22699..=22700 - | 22702..=22703 - | 22711..=22712 - | 22714..=22715 - | 23110..=23111 - | 23113..=23114 - | 23122..=23123 - | 23125..=23126 - | 23521..=23522 - | 23524..=23525 - | 23533..=23534 - | 23536..=23537 - | 23932..=23933 - | 23935..=23936 - | 23944..=23945 - | 23947..=23948 => &SHAPE142, + | 21187..=21188 + | 21190..=21191 + | 21199..=21200 + | 21202..=21203 + | 21598..=21599 + | 21601..=21602 + | 21610..=21611 + | 21613..=21614 + | 22010..=22011 + | 22013..=22014 + | 22022..=22023 + | 22025..=22026 + | 25013..=25014 + | 25016..=25017 + | 25025..=25026 + | 25028..=25029 + | 25424..=25425 + | 25427..=25428 + | 25436..=25437 + | 25439..=25440 + | 25835..=25836 + | 25838..=25839 + | 25847..=25848 + | 25850..=25851 + | 26246..=26247 + | 26249..=26250 + | 26258..=26259 + | 26261..=26262 => &SHAPE142, 7955 | 7958 | 7991 | 7994 | 8279 | 8282 | 8315 | 8318 | 14196 | 14199 | 14232 | 14235 | 14520 | 14523 | 14556 | 14559 | 14844 | 14847 | 14880 | 14883 | 15168 | 15171 | 15204 | 15207 | 15492 | 15495 | 15528 | 15531 | 15816 | 15819 | 15852 @@ -5424,8 +5968,9 @@ impl BlockWithShape for BlockState { | 16791 | 16824 | 16827 | 17112 | 17115 | 17148 | 17151 | 17436 | 17439 | 17472 | 17475 | 17760 | 17763 | 17796 | 17799 | 18084 | 18087 | 18120 | 18123 | 19577 | 19580 | 19613 | 19616 | 19997 | 20000 | 20033 | 20036 | 20434 | 20437 | 20470 - | 20473 | 22716 | 22719 | 22752 | 22755 | 23127 | 23130 | 23163 | 23166 | 23538 - | 23541 | 23574 | 23577 | 23949 | 23952 | 23985 | 23988 => &SHAPE143, + | 20473 | 21204 | 21207 | 21240 | 21243 | 21615 | 21618 | 21651 | 21654 | 22027 + | 22030 | 22063 | 22066 | 25030 | 25033 | 25066 | 25069 | 25441 | 25444 | 25477 + | 25480 | 25852 | 25855 | 25888 | 25891 | 26263 | 26266 | 26299 | 26302 => &SHAPE143, 7956..=7957 | 7959..=7960 | 7992..=7993 @@ -5498,22 +6043,34 @@ impl BlockWithShape for BlockState { | 20438..=20439 | 20471..=20472 | 20474..=20475 - | 22717..=22718 - | 22720..=22721 - | 22753..=22754 - | 22756..=22757 - | 23128..=23129 - | 23131..=23132 - | 23164..=23165 - | 23167..=23168 - | 23539..=23540 - | 23542..=23543 - | 23575..=23576 - | 23578..=23579 - | 23950..=23951 - | 23953..=23954 - | 23986..=23987 - | 23989..=23990 => &SHAPE144, + | 21205..=21206 + | 21208..=21209 + | 21241..=21242 + | 21244..=21245 + | 21616..=21617 + | 21619..=21620 + | 21652..=21653 + | 21655..=21656 + | 22028..=22029 + | 22031..=22032 + | 22064..=22065 + | 22067..=22068 + | 25031..=25032 + | 25034..=25035 + | 25067..=25068 + | 25070..=25071 + | 25442..=25443 + | 25445..=25446 + | 25478..=25479 + | 25481..=25482 + | 25853..=25854 + | 25856..=25857 + | 25889..=25890 + | 25892..=25893 + | 26264..=26265 + | 26267..=26268 + | 26300..=26301 + | 26303..=26304 => &SHAPE144, 7961 | 7964 | 7997 | 8000 | 8285 | 8288 | 8321 | 8324 | 14202 | 14205 | 14238 | 14241 | 14526 | 14529 | 14562 | 14565 | 14850 | 14853 | 14886 | 14889 | 15174 | 15177 | 15210 | 15213 | 15498 | 15501 | 15534 | 15537 | 15822 | 15825 | 15858 @@ -5521,8 +6078,9 @@ impl BlockWithShape for BlockState { | 16797 | 16830 | 16833 | 17118 | 17121 | 17154 | 17157 | 17442 | 17445 | 17478 | 17481 | 17766 | 17769 | 17802 | 17805 | 18090 | 18093 | 18126 | 18129 | 19583 | 19586 | 19619 | 19622 | 20003 | 20006 | 20039 | 20042 | 20440 | 20443 | 20476 - | 20479 | 22722 | 22725 | 22758 | 22761 | 23133 | 23136 | 23169 | 23172 | 23544 - | 23547 | 23580 | 23583 | 23955 | 23958 | 23991 | 23994 => &SHAPE145, + | 20479 | 21210 | 21213 | 21246 | 21249 | 21621 | 21624 | 21657 | 21660 | 22033 + | 22036 | 22069 | 22072 | 25036 | 25039 | 25072 | 25075 | 25447 | 25450 | 25483 + | 25486 | 25858 | 25861 | 25894 | 25897 | 26269 | 26272 | 26305 | 26308 => &SHAPE145, 7962..=7963 | 7965..=7966 | 7998..=7999 @@ -5595,22 +6153,34 @@ impl BlockWithShape for BlockState { | 20444..=20445 | 20477..=20478 | 20480..=20481 - | 22723..=22724 - | 22726..=22727 - | 22759..=22760 - | 22762..=22763 - | 23134..=23135 - | 23137..=23138 - | 23170..=23171 - | 23173..=23174 - | 23545..=23546 - | 23548..=23549 - | 23581..=23582 - | 23584..=23585 - | 23956..=23957 - | 23959..=23960 - | 23992..=23993 - | 23995..=23996 => &SHAPE146, + | 21211..=21212 + | 21214..=21215 + | 21247..=21248 + | 21250..=21251 + | 21622..=21623 + | 21625..=21626 + | 21658..=21659 + | 21661..=21662 + | 22034..=22035 + | 22037..=22038 + | 22070..=22071 + | 22073..=22074 + | 25037..=25038 + | 25040..=25041 + | 25073..=25074 + | 25076..=25077 + | 25448..=25449 + | 25451..=25452 + | 25484..=25485 + | 25487..=25488 + | 25859..=25860 + | 25862..=25863 + | 25895..=25896 + | 25898..=25899 + | 26270..=26271 + | 26273..=26274 + | 26306..=26307 + | 26309..=26310 => &SHAPE146, 7967 | 7970 | 7979 | 7982 | 8003 | 8006 | 8015 | 8018 | 8291 | 8294 | 8303 | 8306 | 8327 | 8330 | 8339 | 8342 | 14208 | 14211 | 14220 | 14223 | 14244 | 14247 | 14256 | 14259 | 14532 | 14535 | 14544 | 14547 | 14568 | 14571 | 14580 | 14583 | 14856 @@ -5625,10 +6195,12 @@ impl BlockWithShape for BlockState { | 18099 | 18108 | 18111 | 18132 | 18135 | 18144 | 18147 | 19589 | 19592 | 19601 | 19604 | 19625 | 19628 | 19637 | 19640 | 20009 | 20012 | 20021 | 20024 | 20045 | 20048 | 20057 | 20060 | 20446 | 20449 | 20458 | 20461 | 20482 | 20485 | 20494 - | 20497 | 22728 | 22731 | 22740 | 22743 | 22764 | 22767 | 22776 | 22779 | 23139 - | 23142 | 23151 | 23154 | 23175 | 23178 | 23187 | 23190 | 23550 | 23553 | 23562 - | 23565 | 23586 | 23589 | 23598 | 23601 | 23961 | 23964 | 23973 | 23976 | 23997 - | 24000 | 24009 | 24012 => &SHAPE147, + | 20497 | 21216 | 21219 | 21228 | 21231 | 21252 | 21255 | 21264 | 21267 | 21627 + | 21630 | 21639 | 21642 | 21663 | 21666 | 21675 | 21678 | 22039 | 22042 | 22051 + | 22054 | 22075 | 22078 | 22087 | 22090 | 25042 | 25045 | 25054 | 25057 | 25078 + | 25081 | 25090 | 25093 | 25453 | 25456 | 25465 | 25468 | 25489 | 25492 | 25501 + | 25504 | 25864 | 25867 | 25876 | 25879 | 25900 | 25903 | 25912 | 25915 | 26275 + | 26278 | 26287 | 26290 | 26311 | 26314 | 26323 | 26326 => &SHAPE147, 7968..=7969 | 7971..=7972 | 7980..=7981 @@ -5773,38 +6345,62 @@ impl BlockWithShape for BlockState { | 20486..=20487 | 20495..=20496 | 20498..=20499 - | 22729..=22730 - | 22732..=22733 - | 22741..=22742 - | 22744..=22745 - | 22765..=22766 - | 22768..=22769 - | 22777..=22778 - | 22780..=22781 - | 23140..=23141 - | 23143..=23144 - | 23152..=23153 - | 23155..=23156 - | 23176..=23177 - | 23179..=23180 - | 23188..=23189 - | 23191..=23192 - | 23551..=23552 - | 23554..=23555 - | 23563..=23564 - | 23566..=23567 - | 23587..=23588 - | 23590..=23591 - | 23599..=23600 - | 23602..=23603 - | 23962..=23963 - | 23965..=23966 - | 23974..=23975 - | 23977..=23978 - | 23998..=23999 - | 24001..=24002 - | 24010..=24011 - | 24013..=24014 => &SHAPE148, + | 21217..=21218 + | 21220..=21221 + | 21229..=21230 + | 21232..=21233 + | 21253..=21254 + | 21256..=21257 + | 21265..=21266 + | 21268..=21269 + | 21628..=21629 + | 21631..=21632 + | 21640..=21641 + | 21643..=21644 + | 21664..=21665 + | 21667..=21668 + | 21676..=21677 + | 21679..=21680 + | 22040..=22041 + | 22043..=22044 + | 22052..=22053 + | 22055..=22056 + | 22076..=22077 + | 22079..=22080 + | 22088..=22089 + | 22091..=22092 + | 25043..=25044 + | 25046..=25047 + | 25055..=25056 + | 25058..=25059 + | 25079..=25080 + | 25082..=25083 + | 25091..=25092 + | 25094..=25095 + | 25454..=25455 + | 25457..=25458 + | 25466..=25467 + | 25469..=25470 + | 25490..=25491 + | 25493..=25494 + | 25502..=25503 + | 25505..=25506 + | 25865..=25866 + | 25868..=25869 + | 25877..=25878 + | 25880..=25881 + | 25901..=25902 + | 25904..=25905 + | 25913..=25914 + | 25916..=25917 + | 26276..=26277 + | 26279..=26280 + | 26288..=26289 + | 26291..=26292 + | 26312..=26313 + | 26315..=26316 + | 26324..=26325 + | 26327..=26328 => &SHAPE148, 7973 | 7976 | 7985 | 7988 | 8009 | 8012 | 8021 | 8024 | 8297 | 8300 | 8309 | 8312 | 8333 | 8336 | 8345 | 8348 | 14214 | 14217 | 14226 | 14229 | 14250 | 14253 | 14262 | 14265 | 14538 | 14541 | 14550 | 14553 | 14574 | 14577 | 14586 | 14589 | 14862 @@ -5819,10 +6415,12 @@ impl BlockWithShape for BlockState { | 18105 | 18114 | 18117 | 18138 | 18141 | 18150 | 18153 | 19595 | 19598 | 19607 | 19610 | 19631 | 19634 | 19643 | 19646 | 20015 | 20018 | 20027 | 20030 | 20051 | 20054 | 20063 | 20066 | 20452 | 20455 | 20464 | 20467 | 20488 | 20491 | 20500 - | 20503 | 22734 | 22737 | 22746 | 22749 | 22770 | 22773 | 22782 | 22785 | 23145 - | 23148 | 23157 | 23160 | 23181 | 23184 | 23193 | 23196 | 23556 | 23559 | 23568 - | 23571 | 23592 | 23595 | 23604 | 23607 | 23967 | 23970 | 23979 | 23982 | 24003 - | 24006 | 24015 | 24018 => &SHAPE149, + | 20503 | 21222 | 21225 | 21234 | 21237 | 21258 | 21261 | 21270 | 21273 | 21633 + | 21636 | 21645 | 21648 | 21669 | 21672 | 21681 | 21684 | 22045 | 22048 | 22057 + | 22060 | 22081 | 22084 | 22093 | 22096 | 25048 | 25051 | 25060 | 25063 | 25084 + | 25087 | 25096 | 25099 | 25459 | 25462 | 25471 | 25474 | 25495 | 25498 | 25507 + | 25510 | 25870 | 25873 | 25882 | 25885 | 25906 | 25909 | 25918 | 25921 | 26281 + | 26284 | 26293 | 26296 | 26317 | 26320 | 26329 | 26332 => &SHAPE149, 7974..=7975 | 7977..=7978 | 7986..=7987 @@ -5967,38 +6565,62 @@ impl BlockWithShape for BlockState { | 20492..=20493 | 20501..=20502 | 20504..=20505 - | 22735..=22736 - | 22738..=22739 - | 22747..=22748 - | 22750..=22751 - | 22771..=22772 - | 22774..=22775 - | 22783..=22784 - | 22786..=22787 - | 23146..=23147 - | 23149..=23150 - | 23158..=23159 - | 23161..=23162 - | 23182..=23183 - | 23185..=23186 - | 23194..=23195 - | 23197..=23198 - | 23557..=23558 - | 23560..=23561 - | 23569..=23570 - | 23572..=23573 - | 23593..=23594 - | 23596..=23597 - | 23605..=23606 - | 23608..=23609 - | 23968..=23969 - | 23971..=23972 - | 23980..=23981 - | 23983..=23984 - | 24004..=24005 - | 24007..=24008 - | 24016..=24017 - | 24019..=24020 => &SHAPE150, + | 21223..=21224 + | 21226..=21227 + | 21235..=21236 + | 21238..=21239 + | 21259..=21260 + | 21262..=21263 + | 21271..=21272 + | 21274..=21275 + | 21634..=21635 + | 21637..=21638 + | 21646..=21647 + | 21649..=21650 + | 21670..=21671 + | 21673..=21674 + | 21682..=21683 + | 21685..=21686 + | 22046..=22047 + | 22049..=22050 + | 22058..=22059 + | 22061..=22062 + | 22082..=22083 + | 22085..=22086 + | 22094..=22095 + | 22097..=22098 + | 25049..=25050 + | 25052..=25053 + | 25061..=25062 + | 25064..=25065 + | 25085..=25086 + | 25088..=25089 + | 25097..=25098 + | 25100..=25101 + | 25460..=25461 + | 25463..=25464 + | 25472..=25473 + | 25475..=25476 + | 25496..=25497 + | 25499..=25500 + | 25508..=25509 + | 25511..=25512 + | 25871..=25872 + | 25874..=25875 + | 25883..=25884 + | 25886..=25887 + | 25907..=25908 + | 25910..=25911 + | 25919..=25920 + | 25922..=25923 + | 26282..=26283 + | 26285..=26286 + | 26294..=26295 + | 26297..=26298 + | 26318..=26319 + | 26321..=26322 + | 26330..=26331 + | 26333..=26334 => &SHAPE150, 8027 | 8030 | 8135 | 8138 | 8351 | 8354 | 8459 | 8462 | 14268 | 14271 | 14376 | 14379 | 14592 | 14595 | 14700 | 14703 | 14916 | 14919 | 15024 | 15027 | 15240 | 15243 | 15348 | 15351 | 15564 | 15567 | 15672 | 15675 | 15888 | 15891 | 15996 @@ -6006,8 +6628,9 @@ impl BlockWithShape for BlockState { | 16863 | 16968 | 16971 | 17184 | 17187 | 17292 | 17295 | 17508 | 17511 | 17616 | 17619 | 17832 | 17835 | 17940 | 17943 | 18156 | 18159 | 18264 | 18267 | 19649 | 19652 | 19757 | 19760 | 20069 | 20072 | 20177 | 20180 | 20506 | 20509 | 20614 - | 20617 | 22788 | 22791 | 22896 | 22899 | 23199 | 23202 | 23307 | 23310 | 23610 - | 23613 | 23718 | 23721 | 24021 | 24024 | 24129 | 24132 => &SHAPE151, + | 20617 | 21276 | 21279 | 21384 | 21387 | 21687 | 21690 | 21795 | 21798 | 22099 + | 22102 | 22207 | 22210 | 25102 | 25105 | 25210 | 25213 | 25513 | 25516 | 25621 + | 25624 | 25924 | 25927 | 26032 | 26035 | 26335 | 26338 | 26443 | 26446 => &SHAPE151, 8028..=8029 | 8031..=8032 | 8136..=8137 @@ -6080,22 +6703,34 @@ impl BlockWithShape for BlockState { | 20510..=20511 | 20615..=20616 | 20618..=20619 - | 22789..=22790 - | 22792..=22793 - | 22897..=22898 - | 22900..=22901 - | 23200..=23201 - | 23203..=23204 - | 23308..=23309 - | 23311..=23312 - | 23611..=23612 - | 23614..=23615 - | 23719..=23720 - | 23722..=23723 - | 24022..=24023 - | 24025..=24026 - | 24130..=24131 - | 24133..=24134 => &SHAPE152, + | 21277..=21278 + | 21280..=21281 + | 21385..=21386 + | 21388..=21389 + | 21688..=21689 + | 21691..=21692 + | 21796..=21797 + | 21799..=21800 + | 22100..=22101 + | 22103..=22104 + | 22208..=22209 + | 22211..=22212 + | 25103..=25104 + | 25106..=25107 + | 25211..=25212 + | 25214..=25215 + | 25514..=25515 + | 25517..=25518 + | 25622..=25623 + | 25625..=25626 + | 25925..=25926 + | 25928..=25929 + | 26033..=26034 + | 26036..=26037 + | 26336..=26337 + | 26339..=26340 + | 26444..=26445 + | 26447..=26448 => &SHAPE152, 8033 | 8036 | 8141 | 8144 | 8357 | 8360 | 8465 | 8468 | 14274 | 14277 | 14382 | 14385 | 14598 | 14601 | 14706 | 14709 | 14922 | 14925 | 15030 | 15033 | 15246 | 15249 | 15354 | 15357 | 15570 | 15573 | 15678 | 15681 | 15894 | 15897 | 16002 @@ -6103,8 +6738,9 @@ impl BlockWithShape for BlockState { | 16869 | 16974 | 16977 | 17190 | 17193 | 17298 | 17301 | 17514 | 17517 | 17622 | 17625 | 17838 | 17841 | 17946 | 17949 | 18162 | 18165 | 18270 | 18273 | 19655 | 19658 | 19763 | 19766 | 20075 | 20078 | 20183 | 20186 | 20512 | 20515 | 20620 - | 20623 | 22794 | 22797 | 22902 | 22905 | 23205 | 23208 | 23313 | 23316 | 23616 - | 23619 | 23724 | 23727 | 24027 | 24030 | 24135 | 24138 => &SHAPE153, + | 20623 | 21282 | 21285 | 21390 | 21393 | 21693 | 21696 | 21801 | 21804 | 22105 + | 22108 | 22213 | 22216 | 25108 | 25111 | 25216 | 25219 | 25519 | 25522 | 25627 + | 25630 | 25930 | 25933 | 26038 | 26041 | 26341 | 26344 | 26449 | 26452 => &SHAPE153, 8034..=8035 | 8037..=8038 | 8142..=8143 @@ -6177,22 +6813,34 @@ impl BlockWithShape for BlockState { | 20516..=20517 | 20621..=20622 | 20624..=20625 - | 22795..=22796 - | 22798..=22799 - | 22903..=22904 - | 22906..=22907 - | 23206..=23207 - | 23209..=23210 - | 23314..=23315 - | 23317..=23318 - | 23617..=23618 - | 23620..=23621 - | 23725..=23726 - | 23728..=23729 - | 24028..=24029 - | 24031..=24032 - | 24136..=24137 - | 24139..=24140 => &SHAPE154, + | 21283..=21284 + | 21286..=21287 + | 21391..=21392 + | 21394..=21395 + | 21694..=21695 + | 21697..=21698 + | 21802..=21803 + | 21805..=21806 + | 22106..=22107 + | 22109..=22110 + | 22214..=22215 + | 22217..=22218 + | 25109..=25110 + | 25112..=25113 + | 25217..=25218 + | 25220..=25221 + | 25520..=25521 + | 25523..=25524 + | 25628..=25629 + | 25631..=25632 + | 25931..=25932 + | 25934..=25935 + | 26039..=26040 + | 26042..=26043 + | 26342..=26343 + | 26345..=26346 + | 26450..=26451 + | 26453..=26454 => &SHAPE154, 8039 | 8042 | 8051 | 8054 | 8147 | 8150 | 8159 | 8162 | 8363 | 8366 | 8375 | 8378 | 8471 | 8474 | 8483 | 8486 | 14280 | 14283 | 14292 | 14295 | 14388 | 14391 | 14400 | 14403 | 14604 | 14607 | 14616 | 14619 | 14712 | 14715 | 14724 | 14727 | 14928 @@ -6207,10 +6855,12 @@ impl BlockWithShape for BlockState { | 18171 | 18180 | 18183 | 18276 | 18279 | 18288 | 18291 | 19661 | 19664 | 19673 | 19676 | 19769 | 19772 | 19781 | 19784 | 20081 | 20084 | 20093 | 20096 | 20189 | 20192 | 20201 | 20204 | 20518 | 20521 | 20530 | 20533 | 20626 | 20629 | 20638 - | 20641 | 22800 | 22803 | 22812 | 22815 | 22908 | 22911 | 22920 | 22923 | 23211 - | 23214 | 23223 | 23226 | 23319 | 23322 | 23331 | 23334 | 23622 | 23625 | 23634 - | 23637 | 23730 | 23733 | 23742 | 23745 | 24033 | 24036 | 24045 | 24048 | 24141 - | 24144 | 24153 | 24156 => &SHAPE155, + | 20641 | 21288 | 21291 | 21300 | 21303 | 21396 | 21399 | 21408 | 21411 | 21699 + | 21702 | 21711 | 21714 | 21807 | 21810 | 21819 | 21822 | 22111 | 22114 | 22123 + | 22126 | 22219 | 22222 | 22231 | 22234 | 25114 | 25117 | 25126 | 25129 | 25222 + | 25225 | 25234 | 25237 | 25525 | 25528 | 25537 | 25540 | 25633 | 25636 | 25645 + | 25648 | 25936 | 25939 | 25948 | 25951 | 26044 | 26047 | 26056 | 26059 | 26347 + | 26350 | 26359 | 26362 | 26455 | 26458 | 26467 | 26470 => &SHAPE155, 8040..=8041 | 8043..=8044 | 8052..=8053 @@ -6355,38 +7005,62 @@ impl BlockWithShape for BlockState { | 20630..=20631 | 20639..=20640 | 20642..=20643 - | 22801..=22802 - | 22804..=22805 - | 22813..=22814 - | 22816..=22817 - | 22909..=22910 - | 22912..=22913 - | 22921..=22922 - | 22924..=22925 - | 23212..=23213 - | 23215..=23216 - | 23224..=23225 - | 23227..=23228 - | 23320..=23321 - | 23323..=23324 - | 23332..=23333 - | 23335..=23336 - | 23623..=23624 - | 23626..=23627 - | 23635..=23636 - | 23638..=23639 - | 23731..=23732 - | 23734..=23735 - | 23743..=23744 - | 23746..=23747 - | 24034..=24035 - | 24037..=24038 - | 24046..=24047 - | 24049..=24050 - | 24142..=24143 - | 24145..=24146 - | 24154..=24155 - | 24157..=24158 => &SHAPE156, + | 21289..=21290 + | 21292..=21293 + | 21301..=21302 + | 21304..=21305 + | 21397..=21398 + | 21400..=21401 + | 21409..=21410 + | 21412..=21413 + | 21700..=21701 + | 21703..=21704 + | 21712..=21713 + | 21715..=21716 + | 21808..=21809 + | 21811..=21812 + | 21820..=21821 + | 21823..=21824 + | 22112..=22113 + | 22115..=22116 + | 22124..=22125 + | 22127..=22128 + | 22220..=22221 + | 22223..=22224 + | 22232..=22233 + | 22235..=22236 + | 25115..=25116 + | 25118..=25119 + | 25127..=25128 + | 25130..=25131 + | 25223..=25224 + | 25226..=25227 + | 25235..=25236 + | 25238..=25239 + | 25526..=25527 + | 25529..=25530 + | 25538..=25539 + | 25541..=25542 + | 25634..=25635 + | 25637..=25638 + | 25646..=25647 + | 25649..=25650 + | 25937..=25938 + | 25940..=25941 + | 25949..=25950 + | 25952..=25953 + | 26045..=26046 + | 26048..=26049 + | 26057..=26058 + | 26060..=26061 + | 26348..=26349 + | 26351..=26352 + | 26360..=26361 + | 26363..=26364 + | 26456..=26457 + | 26459..=26460 + | 26468..=26469 + | 26471..=26472 => &SHAPE156, 8045 | 8048 | 8057 | 8060 | 8153 | 8156 | 8165 | 8168 | 8369 | 8372 | 8381 | 8384 | 8477 | 8480 | 8489 | 8492 | 14286 | 14289 | 14298 | 14301 | 14394 | 14397 | 14406 | 14409 | 14610 | 14613 | 14622 | 14625 | 14718 | 14721 | 14730 | 14733 | 14934 @@ -6401,10 +7075,12 @@ impl BlockWithShape for BlockState { | 18177 | 18186 | 18189 | 18282 | 18285 | 18294 | 18297 | 19667 | 19670 | 19679 | 19682 | 19775 | 19778 | 19787 | 19790 | 20087 | 20090 | 20099 | 20102 | 20195 | 20198 | 20207 | 20210 | 20524 | 20527 | 20536 | 20539 | 20632 | 20635 | 20644 - | 20647 | 22806 | 22809 | 22818 | 22821 | 22914 | 22917 | 22926 | 22929 | 23217 - | 23220 | 23229 | 23232 | 23325 | 23328 | 23337 | 23340 | 23628 | 23631 | 23640 - | 23643 | 23736 | 23739 | 23748 | 23751 | 24039 | 24042 | 24051 | 24054 | 24147 - | 24150 | 24159 | 24162 => &SHAPE157, + | 20647 | 21294 | 21297 | 21306 | 21309 | 21402 | 21405 | 21414 | 21417 | 21705 + | 21708 | 21717 | 21720 | 21813 | 21816 | 21825 | 21828 | 22117 | 22120 | 22129 + | 22132 | 22225 | 22228 | 22237 | 22240 | 25120 | 25123 | 25132 | 25135 | 25228 + | 25231 | 25240 | 25243 | 25531 | 25534 | 25543 | 25546 | 25639 | 25642 | 25651 + | 25654 | 25942 | 25945 | 25954 | 25957 | 26050 | 26053 | 26062 | 26065 | 26353 + | 26356 | 26365 | 26368 | 26461 | 26464 | 26473 | 26476 => &SHAPE157, 8046..=8047 | 8049..=8050 | 8058..=8059 @@ -6549,38 +7225,62 @@ impl BlockWithShape for BlockState { | 20636..=20637 | 20645..=20646 | 20648..=20649 - | 22807..=22808 - | 22810..=22811 - | 22819..=22820 - | 22822..=22823 - | 22915..=22916 - | 22918..=22919 - | 22927..=22928 - | 22930..=22931 - | 23218..=23219 - | 23221..=23222 - | 23230..=23231 - | 23233..=23234 - | 23326..=23327 - | 23329..=23330 - | 23338..=23339 - | 23341..=23342 - | 23629..=23630 - | 23632..=23633 - | 23641..=23642 - | 23644..=23645 - | 23737..=23738 - | 23740..=23741 - | 23749..=23750 - | 23752..=23753 - | 24040..=24041 - | 24043..=24044 - | 24052..=24053 - | 24055..=24056 - | 24148..=24149 - | 24151..=24152 - | 24160..=24161 - | 24163..=24164 => &SHAPE158, + | 21295..=21296 + | 21298..=21299 + | 21307..=21308 + | 21310..=21311 + | 21403..=21404 + | 21406..=21407 + | 21415..=21416 + | 21418..=21419 + | 21706..=21707 + | 21709..=21710 + | 21718..=21719 + | 21721..=21722 + | 21814..=21815 + | 21817..=21818 + | 21826..=21827 + | 21829..=21830 + | 22118..=22119 + | 22121..=22122 + | 22130..=22131 + | 22133..=22134 + | 22226..=22227 + | 22229..=22230 + | 22238..=22239 + | 22241..=22242 + | 25121..=25122 + | 25124..=25125 + | 25133..=25134 + | 25136..=25137 + | 25229..=25230 + | 25232..=25233 + | 25241..=25242 + | 25244..=25245 + | 25532..=25533 + | 25535..=25536 + | 25544..=25545 + | 25547..=25548 + | 25640..=25641 + | 25643..=25644 + | 25652..=25653 + | 25655..=25656 + | 25943..=25944 + | 25946..=25947 + | 25955..=25956 + | 25958..=25959 + | 26051..=26052 + | 26054..=26055 + | 26063..=26064 + | 26066..=26067 + | 26354..=26355 + | 26357..=26358 + | 26366..=26367 + | 26369..=26370 + | 26462..=26463 + | 26465..=26466 + | 26474..=26475 + | 26477..=26478 => &SHAPE158, 8063 | 8066 | 8099 | 8102 | 8171 | 8174 | 8207 | 8210 | 8387 | 8390 | 8423 | 8426 | 8495 | 8498 | 8531 | 8534 | 14304 | 14307 | 14340 | 14343 | 14412 | 14415 | 14448 | 14451 | 14628 | 14631 | 14664 | 14667 | 14736 | 14739 | 14772 | 14775 | 14952 @@ -6595,10 +7295,12 @@ impl BlockWithShape for BlockState { | 18195 | 18228 | 18231 | 18300 | 18303 | 18336 | 18339 | 19685 | 19688 | 19721 | 19724 | 19793 | 19796 | 19829 | 19832 | 20105 | 20108 | 20141 | 20144 | 20213 | 20216 | 20249 | 20252 | 20542 | 20545 | 20578 | 20581 | 20650 | 20653 | 20686 - | 20689 | 22824 | 22827 | 22860 | 22863 | 22932 | 22935 | 22968 | 22971 | 23235 - | 23238 | 23271 | 23274 | 23343 | 23346 | 23379 | 23382 | 23646 | 23649 | 23682 - | 23685 | 23754 | 23757 | 23790 | 23793 | 24057 | 24060 | 24093 | 24096 | 24165 - | 24168 | 24201 | 24204 => &SHAPE159, + | 20689 | 21312 | 21315 | 21348 | 21351 | 21420 | 21423 | 21456 | 21459 | 21723 + | 21726 | 21759 | 21762 | 21831 | 21834 | 21867 | 21870 | 22135 | 22138 | 22171 + | 22174 | 22243 | 22246 | 22279 | 22282 | 25138 | 25141 | 25174 | 25177 | 25246 + | 25249 | 25282 | 25285 | 25549 | 25552 | 25585 | 25588 | 25657 | 25660 | 25693 + | 25696 | 25960 | 25963 | 25996 | 25999 | 26068 | 26071 | 26104 | 26107 | 26371 + | 26374 | 26407 | 26410 | 26479 | 26482 | 26515 | 26518 => &SHAPE159, 8064..=8065 | 8067..=8068 | 8100..=8101 @@ -6743,38 +7445,62 @@ impl BlockWithShape for BlockState { | 20654..=20655 | 20687..=20688 | 20690..=20691 - | 22825..=22826 - | 22828..=22829 - | 22861..=22862 - | 22864..=22865 - | 22933..=22934 - | 22936..=22937 - | 22969..=22970 - | 22972..=22973 - | 23236..=23237 - | 23239..=23240 - | 23272..=23273 - | 23275..=23276 - | 23344..=23345 - | 23347..=23348 - | 23380..=23381 - | 23383..=23384 - | 23647..=23648 - | 23650..=23651 - | 23683..=23684 - | 23686..=23687 - | 23755..=23756 - | 23758..=23759 - | 23791..=23792 - | 23794..=23795 - | 24058..=24059 - | 24061..=24062 - | 24094..=24095 - | 24097..=24098 - | 24166..=24167 - | 24169..=24170 - | 24202..=24203 - | 24205..=24206 => &SHAPE160, + | 21313..=21314 + | 21316..=21317 + | 21349..=21350 + | 21352..=21353 + | 21421..=21422 + | 21424..=21425 + | 21457..=21458 + | 21460..=21461 + | 21724..=21725 + | 21727..=21728 + | 21760..=21761 + | 21763..=21764 + | 21832..=21833 + | 21835..=21836 + | 21868..=21869 + | 21871..=21872 + | 22136..=22137 + | 22139..=22140 + | 22172..=22173 + | 22175..=22176 + | 22244..=22245 + | 22247..=22248 + | 22280..=22281 + | 22283..=22284 + | 25139..=25140 + | 25142..=25143 + | 25175..=25176 + | 25178..=25179 + | 25247..=25248 + | 25250..=25251 + | 25283..=25284 + | 25286..=25287 + | 25550..=25551 + | 25553..=25554 + | 25586..=25587 + | 25589..=25590 + | 25658..=25659 + | 25661..=25662 + | 25694..=25695 + | 25697..=25698 + | 25961..=25962 + | 25964..=25965 + | 25997..=25998 + | 26000..=26001 + | 26069..=26070 + | 26072..=26073 + | 26105..=26106 + | 26108..=26109 + | 26372..=26373 + | 26375..=26376 + | 26408..=26409 + | 26411..=26412 + | 26480..=26481 + | 26483..=26484 + | 26516..=26517 + | 26519..=26520 => &SHAPE160, 8069 | 8072 | 8105 | 8108 | 8177 | 8180 | 8213 | 8216 | 8393 | 8396 | 8429 | 8432 | 8501 | 8504 | 8537 | 8540 | 14310 | 14313 | 14346 | 14349 | 14418 | 14421 | 14454 | 14457 | 14634 | 14637 | 14670 | 14673 | 14742 | 14745 | 14778 | 14781 | 14958 @@ -6789,10 +7515,12 @@ impl BlockWithShape for BlockState { | 18201 | 18234 | 18237 | 18306 | 18309 | 18342 | 18345 | 19691 | 19694 | 19727 | 19730 | 19799 | 19802 | 19835 | 19838 | 20111 | 20114 | 20147 | 20150 | 20219 | 20222 | 20255 | 20258 | 20548 | 20551 | 20584 | 20587 | 20656 | 20659 | 20692 - | 20695 | 22830 | 22833 | 22866 | 22869 | 22938 | 22941 | 22974 | 22977 | 23241 - | 23244 | 23277 | 23280 | 23349 | 23352 | 23385 | 23388 | 23652 | 23655 | 23688 - | 23691 | 23760 | 23763 | 23796 | 23799 | 24063 | 24066 | 24099 | 24102 | 24171 - | 24174 | 24207 | 24210 => &SHAPE161, + | 20695 | 21318 | 21321 | 21354 | 21357 | 21426 | 21429 | 21462 | 21465 | 21729 + | 21732 | 21765 | 21768 | 21837 | 21840 | 21873 | 21876 | 22141 | 22144 | 22177 + | 22180 | 22249 | 22252 | 22285 | 22288 | 25144 | 25147 | 25180 | 25183 | 25252 + | 25255 | 25288 | 25291 | 25555 | 25558 | 25591 | 25594 | 25663 | 25666 | 25699 + | 25702 | 25966 | 25969 | 26002 | 26005 | 26074 | 26077 | 26110 | 26113 | 26377 + | 26380 | 26413 | 26416 | 26485 | 26488 | 26521 | 26524 => &SHAPE161, 8070..=8071 | 8073..=8074 | 8106..=8107 @@ -6937,38 +7665,62 @@ impl BlockWithShape for BlockState { | 20660..=20661 | 20693..=20694 | 20696..=20697 - | 22831..=22832 - | 22834..=22835 - | 22867..=22868 - | 22870..=22871 - | 22939..=22940 - | 22942..=22943 - | 22975..=22976 - | 22978..=22979 - | 23242..=23243 - | 23245..=23246 - | 23278..=23279 - | 23281..=23282 - | 23350..=23351 - | 23353..=23354 - | 23386..=23387 - | 23389..=23390 - | 23653..=23654 - | 23656..=23657 - | 23689..=23690 - | 23692..=23693 - | 23761..=23762 - | 23764..=23765 - | 23797..=23798 - | 23800..=23801 - | 24064..=24065 - | 24067..=24068 - | 24100..=24101 - | 24103..=24104 - | 24172..=24173 - | 24175..=24176 - | 24208..=24209 - | 24211..=24212 => &SHAPE162, + | 21319..=21320 + | 21322..=21323 + | 21355..=21356 + | 21358..=21359 + | 21427..=21428 + | 21430..=21431 + | 21463..=21464 + | 21466..=21467 + | 21730..=21731 + | 21733..=21734 + | 21766..=21767 + | 21769..=21770 + | 21838..=21839 + | 21841..=21842 + | 21874..=21875 + | 21877..=21878 + | 22142..=22143 + | 22145..=22146 + | 22178..=22179 + | 22181..=22182 + | 22250..=22251 + | 22253..=22254 + | 22286..=22287 + | 22289..=22290 + | 25145..=25146 + | 25148..=25149 + | 25181..=25182 + | 25184..=25185 + | 25253..=25254 + | 25256..=25257 + | 25289..=25290 + | 25292..=25293 + | 25556..=25557 + | 25559..=25560 + | 25592..=25593 + | 25595..=25596 + | 25664..=25665 + | 25667..=25668 + | 25700..=25701 + | 25703..=25704 + | 25967..=25968 + | 25970..=25971 + | 26003..=26004 + | 26006..=26007 + | 26075..=26076 + | 26078..=26079 + | 26111..=26112 + | 26114..=26115 + | 26378..=26379 + | 26381..=26382 + | 26414..=26415 + | 26417..=26418 + | 26486..=26487 + | 26489..=26490 + | 26522..=26523 + | 26525..=26526 => &SHAPE162, 8075 | 8078 | 8087 | 8090 | 8111 | 8114 | 8123 | 8126 | 8183 | 8186 | 8195 | 8198 | 8219 | 8222 | 8231 | 8234 | 8399 | 8402 | 8411 | 8414 | 8435 | 8438 | 8447 | 8450 | 8507 | 8510 | 8519 | 8522 | 8543 | 8546 | 8555 | 8558 | 14316 | 14319 | 14328 @@ -6997,13 +7749,18 @@ impl BlockWithShape for BlockState { | 19856 | 20117 | 20120 | 20129 | 20132 | 20153 | 20156 | 20165 | 20168 | 20225 | 20228 | 20237 | 20240 | 20261 | 20264 | 20273 | 20276 | 20554 | 20557 | 20566 | 20569 | 20590 | 20593 | 20602 | 20605 | 20662 | 20665 | 20674 | 20677 | 20698 - | 20701 | 20710 | 20713 | 22836 | 22839 | 22848 | 22851 | 22872 | 22875 | 22884 - | 22887 | 22944 | 22947 | 22956 | 22959 | 22980 | 22983 | 22992 | 22995 | 23247 - | 23250 | 23259 | 23262 | 23283 | 23286 | 23295 | 23298 | 23355 | 23358 | 23367 - | 23370 | 23391 | 23394 | 23403 | 23406 | 23658 | 23661 | 23670 | 23673 | 23694 - | 23697 | 23706 | 23709 | 23766 | 23769 | 23778 | 23781 | 23802 | 23805 | 23814 - | 23817 | 24069 | 24072 | 24081 | 24084 | 24105 | 24108 | 24117 | 24120 | 24177 - | 24180 | 24189 | 24192 | 24213 | 24216 | 24225 | 24228 => &SHAPE163, + | 20701 | 20710 | 20713 | 21324 | 21327 | 21336 | 21339 | 21360 | 21363 | 21372 + | 21375 | 21432 | 21435 | 21444 | 21447 | 21468 | 21471 | 21480 | 21483 | 21735 + | 21738 | 21747 | 21750 | 21771 | 21774 | 21783 | 21786 | 21843 | 21846 | 21855 + | 21858 | 21879 | 21882 | 21891 | 21894 | 22147 | 22150 | 22159 | 22162 | 22183 + | 22186 | 22195 | 22198 | 22255 | 22258 | 22267 | 22270 | 22291 | 22294 | 22303 + | 22306 | 25150 | 25153 | 25162 | 25165 | 25186 | 25189 | 25198 | 25201 | 25258 + | 25261 | 25270 | 25273 | 25294 | 25297 | 25306 | 25309 | 25561 | 25564 | 25573 + | 25576 | 25597 | 25600 | 25609 | 25612 | 25669 | 25672 | 25681 | 25684 | 25705 + | 25708 | 25717 | 25720 | 25972 | 25975 | 25984 | 25987 | 26008 | 26011 | 26020 + | 26023 | 26080 | 26083 | 26092 | 26095 | 26116 | 26119 | 26128 | 26131 | 26383 + | 26386 | 26395 | 26398 | 26419 | 26422 | 26431 | 26434 | 26491 | 26494 | 26503 + | 26506 | 26527 | 26530 | 26539 | 26542 => &SHAPE163, 8076..=8077 | 8079..=8080 | 8088..=8089 @@ -7292,70 +8049,118 @@ impl BlockWithShape for BlockState { | 20702..=20703 | 20711..=20712 | 20714..=20715 - | 22837..=22838 - | 22840..=22841 - | 22849..=22850 - | 22852..=22853 - | 22873..=22874 - | 22876..=22877 - | 22885..=22886 - | 22888..=22889 - | 22945..=22946 - | 22948..=22949 - | 22957..=22958 - | 22960..=22961 - | 22981..=22982 - | 22984..=22985 - | 22993..=22994 - | 22996..=22997 - | 23248..=23249 - | 23251..=23252 - | 23260..=23261 - | 23263..=23264 - | 23284..=23285 - | 23287..=23288 - | 23296..=23297 - | 23299..=23300 - | 23356..=23357 - | 23359..=23360 - | 23368..=23369 - | 23371..=23372 - | 23392..=23393 - | 23395..=23396 - | 23404..=23405 - | 23407..=23408 - | 23659..=23660 - | 23662..=23663 - | 23671..=23672 - | 23674..=23675 - | 23695..=23696 - | 23698..=23699 - | 23707..=23708 - | 23710..=23711 - | 23767..=23768 - | 23770..=23771 - | 23779..=23780 - | 23782..=23783 - | 23803..=23804 - | 23806..=23807 - | 23815..=23816 - | 23818..=23819 - | 24070..=24071 - | 24073..=24074 - | 24082..=24083 - | 24085..=24086 - | 24106..=24107 - | 24109..=24110 - | 24118..=24119 - | 24121..=24122 - | 24178..=24179 - | 24181..=24182 - | 24190..=24191 - | 24193..=24194 - | 24214..=24215 - | 24217..=24218 - | 24226..=24227 - | 24229..=24230 => &SHAPE164, + | 21325..=21326 + | 21328..=21329 + | 21337..=21338 + | 21340..=21341 + | 21361..=21362 + | 21364..=21365 + | 21373..=21374 + | 21376..=21377 + | 21433..=21434 + | 21436..=21437 + | 21445..=21446 + | 21448..=21449 + | 21469..=21470 + | 21472..=21473 + | 21481..=21482 + | 21484..=21485 + | 21736..=21737 + | 21739..=21740 + | 21748..=21749 + | 21751..=21752 + | 21772..=21773 + | 21775..=21776 + | 21784..=21785 + | 21787..=21788 + | 21844..=21845 + | 21847..=21848 + | 21856..=21857 + | 21859..=21860 + | 21880..=21881 + | 21883..=21884 + | 21892..=21893 + | 21895..=21896 + | 22148..=22149 + | 22151..=22152 + | 22160..=22161 + | 22163..=22164 + | 22184..=22185 + | 22187..=22188 + | 22196..=22197 + | 22199..=22200 + | 22256..=22257 + | 22259..=22260 + | 22268..=22269 + | 22271..=22272 + | 22292..=22293 + | 22295..=22296 + | 22304..=22305 + | 22307..=22308 + | 25151..=25152 + | 25154..=25155 + | 25163..=25164 + | 25166..=25167 + | 25187..=25188 + | 25190..=25191 + | 25199..=25200 + | 25202..=25203 + | 25259..=25260 + | 25262..=25263 + | 25271..=25272 + | 25274..=25275 + | 25295..=25296 + | 25298..=25299 + | 25307..=25308 + | 25310..=25311 + | 25562..=25563 + | 25565..=25566 + | 25574..=25575 + | 25577..=25578 + | 25598..=25599 + | 25601..=25602 + | 25610..=25611 + | 25613..=25614 + | 25670..=25671 + | 25673..=25674 + | 25682..=25683 + | 25685..=25686 + | 25706..=25707 + | 25709..=25710 + | 25718..=25719 + | 25721..=25722 + | 25973..=25974 + | 25976..=25977 + | 25985..=25986 + | 25988..=25989 + | 26009..=26010 + | 26012..=26013 + | 26021..=26022 + | 26024..=26025 + | 26081..=26082 + | 26084..=26085 + | 26093..=26094 + | 26096..=26097 + | 26117..=26118 + | 26120..=26121 + | 26129..=26130 + | 26132..=26133 + | 26384..=26385 + | 26387..=26388 + | 26396..=26397 + | 26399..=26400 + | 26420..=26421 + | 26423..=26424 + | 26432..=26433 + | 26435..=26436 + | 26492..=26493 + | 26495..=26496 + | 26504..=26505 + | 26507..=26508 + | 26528..=26529 + | 26531..=26532 + | 26540..=26541 + | 26543..=26544 => &SHAPE164, 8081 | 8084 | 8093 | 8096 | 8117 | 8120 | 8129 | 8132 | 8189 | 8192 | 8201 | 8204 | 8225 | 8228 | 8237 | 8240 | 8405 | 8408 | 8417 | 8420 | 8441 | 8444 | 8453 | 8456 | 8513 | 8516 | 8525 | 8528 | 8549 | 8552 | 8561 | 8564 | 14322 | 14325 | 14334 @@ -7384,13 +8189,18 @@ impl BlockWithShape for BlockState { | 19862 | 20123 | 20126 | 20135 | 20138 | 20159 | 20162 | 20171 | 20174 | 20231 | 20234 | 20243 | 20246 | 20267 | 20270 | 20279 | 20282 | 20560 | 20563 | 20572 | 20575 | 20596 | 20599 | 20608 | 20611 | 20668 | 20671 | 20680 | 20683 | 20704 - | 20707 | 20716 | 20719 | 22842 | 22845 | 22854 | 22857 | 22878 | 22881 | 22890 - | 22893 | 22950 | 22953 | 22962 | 22965 | 22986 | 22989 | 22998 | 23001 | 23253 - | 23256 | 23265 | 23268 | 23289 | 23292 | 23301 | 23304 | 23361 | 23364 | 23373 - | 23376 | 23397 | 23400 | 23409 | 23412 | 23664 | 23667 | 23676 | 23679 | 23700 - | 23703 | 23712 | 23715 | 23772 | 23775 | 23784 | 23787 | 23808 | 23811 | 23820 - | 23823 | 24075 | 24078 | 24087 | 24090 | 24111 | 24114 | 24123 | 24126 | 24183 - | 24186 | 24195 | 24198 | 24219 | 24222 | 24231 | 24234 => &SHAPE165, + | 20707 | 20716 | 20719 | 21330 | 21333 | 21342 | 21345 | 21366 | 21369 | 21378 + | 21381 | 21438 | 21441 | 21450 | 21453 | 21474 | 21477 | 21486 | 21489 | 21741 + | 21744 | 21753 | 21756 | 21777 | 21780 | 21789 | 21792 | 21849 | 21852 | 21861 + | 21864 | 21885 | 21888 | 21897 | 21900 | 22153 | 22156 | 22165 | 22168 | 22189 + | 22192 | 22201 | 22204 | 22261 | 22264 | 22273 | 22276 | 22297 | 22300 | 22309 + | 22312 | 25156 | 25159 | 25168 | 25171 | 25192 | 25195 | 25204 | 25207 | 25264 + | 25267 | 25276 | 25279 | 25300 | 25303 | 25312 | 25315 | 25567 | 25570 | 25579 + | 25582 | 25603 | 25606 | 25615 | 25618 | 25675 | 25678 | 25687 | 25690 | 25711 + | 25714 | 25723 | 25726 | 25978 | 25981 | 25990 | 25993 | 26014 | 26017 | 26026 + | 26029 | 26086 | 26089 | 26098 | 26101 | 26122 | 26125 | 26134 | 26137 | 26389 + | 26392 | 26401 | 26404 | 26425 | 26428 | 26437 | 26440 | 26497 | 26500 | 26509 + | 26512 | 26533 | 26536 | 26545 | 26548 => &SHAPE165, 8082..=8083 | 8085..=8086 | 8094..=8095 @@ -7679,71 +8489,119 @@ impl BlockWithShape for BlockState { | 20708..=20709 | 20717..=20718 | 20720..=20721 - | 22843..=22844 - | 22846..=22847 - | 22855..=22856 - | 22858..=22859 - | 22879..=22880 - | 22882..=22883 - | 22891..=22892 - | 22894..=22895 - | 22951..=22952 - | 22954..=22955 - | 22963..=22964 - | 22966..=22967 - | 22987..=22988 - | 22990..=22991 - | 22999..=23000 - | 23002..=23003 - | 23254..=23255 - | 23257..=23258 - | 23266..=23267 - | 23269..=23270 - | 23290..=23291 - | 23293..=23294 - | 23302..=23303 - | 23305..=23306 - | 23362..=23363 - | 23365..=23366 - | 23374..=23375 - | 23377..=23378 - | 23398..=23399 - | 23401..=23402 - | 23410..=23411 - | 23413..=23414 - | 23665..=23666 - | 23668..=23669 - | 23677..=23678 - | 23680..=23681 - | 23701..=23702 - | 23704..=23705 - | 23713..=23714 - | 23716..=23717 - | 23773..=23774 - | 23776..=23777 - | 23785..=23786 - | 23788..=23789 - | 23809..=23810 - | 23812..=23813 - | 23821..=23822 - | 23824..=23825 - | 24076..=24077 - | 24079..=24080 - | 24088..=24089 - | 24091..=24092 - | 24112..=24113 - | 24115..=24116 - | 24124..=24125 - | 24127..=24128 - | 24184..=24185 - | 24187..=24188 - | 24196..=24197 - | 24199..=24200 - | 24220..=24221 - | 24223..=24224 - | 24232..=24233 - | 24235..=24236 => &SHAPE166, - 8567..=8594 | 12957 | 19455..=19458 | 24247..=24248 => &SHAPE27, + | 21331..=21332 + | 21334..=21335 + | 21343..=21344 + | 21346..=21347 + | 21367..=21368 + | 21370..=21371 + | 21379..=21380 + | 21382..=21383 + | 21439..=21440 + | 21442..=21443 + | 21451..=21452 + | 21454..=21455 + | 21475..=21476 + | 21478..=21479 + | 21487..=21488 + | 21490..=21491 + | 21742..=21743 + | 21745..=21746 + | 21754..=21755 + | 21757..=21758 + | 21778..=21779 + | 21781..=21782 + | 21790..=21791 + | 21793..=21794 + | 21850..=21851 + | 21853..=21854 + | 21862..=21863 + | 21865..=21866 + | 21886..=21887 + | 21889..=21890 + | 21898..=21899 + | 21901..=21902 + | 22154..=22155 + | 22157..=22158 + | 22166..=22167 + | 22169..=22170 + | 22190..=22191 + | 22193..=22194 + | 22202..=22203 + | 22205..=22206 + | 22262..=22263 + | 22265..=22266 + | 22274..=22275 + | 22277..=22278 + | 22298..=22299 + | 22301..=22302 + | 22310..=22311 + | 22313..=22314 + | 25157..=25158 + | 25160..=25161 + | 25169..=25170 + | 25172..=25173 + | 25193..=25194 + | 25196..=25197 + | 25205..=25206 + | 25208..=25209 + | 25265..=25266 + | 25268..=25269 + | 25277..=25278 + | 25280..=25281 + | 25301..=25302 + | 25304..=25305 + | 25313..=25314 + | 25316..=25317 + | 25568..=25569 + | 25571..=25572 + | 25580..=25581 + | 25583..=25584 + | 25604..=25605 + | 25607..=25608 + | 25616..=25617 + | 25619..=25620 + | 25676..=25677 + | 25679..=25680 + | 25688..=25689 + | 25691..=25692 + | 25712..=25713 + | 25715..=25716 + | 25724..=25725 + | 25727..=25728 + | 25979..=25980 + | 25982..=25983 + | 25991..=25992 + | 25994..=25995 + | 26015..=26016 + | 26018..=26019 + | 26027..=26028 + | 26030..=26031 + | 26087..=26088 + | 26090..=26091 + | 26099..=26100 + | 26102..=26103 + | 26123..=26124 + | 26126..=26127 + | 26135..=26136 + | 26138..=26139 + | 26390..=26391 + | 26393..=26394 + | 26402..=26403 + | 26405..=26406 + | 26426..=26427 + | 26429..=26430 + | 26438..=26439 + | 26441..=26442 + | 26498..=26499 + | 26501..=26502 + | 26510..=26511 + | 26513..=26514 + | 26534..=26535 + | 26537..=26538 + | 26546..=26547 + | 26549..=26550 => &SHAPE166, + 8567..=8594 | 12957 | 19455..=19458 | 26561..=26562 => &SHAPE27, 8827..=8858 | 8867..=8898 | 8907..=8938 | 8947..=8978 | 8987..=9018 | 9027..=9058 => { &SHAPE169 } @@ -7816,22 +8674,25 @@ impl BlockWithShape for BlockState { | 19865..=19866 | 19875..=19876 | 20366..=20367 - | 22034..=22035 - | 22040..=22041 - | 22046..=22047 - | 22052..=22053 - | 22386..=22387 - | 22392..=22393 - | 22398..=22399 - | 22404..=22405 - | 22674..=22675 - | 23085..=23086 - | 23496..=23497 - | 23907..=23908 => &SHAPE186, - 10728..=10743 | 22512 => &SHAPE28, - 12334 | 12336 | 22410..=22413 | 22418..=22421 => &SHAPE187, - 12335 | 12337 | 22414..=22417 | 22422..=22425 => &SHAPE188, - 12338..=12339 | 22426..=22433 => &SHAPE90, + | 21082..=21083 + | 21493..=21494 + | 21905..=21906 + | 23276..=23277 + | 23282..=23283 + | 23288..=23289 + | 23294..=23295 + | 23628..=23629 + | 23634..=23635 + | 23640..=23641 + | 23646..=23647 + | 24988..=24989 + | 25399..=25400 + | 25810..=25811 + | 26221..=26222 => &SHAPE186, + 10728..=10743 | 24826 => &SHAPE28, + 12334 | 12336 | 24724..=24727 | 24732..=24735 => &SHAPE187, + 12335 | 12337 | 24728..=24731 | 24736..=24739 => &SHAPE188, + 12338..=12339 | 24740..=24747 => &SHAPE90, 12340 => &SHAPE189, 12341 => &SHAPE190, 12342 => &SHAPE191, @@ -7861,7 +8722,7 @@ impl BlockWithShape for BlockState { 12366 => &SHAPE215, 12367 => &SHAPE216, 12368 => &SHAPE217, - 12369 | 22446..=22449 => &SHAPE68, + 12369 | 24760..=24763 => &SHAPE68, 12370 => &SHAPE218, 12371 => &SHAPE219, 12372 => &SHAPE220, @@ -8023,23 +8884,23 @@ impl BlockWithShape for BlockState { 21075..=21076 => &SHAPE310, 21077..=21078 => &SHAPE311, 21079..=21080 => &SHAPE312, - 22434..=22437 => &SHAPE262, - 22438..=22439 => &SHAPE313, - 22440..=22441 => &SHAPE314, - 22442..=22445 => &SHAPE63, - 22450..=22453 => &SHAPE73, - 22510..=22511 => &SHAPE315, - 22530..=22533 | 22538..=22541 | 22546..=22549 | 22554..=22557 => &SHAPE316, - 22534..=22535 | 22542..=22543 | 22550..=22551 | 22558..=22559 => &SHAPE317, + 24748..=24751 => &SHAPE262, + 24752..=24753 => &SHAPE313, + 24754..=24755 => &SHAPE314, + 24756..=24759 => &SHAPE63, + 24764..=24767 => &SHAPE73, + 24824..=24825 => &SHAPE315, + 24844..=24847 | 24852..=24855 | 24860..=24863 | 24868..=24871 => &SHAPE316, + 24848..=24849 | 24856..=24857 | 24864..=24865 | 24872..=24873 => &SHAPE317, _ => &SHAPE1, } } fn is_shape_empty(&self) -> bool { - matches!(self.id, 0|25..=78|80..=111|1944..=1991|2004..=2010|2063..=2090|2355..=2872|2978..=4273|4278..=4285|4302..=4589|4662..=4681|4762..=5537|5626..=5651|5716..=5733|5738..=5772|5799..=5814|5859..=5863|5865..=5866|6813..=6998|7001..=7002|7005..=7006|7009..=7010|7013..=7014|7017..=7018|7021..=7022|7025..=7026|7385..=7388|7406|7521..=7664|7925|7928|8249|8252|8595..=8826|9143..=9174|9320..=9343|10367..=10398|10747..=11078|11310..=11311|11314..=11315|11318..=11319|11322..=11323|11326..=11327|11330..=11331|11334..=11335|11338..=11339|11342..=11343|11346..=11347|11350..=11351|11354..=11355|11358..=11359|11362..=11363|11366..=11367|11370..=11371|11374..=11375|11378..=11379|11382..=11383|11386..=11387|11390..=11391|11394..=11395|11398..=11399|11402..=11403|11406..=11407|11410..=11411|11414..=11415|11418..=11419|11422..=11423|11426..=11427|11430..=11431|11434..=11435|11438..=11439|11442..=11443|11446..=11447|11450..=11451|11454..=11455|11458..=11459|11462..=11463|11466..=11467|11470..=11471|11474..=11475|11478..=11479|11482..=11483|11486..=11487|11490..=11491|11494..=11495|11498..=11499|11502..=11503|11506..=11507|11510..=11511|11514..=11515|11518..=11519|11522..=11523|11526..=11527|11530..=11531|11534..=11535|11538..=11539|11542..=11543|11546..=11547|11550..=11551|11554..=11555|11558..=11559|11562..=11563|12495..=12496|12499|12501|12503|12505|12507..=12512|12514|12549|12760..=12786|12813..=12932|12944|12958..=12961|14166|14169|14490|14493|14814|14817|15138|15141|15462|15465|15786|15789|16110|16113|16434|16437|16758|16761|17082|17085|17406|17409|17730|17733|18054|18057|18575..=18578|18592|18594..=18595|18609|18611..=18665|18680..=18683|18876..=18877|18880..=18881|18884..=18885|18888..=18889|18892..=18893|18896..=18897|18900..=18901|18904..=18905|18908..=18909|18912..=18913|18916..=18917|18920..=18921|18924..=18925|18928..=18929|18932..=18933|18936..=18937|19100..=19147|19276..=19355|19547|19550|19967|19970|20372..=20397|20404|20407|21084|21566..=21693|22455..=22509|22513..=22528|22536..=22537|22544..=22545|22552..=22553|22560..=22587|22686|22689|23097|23100|23508|23511|23919|23922|24258) + matches!(self.id, 0|25..=78|80..=111|1944..=1991|2004..=2010|2063..=2090|2355..=2872|2978..=4273|4278..=4285|4302..=4589|4662..=4681|4762..=5537|5626..=5651|5716..=5733|5738..=5772|5799..=5814|5858..=5862|5864..=5865|6813..=6998|7001..=7002|7005..=7006|7009..=7010|7013..=7014|7017..=7018|7021..=7022|7025..=7026|7385..=7388|7406|7521..=7664|7925|7928|8249|8252|8595..=8826|9143..=9174|9320..=9343|10367..=10398|10747..=11078|11310..=11311|11314..=11315|11318..=11319|11322..=11323|11326..=11327|11330..=11331|11334..=11335|11338..=11339|11342..=11343|11346..=11347|11350..=11351|11354..=11355|11358..=11359|11362..=11363|11366..=11367|11370..=11371|11374..=11375|11378..=11379|11382..=11383|11386..=11387|11390..=11391|11394..=11395|11398..=11399|11402..=11403|11406..=11407|11410..=11411|11414..=11415|11418..=11419|11422..=11423|11426..=11427|11430..=11431|11434..=11435|11438..=11439|11442..=11443|11446..=11447|11450..=11451|11454..=11455|11458..=11459|11462..=11463|11466..=11467|11470..=11471|11474..=11475|11478..=11479|11482..=11483|11486..=11487|11490..=11491|11494..=11495|11498..=11499|11502..=11503|11506..=11507|11510..=11511|11514..=11515|11518..=11519|11522..=11523|11526..=11527|11530..=11531|11534..=11535|11538..=11539|11542..=11543|11546..=11547|11550..=11551|11554..=11555|11558..=11559|11562..=11563|12495..=12496|12499|12501|12503|12505|12507..=12512|12514|12549|12760..=12786|12813..=12932|12944|12958..=12961|14166|14169|14490|14493|14814|14817|15138|15141|15462|15465|15786|15789|16110|16113|16434|16437|16758|16761|17082|17085|17406|17409|17730|17733|18054|18057|18575..=18578|18592|18594..=18595|18609|18611..=18665|18680..=18683|18876..=18877|18880..=18881|18884..=18885|18888..=18889|18892..=18893|18896..=18897|18900..=18901|18904..=18905|18908..=18909|18912..=18913|18916..=18917|18920..=18921|18924..=18925|18928..=18929|18932..=18933|18936..=18937|19100..=19147|19276..=19355|19547|19550|19967|19970|20372..=20397|20404|20407|21174|21177|21585|21588|21997|22000|22318|22800..=22927|24769..=24823|24827..=24842|24850..=24851|24858..=24859|24866..=24867|24874..=24901|25000|25003|25411|25414|25822|25825|26233|26236|26572) } fn is_shape_full(&self) -> bool { - matches!(self.id, 1..=24|79|112..=1687|1998..=2003|2017..=2022|2047..=2062|2091..=2354|2873|4274..=4277|4294..=4301|5734..=5737|5780..=5781|5798|5815..=5816|5849..=5850|5852..=5858|5864|5867..=5874|5946..=5961|6538..=6741|6812|7269..=7270|7272|7415|7417..=7418|7511..=7512|7665|7906..=7918|9223..=9224|9235..=9239|9344..=9371|10364..=10366|10463..=10465|10710..=10711|10716..=10717|10722..=10727|10744..=10746|11079..=11081|11166..=11167|11172..=11173|11178..=11179|11184..=11185|11190..=11191|11196..=11197|11202..=11203|11208..=11209|11214..=11215|11220..=11221|11226..=11227|11232..=11233|11238..=11239|11244..=11245|11250..=11251|11256..=11257|11262..=11263|11268..=11269|11274..=11275|11280..=11281|11286..=11287|11292..=11293|11298..=11299|11304..=11309|12404..=12413|12494|12515..=12548|12550..=12759|12787|12803..=12812|12941|14086..=14087|14092..=14093|14098..=14099|14104..=14105|14110..=14111|14116..=14117|14122..=14123|14128..=14129|14134..=14135|14140..=14141|14146..=14147|14152..=14153|14158..=14159|18404..=18437|18466|18579..=18591|18593|18596..=18608|18610|18666..=18667|18672..=18673|18678..=18679|19356..=19371|19381..=19444|19446..=19454|19459..=19460|19869..=19874|19879..=19880|20285|20370..=20371|20722..=20724|21031..=21032|21081..=21083|21565|21694..=21695|21704..=21713|22038..=22039|22044..=22045|22050..=22051|22056..=22065|22390..=22391|22396..=22397|22402..=22403|22408..=22409|22454|22529|22588|22590..=22593|22678..=22679|23004|23089..=23090|23415|23500..=23501|23826|23911..=23912|24237..=24246|24249..=24257|24259) + matches!(self.id, 1..=24|79|112..=1687|1998..=2003|2017..=2022|2047..=2062|2091..=2354|2873|4274..=4277|4294..=4301|5734..=5737|5780..=5781|5798|5815..=5816|5849|5851..=5857|5863|5866..=5873|5945..=5960|6537..=6740|6811..=6812|7269..=7270|7272|7415|7417..=7418|7511..=7512|7665|7906..=7918|9223..=9224|9235..=9239|9344..=9371|10364..=10366|10463..=10465|10710..=10711|10716..=10717|10722..=10727|10744..=10746|11079..=11081|11166..=11167|11172..=11173|11178..=11179|11184..=11185|11190..=11191|11196..=11197|11202..=11203|11208..=11209|11214..=11215|11220..=11221|11226..=11227|11232..=11233|11238..=11239|11244..=11245|11250..=11251|11256..=11257|11262..=11263|11268..=11269|11274..=11275|11280..=11281|11286..=11287|11292..=11293|11298..=11299|11304..=11309|12404..=12413|12494|12515..=12548|12550..=12759|12787|12803..=12812|12941|14086..=14087|14092..=14093|14098..=14099|14104..=14105|14110..=14111|14116..=14117|14122..=14123|14128..=14129|14134..=14135|14140..=14141|14146..=14147|14152..=14153|14158..=14159|18404..=18437|18466|18579..=18591|18593|18596..=18608|18610|18666..=18667|18672..=18673|18678..=18679|19356..=19371|19381..=19444|19446..=19454|19459..=19460|19869..=19874|19879..=19880|20285|20370..=20371|20722..=20724|21031..=21032|21081|21086..=21087|21492|21497..=21498|21903..=21904|21909..=21910|22315..=22317|22799|22928..=22929|22938..=22955|23280..=23281|23286..=23287|23292..=23293|23298..=23307|23632..=23633|23638..=23639|23644..=23645|23650..=23651|24676..=24723|24768|24843|24902|24904..=24907|24992..=24993|25318|25403..=25404|25729|25814..=25815|26140|26225..=26226|26551..=26560|26563..=26571|26573|26590..=26643) } } diff --git a/azalea-physics/src/collision/mergers.rs b/azalea-physics/src/collision/mergers.rs index 7bb472a12..5744084e6 100755 --- a/azalea-physics/src/collision/mergers.rs +++ b/azalea-physics/src/collision/mergers.rs @@ -135,7 +135,7 @@ impl IndexMerger { } } - pub fn new_indirect(var1: &Vec, var2: &Vec, var3: bool, var4: bool) -> Self { + pub fn new_indirect(var1: &[f64], var2: &[f64], var3: bool, var4: bool) -> Self { let mut var5 = f64::NAN; let var7 = var1.len(); let var8 = var2.len(); @@ -204,11 +204,7 @@ impl IndexMerger { pub trait IndexConsumer = FnMut(i32, i32, i32) -> bool; -fn for_non_swapped_indexes( - lower: &Vec, - upper: &Vec, - mut consumer: impl IndexConsumer, -) -> bool { +fn for_non_swapped_indexes(lower: &[f64], upper: &[f64], mut consumer: impl IndexConsumer) -> bool { let var2 = lower.len(); for var3 in 0..var2 { if !consumer(var3.try_into().unwrap(), -1, var3.try_into().unwrap()) { @@ -234,6 +230,6 @@ mod tests { #[test] fn test_indirect_index_merger() { - IndexMerger::new_indirect(&vec![0.0, 1.0], &vec![0.0, 0.5, 1.0], true, true); + IndexMerger::new_indirect(&[0.0, 1.0], &[0.0, 0.5, 1.0], true, true); } } diff --git a/azalea-physics/src/collision/mod.rs b/azalea-physics/src/collision/mod.rs index 2c739b24c..72151b6b3 100644 --- a/azalea-physics/src/collision/mod.rs +++ b/azalea-physics/src/collision/mod.rs @@ -136,7 +136,7 @@ pub fn move_colliding( _mover_type: &MoverType, movement: &Vec3, world: &Instance, - mut position: Mut, + position: &mut Mut, physics: &mut azalea_entity::Physics, ) -> Result<(), MoveEntityError> { // TODO: do all these @@ -175,8 +175,8 @@ pub fn move_colliding( } }; - if new_pos != **position { - **position = new_pos; + if new_pos != ***position { + ***position = new_pos; } } @@ -185,11 +185,14 @@ pub fn move_colliding( let horizontal_collision = x_collision || z_collision; let vertical_collision = movement.y != collide_result.y; let on_ground = vertical_collision && movement.y < 0.; + + physics.horizontal_collision = horizontal_collision; + physics.vertical_collision = vertical_collision; physics.on_ground = on_ground; // TODO: minecraft checks for a "minor" horizontal collision here - let _block_pos_below = azalea_entity::on_pos_legacy(&world.chunks, &position); + let _block_pos_below = azalea_entity::on_pos_legacy(&world.chunks, position); // let _block_state_below = self // .world // .get_block_state(&block_pos_below) diff --git a/azalea-physics/src/lib.rs b/azalea-physics/src/lib.rs index 9f02ddc48..a69c1bcf2 100644 --- a/azalea-physics/src/lib.rs +++ b/azalea-physics/src/lib.rs @@ -9,13 +9,14 @@ use azalea_block::{Block, BlockState}; use azalea_core::{ math, position::{BlockPos, Vec3}, + tick::GameTick, }; use azalea_entity::{ metadata::Sprinting, move_relative, Attributes, InLoadedChunk, Jumping, LocalEntity, - LookDirection, Physics, Position, + LookDirection, OnClimbable, Physics, Pose, Position, }; use azalea_world::{Instance, InstanceContainer, InstanceName}; -use bevy_app::{App, FixedUpdate, Plugin}; +use bevy_app::{App, Plugin}; use bevy_ecs::{ query::With, schedule::{IntoSystemConfigs, SystemSet}, @@ -32,7 +33,7 @@ pub struct PhysicsPlugin; impl Plugin for PhysicsPlugin { fn build(&self, app: &mut App) { app.add_systems( - FixedUpdate, + GameTick, (ai_step, travel) .chain() .in_set(PhysicsSet) @@ -51,14 +52,28 @@ fn travel( &mut LookDirection, &mut Position, Option<&Sprinting>, + Option<&Pose>, &Attributes, &InstanceName, + &OnClimbable, + &Jumping, ), (With, With), >, instance_container: Res, ) { - for (mut physics, direction, position, sprinting, attributes, world_name) in &mut query { + for ( + mut physics, + direction, + position, + sprinting, + pose, + attributes, + world_name, + on_climbable, + jumping, + ) in &mut query + { let world_lock = instance_container .get(world_name) .expect("All entities should be in a valid world"); @@ -94,13 +109,18 @@ fn travel( // this applies the current delta let mut movement = handle_relative_friction_and_calculate_movement( - block_friction, - &world, - &mut physics, - &direction, - position, - attributes, - sprinting.map(|s| **s).unwrap_or(false), + HandleRelativeFrictionAndCalculateMovementOpts { + block_friction, + world: &world, + physics: &mut physics, + direction: &direction, + position, + attributes, + is_sprinting: sprinting.map(|s| **s).unwrap_or(false), + on_climbable, + pose, + jumping, + }, ); movement.y -= gravity; @@ -222,15 +242,33 @@ fn get_block_pos_below_that_affects_movement(position: &Position) -> BlockPos { ) } -fn handle_relative_friction_and_calculate_movement( +// opts for handle_relative_friction_and_calculate_movement +struct HandleRelativeFrictionAndCalculateMovementOpts<'a> { block_friction: f32, - world: &Instance, - physics: &mut Physics, - direction: &LookDirection, - // this is kept as a Mut for bevy change tracking - position: Mut, - attributes: &Attributes, + world: &'a Instance, + physics: &'a mut Physics, + direction: &'a LookDirection, + position: Mut<'a, Position>, + attributes: &'a Attributes, is_sprinting: bool, + on_climbable: &'a OnClimbable, + pose: Option<&'a Pose>, + jumping: &'a Jumping, +} + +fn handle_relative_friction_and_calculate_movement( + HandleRelativeFrictionAndCalculateMovementOpts { + block_friction, + world, + physics, + direction, + mut position, + attributes, + is_sprinting, + on_climbable, + pose, + jumping, + }: HandleRelativeFrictionAndCalculateMovementOpts<'_>, ) -> Vec3 { move_relative( physics, @@ -242,12 +280,14 @@ fn handle_relative_friction_and_calculate_movement( z: physics.zza as f64, }, ); - // entity.delta = entity.handle_on_climbable(entity.delta); + + physics.velocity = handle_on_climbable(physics.velocity, on_climbable, &position, world, pose); + move_colliding( &MoverType::Own, &physics.velocity.clone(), world, - position, + &mut position, physics, ) .expect("Entity should exist."); @@ -257,11 +297,58 @@ fn handle_relative_friction_and_calculate_movement( // || entity.getFeetBlockState().is(Blocks.POWDER_SNOW) && // PowderSnowBlock.canEntityWalkOnPowderSnow(entity))) { var3 = new // Vec3(var3.x, 0.2D, var3.z); } - // TODO: powdered snow + + if physics.horizontal_collision || **jumping { + let block_at_feet: azalea_registry::Block = world + .chunks + .get_block_state(&(*position).into()) + .unwrap_or_default() + .into(); + + // TODO: powdered snow + if **on_climbable || block_at_feet == azalea_registry::Block::PowderSnow { + physics.velocity.y = 0.2; + } + } physics.velocity } +fn handle_on_climbable( + velocity: Vec3, + on_climbable: &OnClimbable, + position: &Position, + world: &Instance, + pose: Option<&Pose>, +) -> Vec3 { + if !**on_climbable { + return velocity; + } + + // minecraft does resetFallDistance here + + const CLIMBING_SPEED: f64 = 0.15_f32 as f64; + + let x = f64::clamp(velocity.x, -CLIMBING_SPEED, CLIMBING_SPEED); + let z = f64::clamp(velocity.z, -CLIMBING_SPEED, CLIMBING_SPEED); + let mut y = f64::max(velocity.y, -CLIMBING_SPEED); + + // sneaking on ladders/vines + if y < 0.0 + && pose.copied() == Some(Pose::Sneaking) + && azalea_registry::Block::from( + world + .chunks + .get_block_state(&position.into()) + .unwrap_or_default(), + ) != azalea_registry::Block::Scaffolding + { + y = 0.; + } + + Vec3 { x, y, z } +} + // private float getFrictionInfluencedSpeed(float friction) { // return this.onGround ? this.getSpeed() * (0.21600002F / (friction * // friction * friction)) : this.flyingSpeed; } @@ -335,21 +422,18 @@ fn jump_boost_power() -> f64 { #[cfg(test)] mod tests { - use std::time::Duration; use super::*; - use azalea_core::{position::ChunkPos, resource_location::ResourceLocation}; + use azalea_core::{position::ChunkPos, resource_location::ResourceLocation, tick::GameTick}; use azalea_entity::{EntityBundle, EntityPlugin}; use azalea_world::{Chunk, MinecraftEntityId, PartialInstance}; use bevy_app::App; - use bevy_time::{Fixed, Time}; use uuid::Uuid; /// You need an app to spawn entities in the world and do updates. fn make_test_app() -> App { let mut app = App::new(); app.add_plugins((PhysicsPlugin, EntityPlugin)) - .insert_resource(Time::::from_duration(Duration::from_millis(50))) .init_resource::(); app } @@ -393,7 +477,7 @@ mod tests { assert_eq!(entity_pos.y, 70.); } app.update(); - app.world.run_schedule(FixedUpdate); + app.world.run_schedule(GameTick); app.update(); { let entity_pos = *app.world.get::(entity).unwrap(); @@ -402,7 +486,7 @@ mod tests { let entity_physics = app.world.get::(entity).unwrap(); assert!(entity_physics.velocity.y < 0.); } - app.world.run_schedule(FixedUpdate); + app.world.run_schedule(GameTick); app.update(); { let entity_pos = *app.world.get::(entity).unwrap(); @@ -456,7 +540,7 @@ mod tests { "Block state should exist, if this fails that means the chunk wasn't loaded and the block didn't get placed" ); app.update(); - app.world.run_schedule(FixedUpdate); + app.world.run_schedule(GameTick); app.update(); { let entity_pos = *app.world.get::(entity).unwrap(); @@ -465,7 +549,7 @@ mod tests { let entity_physics = app.world.get::(entity).unwrap(); assert!(entity_physics.velocity.y < 0.); } - app.world.run_schedule(FixedUpdate); + app.world.run_schedule(GameTick); app.update(); { let entity_pos = *app.world.get::(entity).unwrap(); @@ -521,7 +605,7 @@ mod tests { ); // do a few steps so we fall on the slab for _ in 0..20 { - app.world.run_schedule(FixedUpdate); + app.world.run_schedule(GameTick); app.update(); } let entity_pos = app.world.get::(entity).unwrap(); @@ -574,7 +658,7 @@ mod tests { ); // do a few steps so we fall on the slab for _ in 0..20 { - app.world.run_schedule(FixedUpdate); + app.world.run_schedule(GameTick); app.update(); } let entity_pos = app.world.get::(entity).unwrap(); @@ -631,7 +715,7 @@ mod tests { ); // do a few steps so we fall on the wall for _ in 0..20 { - app.world.run_schedule(FixedUpdate); + app.world.run_schedule(GameTick); app.update(); } @@ -693,7 +777,7 @@ mod tests { ); // do a few steps so we fall on the wall for _ in 0..20 { - app.world.run_schedule(FixedUpdate); + app.world.run_schedule(GameTick); app.update(); } diff --git a/azalea-protocol/Cargo.toml b/azalea-protocol/Cargo.toml index 7528b5e30..05329c0fc 100644 --- a/azalea-protocol/Cargo.toml +++ b/azalea-protocol/Cargo.toml @@ -4,39 +4,37 @@ edition = "2021" license = "MIT" name = "azalea-protocol" repository = "https://github.com/azalea-rs/azalea/tree/main/azalea-protocol" -version = "0.8.0" +version = "0.9.0" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -async-compression = { version = "^0.4.5", features = [ - "tokio", - "zlib", -], optional = true } +simdnbt = { version = "0.3", git = "https://github.com/azalea-rs/simdnbt" } async-recursion = "1.0.5" -azalea-auth = { path = "../azalea-auth", version = "0.8.0" } -azalea-block = { path = "../azalea-block", default-features = false, version = "0.8.0" } -azalea-brigadier = { path = "../azalea-brigadier", version = "^0.8.0", features = [ +azalea-auth = { path = "../azalea-auth", version = "0.9.0" } +azalea-block = { path = "../azalea-block", default-features = false, version = "0.9.0" } +azalea-brigadier = { path = "../azalea-brigadier", version = "0.9.0", features = [ "azalea-buf", ] } -azalea-buf = { path = "../azalea-buf", version = "0.8.0" } -azalea-chat = { path = "../azalea-chat", version = "0.8.0" } -azalea-core = { path = "../azalea-core", optional = true, version = "^0.8.0", features = [ +azalea-buf = { path = "../azalea-buf", version = "0.9.0" } +azalea-chat = { path = "../azalea-chat", version = "0.9.0", features = [ + "numbers", +] } +azalea-core = { path = "../azalea-core", optional = true, version = "0.9.0", features = [ "serde", ] } -azalea-crypto = { path = "../azalea-crypto", version = "0.8.0" } -azalea-entity = { version = "0.8.0", path = "../azalea-entity" } -azalea-inventory = { version = "0.8.0", path = "../azalea-inventory" } -simdnbt = { version = "0.2.1" } -azalea-protocol-macros = { path = "./azalea-protocol-macros", version = "0.8.0" } -azalea-registry = { path = "../azalea-registry", version = "0.8.0" } -azalea-world = { path = "../azalea-world", version = "0.8.0" } -bevy_ecs = { version = "0.12.0", default-features = false } +azalea-crypto = { path = "../azalea-crypto", version = "0.9.0" } +azalea-entity = { version = "0.9.0", path = "../azalea-entity" } +azalea-inventory = { version = "0.9.0", path = "../azalea-inventory" } +azalea-protocol-macros = { path = "./azalea-protocol-macros", version = "0.9.0" } +azalea-registry = { path = "../azalea-registry", version = "0.9.0" } +azalea-world = { path = "../azalea-world", version = "0.9.0" } +bevy_ecs = { version = "0.12.1", default-features = false } byteorder = "^1.5.0" bytes = "^1.5.0" flate2 = "1.0.28" futures = "0.3.29" -futures-lite = "2.0.1" +futures-lite = "2.1.0" futures-util = "0.3.29" tracing = "0.1.40" serde = { version = "^1.0", features = ["serde_derive"] } @@ -47,13 +45,14 @@ tokio-util = { version = "0.7.10", features = ["codec"] } trust-dns-resolver = { version = "^0.23.2", default-features = false, features = [ "tokio-runtime", ] } -uuid = "1.5.0" socks5-impl = "0.5.6" +uuid = "1.6.1" +log = "0.4.20" [features] connecting = [] default = ["packets"] -packets = ["connecting", "dep:async-compression", "dep:azalea-core"] +packets = ["connecting", "dep:azalea-core"] strict_registry = ["packets"] [dev-dependencies] diff --git a/azalea-protocol/azalea-protocol-macros/Cargo.toml b/azalea-protocol/azalea-protocol-macros/Cargo.toml index f463e0662..b20ac4da1 100644 --- a/azalea-protocol/azalea-protocol-macros/Cargo.toml +++ b/azalea-protocol/azalea-protocol-macros/Cargo.toml @@ -3,7 +3,7 @@ description = "Macros internally used in azalea-protocol." edition = "2021" license = "MIT" name = "azalea-protocol-macros" -version = "0.8.0" +version = "0.9.0" repository = "https://github.com/azalea-rs/azalea/tree/main/azalea-protocol/azalea-protocol-macros" [lib] @@ -11,6 +11,6 @@ proc-macro = true # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -proc-macro2 = "^1.0.69" +proc-macro2 = "^1.0.70" quote = "^1.0.33" syn = "^2.0.39" diff --git a/azalea-protocol/examples/handshake_proxy.rs b/azalea-protocol/examples/handshake_proxy.rs index 4e9719b02..e63e8197c 100644 --- a/azalea-protocol/examples/handshake_proxy.rs +++ b/azalea-protocol/examples/handshake_proxy.rs @@ -187,7 +187,7 @@ async fn transfer( outbound.set_nodelay(true)?; // Repeat the intent and hello packet - // recieved earlier to the proxy target + // received earlier to the proxy target let mut outbound_conn: Connection = Connection::wrap(outbound); outbound_conn.write(intent.get()).await?; diff --git a/azalea-protocol/src/connect.rs b/azalea-protocol/src/connect.rs index 6b568285c..87e2643e2 100755 --- a/azalea-protocol/src/connect.rs +++ b/azalea-protocol/src/connect.rs @@ -15,7 +15,6 @@ use azalea_auth::game_profile::GameProfile; use azalea_auth::sessionserver::{ClientSessionServerError, ServerSessionServerError}; use azalea_crypto::{Aes128CfbDec, Aes128CfbEnc}; use bytes::BytesMut; -use socks5_impl::protocol::UserKey; use std::fmt::Debug; use std::io::Cursor; use std::marker::PhantomData; @@ -258,14 +257,16 @@ pub enum ConnectionError { Io(#[from] std::io::Error), } +use socks5_impl::protocol::UserKey; + #[derive(Debug, Clone)] pub struct Proxy { pub addr: SocketAddr, - pub auth: Option<(String, String)>, + pub auth: Option } impl Proxy { - pub fn new(addr: SocketAddr, auth: Option<(String, String)>) -> Self { + pub fn new(addr: SocketAddr, auth: Option) -> Self { Self { addr, auth } } } @@ -278,7 +279,7 @@ impl Connection { let proxy_stream = TcpStream::connect(proxy.addr).await?; let mut stream = BufStream::new(proxy_stream); let auth = match proxy.auth { - Some((username, password)) => Some(UserKey::new(&username, &password)), + Some(user_key) => Some(user_key), None => None, }; diff --git a/azalea-protocol/src/lib.rs b/azalea-protocol/src/lib.rs index 973564eb6..8683233ed 100644 --- a/azalea-protocol/src/lib.rs +++ b/azalea-protocol/src/lib.rs @@ -76,30 +76,26 @@ impl Display for ServerAddress { } } -/// -/// Serde Deserialization for ServerAddress -/// This is necessary for config file usage -/// We are not using TryFrom because we want to use the serde error system -/// +/// Serde deserialization for ServerAddress. This is useful for config file +/// usage. impl<'de> serde::Deserialize<'de> for ServerAddress { - fn deserialize(deserializer: D) -> Result where D: serde::Deserializer<'de> { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { let string = String::deserialize(deserializer)?; - let mut parts = string.split(':'); - let host = parts.next().ok_or(serde::de::Error::custom("No host specified"))?.to_string(); - // default the port to 25565 - let port = parts.next().unwrap_or("25565"); - let port = u16::from_str(port).map_err(|_| serde::de::Error::custom("Invalid port specified"))?; - Ok(ServerAddress { host, port }) + ServerAddress::try_from(string.as_str()).map_err(serde::de::Error::custom) } } -/// -/// Serde Serialization for ServerAddress -/// Pretty much like impl Display -/// +/// Serde serialization for ServerAddress. This uses the Display impl, so it +/// will serialize to a string. impl serde::Serialize for ServerAddress { - fn serialize(&self, serializer: S) -> Result where S: serde::Serializer { - serializer.serialize_str(&format!("{}:{}", self.host, self.port)) + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + serializer.serialize_str(&self.to_string()) } } @@ -184,9 +180,7 @@ mod tests { ) .unwrap(); - let buf = compression_encoder(&buf, compression_threshold) - .await - .unwrap(); + let buf = compression_encoder(&buf, compression_threshold).unwrap(); println!("{:?}", buf); diff --git a/azalea-protocol/src/packets/configuration/clientbound_resource_pack_pop_packet.rs b/azalea-protocol/src/packets/configuration/clientbound_resource_pack_pop_packet.rs new file mode 100644 index 000000000..6533b589b --- /dev/null +++ b/azalea-protocol/src/packets/configuration/clientbound_resource_pack_pop_packet.rs @@ -0,0 +1,8 @@ +use azalea_buf::McBuf; +use azalea_protocol_macros::ClientboundConfigurationPacket; +use uuid::Uuid; + +#[derive(Clone, Debug, McBuf, ClientboundConfigurationPacket)] +pub struct ClientboundResourcePackPopPacket { + pub id: Option, +} diff --git a/azalea-protocol/src/packets/configuration/clientbound_resource_pack_push_packet.rs b/azalea-protocol/src/packets/configuration/clientbound_resource_pack_push_packet.rs new file mode 100644 index 000000000..798f5f98f --- /dev/null +++ b/azalea-protocol/src/packets/configuration/clientbound_resource_pack_push_packet.rs @@ -0,0 +1,13 @@ +use azalea_buf::McBuf; +use azalea_chat::FormattedText; +use azalea_protocol_macros::ClientboundConfigurationPacket; +use uuid::Uuid; + +#[derive(Clone, Debug, McBuf, ClientboundConfigurationPacket)] +pub struct ClientboundResourcePackPushPacket { + pub id: Uuid, + pub url: String, + pub hash: String, + pub required: bool, + pub prompt: Option, +} diff --git a/azalea-protocol/src/packets/configuration/mod.rs b/azalea-protocol/src/packets/configuration/mod.rs index 8244e90af..9516935f2 100755 --- a/azalea-protocol/src/packets/configuration/mod.rs +++ b/azalea-protocol/src/packets/configuration/mod.rs @@ -4,7 +4,8 @@ pub mod clientbound_finish_configuration_packet; pub mod clientbound_keep_alive_packet; pub mod clientbound_ping_packet; pub mod clientbound_registry_data_packet; -pub mod clientbound_resource_pack_packet; +pub mod clientbound_resource_pack_pop_packet; +pub mod clientbound_resource_pack_push_packet; pub mod clientbound_update_enabled_features_packet; pub mod clientbound_update_tags_packet; pub mod serverbound_client_information_packet; @@ -32,8 +33,9 @@ declare_state_packets!( 0x03: clientbound_keep_alive_packet::ClientboundKeepAlivePacket, 0x04: clientbound_ping_packet::ClientboundPingPacket, 0x05: clientbound_registry_data_packet::ClientboundRegistryDataPacket, - 0x06: clientbound_resource_pack_packet::ClientboundResourcePackPacket, - 0x07: clientbound_update_enabled_features_packet::ClientboundUpdateEnabledFeaturesPacket, - 0x08: clientbound_update_tags_packet::ClientboundUpdateTagsPacket, + 0x06: clientbound_resource_pack_pop_packet::ClientboundResourcePackPopPacket, + 0x07: clientbound_resource_pack_push_packet::ClientboundResourcePackPushPacket, + 0x08: clientbound_update_enabled_features_packet::ClientboundUpdateEnabledFeaturesPacket, + 0x09: clientbound_update_tags_packet::ClientboundUpdateTagsPacket, } ); diff --git a/azalea-protocol/src/packets/configuration/serverbound_resource_pack_packet.rs b/azalea-protocol/src/packets/configuration/serverbound_resource_pack_packet.rs index 111491150..6f35525e6 100644 --- a/azalea-protocol/src/packets/configuration/serverbound_resource_pack_packet.rs +++ b/azalea-protocol/src/packets/configuration/serverbound_resource_pack_packet.rs @@ -1,8 +1,10 @@ use azalea_buf::McBuf; use azalea_protocol_macros::ServerboundConfigurationPacket; +use uuid::Uuid; #[derive(Clone, Debug, McBuf, ServerboundConfigurationPacket)] pub struct ServerboundResourcePackPacket { + pub id: Uuid, pub action: Action, } @@ -12,4 +14,7 @@ pub enum Action { Declined = 1, FailedDownload = 2, Accepted = 3, + InvalidUrl = 4, + FailedReload = 5, + Discarded = 6, } diff --git a/azalea-protocol/src/packets/game/clientbound_block_entity_data_packet.rs b/azalea-protocol/src/packets/game/clientbound_block_entity_data_packet.rs index 95e9c6c38..3406a75fe 100755 --- a/azalea-protocol/src/packets/game/clientbound_block_entity_data_packet.rs +++ b/azalea-protocol/src/packets/game/clientbound_block_entity_data_packet.rs @@ -1,11 +1,11 @@ use azalea_buf::McBuf; use azalea_core::position::BlockPos; use azalea_protocol_macros::ClientboundGamePacket; -use simdnbt::owned::NbtTag; +use simdnbt::owned::Nbt; #[derive(Clone, Debug, McBuf, ClientboundGamePacket)] pub struct ClientboundBlockEntityDataPacket { pub pos: BlockPos, pub block_entity_type: azalea_registry::BlockEntityKind, - pub tag: NbtTag, + pub tag: Nbt, } diff --git a/azalea-protocol/src/packets/game/clientbound_boss_event_packet.rs b/azalea-protocol/src/packets/game/clientbound_boss_event_packet.rs index d28b1e61c..16fb6f613 100755 --- a/azalea-protocol/src/packets/game/clientbound_boss_event_packet.rs +++ b/azalea-protocol/src/packets/game/clientbound_boss_event_packet.rs @@ -76,16 +76,16 @@ impl McBufWritable for Operation { #[derive(Clone, Debug, McBuf)] pub struct AddOperation { - name: FormattedText, - progress: f32, - style: Style, - properties: Properties, + pub name: FormattedText, + pub progress: f32, + pub style: Style, + pub properties: Properties, } #[derive(Clone, Debug, McBuf)] pub struct Style { - color: BossBarColor, - overlay: BossBarOverlay, + pub color: BossBarColor, + pub overlay: BossBarOverlay, } #[derive(McBuf, Clone, Copy, Debug)] diff --git a/azalea-protocol/src/packets/game/clientbound_commands_packet.rs b/azalea-protocol/src/packets/game/clientbound_commands_packet.rs index 0bc436be5..d55ed50f1 100755 --- a/azalea-protocol/src/packets/game/clientbound_commands_packet.rs +++ b/azalea-protocol/src/packets/game/clientbound_commands_packet.rs @@ -112,6 +112,7 @@ pub enum BrigadierParser { ItemPredicate, Color, FormattedText, + Style, Message, NbtCompoundTag, NbtTag, diff --git a/azalea-protocol/src/packets/game/clientbound_disguised_chat_packet.rs b/azalea-protocol/src/packets/game/clientbound_disguised_chat_packet.rs index 9aa9fd6f9..e1ccff7e2 100644 --- a/azalea-protocol/src/packets/game/clientbound_disguised_chat_packet.rs +++ b/azalea-protocol/src/packets/game/clientbound_disguised_chat_packet.rs @@ -1,10 +1,41 @@ use super::clientbound_player_chat_packet::ChatTypeBound; use azalea_buf::McBuf; -use azalea_chat::FormattedText; +use azalea_chat::{ + translatable_component::{StringOrComponent, TranslatableComponent}, + FormattedText, +}; use azalea_protocol_macros::ClientboundGamePacket; -#[derive(Clone, Debug, McBuf, ClientboundGamePacket)] +// A disguised chat packet is basically the same as a normal +// [`ClientboundPlayerChatPacket`], except that it doesn't have any of the chat +// signing things. Vanilla servers use this when messages are sent from the +// console. +#[derive(Clone, Debug, McBuf, ClientboundGamePacket, PartialEq)] pub struct ClientboundDisguisedChatPacket { pub message: FormattedText, pub chat_type: ChatTypeBound, } + +impl ClientboundDisguisedChatPacket { + /// Get the full message, including the sender part. + #[must_use] + pub fn message(&self) -> FormattedText { + let sender = self.chat_type.name.clone(); + let content = self.message.clone(); + let target = self.chat_type.target_name.clone(); + + let translation_key = self.chat_type.chat_type.chat_translation_key(); + + let mut args = vec![ + StringOrComponent::FormattedText(sender), + StringOrComponent::FormattedText(content), + ]; + if let Some(target) = target { + args.push(StringOrComponent::FormattedText(target)); + } + + let component = TranslatableComponent::new(translation_key.to_string(), args); + + FormattedText::Translatable(component) + } +} diff --git a/azalea-protocol/src/packets/game/clientbound_explode_packet.rs b/azalea-protocol/src/packets/game/clientbound_explode_packet.rs index 720f06a43..ae39135a9 100755 --- a/azalea-protocol/src/packets/game/clientbound_explode_packet.rs +++ b/azalea-protocol/src/packets/game/clientbound_explode_packet.rs @@ -1,8 +1,11 @@ use std::io::{Cursor, Write}; -use azalea_buf::{BufReadError, McBufReadable, McBufVarReadable, McBufVarWritable, McBufWritable}; +use azalea_buf::{ + BufReadError, McBuf, McBufReadable, McBufVarReadable, McBufVarWritable, McBufWritable, +}; use azalea_core::position::BlockPos; use azalea_protocol_macros::ClientboundGamePacket; +use azalea_registry::{ParticleKind, SoundEvent}; #[derive(Clone, Debug, PartialEq, ClientboundGamePacket)] pub struct ClientboundExplodePacket { @@ -14,6 +17,18 @@ pub struct ClientboundExplodePacket { pub knockback_x: f32, pub knockback_y: f32, pub knockback_z: f32, + pub block_interaction: BlockInteraction, + pub small_explosion_particles: ParticleKind, + pub large_explosion_particles: ParticleKind, + pub explosion_sound: SoundEvent, +} + +#[derive(Clone, Copy, Debug, PartialEq, McBuf)] +pub enum BlockInteraction { + Keep, + Destroy, + DestroyWithDecay, + TriggerBlock, } impl McBufReadable for ClientboundExplodePacket { @@ -41,6 +56,11 @@ impl McBufReadable for ClientboundExplodePacket { let knockback_y = f32::read_from(buf)?; let knockback_z = f32::read_from(buf)?; + let block_interaction = BlockInteraction::read_from(buf)?; + let small_explosion_particles = ParticleKind::read_from(buf)?; + let large_explosion_particles = ParticleKind::read_from(buf)?; + let explosion_sound = SoundEvent::read_from(buf)?; + Ok(Self { x, y, @@ -50,6 +70,10 @@ impl McBufReadable for ClientboundExplodePacket { knockback_x, knockback_y, knockback_z, + block_interaction, + small_explosion_particles, + large_explosion_particles, + explosion_sound, }) } } @@ -80,6 +104,12 @@ impl McBufWritable for ClientboundExplodePacket { self.knockback_x.write_into(buf)?; self.knockback_y.write_into(buf)?; self.knockback_z.write_into(buf)?; + + self.block_interaction.write_into(buf)?; + self.small_explosion_particles.write_into(buf)?; + self.large_explosion_particles.write_into(buf)?; + self.explosion_sound.write_into(buf)?; + Ok(()) } } @@ -110,6 +140,10 @@ mod tests { knockback_x: 1_000.0, knockback_y: 2_000.0, knockback_z: 3_000.0, + block_interaction: BlockInteraction::Destroy, + small_explosion_particles: ParticleKind::Explosion, + large_explosion_particles: ParticleKind::ExplosionEmitter, + explosion_sound: SoundEvent::EntityGenericExplode, }; let mut buf = Vec::new(); packet.write_into(&mut buf).unwrap(); diff --git a/azalea-protocol/src/packets/game/clientbound_player_chat_packet.rs b/azalea-protocol/src/packets/game/clientbound_player_chat_packet.rs index 6a80e1727..31f18dde2 100644 --- a/azalea-protocol/src/packets/game/clientbound_player_chat_packet.rs +++ b/azalea-protocol/src/packets/game/clientbound_player_chat_packet.rs @@ -173,37 +173,29 @@ impl McBufWritable for PackedMessageSignature { #[cfg(test)] mod tests { use super::*; - use std::backtrace::Backtrace; - // you can remove or update this test if it breaks because mojang changed the - // structure of the packet again #[test] - fn test_player_chat_packet() { - let data: [u8; 295] = [ - 47, 247, 69, 164, 160, 108, 63, 217, 178, 34, 4, 161, 47, 115, 192, 126, 0, 0, 11, 72, - 101, 108, 108, 111, 32, 119, 111, 114, 108, 100, 0, 0, 1, 132, 209, 9, 72, 139, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 242, 1, 123, 34, 105, 110, 115, 101, 114, 116, 105, 111, - 110, 34, 58, 34, 98, 111, 116, 48, 34, 44, 34, 99, 108, 105, 99, 107, 69, 118, 101, - 110, 116, 34, 58, 123, 34, 97, 99, 116, 105, 111, 110, 34, 58, 34, 115, 117, 103, 103, - 101, 115, 116, 95, 99, 111, 109, 109, 97, 110, 100, 34, 44, 34, 118, 97, 108, 117, 101, - 34, 58, 34, 47, 116, 101, 108, 108, 32, 98, 111, 116, 48, 32, 34, 125, 44, 34, 104, - 111, 118, 101, 114, 69, 118, 101, 110, 116, 34, 58, 123, 34, 97, 99, 116, 105, 111, - 110, 34, 58, 34, 115, 104, 111, 119, 95, 101, 110, 116, 105, 116, 121, 34, 44, 34, 99, - 111, 110, 116, 101, 110, 116, 115, 34, 58, 123, 34, 116, 121, 112, 101, 34, 58, 34, - 109, 105, 110, 101, 99, 114, 97, 102, 116, 58, 112, 108, 97, 121, 101, 114, 34, 44, 34, - 105, 100, 34, 58, 34, 50, 102, 102, 55, 52, 53, 97, 52, 45, 97, 48, 54, 99, 45, 51, - 102, 100, 57, 45, 98, 50, 50, 50, 45, 48, 52, 97, 49, 50, 102, 55, 51, 99, 48, 55, 101, - 34, 44, 34, 110, 97, 109, 101, 34, 58, 123, 34, 116, 101, 120, 116, 34, 58, 34, 98, - 111, 116, 48, 34, 125, 125, 125, 44, 34, 116, 101, 120, 116, 34, 58, 34, 98, 111, 116, - 48, 34, 125, 0, - ]; - // just make sure it doesn't panic - if let Err(e) = ClientboundPlayerChatPacket::read_from(&mut Cursor::new(&data)) { - let default_backtrace = Backtrace::capture(); - let backtrace = std::error::request_ref::(&e).unwrap_or(&default_backtrace); - eprintln!("{e}\n{backtrace}"); - - panic!("failed to read player chat packet"); - } + fn test_read_player_chat_packet() { + let mut bytes = Cursor::new( + &[ + 55, 186, 28, 76, 92, 167, 177, 75, 188, 158, 200, 179, 191, 227, 16, 171, 145, 0, + 0, 4, 116, 101, 115, 116, 0, 0, 1, 140, 178, 225, 89, 103, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 10, 10, 0, 10, 104, 111, 118, 101, 114, 69, 118, 101, 110, 116, 10, 0, + 8, 99, 111, 110, 116, 101, 110, 116, 115, 8, 0, 4, 110, 97, 109, 101, 0, 12, 75, + 97, 115, 117, 109, 105, 77, 97, 114, 105, 115, 97, 11, 0, 2, 105, 100, 0, 0, 0, 4, + 186, 28, 76, 92, 167, 177, 75, 188, 158, 200, 179, 191, 227, 16, 171, 145, 8, 0, 4, + 116, 121, 112, 101, 0, 16, 109, 105, 110, 101, 99, 114, 97, 102, 116, 58, 112, 108, + 97, 121, 101, 114, 0, 8, 0, 6, 97, 99, 116, 105, 111, 110, 0, 11, 115, 104, 111, + 119, 95, 101, 110, 116, 105, 116, 121, 0, 10, 0, 10, 99, 108, 105, 99, 107, 69, + 118, 101, 110, 116, 8, 0, 6, 97, 99, 116, 105, 111, 110, 0, 15, 115, 117, 103, 103, + 101, 115, 116, 95, 99, 111, 109, 109, 97, 110, 100, 8, 0, 5, 118, 97, 108, 117, + 101, 0, 19, 47, 116, 101, 108, 108, 32, 75, 97, 115, 117, 109, 105, 77, 97, 114, + 105, 115, 97, 32, 0, 9, 0, 5, 101, 120, 116, 114, 97, 8, 0, 0, 0, 3, 0, 0, 0, 12, + 75, 97, 115, 117, 109, 105, 77, 97, 114, 105, 115, 97, 0, 0, 8, 0, 9, 105, 110, + 115, 101, 114, 116, 105, 111, 110, 0, 12, 75, 97, 115, 117, 109, 105, 77, 97, 114, + 105, 115, 97, 8, 0, 4, 116, 101, 120, 116, 0, 0, 0, 0, + ][..], + ); + let _packet = ClientboundPlayerChatPacket::read_from(&mut bytes).unwrap(); } } diff --git a/azalea-protocol/src/packets/game/clientbound_player_info_update_packet.rs b/azalea-protocol/src/packets/game/clientbound_player_info_update_packet.rs index 5d54cce2f..345c22f20 100644 --- a/azalea-protocol/src/packets/game/clientbound_player_info_update_packet.rs +++ b/azalea-protocol/src/packets/game/clientbound_player_info_update_packet.rs @@ -59,7 +59,7 @@ pub struct UpdateDisplayNameAction { impl McBufReadable for ClientboundPlayerInfoUpdatePacket { fn read_from(buf: &mut Cursor<&[u8]>) -> Result { let actions = ActionEnumSet::read_from(buf)?; - let mut entries = vec![]; + let mut entries = Vec::new(); let entry_count = u32::var_read_from(buf)?; for _ in 0..entry_count { @@ -221,4 +221,76 @@ mod tests { let read_data = ActionEnumSet::read_from(&mut data_cursor).unwrap(); assert_eq!(read_data, data); } + + #[test] + fn read_player_info_update_packet() { + // from wynncraft + let mut bytes = Cursor::new( + &[ + 63, 1, 196, 217, 99, 243, 221, 101, 79, 183, 167, 88, 48, 71, 25, 49, 5, 142, 5, + 74, 66, 76, 80, 78, 1, 8, 116, 101, 120, 116, 117, 114, 101, 115, 152, 3, 101, 119, + 111, 103, 73, 67, 74, 48, 97, 87, 49, 108, 99, 51, 82, 104, 98, 88, 65, 105, 73, + 68, 111, 103, 77, 84, 99, 119, 77, 106, 99, 49, 78, 106, 89, 48, 77, 68, 81, 120, + 78, 105, 119, 75, 73, 67, 65, 105, 99, 72, 74, 118, 90, 109, 108, 115, 90, 85, 108, + 107, 73, 105, 65, 54, 73, 67, 74, 106, 78, 71, 81, 53, 78, 106, 78, 109, 77, 50, + 82, 107, 78, 106, 85, 48, 90, 109, 73, 51, 89, 84, 99, 49, 79, 68, 77, 119, 78, 68, + 99, 120, 79, 84, 77, 120, 77, 68, 85, 52, 90, 83, 73, 115, 67, 105, 65, 103, 73, + 110, 66, 121, 98, 50, 90, 112, 98, 71, 86, 79, 89, 87, 49, 108, 73, 105, 65, 54, + 73, 67, 74, 75, 81, 107, 120, 81, 84, 105, 73, 115, 67, 105, 65, 103, 73, 110, 78, + 112, 90, 50, 53, 104, 100, 72, 86, 121, 90, 86, 74, 108, 99, 88, 86, 112, 99, 109, + 86, 107, 73, 105, 65, 54, 73, 72, 82, 121, 100, 87, 85, 115, 67, 105, 65, 103, 73, + 110, 82, 108, 101, 72, 82, 49, 99, 109, 86, 122, 73, 105, 65, 54, 73, 72, 115, 75, + 73, 67, 65, 103, 73, 67, 74, 84, 83, 48, 108, 79, 73, 105, 65, 54, 73, 72, 115, 75, + 73, 67, 65, 103, 73, 67, 65, 103, 73, 110, 86, 121, 98, 67, 73, 103, 79, 105, 65, + 105, 97, 72, 82, 48, 99, 68, 111, 118, 76, 51, 82, 108, 101, 72, 82, 49, 99, 109, + 86, 122, 76, 109, 49, 112, 98, 109, 86, 106, 99, 109, 70, 109, 100, 67, 53, 117, + 90, 88, 81, 118, 100, 71, 86, 52, 100, 72, 86, 121, 90, 83, 56, 48, 79, 68, 107, + 120, 89, 84, 107, 50, 89, 84, 74, 107, 77, 84, 103, 120, 78, 84, 69, 49, 79, 68, + 107, 52, 78, 68, 89, 119, 77, 68, 82, 106, 77, 106, 100, 104, 78, 50, 86, 106, 77, + 106, 85, 53, 77, 87, 77, 120, 79, 68, 66, 108, 77, 84, 70, 109, 77, 122, 104, 107, + 89, 122, 69, 52, 77, 84, 74, 109, 77, 106, 100, 104, 77, 71, 82, 108, 78, 50, 69, + 120, 77, 87, 85, 120, 73, 103, 111, 103, 73, 67, 65, 103, 102, 81, 111, 103, 73, + 72, 48, 75, 102, 81, 61, 61, 1, 172, 5, 117, 69, 67, 88, 54, 83, 104, 55, 67, 100, + 87, 49, 77, 99, 78, 88, 122, 72, 73, 105, 90, 86, 43, 103, 111, 121, 47, 120, 53, + 102, 51, 65, 113, 119, 50, 115, 102, 114, 104, 106, 67, 118, 67, 102, 97, 54, 67, + 112, 55, 88, 116, 109, 103, 118, 113, 73, 114, 122, 100, 85, 72, 90, 102, 79, 100, + 100, 112, 109, 87, 70, 110, 70, 119, 97, 85, 109, 97, 76, 106, 86, 102, 121, 88, + 119, 115, 76, 48, 78, 108, 118, 98, 56, 78, 104, 121, 115, 113, 87, 47, 104, 75, + 120, 101, 86, 117, 90, 68, 71, 43, 102, 54, 98, 99, 98, 81, 113, 76, 79, 54, 83, + 66, 88, 111, 81, 74, 85, 104, 74, 66, 90, 102, 88, 78, 53, 51, 100, 102, 80, 98, + 75, 89, 81, 54, 68, 77, 57, 87, 102, 113, 81, 76, 100, 55, 121, 117, 119, 90, 81, + 68, 55, 120, 48, 54, 118, 102, 105, 72, 121, 48, 110, 87, 50, 99, 68, 111, 72, 101, + 71, 102, 72, 67, 53, 104, 52, 112, 84, 109, 65, 101, 100, 101, 109, 116, 48, 67, + 72, 113, 86, 54, 76, 67, 77, 89, 118, 101, 110, 84, 88, 68, 83, 81, 107, 82, 43, + 50, 53, 74, 76, 120, 101, 98, 74, 105, 98, 108, 54, 88, 106, 73, 118, 88, 120, 105, + 87, 68, 121, 85, 49, 65, 43, 121, 48, 79, 104, 53, 89, 115, 116, 121, 86, 116, 106, + 107, 76, 113, 67, 56, 85, 57, 118, 86, 110, 87, 65, 102, 111, 43, 52, 104, 78, 43, + 79, 51, 122, 108, 72, 117, 84, 50, 87, 76, 86, 121, 98, 43, 88, 72, 100, 67, 111, + 111, 88, 75, 82, 75, 83, 86, 71, 101, 122, 103, 75, 78, 47, 53, 65, 53, 67, 119, + 78, 112, 82, 87, 98, 81, 55, 109, 90, 47, 108, 51, 57, 84, 114, 100, 84, 99, 54, + 121, 79, 88, 73, 48, 56, 83, 101, 73, 54, 68, 118, 118, 50, 55, 78, 66, 112, 107, + 47, 97, 72, 119, 65, 49, 116, 105, 78, 108, 55, 122, 49, 103, 97, 79, 107, 113, + 107, 116, 54, 120, 85, 116, 70, 84, 85, 122, 72, 71, 97, 107, 69, 118, 105, 76, 72, + 120, 67, 99, 106, 98, 121, 88, 111, 76, 71, 101, 101, 50, 57, 81, 84, 73, 102, 99, + 97, 69, 56, 104, 108, 110, 73, 97, 74, 111, 115, 72, 117, 57, 116, 100, 54, 52, + 119, 74, 88, 74, 115, 69, 78, 114, 121, 69, 56, 70, 53, 52, 52, 116, 114, 84, 54, + 105, 112, 122, 73, 119, 43, 118, 120, 112, 76, 121, 88, 65, 87, 116, 103, 83, 113, + 76, 108, 107, 121, 78, 50, 77, 115, 57, 74, 89, 110, 100, 79, 111, 90, 57, 77, 53, + 84, 49, 87, 112, 75, 70, 97, 52, 55, 114, 112, 80, 106, 75, 114, 79, 107, 114, 110, + 100, 50, 97, 83, 51, 90, 86, 77, 120, 118, 79, 49, 111, 78, 47, 100, 84, 55, 116, + 77, 119, 82, 52, 109, 97, 55, 85, 73, 68, 50, 48, 84, 113, 105, 83, 75, 56, 108, + 76, 85, 100, 53, 48, 86, 119, 108, 112, 67, 116, 98, 76, 99, 71, 86, 82, 98, 78, + 84, 97, 108, 90, 83, 66, 56, 88, 65, 72, 72, 78, 100, 116, 88, 86, 50, 49, 111, 68, + 77, 116, 77, 122, 79, 104, 82, 109, 43, 57, 88, 81, 90, 79, 50, 55, 66, 69, 71, 65, + 47, 119, 117, 104, 113, 71, 108, 106, 82, 111, 76, 72, 111, 102, 98, 71, 48, 52, + 82, 55, 84, 43, 80, 99, 112, 77, 116, 65, 69, 105, 49, 100, 57, 99, 66, 90, 115, + 119, 84, 105, 107, 113, 114, 89, 49, 86, 49, 48, 106, 104, 77, 76, 118, 99, 99, 78, + 50, 109, 70, 43, 89, 86, 81, 101, 48, 90, 55, 43, 78, 100, 119, 119, 104, 121, 47, + 108, 79, 72, 81, 54, 71, 108, 122, 74, 110, 87, 122, 103, 50, 107, 61, 0, 255, 255, + 255, 255, 15, 1, 255, 255, 255, 255, 15, 1, 10, 8, 0, 4, 116, 101, 120, 116, 0, 0, + 0, + ][..], + ); + let _packet = ClientboundPlayerInfoUpdatePacket::read_from(&mut bytes).unwrap(); + } } diff --git a/azalea-protocol/src/packets/game/clientbound_reset_score_packet.rs b/azalea-protocol/src/packets/game/clientbound_reset_score_packet.rs new file mode 100644 index 000000000..8b24bbdf2 --- /dev/null +++ b/azalea-protocol/src/packets/game/clientbound_reset_score_packet.rs @@ -0,0 +1,8 @@ +use azalea_buf::McBuf; +use azalea_protocol_macros::ClientboundGamePacket; + +#[derive(Clone, Debug, McBuf, ClientboundGamePacket)] +pub struct ClientboundResetScorePacket { + pub owner: String, + pub objective_name: Option, +} diff --git a/azalea-protocol/src/packets/game/clientbound_resource_pack_pop_packet.rs b/azalea-protocol/src/packets/game/clientbound_resource_pack_pop_packet.rs new file mode 100644 index 000000000..34836c852 --- /dev/null +++ b/azalea-protocol/src/packets/game/clientbound_resource_pack_pop_packet.rs @@ -0,0 +1,8 @@ +use azalea_buf::McBuf; +use azalea_protocol_macros::ClientboundGamePacket; +use uuid::Uuid; + +#[derive(Clone, Debug, McBuf, ClientboundGamePacket)] +pub struct ClientboundResourcePackPopPacket { + pub id: Option, +} diff --git a/azalea-protocol/src/packets/game/clientbound_resource_pack_packet.rs b/azalea-protocol/src/packets/game/clientbound_resource_pack_push_packet.rs old mode 100755 new mode 100644 similarity index 76% rename from azalea-protocol/src/packets/game/clientbound_resource_pack_packet.rs rename to azalea-protocol/src/packets/game/clientbound_resource_pack_push_packet.rs index a545ff31d..1d5e00b1f --- a/azalea-protocol/src/packets/game/clientbound_resource_pack_packet.rs +++ b/azalea-protocol/src/packets/game/clientbound_resource_pack_push_packet.rs @@ -1,9 +1,11 @@ use azalea_buf::McBuf; use azalea_chat::FormattedText; use azalea_protocol_macros::ClientboundGamePacket; +use uuid::Uuid; #[derive(Clone, Debug, McBuf, ClientboundGamePacket)] -pub struct ClientboundResourcePackPacket { +pub struct ClientboundResourcePackPushPacket { + pub id: Uuid, pub url: String, pub hash: String, pub required: bool, diff --git a/azalea-protocol/src/packets/game/clientbound_set_objective_packet.rs b/azalea-protocol/src/packets/game/clientbound_set_objective_packet.rs index 3809b5b62..a9481ad15 100755 --- a/azalea-protocol/src/packets/game/clientbound_set_objective_packet.rs +++ b/azalea-protocol/src/packets/game/clientbound_set_objective_packet.rs @@ -1,59 +1,82 @@ -use azalea_buf::{BufReadError, McBuf, McBufReadable, McBufWritable}; -use azalea_chat::FormattedText; -use azalea_protocol_macros::ClientboundGamePacket; use std::io::{Cursor, Write}; +use azalea_buf::{McBuf, McBufReadable, McBufWritable}; +use azalea_chat::{numbers::NumberFormat, FormattedText}; +use azalea_core::objectives::ObjectiveCriteria; +use azalea_protocol_macros::ClientboundGamePacket; + #[derive(Clone, Debug, McBuf, ClientboundGamePacket)] pub struct ClientboundSetObjectivePacket { pub objective_name: String, pub method: Method, } +#[derive(Clone, Copy, Debug, McBuf)] +pub enum MethodKind { + Add, + Remove, + Change, +} + #[derive(Clone, Debug)] pub enum Method { - Add(DisplayInfo), + Add { + display_name: FormattedText, + render_type: ObjectiveCriteria, + number_format: NumberFormat, + }, Remove, - Change(DisplayInfo), + Change { + display_name: FormattedText, + render_type: ObjectiveCriteria, + number_format: NumberFormat, + }, } impl McBufReadable for Method { - fn read_from(buf: &mut Cursor<&[u8]>) -> Result { - Ok(match u8::read_from(buf)? { - 0 => Method::Add(DisplayInfo::read_from(buf)?), - 1 => Method::Remove, - 2 => Method::Change(DisplayInfo::read_from(buf)?), - id => return Err(BufReadError::UnexpectedEnumVariant { id: i32::from(id) }), - }) + fn read_from(buf: &mut Cursor<&[u8]>) -> Result { + let kind = MethodKind::read_from(buf)?; + match kind { + MethodKind::Add => Ok(Method::Add { + display_name: FormattedText::read_from(buf)?, + render_type: ObjectiveCriteria::read_from(buf)?, + number_format: NumberFormat::read_from(buf)?, + }), + MethodKind::Remove => Ok(Method::Remove), + MethodKind::Change => Ok(Method::Change { + display_name: FormattedText::read_from(buf)?, + render_type: ObjectiveCriteria::read_from(buf)?, + number_format: NumberFormat::read_from(buf)?, + }), + } } } impl McBufWritable for Method { fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { match self { - Method::Add(info) => { - 0u8.write_into(buf)?; - info.write_into(buf)?; - } - Method::Remove => { - 1u8.write_into(buf)?; + Method::Add { + display_name, + render_type, + number_format, + } => { + MethodKind::Add.write_into(buf)?; + display_name.write_into(buf)?; + render_type.write_into(buf)?; + number_format.write_into(buf)?; } - Method::Change(info) => { - 2u8.write_into(buf)?; - info.write_into(buf)?; + Method::Remove => MethodKind::Remove.write_into(buf)?, + Method::Change { + display_name, + render_type, + number_format, + } => { + MethodKind::Change.write_into(buf)?; + display_name.write_into(buf)?; + render_type.write_into(buf)?; + number_format.write_into(buf)?; } } Ok(()) } } - -#[derive(McBuf, Clone, Debug)] -pub struct DisplayInfo { - pub display_name: FormattedText, - pub render_type: RenderType, -} - -#[derive(McBuf, Copy, Clone, Debug)] -pub enum RenderType { - Integer, - Hearts, -} diff --git a/azalea-protocol/src/packets/game/clientbound_set_score_packet.rs b/azalea-protocol/src/packets/game/clientbound_set_score_packet.rs index 56d14b942..6de53f749 100755 --- a/azalea-protocol/src/packets/game/clientbound_set_score_packet.rs +++ b/azalea-protocol/src/packets/game/clientbound_set_score_packet.rs @@ -1,61 +1,13 @@ -use azalea_buf::{BufReadError, McBufReadable, McBufVarReadable, McBufVarWritable, McBufWritable}; +use azalea_buf::McBuf; +use azalea_chat::{numbers::NumberFormat, FormattedText}; use azalea_protocol_macros::ClientboundGamePacket; -use std::{ - io::{Cursor, Write}, - ops::Not, -}; -#[derive(Clone, Debug, ClientboundGamePacket)] +#[derive(Clone, Debug, McBuf, ClientboundGamePacket)] pub struct ClientboundSetScorePacket { pub owner: String, - pub method: Method, - pub objective_name: Option, -} - -impl McBufReadable for ClientboundSetScorePacket { - fn read_from(buf: &mut Cursor<&[u8]>) -> Result { - let owner = String::read_from(buf)?; - let method_id = u32::var_read_from(buf)?; - let objective_name = String::read_from(buf)?; - let objective_name = objective_name.is_empty().not().then_some(objective_name); - // if it's change, read the score - let method = match method_id { - 0 => Method::Change { - score: u32::var_read_from(buf)?, - }, - 1 => Method::Remove, - id => return Err(BufReadError::UnexpectedEnumVariant { id: id as i32 }), - }; - Ok(ClientboundSetScorePacket { - owner, - method, - objective_name, - }) - } -} - -impl McBufWritable for ClientboundSetScorePacket { - fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { - self.owner.write_into(buf)?; - match self.method { - Method::Change { .. } => 0u32, - Method::Remove => 1u32, - } - .var_write_into(buf)?; - // convert None to an empty string - self.objective_name - .as_ref() - .unwrap_or(&String::new()) - .write_into(buf)?; - if let Method::Change { score } = self.method { - score.var_write_into(buf)?; - } - Ok(()) - } -} - -#[derive(Clone, Copy, Debug)] -pub enum Method { - Change { score: u32 }, - Remove, + pub objective_name: String, + #[var] + pub score: u32, + pub display: Option, + pub number_format: Option, } diff --git a/azalea-protocol/src/packets/game/clientbound_ticking_state_packet.rs b/azalea-protocol/src/packets/game/clientbound_ticking_state_packet.rs new file mode 100644 index 000000000..a85429a2c --- /dev/null +++ b/azalea-protocol/src/packets/game/clientbound_ticking_state_packet.rs @@ -0,0 +1,8 @@ +use azalea_buf::McBuf; +use azalea_protocol_macros::ClientboundGamePacket; + +#[derive(Clone, Debug, McBuf, ClientboundGamePacket)] +pub struct ClientboundTickingStatePacket { + pub tick_rate: f32, + pub is_frozen: bool, +} diff --git a/azalea-protocol/src/packets/game/clientbound_ticking_step_packet.rs b/azalea-protocol/src/packets/game/clientbound_ticking_step_packet.rs new file mode 100644 index 000000000..5a5e64406 --- /dev/null +++ b/azalea-protocol/src/packets/game/clientbound_ticking_step_packet.rs @@ -0,0 +1,8 @@ +use azalea_buf::McBuf; +use azalea_protocol_macros::ClientboundGamePacket; + +#[derive(Clone, Debug, McBuf, ClientboundGamePacket)] +pub struct ClientboundTickingStepPacket { + #[var] + pub tick_steps: u32, +} diff --git a/azalea-protocol/src/packets/game/clientbound_update_advancements_packet.rs b/azalea-protocol/src/packets/game/clientbound_update_advancements_packet.rs index 2eb935194..efd557618 100755 --- a/azalea-protocol/src/packets/game/clientbound_update_advancements_packet.rs +++ b/azalea-protocol/src/packets/game/clientbound_update_advancements_packet.rs @@ -44,10 +44,10 @@ impl azalea_buf::McBufWritable for DisplayInfo { let mut data: u32 = 0; if self.background.is_some() { - data |= 0b1; + data |= 0b001; } if self.show_toast { - data |= 0b10; + data |= 0b010; } if self.hidden { data |= 0b100; diff --git a/azalea-protocol/src/packets/game/clientbound_update_recipes_packet.rs b/azalea-protocol/src/packets/game/clientbound_update_recipes_packet.rs index 0699eb30b..291cb580c 100755 --- a/azalea-protocol/src/packets/game/clientbound_update_recipes_packet.rs +++ b/azalea-protocol/src/packets/game/clientbound_update_recipes_packet.rs @@ -29,66 +29,58 @@ pub struct ShapelessRecipe { pub ingredients: Vec, pub result: ItemSlot, } -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Debug, PartialEq, McBuf)] pub struct ShapedRecipe { - pub width: usize, - pub height: usize, pub group: String, pub category: CraftingBookCategory, - pub ingredients: Vec, + pub pattern: ShapedRecipePattern, pub result: ItemSlot, pub show_notification: bool, } -#[derive(Clone, Debug, Copy, PartialEq, McBuf)] -pub enum CraftingBookCategory { - Building = 0, - Redstone, - Equipment, - Misc, +#[derive(Clone, Debug, PartialEq)] +pub struct ShapedRecipePattern { + pub width: usize, + pub height: usize, + pub ingredients: Vec, } -impl McBufWritable for ShapedRecipe { +impl McBufWritable for ShapedRecipePattern { fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { (self.width as u32).var_write_into(buf)?; (self.height as u32).var_write_into(buf)?; - self.group.write_into(buf)?; - self.category.write_into(buf)?; debug_assert_eq!(self.width * self.height, self.ingredients.len()); for ingredient in &self.ingredients { ingredient.write_into(buf)?; } - self.result.write_into(buf)?; - self.show_notification.write_into(buf)?; - Ok(()) } } -impl McBufReadable for ShapedRecipe { + +impl McBufReadable for ShapedRecipePattern { fn read_from(buf: &mut Cursor<&[u8]>) -> Result { let width = u32::var_read_from(buf)? as usize; let height = u32::var_read_from(buf)? as usize; - let group = String::read_from(buf)?; - let category = CraftingBookCategory::read_from(buf)?; let mut ingredients = Vec::with_capacity(width * height); for _ in 0..width * height { ingredients.push(Ingredient::read_from(buf)?); } - let result = ItemSlot::read_from(buf)?; - let show_notification = bool::read_from(buf)?; - - Ok(ShapedRecipe { + Ok(ShapedRecipePattern { width, height, - group, - category, ingredients, - result, - show_notification, }) } } +#[derive(Clone, Debug, Copy, PartialEq, McBuf)] +pub enum CraftingBookCategory { + Building = 0, + Redstone, + Equipment, + Misc, +} + #[derive(Clone, Debug, PartialEq, McBuf)] pub struct CookingRecipe { pub group: String, @@ -315,24 +307,26 @@ mod tests { let recipe = Recipe { identifier: ResourceLocation::new("minecraft:crafting_shaped"), data: RecipeData::CraftingShaped(ShapedRecipe { - width: 2, - height: 2, group: String::new(), category: CraftingBookCategory::Building, - ingredients: vec![ - Ingredient { - allowed: vec![ItemSlot::Empty], - }, - Ingredient { - allowed: vec![ItemSlot::Empty], - }, - Ingredient { - allowed: vec![ItemSlot::Empty], - }, - Ingredient { - allowed: vec![ItemSlot::Empty], - }, - ], + pattern: ShapedRecipePattern { + width: 2, + height: 2, + ingredients: vec![ + Ingredient { + allowed: vec![ItemSlot::Empty], + }, + Ingredient { + allowed: vec![ItemSlot::Empty], + }, + Ingredient { + allowed: vec![ItemSlot::Empty], + }, + Ingredient { + allowed: vec![ItemSlot::Empty], + }, + ], + }, result: ItemSlot::Empty, show_notification: false, }), diff --git a/azalea-protocol/src/packets/game/mod.rs b/azalea-protocol/src/packets/game/mod.rs index c806f21d8..d45ea7b14 100755 --- a/azalea-protocol/src/packets/game/mod.rs +++ b/azalea-protocol/src/packets/game/mod.rs @@ -64,7 +64,9 @@ pub mod clientbound_pong_response_packet; pub mod clientbound_recipe_packet; pub mod clientbound_remove_entities_packet; pub mod clientbound_remove_mob_effect_packet; -pub mod clientbound_resource_pack_packet; +pub mod clientbound_reset_score_packet; +pub mod clientbound_resource_pack_pop_packet; +pub mod clientbound_resource_pack_push_packet; pub mod clientbound_respawn_packet; pub mod clientbound_rotate_head_packet; pub mod clientbound_section_blocks_update_packet; @@ -106,6 +108,8 @@ pub mod clientbound_tab_list_packet; pub mod clientbound_tag_query_packet; pub mod clientbound_take_item_entity_packet; pub mod clientbound_teleport_entity_packet; +pub mod clientbound_ticking_state_packet; +pub mod clientbound_ticking_step_packet; pub mod clientbound_update_advancements_packet; pub mod clientbound_update_attributes_packet; pub mod clientbound_update_mob_effect_packet; @@ -126,6 +130,7 @@ pub mod serverbound_configuration_acknowledged_packet; pub mod serverbound_container_button_click_packet; pub mod serverbound_container_click_packet; pub mod serverbound_container_close_packet; +pub mod serverbound_container_slot_state_changed_packet; pub mod serverbound_custom_payload_packet; pub mod serverbound_edit_book_packet; pub mod serverbound_entity_tag_query; @@ -186,45 +191,46 @@ declare_state_packets!( 0x0c: serverbound_container_button_click_packet::ServerboundContainerButtonClickPacket, 0x0d: serverbound_container_click_packet::ServerboundContainerClickPacket, 0x0e: serverbound_container_close_packet::ServerboundContainerClosePacket, - 0x0f: serverbound_custom_payload_packet::ServerboundCustomPayloadPacket, - 0x10: serverbound_edit_book_packet::ServerboundEditBookPacket, - 0x11: serverbound_entity_tag_query::ServerboundEntityTagQuery, - 0x12: serverbound_interact_packet::ServerboundInteractPacket, - 0x13: serverbound_jigsaw_generate_packet::ServerboundJigsawGeneratePacket, - 0x14: serverbound_keep_alive_packet::ServerboundKeepAlivePacket, - 0x15: serverbound_lock_difficulty_packet::ServerboundLockDifficultyPacket, - 0x16: serverbound_move_player_pos_packet::ServerboundMovePlayerPosPacket, - 0x17: serverbound_move_player_pos_rot_packet::ServerboundMovePlayerPosRotPacket, - 0x18: serverbound_move_player_rot_packet::ServerboundMovePlayerRotPacket, - 0x19: serverbound_move_player_status_only_packet::ServerboundMovePlayerStatusOnlyPacket, - 0x1a: serverbound_move_vehicle_packet::ServerboundMoveVehiclePacket, - 0x1b: serverbound_paddle_boat_packet::ServerboundPaddleBoatPacket, - 0x1c: serverbound_pick_item_packet::ServerboundPickItemPacket, - 0x1d: serverbound_ping_request_packet::ServerboundPingRequestPacket, - 0x1e: serverbound_place_recipe_packet::ServerboundPlaceRecipePacket, - 0x1f: serverbound_player_abilities_packet::ServerboundPlayerAbilitiesPacket, - 0x20: serverbound_player_action_packet::ServerboundPlayerActionPacket, - 0x21: serverbound_player_command_packet::ServerboundPlayerCommandPacket, - 0x22: serverbound_player_input_packet::ServerboundPlayerInputPacket, - 0x23: serverbound_pong_packet::ServerboundPongPacket, - 0x24: serverbound_recipe_book_change_settings_packet::ServerboundRecipeBookChangeSettingsPacket, - 0x25: serverbound_recipe_book_seen_recipe_packet::ServerboundRecipeBookSeenRecipePacket, - 0x26: serverbound_rename_item_packet::ServerboundRenameItemPacket, - 0x27: serverbound_resource_pack_packet::ServerboundResourcePackPacket, - 0x28: serverbound_seen_advancements_packet::ServerboundSeenAdvancementsPacket, - 0x29: serverbound_select_trade_packet::ServerboundSelectTradePacket, - 0x2a: serverbound_set_beacon_packet::ServerboundSetBeaconPacket, - 0x2b: serverbound_set_carried_item_packet::ServerboundSetCarriedItemPacket, - 0x2c: serverbound_set_command_block_packet::ServerboundSetCommandBlockPacket, - 0x2d: serverbound_set_command_minecart_packet::ServerboundSetCommandMinecartPacket, - 0x2e: serverbound_set_creative_mode_slot_packet::ServerboundSetCreativeModeSlotPacket, - 0x2f: serverbound_set_jigsaw_block_packet::ServerboundSetJigsawBlockPacket, - 0x30: serverbound_set_structure_block_packet::ServerboundSetStructureBlockPacket, - 0x31: serverbound_sign_update_packet::ServerboundSignUpdatePacket, - 0x32: serverbound_swing_packet::ServerboundSwingPacket, - 0x33: serverbound_teleport_to_entity_packet::ServerboundTeleportToEntityPacket, - 0x34: serverbound_use_item_on_packet::ServerboundUseItemOnPacket, - 0x35: serverbound_use_item_packet::ServerboundUseItemPacket, + 0x0f: serverbound_container_slot_state_changed_packet::ServerboundContainerSlotStateChangedPacket, + 0x10: serverbound_custom_payload_packet::ServerboundCustomPayloadPacket, + 0x11: serverbound_edit_book_packet::ServerboundEditBookPacket, + 0x12: serverbound_entity_tag_query::ServerboundEntityTagQuery, + 0x13: serverbound_interact_packet::ServerboundInteractPacket, + 0x14: serverbound_jigsaw_generate_packet::ServerboundJigsawGeneratePacket, + 0x15: serverbound_keep_alive_packet::ServerboundKeepAlivePacket, + 0x16: serverbound_lock_difficulty_packet::ServerboundLockDifficultyPacket, + 0x17: serverbound_move_player_pos_packet::ServerboundMovePlayerPosPacket, + 0x18: serverbound_move_player_pos_rot_packet::ServerboundMovePlayerPosRotPacket, + 0x19: serverbound_move_player_rot_packet::ServerboundMovePlayerRotPacket, + 0x1a: serverbound_move_player_status_only_packet::ServerboundMovePlayerStatusOnlyPacket, + 0x1b: serverbound_move_vehicle_packet::ServerboundMoveVehiclePacket, + 0x1c: serverbound_paddle_boat_packet::ServerboundPaddleBoatPacket, + 0x1d: serverbound_pick_item_packet::ServerboundPickItemPacket, + 0x1e: serverbound_ping_request_packet::ServerboundPingRequestPacket, + 0x1f: serverbound_place_recipe_packet::ServerboundPlaceRecipePacket, + 0x20: serverbound_player_abilities_packet::ServerboundPlayerAbilitiesPacket, + 0x21: serverbound_player_action_packet::ServerboundPlayerActionPacket, + 0x22: serverbound_player_command_packet::ServerboundPlayerCommandPacket, + 0x23: serverbound_player_input_packet::ServerboundPlayerInputPacket, + 0x24: serverbound_pong_packet::ServerboundPongPacket, + 0x25: serverbound_recipe_book_change_settings_packet::ServerboundRecipeBookChangeSettingsPacket, + 0x26: serverbound_recipe_book_seen_recipe_packet::ServerboundRecipeBookSeenRecipePacket, + 0x27: serverbound_rename_item_packet::ServerboundRenameItemPacket, + 0x28: serverbound_resource_pack_packet::ServerboundResourcePackPacket, + 0x29: serverbound_seen_advancements_packet::ServerboundSeenAdvancementsPacket, + 0x2a: serverbound_select_trade_packet::ServerboundSelectTradePacket, + 0x2b: serverbound_set_beacon_packet::ServerboundSetBeaconPacket, + 0x2c: serverbound_set_carried_item_packet::ServerboundSetCarriedItemPacket, + 0x2d: serverbound_set_command_block_packet::ServerboundSetCommandBlockPacket, + 0x2e: serverbound_set_command_minecart_packet::ServerboundSetCommandMinecartPacket, + 0x2f: serverbound_set_creative_mode_slot_packet::ServerboundSetCreativeModeSlotPacket, + 0x30: serverbound_set_jigsaw_block_packet::ServerboundSetJigsawBlockPacket, + 0x31: serverbound_set_structure_block_packet::ServerboundSetStructureBlockPacket, + 0x32: serverbound_sign_update_packet::ServerboundSignUpdatePacket, + 0x33: serverbound_swing_packet::ServerboundSwingPacket, + 0x34: serverbound_teleport_to_entity_packet::ServerboundTeleportToEntityPacket, + 0x35: serverbound_use_item_on_packet::ServerboundUseItemOnPacket, + 0x36: serverbound_use_item_packet::ServerboundUseItemPacket, }, Clientbound => { 0x00: clientbound_bundle_packet::ClientboundBundlePacket, @@ -293,52 +299,56 @@ declare_state_packets!( 0x3f: clientbound_recipe_packet::ClientboundRecipePacket, 0x40: clientbound_remove_entities_packet::ClientboundRemoveEntitiesPacket, 0x41: clientbound_remove_mob_effect_packet::ClientboundRemoveMobEffectPacket, - 0x42: clientbound_resource_pack_packet::ClientboundResourcePackPacket, - 0x43: clientbound_respawn_packet::ClientboundRespawnPacket, - 0x44: clientbound_rotate_head_packet::ClientboundRotateHeadPacket, - 0x45: clientbound_section_blocks_update_packet::ClientboundSectionBlocksUpdatePacket, - 0x46: clientbound_select_advancements_tab_packet::ClientboundSelectAdvancementsTabPacket, - 0x47: clientbound_server_data_packet::ClientboundServerDataPacket, - 0x48: clientbound_set_action_bar_text_packet::ClientboundSetActionBarTextPacket, - 0x49: clientbound_set_border_center_packet::ClientboundSetBorderCenterPacket, - 0x4a: clientbound_set_border_lerp_size_packet::ClientboundSetBorderLerpSizePacket, - 0x4b: clientbound_set_border_size_packet::ClientboundSetBorderSizePacket, - 0x4c: clientbound_set_border_warning_delay_packet::ClientboundSetBorderWarningDelayPacket, - 0x4d: clientbound_set_border_warning_distance_packet::ClientboundSetBorderWarningDistancePacket, - 0x4e: clientbound_set_camera_packet::ClientboundSetCameraPacket, - 0x4f: clientbound_set_carried_item_packet::ClientboundSetCarriedItemPacket, - 0x50: clientbound_set_chunk_cache_center_packet::ClientboundSetChunkCacheCenterPacket, - 0x51: clientbound_set_chunk_cache_radius_packet::ClientboundSetChunkCacheRadiusPacket, - 0x52: clientbound_set_default_spawn_position_packet::ClientboundSetDefaultSpawnPositionPacket, - 0x53: clientbound_set_display_objective_packet::ClientboundSetDisplayObjectivePacket, - 0x54: clientbound_set_entity_data_packet::ClientboundSetEntityDataPacket, - 0x55: clientbound_set_entity_link_packet::ClientboundSetEntityLinkPacket, - 0x56: clientbound_set_entity_motion_packet::ClientboundSetEntityMotionPacket, - 0x57: clientbound_set_equipment_packet::ClientboundSetEquipmentPacket, - 0x58: clientbound_set_experience_packet::ClientboundSetExperiencePacket, - 0x59: clientbound_set_health_packet::ClientboundSetHealthPacket, - 0x5a: clientbound_set_objective_packet::ClientboundSetObjectivePacket, - 0x5b: clientbound_set_passengers_packet::ClientboundSetPassengersPacket, - 0x5c: clientbound_set_player_team_packet::ClientboundSetPlayerTeamPacket, - 0x5d: clientbound_set_score_packet::ClientboundSetScorePacket, - 0x5e: clientbound_set_simulation_distance_packet::ClientboundSetSimulationDistancePacket, - 0x5f: clientbound_set_subtitle_text_packet::ClientboundSetSubtitleTextPacket, - 0x60: clientbound_set_time_packet::ClientboundSetTimePacket, - 0x61: clientbound_set_title_text_packet::ClientboundSetTitleTextPacket, - 0x62: clientbound_set_titles_animation_packet::ClientboundSetTitlesAnimationPacket, - 0x63: clientbound_sound_entity_packet::ClientboundSoundEntityPacket, - 0x64: clientbound_sound_packet::ClientboundSoundPacket, - 0x65: clientbound_start_configuration_packet::ClientboundStartConfigurationPacket, - 0x66: clientbound_stop_sound_packet::ClientboundStopSoundPacket, - 0x67: clientbound_system_chat_packet::ClientboundSystemChatPacket, - 0x68: clientbound_tab_list_packet::ClientboundTabListPacket, - 0x69: clientbound_tag_query_packet::ClientboundTagQueryPacket, - 0x6a: clientbound_take_item_entity_packet::ClientboundTakeItemEntityPacket, - 0x6b: clientbound_teleport_entity_packet::ClientboundTeleportEntityPacket, - 0x6c: clientbound_update_advancements_packet::ClientboundUpdateAdvancementsPacket, - 0x6d: clientbound_update_attributes_packet::ClientboundUpdateAttributesPacket, - 0x6e: clientbound_update_mob_effect_packet::ClientboundUpdateMobEffectPacket, - 0x6f: clientbound_update_recipes_packet::ClientboundUpdateRecipesPacket, - 0x70: clientbound_update_tags_packet::ClientboundUpdateTagsPacket, + 0x42: clientbound_reset_score_packet::ClientboundResetScorePacket, + 0x43: clientbound_resource_pack_pop_packet::ClientboundResourcePackPopPacket, + 0x44: clientbound_resource_pack_push_packet::ClientboundResourcePackPushPacket, + 0x45: clientbound_respawn_packet::ClientboundRespawnPacket, + 0x46: clientbound_rotate_head_packet::ClientboundRotateHeadPacket, + 0x47: clientbound_section_blocks_update_packet::ClientboundSectionBlocksUpdatePacket, + 0x48: clientbound_select_advancements_tab_packet::ClientboundSelectAdvancementsTabPacket, + 0x49: clientbound_server_data_packet::ClientboundServerDataPacket, + 0x4a: clientbound_set_action_bar_text_packet::ClientboundSetActionBarTextPacket, + 0x4b: clientbound_set_border_center_packet::ClientboundSetBorderCenterPacket, + 0x4c: clientbound_set_border_lerp_size_packet::ClientboundSetBorderLerpSizePacket, + 0x4d: clientbound_set_border_size_packet::ClientboundSetBorderSizePacket, + 0x4e: clientbound_set_border_warning_delay_packet::ClientboundSetBorderWarningDelayPacket, + 0x4f: clientbound_set_border_warning_distance_packet::ClientboundSetBorderWarningDistancePacket, + 0x50: clientbound_set_camera_packet::ClientboundSetCameraPacket, + 0x51: clientbound_set_carried_item_packet::ClientboundSetCarriedItemPacket, + 0x52: clientbound_set_chunk_cache_center_packet::ClientboundSetChunkCacheCenterPacket, + 0x53: clientbound_set_chunk_cache_radius_packet::ClientboundSetChunkCacheRadiusPacket, + 0x54: clientbound_set_default_spawn_position_packet::ClientboundSetDefaultSpawnPositionPacket, + 0x55: clientbound_set_display_objective_packet::ClientboundSetDisplayObjectivePacket, + 0x56: clientbound_set_entity_data_packet::ClientboundSetEntityDataPacket, + 0x57: clientbound_set_entity_link_packet::ClientboundSetEntityLinkPacket, + 0x58: clientbound_set_entity_motion_packet::ClientboundSetEntityMotionPacket, + 0x59: clientbound_set_equipment_packet::ClientboundSetEquipmentPacket, + 0x5a: clientbound_set_experience_packet::ClientboundSetExperiencePacket, + 0x5b: clientbound_set_health_packet::ClientboundSetHealthPacket, + 0x5c: clientbound_set_objective_packet::ClientboundSetObjectivePacket, + 0x5d: clientbound_set_passengers_packet::ClientboundSetPassengersPacket, + 0x5e: clientbound_set_player_team_packet::ClientboundSetPlayerTeamPacket, + 0x5f: clientbound_set_score_packet::ClientboundSetScorePacket, + 0x60: clientbound_set_simulation_distance_packet::ClientboundSetSimulationDistancePacket, + 0x61: clientbound_set_subtitle_text_packet::ClientboundSetSubtitleTextPacket, + 0x62: clientbound_set_time_packet::ClientboundSetTimePacket, + 0x63: clientbound_set_title_text_packet::ClientboundSetTitleTextPacket, + 0x64: clientbound_set_titles_animation_packet::ClientboundSetTitlesAnimationPacket, + 0x65: clientbound_sound_entity_packet::ClientboundSoundEntityPacket, + 0x66: clientbound_sound_packet::ClientboundSoundPacket, + 0x67: clientbound_start_configuration_packet::ClientboundStartConfigurationPacket, + 0x68: clientbound_stop_sound_packet::ClientboundStopSoundPacket, + 0x69: clientbound_system_chat_packet::ClientboundSystemChatPacket, + 0x6a: clientbound_tab_list_packet::ClientboundTabListPacket, + 0x6b: clientbound_tag_query_packet::ClientboundTagQueryPacket, + 0x6c: clientbound_take_item_entity_packet::ClientboundTakeItemEntityPacket, + 0x6d: clientbound_teleport_entity_packet::ClientboundTeleportEntityPacket, + 0x6e: clientbound_ticking_state_packet::ClientboundTickingStatePacket, + 0x6f: clientbound_ticking_step_packet::ClientboundTickingStepPacket, + 0x70: clientbound_update_advancements_packet::ClientboundUpdateAdvancementsPacket, + 0x71: clientbound_update_attributes_packet::ClientboundUpdateAttributesPacket, + 0x72: clientbound_update_mob_effect_packet::ClientboundUpdateMobEffectPacket, + 0x73: clientbound_update_recipes_packet::ClientboundUpdateRecipesPacket, + 0x74: clientbound_update_tags_packet::ClientboundUpdateTagsPacket, } ); diff --git a/azalea-protocol/src/packets/game/serverbound_container_slot_state_changed_packet.rs b/azalea-protocol/src/packets/game/serverbound_container_slot_state_changed_packet.rs new file mode 100644 index 000000000..8a83f4e2c --- /dev/null +++ b/azalea-protocol/src/packets/game/serverbound_container_slot_state_changed_packet.rs @@ -0,0 +1,11 @@ +use azalea_buf::McBuf; +use azalea_protocol_macros::ServerboundGamePacket; + +#[derive(Clone, Debug, McBuf, ServerboundGamePacket)] +pub struct ServerboundContainerSlotStateChangedPacket { + #[var] + pub slot_id: u32, + #[var] + pub container_id: u32, + pub new_state: bool, +} diff --git a/azalea-protocol/src/packets/game/serverbound_resource_pack_packet.rs b/azalea-protocol/src/packets/game/serverbound_resource_pack_packet.rs index 104f8f732..f285707b2 100755 --- a/azalea-protocol/src/packets/game/serverbound_resource_pack_packet.rs +++ b/azalea-protocol/src/packets/game/serverbound_resource_pack_packet.rs @@ -1,8 +1,10 @@ use azalea_buf::McBuf; use azalea_protocol_macros::ServerboundGamePacket; +use uuid::Uuid; #[derive(Clone, Debug, McBuf, ServerboundGamePacket)] pub struct ServerboundResourcePackPacket { + pub id: Uuid, pub action: Action, } @@ -12,4 +14,7 @@ pub enum Action { Declined = 1, FailedDownload = 2, Accepted = 3, + InvalidUrl = 4, + FailedReload = 5, + Discarded = 6, } diff --git a/azalea-protocol/src/packets/game/serverbound_set_jigsaw_block_packet.rs b/azalea-protocol/src/packets/game/serverbound_set_jigsaw_block_packet.rs index a207147ac..a4b6f7262 100755 --- a/azalea-protocol/src/packets/game/serverbound_set_jigsaw_block_packet.rs +++ b/azalea-protocol/src/packets/game/serverbound_set_jigsaw_block_packet.rs @@ -16,6 +16,10 @@ pub struct ServerboundSetJigsawBlockPacket { pub pool: ResourceLocation, pub final_state: String, pub joint: String, + #[var] + pub selection_priority: i32, + #[var] + pub placement_priority: i32, } pub enum JointType { diff --git a/azalea-protocol/src/packets/handshaking/client_intention_packet.rs b/azalea-protocol/src/packets/handshaking/client_intention_packet.rs index 1900d4762..cac64a01e 100755 --- a/azalea-protocol/src/packets/handshaking/client_intention_packet.rs +++ b/azalea-protocol/src/packets/handshaking/client_intention_packet.rs @@ -6,7 +6,7 @@ use std::hash::Hash; #[derive(Hash, Clone, Debug, McBuf, ServerboundHandshakePacket)] pub struct ClientIntentionPacket { #[var] - pub protocol_version: u32, + pub protocol_version: i32, pub hostname: String, pub port: u16, pub intention: ConnectionProtocol, diff --git a/azalea-protocol/src/packets/login/clientbound_login_disconnect_packet.rs b/azalea-protocol/src/packets/login/clientbound_login_disconnect_packet.rs index 31cd370da..416ec63b4 100755 --- a/azalea-protocol/src/packets/login/clientbound_login_disconnect_packet.rs +++ b/azalea-protocol/src/packets/login/clientbound_login_disconnect_packet.rs @@ -1,8 +1,34 @@ -use azalea_buf::McBuf; +use std::io::{Cursor, Write}; + +use azalea_buf::{BufReadError, McBufReadable, McBufWritable}; use azalea_chat::FormattedText; use azalea_protocol_macros::ClientboundLoginPacket; +use serde::{Deserialize, Serialize}; -#[derive(Clone, Debug, McBuf, ClientboundLoginPacket)] +#[derive(Clone, Debug, ClientboundLoginPacket)] pub struct ClientboundLoginDisconnectPacket { pub reason: FormattedText, } + +impl McBufReadable for ClientboundLoginDisconnectPacket { + fn read_from( + buf: &mut Cursor<&[u8]>, + ) -> Result { + let disconnect_string = String::read_from(buf)?; + let disconnect_json: serde_json::Value = serde_json::from_str(disconnect_string.as_str())?; + + Ok(ClientboundLoginDisconnectPacket { + reason: FormattedText::deserialize(disconnect_json)?, + }) + } +} + +impl McBufWritable for ClientboundLoginDisconnectPacket { + fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { + let status_string = FormattedText::serialize(&self.reason, serde_json::value::Serializer) + .unwrap() + .to_string(); + status_string.write_into(buf)?; + Ok(()) + } +} diff --git a/azalea-protocol/src/packets/mod.rs b/azalea-protocol/src/packets/mod.rs index eb902da2f..4bb4b7dec 100755 --- a/azalea-protocol/src/packets/mod.rs +++ b/azalea-protocol/src/packets/mod.rs @@ -12,7 +12,7 @@ use std::io::{Cursor, Write}; // TODO: rename the packet files to just like clientbound_add_entity instead of // clientbound_add_entity_packet -pub const PROTOCOL_VERSION: u32 = 764; +pub const PROTOCOL_VERSION: i32 = 765; #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum ConnectionProtocol { diff --git a/azalea-protocol/src/packets/status/clientbound_status_response_packet.rs b/azalea-protocol/src/packets/status/clientbound_status_response_packet.rs index c8b9a208c..332bfdc44 100755 --- a/azalea-protocol/src/packets/status/clientbound_status_response_packet.rs +++ b/azalea-protocol/src/packets/status/clientbound_status_response_packet.rs @@ -2,7 +2,7 @@ use azalea_buf::{BufReadError, McBufReadable, McBufWritable}; use azalea_chat::FormattedText; use azalea_protocol_macros::ClientboundStatusPacket; use serde::{Deserialize, Serialize}; -use serde_json::{value::Serializer, Value}; +use serde_json::value::Serializer; use std::io::{Cursor, Write}; #[derive(Clone, Debug, Serialize, Deserialize)] @@ -43,7 +43,7 @@ pub struct ClientboundStatusResponsePacket { impl McBufReadable for ClientboundStatusResponsePacket { fn read_from(buf: &mut Cursor<&[u8]>) -> Result { let status_string = String::read_from(buf)?; - let status_json: Value = serde_json::from_str(status_string.as_str())?; + let status_json: serde_json::Value = serde_json::from_str(status_string.as_str())?; Ok(ClientboundStatusResponsePacket::deserialize(status_json)?) } diff --git a/azalea-protocol/src/read.rs b/azalea-protocol/src/read.rs index bbae76246..7c641c761 100755 --- a/azalea-protocol/src/read.rs +++ b/azalea-protocol/src/read.rs @@ -17,7 +17,6 @@ use std::{ use thiserror::Error; use tokio::io::AsyncRead; use tokio_util::codec::{BytesCodec, FramedRead}; -use tracing::if_log_enabled; #[derive(Error, Debug)] pub enum ReadPacketError { @@ -244,6 +243,8 @@ pub async fn read_raw_packet<'a, R>( stream: &'a mut R, buffer: &mut BytesMut, compression_threshold: Option, + // this has to be a &mut Option instead of an Option<&mut T> because + // otherwise the borrow checker complains about the cipher being moved cipher: &mut Option, ) -> Result, Box> where @@ -346,8 +347,7 @@ where .map_err(ReadPacketError::from)?; } - if_log_enabled!(tracing::Level::TRACE, { - use tracing::trace; + if log::log_enabled!(log::Level::Trace) { let buf_string: String = { if buf.len() > 500 { let cut_off_buf = &buf[..500]; @@ -356,8 +356,8 @@ where format!("{buf:?}") } }; - trace!("Reading packet with bytes: {buf_string}"); - }); + tracing::trace!("Reading packet with bytes: {buf_string}"); + }; Ok(Some(buf)) } diff --git a/azalea-protocol/src/resolver.rs b/azalea-protocol/src/resolver.rs index f803df23b..af59be72c 100755 --- a/azalea-protocol/src/resolver.rs +++ b/azalea-protocol/src/resolver.rs @@ -6,7 +6,7 @@ use std::net::{IpAddr, SocketAddr}; use thiserror::Error; use trust_dns_resolver::{ config::{ResolverConfig, ResolverOpts}, - TokioAsyncResolver, Name, + Name, TokioAsyncResolver, }; #[derive(Error, Debug)] @@ -62,8 +62,7 @@ pub async fn resolve_address(address: &ServerAddress) -> Result Result, PacketCompressError> { @@ -64,10 +64,10 @@ pub async fn compression_encoder( Ok(buf) } else { // otherwise, compress - let mut deflater = ZlibEncoder::new(data); + let mut deflater = ZlibEncoder::new(data, Compression::default()); // write deflated data to buf let mut compressed_data = Vec::new(); - deflater.read_to_end(&mut compressed_data).await?; + deflater.read_to_end(&mut compressed_data)?; // prepend the length let mut len_prepended_compressed_data = Vec::new(); @@ -105,7 +105,7 @@ where trace!("Writing raw packet: {raw_packet:?}"); let mut raw_packet = raw_packet.to_vec(); if let Some(threshold) = compression_threshold { - raw_packet = compression_encoder(&raw_packet, threshold).await.unwrap(); + raw_packet = compression_encoder(&raw_packet, threshold).unwrap(); } raw_packet = frame_prepender(raw_packet).unwrap(); // if we were given a cipher, encrypt the packet diff --git a/azalea-registry/Cargo.toml b/azalea-registry/Cargo.toml index ffd59035a..5a86d30bd 100644 --- a/azalea-registry/Cargo.toml +++ b/azalea-registry/Cargo.toml @@ -4,16 +4,16 @@ edition = "2021" license = "MIT" name = "azalea-registry" repository = "https://github.com/azalea-rs/azalea/tree/main/azalea-registry" -version = "0.8.0" +version = "0.9.0" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -azalea-buf = { path = "../azalea-buf", version = "0.8.0" } -azalea-registry-macros = { path = "./azalea-registry-macros", version = "0.8.0" } -once_cell = "1.18.0" -simdnbt = { version = "0.2.1" } +simdnbt = { version = "0.3", git = "https://github.com/azalea-rs/simdnbt" } +azalea-buf = { path = "../azalea-buf", version = "0.9.0" } +azalea-registry-macros = { path = "./azalea-registry-macros", version = "0.9.0" } +once_cell = "1.18.0" [features] serde = ["azalea-registry-macros/serde"] default = ["serde"] diff --git a/azalea-registry/azalea-registry-macros/Cargo.toml b/azalea-registry/azalea-registry-macros/Cargo.toml index f8a8d8fce..5941f0404 100644 --- a/azalea-registry/azalea-registry-macros/Cargo.toml +++ b/azalea-registry/azalea-registry-macros/Cargo.toml @@ -4,7 +4,7 @@ edition = "2021" license = "MIT" name = "azalea-registry-macros" repository = "https://github.com/azalea-rs/azalea/tree/main/azalea-registry/azalea-registry-macros" -version = "0.8.0" +version = "0.9.0" [lib] proc-macro = true @@ -12,7 +12,7 @@ proc-macro = true # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -proc-macro2 = "1.0.69" +proc-macro2 = "1.0.70" quote = "1.0.33" syn = "2.0.39" diff --git a/azalea-registry/src/lib.rs b/azalea-registry/src/lib.rs index c6c67367a..448dd3340 100755 --- a/azalea-registry/src/lib.rs +++ b/azalea-registry/src/lib.rs @@ -111,20 +111,20 @@ enum Activity { registry! { enum Attribute { - GenericMaxHealth => "minecraft:generic.max_health", - GenericFollowRange => "minecraft:generic.follow_range", - GenericKnockbackResistance => "minecraft:generic.knockback_resistance", - GenericMovementSpeed => "minecraft:generic.movement_speed", - GenericFlyingSpeed => "minecraft:generic.flying_speed", + GenericArmor => "minecraft:generic.armor", + GenericArmorToughness => "minecraft:generic.armor_toughness", GenericAttackDamage => "minecraft:generic.attack_damage", GenericAttackKnockback => "minecraft:generic.attack_knockback", GenericAttackSpeed => "minecraft:generic.attack_speed", - GenericArmor => "minecraft:generic.armor", - GenericArmorToughness => "minecraft:generic.armor_toughness", + GenericFlyingSpeed => "minecraft:generic.flying_speed", + GenericFollowRange => "minecraft:generic.follow_range", + HorseJumpStrength => "minecraft:horse.jump_strength", + GenericKnockbackResistance => "minecraft:generic.knockback_resistance", GenericLuck => "minecraft:generic.luck", GenericMaxAbsorption => "minecraft:generic.max_absorption", + GenericMaxHealth => "minecraft:generic.max_health", + GenericMovementSpeed => "minecraft:generic.movement_speed", ZombieSpawnReinforcements => "minecraft:zombie.spawn_reinforcements", - HorseJumpStrength => "minecraft:horse.jump_strength", } } @@ -304,7 +304,7 @@ enum Block { DetectorRail => "minecraft:detector_rail", StickyPiston => "minecraft:sticky_piston", Cobweb => "minecraft:cobweb", - Grass => "minecraft:grass", + ShortGrass => "minecraft:short_grass", Fern => "minecraft:fern", DeadBush => "minecraft:dead_bush", Seagrass => "minecraft:seagrass", @@ -436,7 +436,6 @@ enum Block { SugarCane => "minecraft:sugar_cane", Jukebox => "minecraft:jukebox", OakFence => "minecraft:oak_fence", - Pumpkin => "minecraft:pumpkin", Netherrack => "minecraft:netherrack", SoulSand => "minecraft:soul_sand", SoulSoil => "minecraft:soul_soil", @@ -493,6 +492,7 @@ enum Block { IronBars => "minecraft:iron_bars", Chain => "minecraft:chain", GlassPane => "minecraft:glass_pane", + Pumpkin => "minecraft:pumpkin", Melon => "minecraft:melon", AttachedPumpkinStem => "minecraft:attached_pumpkin_stem", AttachedMelonStem => "minecraft:attached_melon_stem", @@ -1091,6 +1091,19 @@ enum Block { MediumAmethystBud => "minecraft:medium_amethyst_bud", SmallAmethystBud => "minecraft:small_amethyst_bud", Tuff => "minecraft:tuff", + TuffSlab => "minecraft:tuff_slab", + TuffStairs => "minecraft:tuff_stairs", + TuffWall => "minecraft:tuff_wall", + PolishedTuff => "minecraft:polished_tuff", + PolishedTuffSlab => "minecraft:polished_tuff_slab", + PolishedTuffStairs => "minecraft:polished_tuff_stairs", + PolishedTuffWall => "minecraft:polished_tuff_wall", + ChiseledTuff => "minecraft:chiseled_tuff", + TuffBricks => "minecraft:tuff_bricks", + TuffBrickSlab => "minecraft:tuff_brick_slab", + TuffBrickStairs => "minecraft:tuff_brick_stairs", + TuffBrickWall => "minecraft:tuff_brick_wall", + ChiseledTuffBricks => "minecraft:chiseled_tuff_bricks", Calcite => "minecraft:calcite", TintedGlass => "minecraft:tinted_glass", PowderSnow => "minecraft:powder_snow", @@ -1100,16 +1113,24 @@ enum Block { SculkVein => "minecraft:sculk_vein", SculkCatalyst => "minecraft:sculk_catalyst", SculkShrieker => "minecraft:sculk_shrieker", - OxidizedCopper => "minecraft:oxidized_copper", - WeatheredCopper => "minecraft:weathered_copper", - ExposedCopper => "minecraft:exposed_copper", CopperBlock => "minecraft:copper_block", + ExposedCopper => "minecraft:exposed_copper", + WeatheredCopper => "minecraft:weathered_copper", + OxidizedCopper => "minecraft:oxidized_copper", CopperOre => "minecraft:copper_ore", DeepslateCopperOre => "minecraft:deepslate_copper_ore", OxidizedCutCopper => "minecraft:oxidized_cut_copper", WeatheredCutCopper => "minecraft:weathered_cut_copper", ExposedCutCopper => "minecraft:exposed_cut_copper", CutCopper => "minecraft:cut_copper", + OxidizedChiseledCopper => "minecraft:oxidized_chiseled_copper", + WeatheredChiseledCopper => "minecraft:weathered_chiseled_copper", + ExposedChiseledCopper => "minecraft:exposed_chiseled_copper", + ChiseledCopper => "minecraft:chiseled_copper", + WaxedOxidizedChiseledCopper => "minecraft:waxed_oxidized_chiseled_copper", + WaxedWeatheredChiseledCopper => "minecraft:waxed_weathered_chiseled_copper", + WaxedExposedChiseledCopper => "minecraft:waxed_exposed_chiseled_copper", + WaxedChiseledCopper => "minecraft:waxed_chiseled_copper", OxidizedCutCopperStairs => "minecraft:oxidized_cut_copper_stairs", WeatheredCutCopperStairs => "minecraft:weathered_cut_copper_stairs", ExposedCutCopperStairs => "minecraft:exposed_cut_copper_stairs", @@ -1134,6 +1155,38 @@ enum Block { WaxedWeatheredCutCopperSlab => "minecraft:waxed_weathered_cut_copper_slab", WaxedExposedCutCopperSlab => "minecraft:waxed_exposed_cut_copper_slab", WaxedCutCopperSlab => "minecraft:waxed_cut_copper_slab", + CopperDoor => "minecraft:copper_door", + ExposedCopperDoor => "minecraft:exposed_copper_door", + OxidizedCopperDoor => "minecraft:oxidized_copper_door", + WeatheredCopperDoor => "minecraft:weathered_copper_door", + WaxedCopperDoor => "minecraft:waxed_copper_door", + WaxedExposedCopperDoor => "minecraft:waxed_exposed_copper_door", + WaxedOxidizedCopperDoor => "minecraft:waxed_oxidized_copper_door", + WaxedWeatheredCopperDoor => "minecraft:waxed_weathered_copper_door", + CopperTrapdoor => "minecraft:copper_trapdoor", + ExposedCopperTrapdoor => "minecraft:exposed_copper_trapdoor", + OxidizedCopperTrapdoor => "minecraft:oxidized_copper_trapdoor", + WeatheredCopperTrapdoor => "minecraft:weathered_copper_trapdoor", + WaxedCopperTrapdoor => "minecraft:waxed_copper_trapdoor", + WaxedExposedCopperTrapdoor => "minecraft:waxed_exposed_copper_trapdoor", + WaxedOxidizedCopperTrapdoor => "minecraft:waxed_oxidized_copper_trapdoor", + WaxedWeatheredCopperTrapdoor => "minecraft:waxed_weathered_copper_trapdoor", + CopperGrate => "minecraft:copper_grate", + ExposedCopperGrate => "minecraft:exposed_copper_grate", + WeatheredCopperGrate => "minecraft:weathered_copper_grate", + OxidizedCopperGrate => "minecraft:oxidized_copper_grate", + WaxedCopperGrate => "minecraft:waxed_copper_grate", + WaxedExposedCopperGrate => "minecraft:waxed_exposed_copper_grate", + WaxedWeatheredCopperGrate => "minecraft:waxed_weathered_copper_grate", + WaxedOxidizedCopperGrate => "minecraft:waxed_oxidized_copper_grate", + CopperBulb => "minecraft:copper_bulb", + ExposedCopperBulb => "minecraft:exposed_copper_bulb", + WeatheredCopperBulb => "minecraft:weathered_copper_bulb", + OxidizedCopperBulb => "minecraft:oxidized_copper_bulb", + WaxedCopperBulb => "minecraft:waxed_copper_bulb", + WaxedExposedCopperBulb => "minecraft:waxed_exposed_copper_bulb", + WaxedWeatheredCopperBulb => "minecraft:waxed_weathered_copper_bulb", + WaxedOxidizedCopperBulb => "minecraft:waxed_oxidized_copper_bulb", LightningRod => "minecraft:lightning_rod", PointedDripstone => "minecraft:pointed_dripstone", DripstoneBlock => "minecraft:dripstone_block", @@ -1184,6 +1237,8 @@ enum Block { Frogspawn => "minecraft:frogspawn", ReinforcedDeepslate => "minecraft:reinforced_deepslate", DecoratedPot => "minecraft:decorated_pot", + Crafter => "minecraft:crafter", + TrialSpawner => "minecraft:trial_spawner", } } @@ -1233,6 +1288,8 @@ enum BlockEntityKind { ChiseledBookshelf => "minecraft:chiseled_bookshelf", BrushableBlock => "minecraft:brushable_block", DecoratedPot => "minecraft:decorated_pot", + Crafter => "minecraft:crafter", + TrialSpawner => "minecraft:trial_spawner", } } @@ -1306,6 +1363,7 @@ enum CommandArgumentKind { ItemPredicate => "minecraft:item_predicate", Color => "minecraft:color", Component => "minecraft:component", + Style => "minecraft:style", Message => "minecraft:message", NbtCompoundTag => "minecraft:nbt_compound_tag", NbtTag => "minecraft:nbt_tag", @@ -1477,6 +1535,7 @@ enum EntityKind { Blaze => "minecraft:blaze", BlockDisplay => "minecraft:block_display", Boat => "minecraft:boat", + Breeze => "minecraft:breeze", Camel => "minecraft:camel", Cat => "minecraft:cat", CaveSpider => "minecraft:cave_spider", @@ -1579,6 +1638,7 @@ enum EntityKind { Vindicator => "minecraft:vindicator", WanderingTrader => "minecraft:wandering_trader", Warden => "minecraft:warden", + WindCharge => "minecraft:wind_charge", Witch => "minecraft:witch", Wither => "minecraft:wither", WitherSkeleton => "minecraft:wither_skeleton", @@ -1736,6 +1796,19 @@ enum Item { PolishedDeepslate => "minecraft:polished_deepslate", Calcite => "minecraft:calcite", Tuff => "minecraft:tuff", + TuffSlab => "minecraft:tuff_slab", + TuffStairs => "minecraft:tuff_stairs", + TuffWall => "minecraft:tuff_wall", + ChiseledTuff => "minecraft:chiseled_tuff", + PolishedTuff => "minecraft:polished_tuff", + PolishedTuffSlab => "minecraft:polished_tuff_slab", + PolishedTuffStairs => "minecraft:polished_tuff_stairs", + PolishedTuffWall => "minecraft:polished_tuff_wall", + TuffBricks => "minecraft:tuff_bricks", + TuffBrickSlab => "minecraft:tuff_brick_slab", + TuffBrickStairs => "minecraft:tuff_brick_stairs", + TuffBrickWall => "minecraft:tuff_brick_wall", + ChiseledTuffBricks => "minecraft:chiseled_tuff_bricks", DripstoneBlock => "minecraft:dripstone_block", GrassBlock => "minecraft:grass_block", Dirt => "minecraft:dirt", @@ -1805,6 +1878,10 @@ enum Item { ExposedCopper => "minecraft:exposed_copper", WeatheredCopper => "minecraft:weathered_copper", OxidizedCopper => "minecraft:oxidized_copper", + ChiseledCopper => "minecraft:chiseled_copper", + ExposedChiseledCopper => "minecraft:exposed_chiseled_copper", + WeatheredChiseledCopper => "minecraft:weathered_chiseled_copper", + OxidizedChiseledCopper => "minecraft:oxidized_chiseled_copper", CutCopper => "minecraft:cut_copper", ExposedCutCopper => "minecraft:exposed_cut_copper", WeatheredCutCopper => "minecraft:weathered_cut_copper", @@ -1821,6 +1898,10 @@ enum Item { WaxedExposedCopper => "minecraft:waxed_exposed_copper", WaxedWeatheredCopper => "minecraft:waxed_weathered_copper", WaxedOxidizedCopper => "minecraft:waxed_oxidized_copper", + WaxedChiseledCopper => "minecraft:waxed_chiseled_copper", + WaxedExposedChiseledCopper => "minecraft:waxed_exposed_chiseled_copper", + WaxedWeatheredChiseledCopper => "minecraft:waxed_weathered_chiseled_copper", + WaxedOxidizedChiseledCopper => "minecraft:waxed_oxidized_chiseled_copper", WaxedCutCopper => "minecraft:waxed_cut_copper", WaxedExposedCutCopper => "minecraft:waxed_exposed_cut_copper", WaxedWeatheredCutCopper => "minecraft:waxed_weathered_cut_copper", @@ -1896,7 +1977,7 @@ enum Item { ChiseledSandstone => "minecraft:chiseled_sandstone", CutSandstone => "minecraft:cut_sandstone", Cobweb => "minecraft:cobweb", - Grass => "minecraft:grass", + ShortGrass => "minecraft:short_grass", Fern => "minecraft:fern", Azalea => "minecraft:azalea", FloweringAzalea => "minecraft:flowering_azalea", @@ -2423,6 +2504,14 @@ enum Item { BambooDoor => "minecraft:bamboo_door", CrimsonDoor => "minecraft:crimson_door", WarpedDoor => "minecraft:warped_door", + CopperDoor => "minecraft:copper_door", + ExposedCopperDoor => "minecraft:exposed_copper_door", + WeatheredCopperDoor => "minecraft:weathered_copper_door", + OxidizedCopperDoor => "minecraft:oxidized_copper_door", + WaxedCopperDoor => "minecraft:waxed_copper_door", + WaxedExposedCopperDoor => "minecraft:waxed_exposed_copper_door", + WaxedWeatheredCopperDoor => "minecraft:waxed_weathered_copper_door", + WaxedOxidizedCopperDoor => "minecraft:waxed_oxidized_copper_door", IronTrapdoor => "minecraft:iron_trapdoor", OakTrapdoor => "minecraft:oak_trapdoor", SpruceTrapdoor => "minecraft:spruce_trapdoor", @@ -2435,6 +2524,14 @@ enum Item { BambooTrapdoor => "minecraft:bamboo_trapdoor", CrimsonTrapdoor => "minecraft:crimson_trapdoor", WarpedTrapdoor => "minecraft:warped_trapdoor", + CopperTrapdoor => "minecraft:copper_trapdoor", + ExposedCopperTrapdoor => "minecraft:exposed_copper_trapdoor", + WeatheredCopperTrapdoor => "minecraft:weathered_copper_trapdoor", + OxidizedCopperTrapdoor => "minecraft:oxidized_copper_trapdoor", + WaxedCopperTrapdoor => "minecraft:waxed_copper_trapdoor", + WaxedExposedCopperTrapdoor => "minecraft:waxed_exposed_copper_trapdoor", + WaxedWeatheredCopperTrapdoor => "minecraft:waxed_weathered_copper_trapdoor", + WaxedOxidizedCopperTrapdoor => "minecraft:waxed_oxidized_copper_trapdoor", OakFenceGate => "minecraft:oak_fence_gate", SpruceFenceGate => "minecraft:spruce_fence_gate", BirchFenceGate => "minecraft:birch_fence_gate", @@ -2664,6 +2761,7 @@ enum Item { RedBed => "minecraft:red_bed", BlackBed => "minecraft:black_bed", Cookie => "minecraft:cookie", + Crafter => "minecraft:crafter", FilledMap => "minecraft:filled_map", Shears => "minecraft:shears", MelonSlice => "minecraft:melon_slice", @@ -2695,6 +2793,7 @@ enum Item { BatSpawnEgg => "minecraft:bat_spawn_egg", BeeSpawnEgg => "minecraft:bee_spawn_egg", BlazeSpawnEgg => "minecraft:blaze_spawn_egg", + BreezeSpawnEgg => "minecraft:breeze_spawn_egg", CatSpawnEgg => "minecraft:cat_spawn_egg", CamelSpawnEgg => "minecraft:camel_spawn_egg", CaveSpiderSpawnEgg => "minecraft:cave_spider_spawn_egg", @@ -2978,6 +3077,24 @@ enum Item { ShelterPotterySherd => "minecraft:shelter_pottery_sherd", SkullPotterySherd => "minecraft:skull_pottery_sherd", SnortPotterySherd => "minecraft:snort_pottery_sherd", + CopperGrate => "minecraft:copper_grate", + ExposedCopperGrate => "minecraft:exposed_copper_grate", + WeatheredCopperGrate => "minecraft:weathered_copper_grate", + OxidizedCopperGrate => "minecraft:oxidized_copper_grate", + WaxedCopperGrate => "minecraft:waxed_copper_grate", + WaxedExposedCopperGrate => "minecraft:waxed_exposed_copper_grate", + WaxedWeatheredCopperGrate => "minecraft:waxed_weathered_copper_grate", + WaxedOxidizedCopperGrate => "minecraft:waxed_oxidized_copper_grate", + CopperBulb => "minecraft:copper_bulb", + ExposedCopperBulb => "minecraft:exposed_copper_bulb", + WeatheredCopperBulb => "minecraft:weathered_copper_bulb", + OxidizedCopperBulb => "minecraft:oxidized_copper_bulb", + WaxedCopperBulb => "minecraft:waxed_copper_bulb", + WaxedExposedCopperBulb => "minecraft:waxed_exposed_copper_bulb", + WaxedWeatheredCopperBulb => "minecraft:waxed_weathered_copper_bulb", + WaxedOxidizedCopperBulb => "minecraft:waxed_oxidized_copper_bulb", + TrialSpawner => "minecraft:trial_spawner", + TrialKey => "minecraft:trial_key", } } @@ -3171,6 +3288,13 @@ enum MemoryModuleKind { SnifferSniffingTarget => "minecraft:sniffer_sniffing_target", SnifferDigging => "minecraft:sniffer_digging", SnifferHappy => "minecraft:sniffer_happy", + BreezeJumpCooldown => "minecraft:breeze_jump_cooldown", + BreezeShoot => "minecraft:breeze_shoot", + BreezeShootCharging => "minecraft:breeze_shoot_charging", + BreezeShootRecover => "minecraft:breeze_shoot_recover", + BreezeShootCooldown => "minecraft:breeze_shoot_cooldown", + BreezeJumpInhaling => "minecraft:breeze_jump_inhaling", + BreezeJumpTarget => "minecraft:breeze_jump_target", } } @@ -3302,6 +3426,8 @@ enum ParticleKind { EntityEffect => "minecraft:entity_effect", ExplosionEmitter => "minecraft:explosion_emitter", Explosion => "minecraft:explosion", + Gust => "minecraft:gust", + GustEmitter => "minecraft:gust_emitter", SonicBoom => "minecraft:sonic_boom", FallingDust => "minecraft:falling_dust", Firework => "minecraft:firework", @@ -3330,6 +3456,7 @@ enum ParticleKind { Portal => "minecraft:portal", Rain => "minecraft:rain", Smoke => "minecraft:smoke", + WhiteSmoke => "minecraft:white_smoke", Sneeze => "minecraft:sneeze", Spit => "minecraft:spit", SquidInk => "minecraft:squid_ink", @@ -3373,6 +3500,9 @@ enum ParticleKind { Scrape => "minecraft:scrape", Shriek => "minecraft:shriek", EggCrack => "minecraft:egg_crack", + DustPlume => "minecraft:dust_plume", + GustDust => "minecraft:gust_dust", + TrialSpawnerDetection => "minecraft:trial_spawner_detection", } } @@ -3549,6 +3679,7 @@ enum SensorKind { IsInWater => "minecraft:is_in_water", WardenEntitySensor => "minecraft:warden_entity_sensor", SnifferTemptations => "minecraft:sniffer_temptations", + BreezeAttackEntitySensor => "minecraft:breeze_attack_entity_sensor", } } @@ -3720,6 +3851,15 @@ enum SoundEvent { ItemBottleEmpty => "minecraft:item.bottle.empty", ItemBottleFill => "minecraft:item.bottle.fill", ItemBottleFillDragonbreath => "minecraft:item.bottle.fill_dragonbreath", + EntityBreezeInhale => "minecraft:entity.breeze.inhale", + EntityBreezeIdleGround => "minecraft:entity.breeze.idle_ground", + EntityBreezeIdleAir => "minecraft:entity.breeze.idle_air", + EntityBreezeShoot => "minecraft:entity.breeze.shoot", + EntityBreezeJump => "minecraft:entity.breeze.jump", + EntityBreezeLand => "minecraft:entity.breeze.land", + EntityBreezeSlide => "minecraft:entity.breeze.slide", + EntityBreezeDeath => "minecraft:entity.breeze.death", + EntityBreezeHurt => "minecraft:entity.breeze.hurt", BlockBrewingStandBrew => "minecraft:block.brewing_stand.brew", ItemBrushBrushingGeneric => "minecraft:item.brush.brushing.generic", ItemBrushBrushingSand => "minecraft:item.brush.brushing.sand", @@ -3855,11 +3995,27 @@ enum SoundEvent { BlockConduitAmbientShort => "minecraft:block.conduit.ambient.short", BlockConduitAttackTarget => "minecraft:block.conduit.attack.target", BlockConduitDeactivate => "minecraft:block.conduit.deactivate", + BlockCopperBulbBreak => "minecraft:block.copper_bulb.break", + BlockCopperBulbStep => "minecraft:block.copper_bulb.step", + BlockCopperBulbPlace => "minecraft:block.copper_bulb.place", + BlockCopperBulbHit => "minecraft:block.copper_bulb.hit", + BlockCopperBulbFall => "minecraft:block.copper_bulb.fall", + BlockCopperBulbTurnOn => "minecraft:block.copper_bulb.turn_on", + BlockCopperBulbTurnOff => "minecraft:block.copper_bulb.turn_off", BlockCopperBreak => "minecraft:block.copper.break", BlockCopperStep => "minecraft:block.copper.step", BlockCopperPlace => "minecraft:block.copper.place", BlockCopperHit => "minecraft:block.copper.hit", BlockCopperFall => "minecraft:block.copper.fall", + BlockCopperDoorClose => "minecraft:block.copper_door.close", + BlockCopperDoorOpen => "minecraft:block.copper_door.open", + BlockCopperGrateBreak => "minecraft:block.copper_grate.break", + BlockCopperGrateStep => "minecraft:block.copper_grate.step", + BlockCopperGratePlace => "minecraft:block.copper_grate.place", + BlockCopperGrateHit => "minecraft:block.copper_grate.hit", + BlockCopperGrateFall => "minecraft:block.copper_grate.fall", + BlockCopperTrapdoorClose => "minecraft:block.copper_trapdoor.close", + BlockCopperTrapdoorOpen => "minecraft:block.copper_trapdoor.open", BlockCoralBlockBreak => "minecraft:block.coral_block.break", BlockCoralBlockFall => "minecraft:block.coral_block.fall", BlockCoralBlockHit => "minecraft:block.coral_block.hit", @@ -3870,6 +4026,8 @@ enum SoundEvent { EntityCowHurt => "minecraft:entity.cow.hurt", EntityCowMilk => "minecraft:entity.cow.milk", EntityCowStep => "minecraft:entity.cow.step", + BlockCrafterCraft => "minecraft:block.crafter.craft", + BlockCrafterFail => "minecraft:block.crafter.fail", EntityCreeperDeath => "minecraft:entity.creeper.death", EntityCreeperHurt => "minecraft:entity.creeper.hurt", EntityCreeperPrimed => "minecraft:entity.creeper.primed", @@ -3886,6 +4044,8 @@ enum SoundEvent { BlockDecoratedPotBreak => "minecraft:block.decorated_pot.break", BlockDecoratedPotFall => "minecraft:block.decorated_pot.fall", BlockDecoratedPotHit => "minecraft:block.decorated_pot.hit", + BlockDecoratedPotInsert => "minecraft:block.decorated_pot.insert", + BlockDecoratedPotInsertFail => "minecraft:block.decorated_pot.insert_fail", BlockDecoratedPotStep => "minecraft:block.decorated_pot.step", BlockDecoratedPotPlace => "minecraft:block.decorated_pot.place", BlockDecoratedPotShatter => "minecraft:block.decorated_pot.shatter", @@ -4163,6 +4323,17 @@ enum SoundEvent { BlockBambooWoodHangingSignFall => "minecraft:block.bamboo_wood_hanging_sign.fall", BlockBambooWoodHangingSignHit => "minecraft:block.bamboo_wood_hanging_sign.hit", BlockBambooWoodHangingSignPlace => "minecraft:block.bamboo_wood_hanging_sign.place", + BlockTrialSpawnerBreak => "minecraft:block.trial_spawner.break", + BlockTrialSpawnerStep => "minecraft:block.trial_spawner.step", + BlockTrialSpawnerPlace => "minecraft:block.trial_spawner.place", + BlockTrialSpawnerHit => "minecraft:block.trial_spawner.hit", + BlockTrialSpawnerFall => "minecraft:block.trial_spawner.fall", + BlockTrialSpawnerSpawnMob => "minecraft:block.trial_spawner.spawn_mob", + BlockTrialSpawnerDetectPlayer => "minecraft:block.trial_spawner.detect_player", + BlockTrialSpawnerAmbient => "minecraft:block.trial_spawner.ambient", + BlockTrialSpawnerOpenShutter => "minecraft:block.trial_spawner.open_shutter", + BlockTrialSpawnerCloseShutter => "minecraft:block.trial_spawner.close_shutter", + BlockTrialSpawnerEjectItem => "minecraft:block.trial_spawner.eject_item", ItemHoeTill => "minecraft:item.hoe.till", EntityHoglinAmbient => "minecraft:entity.hoglin.ambient", EntityHoglinAngry => "minecraft:entity.hoglin.angry", @@ -4497,6 +4668,7 @@ enum SoundEvent { EntityParrotFly => "minecraft:entity.parrot.fly", EntityParrotHurt => "minecraft:entity.parrot.hurt", EntityParrotImitateBlaze => "minecraft:entity.parrot.imitate.blaze", + EntityParrotImitateBreeze => "minecraft:entity.parrot.imitate.breeze", EntityParrotImitateCreeper => "minecraft:entity.parrot.imitate.creeper", EntityParrotImitateDrowned => "minecraft:entity.parrot.imitate.drowned", EntityParrotImitateElderGuardian => "minecraft:entity.parrot.imitate.elder_guardian", @@ -4583,6 +4755,7 @@ enum SoundEvent { EntityPlayerSplash => "minecraft:entity.player.splash", EntityPlayerSplashHighSpeed => "minecraft:entity.player.splash.high_speed", EntityPlayerSwim => "minecraft:entity.player.swim", + EntityPlayerTeleport => "minecraft:entity.player.teleport", EntityPolarBearAmbient => "minecraft:entity.polar_bear.ambient", EntityPolarBearAmbientBaby => "minecraft:entity.polar_bear.ambient_baby", EntityPolarBearDeath => "minecraft:entity.polar_bear.death", @@ -4868,6 +5041,16 @@ enum SoundEvent { BlockTuffPlace => "minecraft:block.tuff.place", BlockTuffHit => "minecraft:block.tuff.hit", BlockTuffFall => "minecraft:block.tuff.fall", + BlockTuffBricksBreak => "minecraft:block.tuff_bricks.break", + BlockTuffBricksFall => "minecraft:block.tuff_bricks.fall", + BlockTuffBricksHit => "minecraft:block.tuff_bricks.hit", + BlockTuffBricksPlace => "minecraft:block.tuff_bricks.place", + BlockTuffBricksStep => "minecraft:block.tuff_bricks.step", + BlockPolishedTuffBreak => "minecraft:block.polished_tuff.break", + BlockPolishedTuffFall => "minecraft:block.polished_tuff.fall", + BlockPolishedTuffHit => "minecraft:block.polished_tuff.hit", + BlockPolishedTuffPlace => "minecraft:block.polished_tuff.place", + BlockPolishedTuffStep => "minecraft:block.polished_tuff.step", EntityTurtleAmbientLand => "minecraft:entity.turtle.ambient_land", EntityTurtleDeath => "minecraft:entity.turtle.death", EntityTurtleDeathBaby => "minecraft:entity.turtle.death_baby", @@ -4953,6 +5136,7 @@ enum SoundEvent { EntityWardenSonicCharge => "minecraft:entity.warden.sonic_charge", EntityWardenStep => "minecraft:entity.warden.step", EntityWardenTendrilClicks => "minecraft:entity.warden.tendril_clicks", + BlockHangingSignWaxedInteractFail => "minecraft:block.hanging_sign.waxed_interact_fail", BlockSignWaxedInteractFail => "minecraft:block.sign.waxed_interact_fail", BlockWaterAmbient => "minecraft:block.water.ambient", WeatherRain => "minecraft:weather.rain", @@ -4967,6 +5151,7 @@ enum SoundEvent { BlockWetSpongeHit => "minecraft:block.wet_sponge.hit", BlockWetSpongePlace => "minecraft:block.wet_sponge.place", BlockWetSpongeStep => "minecraft:block.wet_sponge.step", + EntityGenericWindBurst => "minecraft:entity.generic.wind_burst", EntityWitchAmbient => "minecraft:entity.witch.ambient", EntityWitchCelebrate => "minecraft:entity.witch.celebrate", EntityWitchDeath => "minecraft:entity.witch.death", @@ -5506,6 +5691,7 @@ enum MenuKind { Generic9x5 => "minecraft:generic_9x5", Generic9x6 => "minecraft:generic_9x6", Generic3x3 => "minecraft:generic_3x3", + Crafter3x3 => "minecraft:crafter_3x3", Anvil => "minecraft:anvil", Beacon => "minecraft:beacon", BlastFurnace => "minecraft:blast_furnace", @@ -5525,3 +5711,320 @@ enum MenuKind { Stonecutter => "minecraft:stonecutter", } } + +registry! { +enum BlockKind { + Block => "minecraft:block", + Air => "minecraft:air", + Amethyst => "minecraft:amethyst", + AmethystCluster => "minecraft:amethyst_cluster", + Anvil => "minecraft:anvil", + AttachedStem => "minecraft:attached_stem", + Azalea => "minecraft:azalea", + BambooSapling => "minecraft:bamboo_sapling", + BambooStalk => "minecraft:bamboo_stalk", + Banner => "minecraft:banner", + Barrel => "minecraft:barrel", + Barrier => "minecraft:barrier", + BaseCoralFan => "minecraft:base_coral_fan", + BaseCoralPlant => "minecraft:base_coral_plant", + BaseCoralWallFan => "minecraft:base_coral_wall_fan", + Beacon => "minecraft:beacon", + Bed => "minecraft:bed", + Beehive => "minecraft:beehive", + Beetroot => "minecraft:beetroot", + Bell => "minecraft:bell", + BigDripleaf => "minecraft:big_dripleaf", + BigDripleafStem => "minecraft:big_dripleaf_stem", + BlastFurnace => "minecraft:blast_furnace", + BrewingStand => "minecraft:brewing_stand", + Brushable => "minecraft:brushable", + BubbleColumn => "minecraft:bubble_column", + BuddingAmethyst => "minecraft:budding_amethyst", + Button => "minecraft:button", + Cactus => "minecraft:cactus", + Cake => "minecraft:cake", + CalibratedSculkSensor => "minecraft:calibrated_sculk_sensor", + Campfire => "minecraft:campfire", + CandleCake => "minecraft:candle_cake", + Candle => "minecraft:candle", + Carpet => "minecraft:carpet", + Carrot => "minecraft:carrot", + CartographyTable => "minecraft:cartography_table", + CarvedPumpkin => "minecraft:carved_pumpkin", + Cauldron => "minecraft:cauldron", + CaveVines => "minecraft:cave_vines", + CaveVinesPlant => "minecraft:cave_vines_plant", + CeilingHangingSign => "minecraft:ceiling_hanging_sign", + Chain => "minecraft:chain", + CherryLeaves => "minecraft:cherry_leaves", + Chest => "minecraft:chest", + ChiseledBookShelf => "minecraft:chiseled_book_shelf", + ChorusFlower => "minecraft:chorus_flower", + ChorusPlant => "minecraft:chorus_plant", + Cocoa => "minecraft:cocoa", + ColoredFalling => "minecraft:colored_falling", + Command => "minecraft:command", + Comparator => "minecraft:comparator", + Composter => "minecraft:composter", + ConcretePowder => "minecraft:concrete_powder", + Conduit => "minecraft:conduit", + CopperBulbBlock => "minecraft:copper_bulb_block", + Coral => "minecraft:coral", + CoralFan => "minecraft:coral_fan", + CoralPlant => "minecraft:coral_plant", + CoralWallFan => "minecraft:coral_wall_fan", + Crafter => "minecraft:crafter", + CraftingTable => "minecraft:crafting_table", + Crop => "minecraft:crop", + CryingObsidian => "minecraft:crying_obsidian", + DaylightDetector => "minecraft:daylight_detector", + DeadBush => "minecraft:dead_bush", + DecoratedPot => "minecraft:decorated_pot", + DetectorRail => "minecraft:detector_rail", + DirtPath => "minecraft:dirt_path", + Dispenser => "minecraft:dispenser", + Door => "minecraft:door", + DoublePlant => "minecraft:double_plant", + DragonEgg => "minecraft:dragon_egg", + DropExperience => "minecraft:drop_experience", + Dropper => "minecraft:dropper", + EnchantmentTable => "minecraft:enchantment_table", + EnderChest => "minecraft:ender_chest", + EndGateway => "minecraft:end_gateway", + EndPortal => "minecraft:end_portal", + EndPortalFrame => "minecraft:end_portal_frame", + EndRod => "minecraft:end_rod", + Farm => "minecraft:farm", + Fence => "minecraft:fence", + FenceGate => "minecraft:fence_gate", + Fire => "minecraft:fire", + FletchingTable => "minecraft:fletching_table", + Flower => "minecraft:flower", + FlowerPot => "minecraft:flower_pot", + Frogspawn => "minecraft:frogspawn", + FrostedIce => "minecraft:frosted_ice", + Fungus => "minecraft:fungus", + Furnace => "minecraft:furnace", + GlazedTerracotta => "minecraft:glazed_terracotta", + GlowLichen => "minecraft:glow_lichen", + Grass => "minecraft:grass", + Grindstone => "minecraft:grindstone", + HalfTransparent => "minecraft:half_transparent", + HangingRoots => "minecraft:hanging_roots", + Hay => "minecraft:hay", + Honey => "minecraft:honey", + Hopper => "minecraft:hopper", + HugeMushroom => "minecraft:huge_mushroom", + Ice => "minecraft:ice", + Infested => "minecraft:infested", + InfestedRotatedPillar => "minecraft:infested_rotated_pillar", + IronBars => "minecraft:iron_bars", + JackOLantern => "minecraft:jack_o_lantern", + Jigsaw => "minecraft:jigsaw", + Jukebox => "minecraft:jukebox", + Kelp => "minecraft:kelp", + KelpPlant => "minecraft:kelp_plant", + Ladder => "minecraft:ladder", + Lantern => "minecraft:lantern", + LavaCauldron => "minecraft:lava_cauldron", + LayeredCauldron => "minecraft:layered_cauldron", + Leaves => "minecraft:leaves", + Lectern => "minecraft:lectern", + Lever => "minecraft:lever", + Light => "minecraft:light", + LightningRod => "minecraft:lightning_rod", + Liquid => "minecraft:liquid", + Loom => "minecraft:loom", + Magma => "minecraft:magma", + MangroveLeaves => "minecraft:mangrove_leaves", + MangrovePropagule => "minecraft:mangrove_propagule", + MangroveRoots => "minecraft:mangrove_roots", + Moss => "minecraft:moss", + MovingPiston => "minecraft:moving_piston", + Mud => "minecraft:mud", + Mushroom => "minecraft:mushroom", + Mycelium => "minecraft:mycelium", + NetherPortal => "minecraft:nether_portal", + Netherrack => "minecraft:netherrack", + NetherSprouts => "minecraft:nether_sprouts", + NetherWart => "minecraft:nether_wart", + Note => "minecraft:note", + Nylium => "minecraft:nylium", + Observer => "minecraft:observer", + Piglinwallskull => "minecraft:piglinwallskull", + PinkPetals => "minecraft:pink_petals", + PistonBase => "minecraft:piston_base", + PistonHead => "minecraft:piston_head", + PitcherCrop => "minecraft:pitcher_crop", + PlayerHead => "minecraft:player_head", + PlayerWallHead => "minecraft:player_wall_head", + PointedDripstone => "minecraft:pointed_dripstone", + Potato => "minecraft:potato", + PowderSnow => "minecraft:powder_snow", + Powered => "minecraft:powered", + PoweredRail => "minecraft:powered_rail", + PressurePlate => "minecraft:pressure_plate", + Pumpkin => "minecraft:pumpkin", + Rail => "minecraft:rail", + RedstoneLamp => "minecraft:redstone_lamp", + RedstoneOre => "minecraft:redstone_ore", + RedstoneTorch => "minecraft:redstone_torch", + RedstoneWallTorch => "minecraft:redstone_wall_torch", + RedstoneWire => "minecraft:redstone_wire", + Repeater => "minecraft:repeater", + RespawnAnchor => "minecraft:respawn_anchor", + RootedDirt => "minecraft:rooted_dirt", + Roots => "minecraft:roots", + RotatedPillar => "minecraft:rotated_pillar", + Sapling => "minecraft:sapling", + Scaffolding => "minecraft:scaffolding", + SculkCatalyst => "minecraft:sculk_catalyst", + Sculk => "minecraft:sculk", + SculkSensor => "minecraft:sculk_sensor", + SculkShrieker => "minecraft:sculk_shrieker", + SculkVein => "minecraft:sculk_vein", + Seagrass => "minecraft:seagrass", + SeaPickle => "minecraft:sea_pickle", + ShulkerBox => "minecraft:shulker_box", + Skull => "minecraft:skull", + Slab => "minecraft:slab", + Slime => "minecraft:slime", + SmallDripleaf => "minecraft:small_dripleaf", + SmithingTable => "minecraft:smithing_table", + Smoker => "minecraft:smoker", + SnifferEgg => "minecraft:sniffer_egg", + SnowLayer => "minecraft:snow_layer", + SnowyDirt => "minecraft:snowy_dirt", + SoulFire => "minecraft:soul_fire", + SoulSand => "minecraft:soul_sand", + Spawner => "minecraft:spawner", + Sponge => "minecraft:sponge", + SporeBlossom => "minecraft:spore_blossom", + StainedGlassPane => "minecraft:stained_glass_pane", + StainedGlass => "minecraft:stained_glass", + Stair => "minecraft:stair", + StandingSign => "minecraft:standing_sign", + Stem => "minecraft:stem", + Stonecutter => "minecraft:stonecutter", + Structure => "minecraft:structure", + StructureVoid => "minecraft:structure_void", + SugarCane => "minecraft:sugar_cane", + SweetBerryBush => "minecraft:sweet_berry_bush", + TallFlower => "minecraft:tall_flower", + TallGrass => "minecraft:tall_grass", + TallSeagrass => "minecraft:tall_seagrass", + Target => "minecraft:target", + TintedGlass => "minecraft:tinted_glass", + Tnt => "minecraft:tnt", + TorchflowerCrop => "minecraft:torchflower_crop", + Torch => "minecraft:torch", + Transparent => "minecraft:transparent", + Trapdoor => "minecraft:trapdoor", + TrappedChest => "minecraft:trapped_chest", + TrialSpawner => "minecraft:trial_spawner", + TripWireHook => "minecraft:trip_wire_hook", + Tripwire => "minecraft:tripwire", + TurtleEgg => "minecraft:turtle_egg", + TwistingVinesPlant => "minecraft:twisting_vines_plant", + TwistingVines => "minecraft:twisting_vines", + Vine => "minecraft:vine", + WallBanner => "minecraft:wall_banner", + WallHangingSign => "minecraft:wall_hanging_sign", + WallSign => "minecraft:wall_sign", + WallSkull => "minecraft:wall_skull", + WallTorch => "minecraft:wall_torch", + Wall => "minecraft:wall", + Waterlily => "minecraft:waterlily", + WaterloggedTransparent => "minecraft:waterlogged_transparent", + WeatheringCopperBulb => "minecraft:weathering_copper_bulb", + WeatheringCopperDoor => "minecraft:weathering_copper_door", + WeatheringCopperFull => "minecraft:weathering_copper_full", + WeatheringCopperGrate => "minecraft:weathering_copper_grate", + WeatheringCopperSlab => "minecraft:weathering_copper_slab", + WeatheringCopperStair => "minecraft:weathering_copper_stair", + WeatheringCopperTrapDoor => "minecraft:weathering_copper_trap_door", + Web => "minecraft:web", + WeepingVinesPlant => "minecraft:weeping_vines_plant", + WeepingVines => "minecraft:weeping_vines", + WeightedPressurePlate => "minecraft:weighted_pressure_plate", + WetSponge => "minecraft:wet_sponge", + WitherRose => "minecraft:wither_rose", + WitherSkull => "minecraft:wither_skull", + WitherWallSkull => "minecraft:wither_wall_skull", + WoolCarpet => "minecraft:wool_carpet", +} +} + +registry! { +enum WorldgenPoolAliasBinding { + Random => "minecraft:random", + RandomGroup => "minecraft:random_group", + Direct => "minecraft:direct", +} +} + +registry! { +enum TriggerKind { + Impossible => "minecraft:impossible", + PlayerKilledEntity => "minecraft:player_killed_entity", + EntityKilledPlayer => "minecraft:entity_killed_player", + EnterBlock => "minecraft:enter_block", + InventoryChanged => "minecraft:inventory_changed", + RecipeUnlocked => "minecraft:recipe_unlocked", + PlayerHurtEntity => "minecraft:player_hurt_entity", + EntityHurtPlayer => "minecraft:entity_hurt_player", + EnchantedItem => "minecraft:enchanted_item", + FilledBucket => "minecraft:filled_bucket", + BrewedPotion => "minecraft:brewed_potion", + ConstructBeacon => "minecraft:construct_beacon", + UsedEnderEye => "minecraft:used_ender_eye", + SummonedEntity => "minecraft:summoned_entity", + BredAnimals => "minecraft:bred_animals", + Location => "minecraft:location", + SleptInBed => "minecraft:slept_in_bed", + CuredZombieVillager => "minecraft:cured_zombie_villager", + VillagerTrade => "minecraft:villager_trade", + ItemDurabilityChanged => "minecraft:item_durability_changed", + Levitation => "minecraft:levitation", + ChangedDimension => "minecraft:changed_dimension", + Tick => "minecraft:tick", + TameAnimal => "minecraft:tame_animal", + PlacedBlock => "minecraft:placed_block", + ConsumeItem => "minecraft:consume_item", + EffectsChanged => "minecraft:effects_changed", + UsedTotem => "minecraft:used_totem", + NetherTravel => "minecraft:nether_travel", + FishingRodHooked => "minecraft:fishing_rod_hooked", + ChanneledLightning => "minecraft:channeled_lightning", + ShotCrossbow => "minecraft:shot_crossbow", + KilledByCrossbow => "minecraft:killed_by_crossbow", + HeroOfTheVillage => "minecraft:hero_of_the_village", + VoluntaryExile => "minecraft:voluntary_exile", + SlideDownBlock => "minecraft:slide_down_block", + BeeNestDestroyed => "minecraft:bee_nest_destroyed", + TargetHit => "minecraft:target_hit", + ItemUsedOnBlock => "minecraft:item_used_on_block", + PlayerGeneratesContainerLoot => "minecraft:player_generates_container_loot", + ThrownItemPickedUpByEntity => "minecraft:thrown_item_picked_up_by_entity", + ThrownItemPickedUpByPlayer => "minecraft:thrown_item_picked_up_by_player", + PlayerInteractedWithEntity => "minecraft:player_interacted_with_entity", + StartedRiding => "minecraft:started_riding", + LightningStrike => "minecraft:lightning_strike", + UsingItem => "minecraft:using_item", + FallFromHeight => "minecraft:fall_from_height", + RideEntityInLava => "minecraft:ride_entity_in_lava", + KillMobNearSculkCatalyst => "minecraft:kill_mob_near_sculk_catalyst", + AllayDropItemOnBlock => "minecraft:allay_drop_item_on_block", + AvoidVibration => "minecraft:avoid_vibration", + RecipeCrafted => "minecraft:recipe_crafted", +} +} + +registry! { +enum NumberFormatKind { + Blank => "minecraft:blank", + Styled => "minecraft:styled", + Fixed => "minecraft:fixed", +} +} diff --git a/azalea-registry/src/tags/blocks.rs b/azalea-registry/src/tags/blocks.rs index f21a80128..0396eb877 100644 --- a/azalea-registry/src/tags/blocks.rs +++ b/azalea-registry/src/tags/blocks.rs @@ -80,7 +80,7 @@ pub static MINEABLE_AXE: Lazy> = Lazy::new(|| { Block::Fern, Block::FletchingTable, Block::GlowLichen, - Block::Grass, + Block::ShortGrass, Block::HangingRoots, Block::JackOLantern, Block::Jukebox, @@ -845,7 +845,7 @@ pub static BAMBOO_PLANTABLE_ON: Lazy> = Lazy::new(|| { }); pub static SWORD_EFFICIENT: Lazy> = Lazy::new(|| { HashSet::from_iter(vec![ - Block::Grass, + Block::ShortGrass, Block::Fern, Block::DeadBush, Block::Vine, @@ -1534,7 +1534,7 @@ pub static DRAGON_TRANSPARENT: Lazy> = Lazy::new(|| HashSet::from_iter(vec![Block::Light, Block::Fire, Block::SoulFire])); pub static REPLACEABLE_BY_TREES: Lazy> = Lazy::new(|| { HashSet::from_iter(vec![ - Block::Grass, + Block::ShortGrass, Block::Fern, Block::DeadBush, Block::Vine, @@ -2042,7 +2042,7 @@ pub static ENCHANTMENT_POWER_TRANSMITTER: Lazy> = Lazy::new(|| { Block::Air, Block::Water, Block::Lava, - Block::Grass, + Block::ShortGrass, Block::Fern, Block::DeadBush, Block::Seagrass, @@ -2497,7 +2497,7 @@ pub static REPLACEABLE: Lazy> = Lazy::new(|| { Block::Air, Block::Water, Block::Lava, - Block::Grass, + Block::ShortGrass, Block::Fern, Block::DeadBush, Block::Seagrass, diff --git a/azalea-world/Cargo.toml b/azalea-world/Cargo.toml index ef2d65176..81a7839d1 100644 --- a/azalea-world/Cargo.toml +++ b/azalea-world/Cargo.toml @@ -4,20 +4,20 @@ edition = "2021" license = "MIT" name = "azalea-world" repository = "https://github.com/azalea-rs/azalea/tree/main/azalea-world" -version = "0.8.0" +version = "0.9.0" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -azalea-block = { path = "../azalea-block", default-features = false, version = "0.8.0" } -azalea-buf = { path = "../azalea-buf", version = "0.8.0" } -azalea-core = { path = "../azalea-core", version = "^0.8.0", features = [ +simdnbt = { version = "0.3", git = "https://github.com/azalea-rs/simdnbt" } +azalea-block = { path = "../azalea-block", default-features = false, version = "0.9.0" } +azalea-buf = { path = "../azalea-buf", version = "0.9.0" } +azalea-core = { path = "../azalea-core", version = "0.9.0", features = [ "bevy_ecs", ] } -azalea-inventory = { version = "0.8.0", path = "../azalea-inventory" } -simdnbt = { version = "0.2.1" } -azalea-registry = { path = "../azalea-registry", version = "0.8.0" } -bevy_ecs = "0.12.0" +azalea-inventory = { version = "0.9.0", path = "../azalea-inventory" } +azalea-registry = { path = "../azalea-registry", version = "0.9.0" } +bevy_ecs = "0.12.1" derive_more = { version = "0.99.17", features = ["deref", "deref_mut"] } enum-as-inner = "0.6.0" tracing = "0.1.40" @@ -25,9 +25,9 @@ nohash-hasher = "0.2.0" once_cell = "1.18.0" parking_lot = "^0.12.1" thiserror = "1.0.50" -uuid = "1.5.0" +uuid = "1.6.1" serde_json = "1.0.108" -serde = "1.0.192" +serde = "1.0.193" [dev-dependencies] azalea-client = { path = "../azalea-client" } diff --git a/azalea-world/src/chunk_storage.rs b/azalea-world/src/chunk_storage.rs index ac81fd09f..8bc0b32cb 100755 --- a/azalea-world/src/chunk_storage.rs +++ b/azalea-world/src/chunk_storage.rs @@ -34,7 +34,7 @@ pub struct PartialChunkStorage { /// A storage for chunks where they're only stored weakly, so if they're not /// actively being used somewhere else they'll be forgotten. This is used for /// shared worlds. -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct ChunkStorage { pub height: u32, pub min_y: i32, @@ -123,18 +123,30 @@ impl PartialChunkStorage { self.view_center } + pub fn view_range(&self) -> u32 { + self.view_range + } + pub fn index_from_chunk_pos(&self, chunk_pos: &ChunkPos) -> usize { - (i32::rem_euclid(chunk_pos.x, self.view_range as i32) * (self.view_range as i32) - + i32::rem_euclid(chunk_pos.z, self.view_range as i32)) as usize + let view_range = self.view_range as i32; + + let x = i32::rem_euclid(chunk_pos.x, view_range) * view_range; + let z = i32::rem_euclid(chunk_pos.z, view_range); + (x + z) as usize } pub fn chunk_pos_from_index(&self, index: usize) -> ChunkPos { - let x = index as i32 % self.view_range as i32; - let z = index as i32 / self.view_range as i32; - ChunkPos::new( - x + self.view_center.x - self.chunk_radius as i32, - z + self.view_center.z - self.chunk_radius as i32, - ) + let view_range = self.view_range as i32; + + // find the base from the view center + let base_x = self.view_center.x.div_euclid(view_range) * view_range; + let base_z = self.view_center.z.div_euclid(view_range) * view_range; + + // add the offset from the base + let offset_x = index as i32 / view_range; + let offset_z = index as i32 % view_range; + + ChunkPos::new(base_x + offset_x, base_z + offset_z) } pub fn in_range(&self, chunk_pos: &ChunkPos) -> bool { @@ -207,12 +219,13 @@ impl PartialChunkStorage { } let index = self.index_from_chunk_pos(pos); + Some(&mut self.chunks[index]) } /// Set a chunk in the shared storage and reference it from the limited - /// storage. Use [`Self::set_with_shared_reference`] if you already have - /// an `Arc>`. + /// storage. Use [`Self::limited_set`] if you already have an + /// `Arc>`. /// /// # Panics /// If the chunk is not in the render distance. @@ -501,7 +514,12 @@ impl Default for ChunkStorage { /// and the minimum y coordinate of the world. #[inline] pub fn section_index(y: i32, min_y: i32) -> u32 { - assert!(y >= min_y, "y ({y}) must be at least {min_y}"); + if y < min_y { + #[cfg(debug_assertions)] + panic!("y ({y}) must be at most {min_y}"); + #[cfg(not(debug_assertions))] + tracing::error!("y ({y}) must be at least {min_y}") + }; let min_section_index = min_y >> 4; ((y >> 4) - min_section_index) as u32 } @@ -547,4 +565,16 @@ mod tests { .get_block_state(&BlockPos { x: 0, y: -65, z: 0 }) .is_none()); } + + #[test] + fn test_chunk_pos_from_index() { + let mut partial_chunk_storage = PartialChunkStorage::new(5); + partial_chunk_storage.update_view_center(ChunkPos::new(0, -1)); + assert_eq!( + partial_chunk_storage.chunk_pos_from_index( + partial_chunk_storage.index_from_chunk_pos(&ChunkPos::new(2, -1)) + ), + ChunkPos::new(2, -1), + ); + } } diff --git a/azalea-world/src/find_blocks.rs b/azalea-world/src/find_blocks.rs new file mode 100644 index 000000000..2d2c6d7ac --- /dev/null +++ b/azalea-world/src/find_blocks.rs @@ -0,0 +1,306 @@ +use azalea_block::{BlockState, BlockStates}; +use azalea_core::position::{BlockPos, ChunkPos}; + +use crate::{iterators::ChunkIterator, palette::Palette, ChunkStorage, Instance}; + +fn palette_maybe_has_block(palette: &Palette, block_states: &BlockStates) -> bool { + match &palette { + Palette::SingleValue(id) => block_states.contains(&BlockState { id: *id }), + Palette::Linear(ids) => ids + .iter() + .any(|&id| block_states.contains(&BlockState { id })), + Palette::Hashmap(ids) => ids + .iter() + .any(|&id| block_states.contains(&BlockState { id })), + Palette::Global => true, + } +} + +impl Instance { + /// Find the coordinates of a block in the world. + /// + /// Note that this is sorted by `x+y+z` and not `x^2+y^2+z^2` for + /// optimization purposes. + /// + /// ``` + /// # fn example(client: &azalea_client::Client) { + /// client.world().read().find_block(client.position(), &azalea_registry::Block::Chest.into()); + /// # } + /// ``` + pub fn find_block( + &self, + nearest_to: impl Into, + block_states: &BlockStates, + ) -> Option { + // iterate over every chunk in a 3d spiral pattern + // and then check the palette for the block state + + let nearest_to: BlockPos = nearest_to.into(); + let start_chunk: ChunkPos = (&nearest_to).into(); + let mut iter = ChunkIterator::new(start_chunk, 32); + + let mut nearest_found_pos: Option = None; + let mut nearest_found_distance = 0; + + // we do `while` instead of `for` so we can access iter later + while let Some(chunk_pos) = iter.next() { + let Some(chunk) = self.chunks.get(&chunk_pos) else { + // if the chunk isn't loaded then we skip it. + // we don't just return since it *could* cause issues if there's a random + // unloaded chunk and then more that are loaded. + // unlikely but still something to consider, and it's not like this slows it + // down much anyways. + continue; + }; + + for (section_index, section) in chunk.read().sections.iter().enumerate() { + let maybe_has_block = + palette_maybe_has_block(§ion.states.palette, block_states); + if !maybe_has_block { + continue; + } + + for i in 0..4096 { + let block_state = section.states.get_at_index(i); + let block_state = BlockState { id: block_state }; + + if block_states.contains(&block_state) { + let (section_x, section_y, section_z) = section.states.coords_from_index(i); + let (x, y, z) = ( + chunk_pos.x * 16 + (section_x as i32), + self.chunks.min_y + (section_index * 16) as i32 + section_y as i32, + chunk_pos.z * 16 + (section_z as i32), + ); + let this_block_pos = BlockPos { x, y, z }; + let this_block_distance = (nearest_to - this_block_pos).length_manhattan(); + // only update if it's closer + if nearest_found_pos.is_none() + || this_block_distance < nearest_found_distance + { + nearest_found_pos = Some(this_block_pos); + nearest_found_distance = this_block_distance; + } + } + } + } + + if let Some(nearest_found_pos) = nearest_found_pos { + // this is required because find_block searches chunk-by-chunk, which can cause + // us to find blocks first that aren't actually the closest + let required_chunk_distance = u32::max( + u32::max( + (chunk_pos.x - start_chunk.x).unsigned_abs(), + (chunk_pos.z - start_chunk.z).unsigned_abs(), + ), + (nearest_to.y - nearest_found_pos.y) + .unsigned_abs() + .div_ceil(16), + ) + 1; + let nearest_chunk_distance = iter.layer; + + // if we found the position and there's no chance there's something closer, + // return it + if nearest_chunk_distance > required_chunk_distance { + return Some(nearest_found_pos); + } + } + } + + if nearest_found_pos.is_some() { + nearest_found_pos + } else { + None + } + } + + /// Find all the coordinates of a block in the world. + /// + /// This returns an iterator that yields the [`BlockPos`]s of blocks that + /// are in the given block states. It's sorted by `x+y+z`. + pub fn find_blocks<'a>( + &'a self, + nearest_to: impl Into, + block_states: &'a BlockStates, + ) -> FindBlocks<'a> { + FindBlocks::new(nearest_to.into(), &self.chunks, block_states) + } +} + +pub struct FindBlocks<'a> { + nearest_to: BlockPos, + start_chunk: ChunkPos, + chunk_iterator: ChunkIterator, + chunks: &'a ChunkStorage, + block_states: &'a BlockStates, + + queued: Vec, +} + +impl<'a> FindBlocks<'a> { + pub fn new( + nearest_to: BlockPos, + chunks: &'a ChunkStorage, + block_states: &'a BlockStates, + ) -> Self { + let start_chunk: ChunkPos = (&nearest_to).into(); + Self { + nearest_to, + start_chunk, + chunk_iterator: ChunkIterator::new(start_chunk, 32), + chunks, + block_states, + + queued: Vec::new(), + } + } +} + +impl<'a> Iterator for FindBlocks<'a> { + type Item = BlockPos; + + fn next(&mut self) -> Option { + if let Some(queued) = self.queued.pop() { + return Some(queued); + } + + let mut found = Vec::new(); + + let mut nearest_found_pos: Option = None; + let mut nearest_found_distance = 0; + + while let Some(chunk_pos) = self.chunk_iterator.next() { + let Some(chunk) = self.chunks.get(&chunk_pos) else { + // if the chunk isn't loaded then we skip it. + // we don't just return since it *could* cause issues if there's a random + // unloaded chunk and then more that are loaded. + // unlikely but still something to consider, and it's not like this slows it + // down much anyways. + continue; + }; + + for (section_index, section) in chunk.read().sections.iter().enumerate() { + let maybe_has_block = + palette_maybe_has_block(§ion.states.palette, self.block_states); + if !maybe_has_block { + continue; + } + + for i in 0..4096 { + let block_state = section.states.get_at_index(i); + let block_state = BlockState { id: block_state }; + + if self.block_states.contains(&block_state) { + let (section_x, section_y, section_z) = section.states.coords_from_index(i); + let (x, y, z) = ( + chunk_pos.x * 16 + (section_x as i32), + self.chunks.min_y + (section_index * 16) as i32 + section_y as i32, + chunk_pos.z * 16 + (section_z as i32), + ); + let this_block_pos = BlockPos { x, y, z }; + let this_block_distance = + (self.nearest_to - this_block_pos).length_manhattan(); + + found.push((this_block_pos, this_block_distance)); + + if nearest_found_pos.is_none() + || this_block_distance < nearest_found_distance + { + nearest_found_pos = Some(this_block_pos); + nearest_found_distance = this_block_distance; + } + } + } + } + + if let Some(nearest_found_pos) = nearest_found_pos { + // this is required because find_block searches chunk-by-chunk, which can cause + // us to find blocks first that aren't actually the closest + let required_chunk_distance = u32::max( + u32::max( + (chunk_pos.x - self.start_chunk.x).unsigned_abs(), + (chunk_pos.z - self.start_chunk.z).unsigned_abs(), + ), + (self.nearest_to.y - nearest_found_pos.y) + .unsigned_abs() + .div_ceil(16), + ) + 1; + let nearest_chunk_distance = self.chunk_iterator.layer; + + // if we found the position and there's no chance there's something closer, + // return it + if nearest_chunk_distance > required_chunk_distance { + // sort so nearest is at the end + found.sort_unstable_by_key(|(_, distance)| u32::MAX - distance); + + self.queued = found.into_iter().map(|(pos, _)| pos).collect(); + return self.queued.pop(); + } + } + } + + None + } +} + +#[cfg(test)] +mod tests { + use azalea_registry::Block; + + use crate::{Chunk, PartialChunkStorage}; + + use super::*; + + #[test] + fn find_block() { + let mut instance = Instance::default(); + + let chunk_storage = &mut instance.chunks; + let mut partial_chunk_storage = PartialChunkStorage::default(); + + // block at (17, 0, 0) and (0, 18, 0) + + partial_chunk_storage.set( + &ChunkPos { x: 0, z: 0 }, + Some(Chunk::default()), + chunk_storage, + ); + partial_chunk_storage.set( + &ChunkPos { x: 1, z: 0 }, + Some(Chunk::default()), + chunk_storage, + ); + + chunk_storage.set_block_state(&BlockPos { x: 17, y: 0, z: 0 }, Block::Stone.into()); + chunk_storage.set_block_state(&BlockPos { x: 0, y: 18, z: 0 }, Block::Stone.into()); + + let pos = instance.find_block(BlockPos { x: 0, y: 0, z: 0 }, &Block::Stone.into()); + assert_eq!(pos, Some(BlockPos { x: 17, y: 0, z: 0 })); + } + + #[test] + fn find_block_next_to_chunk_border() { + let mut instance = Instance::default(); + + let chunk_storage = &mut instance.chunks; + let mut partial_chunk_storage = PartialChunkStorage::default(); + + // block at (-1, 0, 0) and (15, 0, 0) + + partial_chunk_storage.set( + &ChunkPos { x: -1, z: 0 }, + Some(Chunk::default()), + chunk_storage, + ); + partial_chunk_storage.set( + &ChunkPos { x: 0, z: 0 }, + Some(Chunk::default()), + chunk_storage, + ); + + chunk_storage.set_block_state(&BlockPos { x: -1, y: 0, z: 0 }, Block::Stone.into()); + chunk_storage.set_block_state(&BlockPos { x: 15, y: 0, z: 0 }, Block::Stone.into()); + + let pos = instance.find_block(BlockPos { x: 0, y: 0, z: 0 }, &Block::Stone.into()); + assert_eq!(pos, Some(BlockPos { x: -1, y: 0, z: 0 })); + } +} diff --git a/azalea-world/src/heightmap.rs b/azalea-world/src/heightmap.rs index 12aaa1590..c38f3edcc 100644 --- a/azalea-world/src/heightmap.rs +++ b/azalea-world/src/heightmap.rs @@ -32,7 +32,10 @@ fn blocks_motion(block_state: BlockState) -> bool { fn motion_blocking(block_state: BlockState) -> bool { // TODO - !block_state.is_air() || block_state.waterlogged() + !block_state.is_air() + || block_state + .property::() + .unwrap_or_default() } impl HeightmapKind { diff --git a/azalea-world/src/lib.rs b/azalea-world/src/lib.rs index 6677b326e..8514ce242 100644 --- a/azalea-world/src/lib.rs +++ b/azalea-world/src/lib.rs @@ -4,6 +4,7 @@ mod bit_storage; pub mod chunk_storage; mod container; +pub mod find_blocks; pub mod heightmap; pub mod iterators; pub mod palette; diff --git a/azalea-world/src/world.rs b/azalea-world/src/world.rs index 7b6854f72..84a5857ce 100644 --- a/azalea-world/src/world.rs +++ b/azalea-world/src/world.rs @@ -1,5 +1,5 @@ -use crate::{iterators::ChunkIterator, palette::Palette, ChunkStorage, PartialChunkStorage}; -use azalea_block::{BlockState, BlockStates, FluidState}; +use crate::{ChunkStorage, PartialChunkStorage}; +use azalea_block::{BlockState, FluidState}; use azalea_core::position::{BlockPos, ChunkPos}; use azalea_core::registry_holder::RegistryHolder; use bevy_ecs::{component::Component, entity::Entity}; @@ -104,110 +104,6 @@ impl Instance { pub fn set_block_state(&self, pos: &BlockPos, state: BlockState) -> Option { self.chunks.set_block_state(pos, state) } - - /// Find the coordinates of a block in the world. - /// - /// Note that this is sorted by `x+y+z` and not `x^2+y^2+z^2` for - /// optimization purposes. - /// - /// ``` - /// # fn example(client: &azalea_client::Client) { - /// client.world().read().find_block(client.position(), &azalea_registry::Block::Chest.into()); - /// # } - /// ``` - pub fn find_block( - &self, - nearest_to: impl Into, - block_states: &BlockStates, - ) -> Option { - // iterate over every chunk in a 3d spiral pattern - // and then check the palette for the block state - - let nearest_to: BlockPos = nearest_to.into(); - let start_chunk: ChunkPos = (&nearest_to).into(); - let mut iter = ChunkIterator::new(start_chunk, 32); - - let mut nearest_found_pos: Option = None; - let mut nearest_found_distance = 0; - - // we do `while` instead of `for` so we can access iter later - while let Some(chunk_pos) = iter.next() { - let Some(chunk) = self.chunks.get(&chunk_pos) else { - // if the chunk isn't loaded then we skip it. - // we don't just return since it *could* cause issues if there's a random - // unloaded chunk and then more that are loaded. - // unlikely but still something to consider, and it's not like this slows it - // down much anyways. - continue; - }; - - for (section_index, section) in chunk.read().sections.iter().enumerate() { - let maybe_has_block = match §ion.states.palette { - Palette::SingleValue(id) => block_states.contains(&BlockState { id: *id }), - Palette::Linear(ids) => ids - .iter() - .any(|&id| block_states.contains(&BlockState { id })), - Palette::Hashmap(ids) => ids - .iter() - .any(|&id| block_states.contains(&BlockState { id })), - Palette::Global => true, - }; - if !maybe_has_block { - continue; - } - - for i in 0..4096 { - let block_state = section.states.get_at_index(i); - let block_state = BlockState { id: block_state }; - - if block_states.contains(&block_state) { - let (section_x, section_y, section_z) = section.states.coords_from_index(i); - let (x, y, z) = ( - chunk_pos.x * 16 + (section_x as i32), - self.chunks.min_y + (section_index * 16) as i32 + section_y as i32, - chunk_pos.z * 16 + (section_z as i32), - ); - let this_block_pos = BlockPos { x, y, z }; - let this_block_distance = (nearest_to - this_block_pos).length_manhattan(); - // only update if it's closer - if nearest_found_pos.is_none() - || this_block_distance < nearest_found_distance - { - nearest_found_pos = Some(this_block_pos); - nearest_found_distance = this_block_distance; - } - } - } - } - - if let Some(nearest_found_pos) = nearest_found_pos { - // this is required because find_block searches chunk-by-chunk, which can cause - // us to find blocks first that aren't actually the closest - let required_chunk_distance = u32::max( - u32::max( - (chunk_pos.x - start_chunk.x).unsigned_abs(), - (chunk_pos.z - start_chunk.z).unsigned_abs(), - ), - (nearest_to.y - nearest_found_pos.y) - .unsigned_abs() - .div_ceil(16), - ); - let nearest_chunk_distance = iter.layer; - - // if we found the position and there's no chance there's something closer, - // return it - if nearest_chunk_distance >= required_chunk_distance { - return Some(nearest_found_pos); - } - } - } - - if nearest_found_pos.is_some() { - nearest_found_pos - } else { - None - } - } } impl Debug for PartialInstance { @@ -244,39 +140,3 @@ impl From for Instance { } } } - -#[cfg(test)] -mod tests { - use azalea_registry::Block; - - use crate::Chunk; - - use super::*; - - #[test] - fn find_block() { - let mut instance = Instance::default(); - - let chunk_storage = &mut instance.chunks; - let mut partial_chunk_storage = PartialChunkStorage::default(); - - // block at (17, 0, 0) and (0, 18, 0) - - partial_chunk_storage.set( - &ChunkPos { x: 0, z: 0 }, - Some(Chunk::default()), - chunk_storage, - ); - partial_chunk_storage.set( - &ChunkPos { x: 1, z: 0 }, - Some(Chunk::default()), - chunk_storage, - ); - - chunk_storage.set_block_state(&BlockPos { x: 17, y: 0, z: 0 }, Block::Stone.into()); - chunk_storage.set_block_state(&BlockPos { x: 0, y: 18, z: 0 }, Block::Stone.into()); - - let pos = instance.find_block(BlockPos { x: 0, y: 0, z: 0 }, &Block::Stone.into()); - assert_eq!(pos, Some(BlockPos { x: 17, y: 0, z: 0 })); - } -} diff --git a/azalea/Cargo.toml b/azalea/Cargo.toml index dad1043be..2e8ed752a 100644 --- a/azalea/Cargo.toml +++ b/azalea/Cargo.toml @@ -4,7 +4,7 @@ edition = "2021" license = "MIT" name = "azalea" repository = "https://github.com/azalea-rs/azalea/tree/main/azalea" -version = "0.8.0" +version = "0.9.0" [package.metadata.release] pre-release-replacements = [ @@ -14,23 +14,24 @@ pre-release-replacements = [ [dependencies] anyhow = "^1.0.75" async-trait = "0.1.74" -azalea-block = { version = "0.8.0", path = "../azalea-block" } -azalea-chat = { version = "0.8.0", path = "../azalea-chat" } -azalea-client = { version = "0.8.0", path = "../azalea-client", default-features = false } -azalea-core = { version = "0.8.0", path = "../azalea-core" } -azalea-inventory = { version = "0.8.0", path = "../azalea-inventory" } -azalea-physics = { version = "0.8.0", path = "../azalea-physics" } -azalea-protocol = { version = "0.8.0", path = "../azalea-protocol" } -azalea-registry = { version = "0.8.0", path = "../azalea-registry" } -azalea-world = { version = "0.8.0", path = "../azalea-world" } -azalea-auth = { version = "0.8.0", path = "../azalea-auth" } -azalea-brigadier = { version = "0.8.0", path = "../azalea-brigadier" } -bevy_app = "0.12.0" -bevy_ecs = "0.12.0" -bevy_tasks = { version = "0.12.0", features = ["multi-threaded"] } +azalea-block = { version = "0.9.0", path = "../azalea-block" } +azalea-chat = { version = "0.9.0", path = "../azalea-chat" } +azalea-client = { version = "0.9.0", path = "../azalea-client", default-features = false } +azalea-core = { version = "0.9.0", path = "../azalea-core" } +azalea-inventory = { version = "0.9.0", path = "../azalea-inventory" } +azalea-physics = { version = "0.9.0", path = "../azalea-physics" } +azalea-protocol = { version = "0.9.0", path = "../azalea-protocol" } +azalea-registry = { version = "0.9.0", path = "../azalea-registry" } +azalea-world = { version = "0.9.0", path = "../azalea-world" } +azalea-auth = { version = "0.9.0", path = "../azalea-auth" } +azalea-brigadier = { version = "0.9.0", path = "../azalea-brigadier" } +azalea-buf = { version = "0.9.0", path = "../azalea-buf" } +bevy_app = "0.12.1" +bevy_ecs = "0.12.1" +bevy_tasks = { version = "0.12.1", features = ["multi-threaded"] } derive_more = { version = "0.99.17", features = ["deref", "deref_mut"] } futures = "0.3.29" -futures-lite = "2.0.1" +futures-lite = "2.1.0" tracing = "0.1.40" nohash-hasher = "0.2.0" num-traits = "0.2.17" @@ -38,15 +39,15 @@ parking_lot = { version = "^0.12.1", features = ["deadlock_detection"] } priority-queue = "1.3.2" thiserror = "^1.0.50" tokio = "^1.34.0" -uuid = "1.5.0" -bevy_log = "0.12.0" -azalea-entity = { version = "0.8.0", path = "../azalea-entity" } -bevy_time = "0.12.0" +uuid = "1.6.1" +bevy_log = "0.12.1" +azalea-entity = { version = "0.9.0", path = "../azalea-entity" } +bevy_time = "0.12.1" rustc-hash = "1.1.0" +rand = "0.8.5" [dev-dependencies] criterion = "0.5.1" -rand = "0.8.5" [features] default = ["log"] diff --git a/azalea/README.md b/azalea/README.md index 1ec82cd3b..339c57a2e 100755 --- a/azalea/README.md +++ b/azalea/README.md @@ -1,33 +1,34 @@ Azalea is a framework for creating Minecraft bots. This page is primarily meant for developers that already know they want to use Azalea. -See the [readme](https://github.com/azalea-rs/azalea) for an overview of why you might want to use it. +See the [readme](https://github.com/azalea-rs/azalea) for a higher-level overview of Azalea. # Installation -First, install Rust nightly with `rustup install nightly` and `rustup -default nightly`. +First, install Rust nightly with `rustup install nightly` and `rustup default nightly`. -Then, add one of the following lines to your Cargo.toml: +Then, use one of the following commands to add Azalea to your project: -- Latest bleeding-edge version (recommended): `azalea = { git="https://github.com/azalea-rs/azalea" }`\ -- Latest "stable" release: `azalea = "0.8.0"` +- Latest bleeding-edge version (recommended): `cargo add azalea --git=https://github.com/azalea-rs/azalea`\ +- Latest "stable" release: `cargo add azalea` ## Optimization For faster compile times, make a `.cargo/config.toml` file in your project and copy -[this file](https://github.com/azalea-rs/azalea/blob/main/.cargo/config.toml) +[this file](https://github.com/azalea-rs/azalea/blob/main/.cargo/config_fast_builds) into it. You may have to install the LLD linker. For faster performance in debug mode, add the following code to your Cargo.toml: + ```toml [profile.dev] opt-level = 1 [profile.dev.package."*"] opt-level = 3 ``` + # Documentation The documentation for the latest Azalea crates.io release is available at [docs.rs/azalea](https://docs.rs/azalea/latest/azalea/) and the docs for the latest bleeding-edge (git) version are at [azalea.matdoes.dev](https://azalea.matdoes.dev/azalea/). @@ -36,7 +37,6 @@ Note that the `azalea` crate is technically just a wrapper over [`azalea_client` Because of this, some of the documentation will refer to `azalea_client`. You can just replace these with `azalea` in your code since everything from `azalea_client` is re-exported in azalea. - # Examples ```rust,no_run @@ -51,13 +51,11 @@ async fn main() { let account = Account::offline("bot"); // or Account::microsoft("example@example.com").await.unwrap(); - loop { - let e = ClientBuilder::new() - .set_handler(handle) - .start(account.clone(), "localhost") - .await; - eprintln!("{e:?}"); - } + ClientBuilder::new() + .set_handler(handle) + .start(account.clone(), "localhost") + .await + .unwrap(); } #[derive(Default, Clone, Component)] diff --git a/azalea/benches/pathfinder.rs b/azalea/benches/pathfinder.rs index 406b726db..842c6b5e2 100644 --- a/azalea/benches/pathfinder.rs +++ b/azalea/benches/pathfinder.rs @@ -3,19 +3,18 @@ use std::{hint::black_box, sync::Arc, time::Duration}; use azalea::{ pathfinder::{ astar::{self, a_star}, - goals::BlockPosGoal, + goals::{BlockPosGoal, Goal}, mining::MiningCache, world::CachedWorld, - Goal, }, BlockPos, }; use azalea_core::position::{ChunkBlockPos, ChunkPos}; use azalea_inventory::Menu; use azalea_world::{Chunk, ChunkStorage, PartialChunkStorage}; -use criterion::{criterion_group, criterion_main, Criterion}; +use criterion::{criterion_group, criterion_main, Bencher, Criterion}; use parking_lot::RwLock; -use rand::Rng; +use rand::{rngs::StdRng, Rng, SeedableRng}; fn generate_bedrock_world( partial_chunks: &mut PartialChunkStorage, @@ -31,7 +30,7 @@ fn generate_bedrock_world( } } - let mut rng = rand::thread_rng(); + let mut rng = StdRng::seed_from_u64(0); for chunk_x in -size..size { for chunk_z in -size..size { @@ -58,14 +57,14 @@ fn generate_bedrock_world( } let mut start = BlockPos::new(-64, 4, -64); - // move start down until it's on bedrock + // move start down until it's on a solid block while chunks.get_block_state(&start).unwrap().is_air() { start = start.down(1); } start = start.up(1); let mut end = BlockPos::new(63, 4, 63); - // move end down until it's on bedrock + // move end down until it's on a solid block while chunks.get_block_state(&end).unwrap().is_air() { end = end.down(1); } @@ -74,37 +73,90 @@ fn generate_bedrock_world( (chunks, start, end) } +fn generate_mining_world( + partial_chunks: &mut PartialChunkStorage, + size: u32, +) -> (ChunkStorage, BlockPos, BlockPos) { + let size = size as i32; + + let mut chunks = ChunkStorage::default(); + for chunk_x in -size..size { + for chunk_z in -size..size { + let chunk_pos = ChunkPos::new(chunk_x, chunk_z); + partial_chunks.set(&chunk_pos, Some(Chunk::default()), &mut chunks); + } + } + + let mut rng = StdRng::seed_from_u64(0); + + for chunk_x in -size..size { + for chunk_z in -size..size { + let chunk_pos = ChunkPos::new(chunk_x, chunk_z); + let chunk = chunks.get(&chunk_pos).unwrap(); + let mut chunk = chunk.write(); + for y in chunks.min_y..(chunks.min_y + chunks.height as i32) { + for x in 0..16_u8 { + for z in 0..16_u8 { + chunk.set( + &ChunkBlockPos::new(x, y, z), + azalea_registry::Block::Stone.into(), + chunks.min_y, + ); + } + } + } + } + } + + let start = BlockPos::new(-64, 4, -64); + let end = BlockPos::new(0, 4, 0); + + (chunks, start, end) +} + +fn run_pathfinder_benchmark( + b: &mut Bencher<'_>, + generate_world: fn(&mut PartialChunkStorage, u32) -> (ChunkStorage, BlockPos, BlockPos), +) { + let mut partial_chunks = PartialChunkStorage::new(32); + let successors_fn = azalea::pathfinder::moves::default_move; + + let (world, start, end) = generate_world(&mut partial_chunks, 4); + + b.iter(|| { + let cached_world = CachedWorld::new(Arc::new(RwLock::new(world.clone().into()))); + let mining_cache = + MiningCache::new(Some(Menu::Player(azalea_inventory::Player::default()))); + let goal = BlockPosGoal(end); + + let successors = |pos: BlockPos| { + azalea::pathfinder::call_successors_fn(&cached_world, &mining_cache, successors_fn, pos) + }; + + let astar::Path { movements, partial } = a_star( + start, + |n| goal.heuristic(n), + successors, + |n| goal.success(n), + Duration::MAX, + ); + + assert!(!partial); + + black_box((movements, partial)); + }) +} + fn bench_pathfinder(c: &mut Criterion) { - c.bench_function("bedrock", |b| { - let mut partial_chunks = PartialChunkStorage::new(32); - let successors_fn = azalea::pathfinder::moves::default_move; - - b.iter(|| { - let (world, start, end) = generate_bedrock_world(&mut partial_chunks, 4); - let cached_world = CachedWorld::new(Arc::new(RwLock::new(world.into()))); - let mining_cache = MiningCache::new(Menu::Player(azalea_inventory::Player::default())); - let goal = BlockPosGoal(end); - - let successors = |pos: BlockPos| { - azalea::pathfinder::call_successors_fn( - &cached_world, - &mining_cache, - successors_fn, - pos, - ) - }; - - let astar::Path { movements, partial } = a_star( - start, - |n| goal.heuristic(n), - successors, - |n| goal.success(n), - Duration::MAX, - ); - - black_box((movements, partial)); - }) + // c.bench_function("bedrock", |b| { + // run_pathfinder_benchmark(b, generate_bedrock_world); + // }); + let mut slow_group = c.benchmark_group("slow"); + slow_group.sample_size(10); + slow_group.bench_function("mining", |b| { + run_pathfinder_benchmark(b, generate_mining_world); }); + slow_group.finish(); } criterion_group!(benches, bench_pathfinder); diff --git a/azalea/examples/nearest_entity.rs b/azalea/examples/nearest_entity.rs index 2105d29e4..d50e6c467 100644 --- a/azalea/examples/nearest_entity.rs +++ b/azalea/examples/nearest_entity.rs @@ -2,9 +2,10 @@ use azalea::nearest_entity::EntityFinder; use azalea::ClientBuilder; use azalea::{Bot, LookAtEvent}; use azalea_client::Account; +use azalea_core::tick::GameTick; use azalea_entity::metadata::{ItemItem, Player}; use azalea_entity::{EyeHeight, LocalEntity, Position}; -use bevy_app::{FixedUpdate, Plugin}; +use bevy_app::Plugin; use bevy_ecs::{ prelude::{Entity, EventWriter}, query::With, @@ -25,7 +26,7 @@ async fn main() { pub struct LookAtStuffPlugin; impl Plugin for LookAtStuffPlugin { fn build(&self, app: &mut bevy_app::App) { - app.add_systems(FixedUpdate, (look_at_everything, log_nearby_item_drops)); + app.add_systems(GameTick, (look_at_everything, log_nearby_item_drops)); } } diff --git a/azalea/examples/testbot.rs b/azalea/examples/testbot.rs index b26641117..7e7b2ca08 100644 --- a/azalea/examples/testbot.rs +++ b/azalea/examples/testbot.rs @@ -21,7 +21,7 @@ struct State {} struct SwarmState {} #[tokio::main] -async fn main() -> anyhow::Result<()> { +async fn main() { { use parking_lot::deadlock; use std::thread; @@ -51,20 +51,14 @@ async fn main() -> anyhow::Result<()> { accounts.push(Account::offline(&format!("bot{i}"))); } - loop { - let e = SwarmBuilder::new() - .add_accounts(accounts.clone()) - .set_handler(handle) - .set_swarm_handler(swarm_handle) - .join_delay(Duration::from_millis(100)) - .start("localhost") - .await; - // let e = azalea::ClientBuilder::new() - // .set_handler(handle) - // .start(Account::offline("bot"), "localhost") - // .await; - eprintln!("{e:?}"); - } + SwarmBuilder::new() + .add_accounts(accounts.clone()) + .set_handler(handle) + .set_swarm_handler(swarm_handle) + .join_delay(Duration::from_millis(100)) + .start("localhost") + .await + .unwrap(); } async fn handle(mut bot: Client, event: Event, _state: State) -> anyhow::Result<()> { @@ -307,44 +301,70 @@ async fn handle(mut bot: Client, event: Event, _state: State) -> anyhow::Result< bot.chat("no chunk found"); } } + "debugblock" => { + // send the block that we're standing on + let block_pos = BlockPos::from(bot.position().down(0.1)); + let block = bot.world().read().get_block_state(&block_pos); + bot.chat(&format!("block: {block:?}")); + } "debugchunks" => { - println!("shared:"); + { + println!("shared:"); - let partial_instance_lock = bot.component::().partial_instance; - let local_chunk_storage = &partial_instance_lock.read().chunks; + let mut ecs = bot.ecs.lock(); - let mut total_loaded_chunks_count = 0; - for (chunk_pos, chunk) in &bot.world().read().chunks.map { - if let Some(chunk) = chunk.upgrade() { - let in_range = local_chunk_storage.in_range(chunk_pos); - println!( - "{chunk_pos:?} has {} references{}", - std::sync::Arc::strong_count(&chunk) - 1, - if in_range { "" } else { " (out of range)" } - ); - total_loaded_chunks_count += 1; + let instance_holder = bot.query::<&InstanceHolder>(&mut ecs).clone(); + drop(ecs); + let local_chunk_storage = &instance_holder.partial_instance.read().chunks; + let shared_chunk_storage = instance_holder.instance.read(); + + let mut total_loaded_chunks_count = 0; + for (chunk_pos, chunk) in &shared_chunk_storage.chunks.map { + if let Some(chunk) = chunk.upgrade() { + let in_range = local_chunk_storage.in_range(chunk_pos); + println!( + "{chunk_pos:?} has {} references{}", + std::sync::Arc::strong_count(&chunk) - 1, + if in_range { "" } else { " (out of range)" } + ); + total_loaded_chunks_count += 1; + } } - } - println!("local:"); + println!("local:"); + println!("view range: {}", local_chunk_storage.view_range()); + println!("view center: {:?}", local_chunk_storage.view_center()); - let mut local_loaded_chunks_count = 0; - for (i, chunk) in local_chunk_storage.chunks().enumerate() { - if let Some(chunk) = chunk { - let chunk_pos = local_chunk_storage.chunk_pos_from_index(i); - println!( - "{chunk_pos:?} has {} references", - std::sync::Arc::strong_count(&chunk) - ); - local_loaded_chunks_count += 1; + let mut local_loaded_chunks_count = 0; + for (i, chunk) in local_chunk_storage.chunks().enumerate() { + if let Some(chunk) = chunk { + let chunk_pos = local_chunk_storage.chunk_pos_from_index(i); + println!( + "{chunk_pos:?} (#{i}) has {} references", + std::sync::Arc::strong_count(&chunk) + ); + local_loaded_chunks_count += 1; + } } + + println!("total loaded chunks: {total_loaded_chunks_count}"); + println!( + "local loaded chunks: {local_loaded_chunks_count}/{}", + local_chunk_storage.chunks().collect::>().len() + ); } + { + let local_chunk_storage_lock = bot.partial_world(); + let local_chunk_storage = local_chunk_storage_lock.read(); + let current_chunk_loaded = local_chunk_storage + .chunks + .limited_get(&ChunkPos::from(bot.position())); - println!("total loaded chunks: {total_loaded_chunks_count}"); - println!( - "local loaded chunks: {local_loaded_chunks_count}/{}", - local_chunk_storage.chunks().collect::>().len() - ); + bot.chat(&format!( + "current chunk loaded: {}", + current_chunk_loaded.is_some() + )); + } } _ => {} } @@ -354,6 +374,13 @@ async fn handle(mut bot: Client, event: Event, _state: State) -> anyhow::Result< println!("login packet"); } } + Event::Disconnect(reason) => { + if let Some(reason) = reason { + println!("bot got kicked for reason: {}", reason.to_ansi()); + } else { + println!("bot got kicked"); + } + } _ => {} } @@ -369,9 +396,7 @@ async fn swarm_handle( SwarmEvent::Disconnect(account) => { println!("bot got kicked! {}", account.username); tokio::time::sleep(Duration::from_secs(5)).await; - swarm - .add_with_exponential_backoff(account, None, State::default()) - .await; + swarm.add_and_retry_forever(account, State::default()).await; } SwarmEvent::Chat(m) => { println!("swarm chat message: {}", m.message().to_ansi()); diff --git a/azalea/examples/todo/mine_a_chunk.rs b/azalea/examples/todo/mine_a_chunk.rs index 74ffacac2..0c439f265 100644 --- a/azalea/examples/todo/mine_a_chunk.rs +++ b/azalea/examples/todo/mine_a_chunk.rs @@ -10,12 +10,13 @@ async fn main() { states.push(State::default()); } - let e = SwarmBuilder::new() + SwarmBuilder::new() .add_accounts(accounts.clone()) .set_handler(handle) .set_swarm_handler(swarm_handle) .start("localhost") - .await; + .await + .unwrap(); } #[derive(Default, Clone, Component)] diff --git a/azalea/src/accept_resource_packs.rs b/azalea/src/accept_resource_packs.rs index 6fdb40dbd..02953d9e8 100644 --- a/azalea/src/accept_resource_packs.rs +++ b/azalea/src/accept_resource_packs.rs @@ -1,9 +1,9 @@ use crate::app::{App, Plugin}; use azalea_client::chunks::handle_chunk_batch_finished_event; use azalea_client::inventory::InventorySet; +use azalea_client::packet_handling::game::SendPacketEvent; use azalea_client::packet_handling::{death_event_on_0_health, game::ResourcePackEvent}; use azalea_client::respawn::perform_respawn; -use azalea_client::SendPacketEvent; use azalea_protocol::packets::game::serverbound_resource_pack_packet::{ self, ServerboundResourcePackPacket, }; @@ -34,6 +34,7 @@ fn accept_resource_pack( send_packet_events.send(SendPacketEvent { entity: event.entity, packet: ServerboundResourcePackPacket { + id: event.id, action: serverbound_resource_pack_packet::Action::Accepted, } .get(), @@ -41,6 +42,7 @@ fn accept_resource_pack( send_packet_events.send(SendPacketEvent { entity: event.entity, packet: ServerboundResourcePackPacket { + id: event.id, action: serverbound_resource_pack_packet::Action::SuccessfullyLoaded, } .get(), diff --git a/azalea/src/auto_tool.rs b/azalea/src/auto_tool.rs index 55ec69248..bc9bb4747 100644 --- a/azalea/src/auto_tool.rs +++ b/azalea/src/auto_tool.rs @@ -4,6 +4,7 @@ use azalea_entity::{FluidOnEyes, Physics}; use azalea_inventory::{ItemSlot, Menu}; use azalea_registry::Fluid; +#[derive(Debug)] pub struct BestToolResult { pub index: usize, pub percentage_per_tick: f32, @@ -43,6 +44,8 @@ pub fn best_tool_in_hotbar_for_block(block: BlockState, menu: &Menu) -> BestTool dimensions: Default::default(), bounding_box: Default::default(), has_impulse: Default::default(), + horizontal_collision: Default::default(), + vertical_collision: Default::default(), }, &FluidOnEyes::new(Fluid::Empty), ) @@ -60,7 +63,57 @@ pub fn accurate_best_tool_in_hotbar_for_block( let mut best_slot = None; let block = Box::::from(block); + let registry_block = block.as_registry_block(); + if matches!( + registry_block, + azalea_registry::Block::Water | azalea_registry::Block::Lava + ) { + // can't mine fluids + return BestToolResult { + index: 0, + percentage_per_tick: 0., + }; + } + + // find the first slot that has an item without durability + for (i, item_slot) in hotbar_slots.iter().enumerate() { + let this_item_speed; + match item_slot { + ItemSlot::Empty => { + this_item_speed = Some(azalea_entity::mining::get_mine_progress( + block.as_ref(), + azalea_registry::Item::Air, + menu, + fluid_on_eyes, + physics, + )); + } + ItemSlot::Present(item_slot) => { + // lazy way to avoid checking durability since azalea doesn't have durability + // data yet + if item_slot.nbt.is_none() { + this_item_speed = Some(azalea_entity::mining::get_mine_progress( + block.as_ref(), + item_slot.kind, + menu, + fluid_on_eyes, + physics, + )); + } else { + this_item_speed = None; + } + } + } + if let Some(this_item_speed) = this_item_speed { + if this_item_speed > best_speed { + best_slot = Some(i); + best_speed = this_item_speed; + } + } + } + + // now check every item for (i, item_slot) in hotbar_slots.iter().enumerate() { if let ItemSlot::Present(item_slot) = item_slot { let this_item_speed = azalea_entity::mining::get_mine_progress( diff --git a/azalea/src/bot.rs b/azalea/src/bot.rs index 2281deaff..529bb251a 100644 --- a/azalea/src/bot.rs +++ b/azalea/src/bot.rs @@ -13,12 +13,13 @@ use azalea_client::interact::SwingArmEvent; use azalea_client::mining::Mining; use azalea_client::TickBroadcast; use azalea_core::position::{BlockPos, Vec3}; +use azalea_core::tick::GameTick; use azalea_entity::{ clamp_look_direction, metadata::Player, EyeHeight, Jumping, LocalEntity, LookDirection, Position, }; use azalea_physics::PhysicsSet; -use bevy_app::{FixedUpdate, Update}; +use bevy_app::Update; use bevy_ecs::prelude::Event; use bevy_ecs::schedule::IntoSystemConfigs; use futures_lite::Future; @@ -41,7 +42,7 @@ impl Plugin for BotPlugin { jump_listener, ), ) - .add_systems(FixedUpdate, stop_jumping.after(PhysicsSet)); + .add_systems(GameTick, stop_jumping.after(PhysicsSet)); } } @@ -169,27 +170,35 @@ fn look_at_listener( ) { for event in events.read() { if let Ok((position, eye_height, mut look_direction)) = query.get_mut(event.entity) { - let (y_rot, x_rot) = + let new_look_direction = direction_looking_at(&position.up(eye_height.into()), &event.position); trace!( "look at {:?} (currently at {:?})", event.position, **position ); - (look_direction.y_rot, look_direction.x_rot) = (y_rot, x_rot); + *look_direction = new_look_direction; } } } -/// Return the (`y_rot`, `x_rot`) that would make a client at `current` be +/// Return the look direction that would make a client at `current` be /// looking at `target`. -fn direction_looking_at(current: &Vec3, target: &Vec3) -> (f32, f32) { +pub fn direction_looking_at(current: &Vec3, target: &Vec3) -> LookDirection { // borrowed from mineflayer's Bot.lookAt because i didn't want to do math let delta = target - current; let y_rot = (PI - f64::atan2(-delta.x, -delta.z)) * (180.0 / PI); let ground_distance = f64::sqrt(delta.x * delta.x + delta.z * delta.z); let x_rot = f64::atan2(delta.y, ground_distance) * -(180.0 / PI); - (y_rot as f32, x_rot as f32) + + // clamp + let y_rot = y_rot.rem_euclid(360.0); + let x_rot = x_rot.clamp(-90.0, 90.0) % 360.0; + + LookDirection { + x_rot: x_rot as f32, + y_rot: y_rot as f32, + } } /// A [`PluginGroup`] for the plugins that add extra bot functionality to the diff --git a/azalea/src/container.rs b/azalea/src/container.rs index 5406170a9..0f4170613 100644 --- a/azalea/src/container.rs +++ b/azalea/src/container.rs @@ -140,7 +140,7 @@ impl ContainerHandle { /// /// Note that any modifications you make to the `Menu` you're given will not /// actually cause any packets to be sent. If you're trying to modify your - /// inventory, use [`Client::open_inventory`] instead + /// inventory, use [`ContainerHandle::click`] instead pub fn menu(&self) -> Option { let ecs = self.client.ecs.lock(); let inventory = ecs diff --git a/azalea/src/lib.rs b/azalea/src/lib.rs index 37779d675..fd2cb83ac 100644 --- a/azalea/src/lib.rs +++ b/azalea/src/lib.rs @@ -3,9 +3,10 @@ #![feature(type_changing_struct_update)] #![feature(lazy_cell)] #![feature(let_chains)] +#![feature(never_type)] pub mod accept_resource_packs; -mod auto_respawn; +pub mod auto_respawn; pub mod auto_tool; mod bot; pub mod container; @@ -14,10 +15,11 @@ pub mod pathfinder; pub mod prelude; pub mod swarm; -use app::{App, Plugins}; +use app::Plugins; pub use azalea_auth as auth; pub use azalea_block as blocks; pub use azalea_brigadier as brigadier; +pub use azalea_buf as buf; pub use azalea_chat::FormattedText; pub use azalea_client::*; pub use azalea_core as core; @@ -33,13 +35,9 @@ pub use azalea_world as world; pub use bot::*; use ecs::component::Component; use futures::{future::BoxFuture, Future}; -use protocol::connect::Proxy; -use protocol::{ - resolver::{self, ResolverError}, - ServerAddress, -}; +use protocol::{resolver::ResolverError, ServerAddress}; +use swarm::SwarmBuilder; use thiserror::Error; -use tokio::sync::mpsc; pub use bevy_app as app; pub use bevy_ecs as ecs; @@ -54,8 +52,6 @@ pub enum StartError { InvalidAddress, #[error(transparent)] ResolveAddress(#[from] ResolverError), - #[error("Join error: {0}")] - Join(#[from] azalea_client::JoinError), } /// A builder for creating new [`Client`]s. This is the recommended way of @@ -80,10 +76,10 @@ pub struct ClientBuilder where S: Default + Send + Sync + Clone + Component + 'static, { - app: App, - /// The function that's called every time a bot receives an [`Event`]. - handler: Option>, - state: S, + /// Internally, ClientBuilder is just a wrapper over SwarmBuilder since it's + /// technically just a subset of it so we can avoid duplicating code this + /// way. + swarm: SwarmBuilder, } impl ClientBuilder { /// Start building a client that can join the world. @@ -120,11 +116,7 @@ impl ClientBuilder { #[must_use] pub fn new_without_plugins() -> ClientBuilder { Self { - // we create the app here so plugins can add onto it. - // the schedules won't run until [`Self::start`] is called. - app: App::new(), - handler: None, - state: NoState, + swarm: SwarmBuilder::new_without_plugins(), } } @@ -151,11 +143,7 @@ impl ClientBuilder { Fut: Future> + Send + 'static, { ClientBuilder { - handler: Some(Box::new(move |bot, event, state| { - Box::pin(handler(bot, event, state)) - })), - state: S::default(), - ..self + swarm: self.swarm.set_handler(handler), } } } @@ -166,55 +154,39 @@ where /// Set the client state instead of initializing defaults. #[must_use] pub fn set_state(mut self, state: S) -> Self { - self.state = state; + self.swarm.states = vec![state]; self } /// Add a group of plugins to the client. #[must_use] pub fn add_plugins(mut self, plugins: impl Plugins) -> Self { - self.app.add_plugins(plugins); + self.swarm = self.swarm.add_plugins(plugins); self } /// Build this `ClientBuilder` into an actual [`Client`] and join the given - /// server. + /// server. If the client can't join, it'll keep retrying forever until it + /// can. /// /// The `address` argument can be a `&str`, [`ServerAddress`], or anything /// that implements `TryInto`. /// + /// # Errors + /// + /// This will error if the given address is invalid or couldn't be resolved + /// to a Minecraft server. + /// /// [`ServerAddress`]: azalea_protocol::ServerAddress pub async fn start( - self, + mut self, account: Account, address: impl TryInto, - proxy: Option, - ) -> Result<(), StartError> { - let address: ServerAddress = address.try_into().map_err(|_| JoinError::InvalidAddress)?; - let resolved_address = resolver::resolve_address(&address).await?; - - // An event that causes the schedule to run. This is only used internally. - let (run_schedule_sender, run_schedule_receiver) = mpsc::unbounded_channel(); - - let ecs_lock = - start_ecs_runner(self.app, run_schedule_receiver, run_schedule_sender.clone()); - - let (bot, mut rx) = Client::start_client( - ecs_lock, - &account, - &address, - &resolved_address, - proxy, - run_schedule_sender, - ) - .await?; - - while let Some(event) = rx.recv().await { - if let Some(handler) = &self.handler { - tokio::spawn((handler)(bot.clone(), event.clone(), self.state.clone())); - } + ) -> Result { + self.swarm.accounts = vec![account]; + if self.swarm.states.is_empty() { + self.swarm.states = vec![S::default()]; } - - Ok(()) + self.swarm.start(address).await } } impl Default for ClientBuilder { diff --git a/azalea/src/pathfinder/astar.rs b/azalea/src/pathfinder/astar.rs index 163189af8..cc1e2242d 100644 --- a/azalea/src/pathfinder/astar.rs +++ b/azalea/src/pathfinder/astar.rs @@ -48,7 +48,7 @@ where movement_data: None, came_from: None, g_score: f32::default(), - f_score: f32::MAX, + f_score: f32::INFINITY, }, ); @@ -70,14 +70,14 @@ where let current_g_score = nodes .get(¤t_node) .map(|n| n.g_score) - .unwrap_or(f32::MAX); + .unwrap_or(f32::INFINITY); for neighbor in successors(current_node) { let tentative_g_score = current_g_score + neighbor.cost; let neighbor_g_score = nodes .get(&neighbor.movement.target) .map(|n| n.g_score) - .unwrap_or(f32::MAX); + .unwrap_or(f32::INFINITY); if tentative_g_score - neighbor_g_score < MIN_IMPROVEMENT { let heuristic = heuristic(neighbor.movement.target); let f_score = tentative_g_score + heuristic; diff --git a/azalea/src/pathfinder/costs.rs b/azalea/src/pathfinder/costs.rs index 5c72b73a7..f9b67e5fa 100644 --- a/azalea/src/pathfinder/costs.rs +++ b/azalea/src/pathfinder/costs.rs @@ -10,6 +10,15 @@ pub const SPRINT_MULTIPLIER: f32 = SPRINT_ONE_BLOCK_COST / WALK_ONE_BLOCK_COST; pub const JUMP_PENALTY: f32 = 2.; pub const CENTER_AFTER_FALL_COST: f32 = WALK_ONE_BLOCK_COST - WALK_OFF_BLOCK_COST; // 0.927 +// explanation here: +// https://github.com/cabaletta/baritone/blob/f147519a5c291015d4f18c94558a3f1bdcdb9588/src/api/java/baritone/api/Settings.java#L405 +// it's basically just the heuristic multiplier +pub const COST_HEURISTIC: f32 = 3.563; + +// this one is also from baritone, it's helpful as a tiebreaker to avoid +// breaking blocks if it can be avoided +pub const BLOCK_BREAK_ADDITIONAL_PENALTY: f32 = 2.; + pub static FALL_1_25_BLOCKS_COST: LazyLock = LazyLock::new(|| distance_to_ticks(1.25)); pub static FALL_0_25_BLOCKS_COST: LazyLock = LazyLock::new(|| distance_to_ticks(0.25)); pub static JUMP_ONE_BLOCK_COST: LazyLock = diff --git a/azalea/src/pathfinder/debug.rs b/azalea/src/pathfinder/debug.rs new file mode 100644 index 000000000..201803c9b --- /dev/null +++ b/azalea/src/pathfinder/debug.rs @@ -0,0 +1,113 @@ +use azalea_client::{chat::SendChatEvent, InstanceHolder}; +use azalea_core::position::Vec3; +use bevy_ecs::prelude::*; + +use super::ExecutingPath; + +/// A component that makes bots run /particle commands while pathfinding to show +/// where they're going. This requires the bots to have server operator +/// permissions, and it'll make them spam *a lot* of commands. +/// +/// ``` +/// # use azalea::prelude::*; +/// # use azalea::pathfinder::PathfinderDebugParticles; +/// # #[derive(Component, Clone, Default)] +/// # pub struct State; +/// +/// async fn handle(mut bot: Client, event: azalea::Event, state: State) -> anyhow::Result<()> { +/// match event { +/// azalea::Event::Init => { +/// bot.ecs +/// .lock() +/// .entity_mut(bot.entity) +/// .insert(PathfinderDebugParticles); +/// } +/// _ => {} +/// } +/// Ok(()) +/// } +/// ``` +#[derive(Component)] +pub struct PathfinderDebugParticles; + +pub fn debug_render_path_with_particles( + mut query: Query<(Entity, &ExecutingPath, &InstanceHolder), With>, + // chat_events is Option because the tests don't have SendChatEvent + // and we have to use ResMut because bevy doesn't support Option + chat_events: Option>>, + mut tick_count: Local, +) { + let Some(mut chat_events) = chat_events else { + return; + }; + if *tick_count >= 2 { + *tick_count = 0; + } else { + *tick_count += 1; + return; + } + for (entity, executing_path, instance_holder) in &mut query { + if executing_path.path.is_empty() { + continue; + } + + let chunks = &instance_holder.instance.read().chunks; + + let mut start = executing_path.last_reached_node; + for (i, movement) in executing_path.path.iter().enumerate() { + // /particle dust 0 1 1 1 ~ ~ ~ 0 0 0.2 0 100 + + let end = movement.target; + + let start_vec3 = start.center(); + let end_vec3 = end.center(); + + let step_count = (start_vec3.distance_to_sqr(&end_vec3).sqrt() * 4.0) as usize; + + let target_block_state = chunks.get_block_state(&movement.target).unwrap_or_default(); + let above_target_block_state = chunks + .get_block_state(&movement.target.up(1)) + .unwrap_or_default(); + // this isn't foolproof, there might be another block that could be mined + // depending on the move, but it's good enough for debugging + // purposes + let is_mining = !super::world::is_block_state_passable(target_block_state) + || !super::world::is_block_state_passable(above_target_block_state); + + let (r, g, b): (f64, f64, f64) = if i == 0 { + (0., 1., 0.) + } else if is_mining { + (1., 0., 0.) + } else { + (0., 1., 1.) + }; + + // interpolate between the start and end positions + for i in 0..step_count { + let percent = i as f64 / step_count as f64; + let pos = Vec3 { + x: start_vec3.x + (end_vec3.x - start_vec3.x) * percent, + y: start_vec3.y + (end_vec3.y - start_vec3.y) * percent, + z: start_vec3.z + (end_vec3.z - start_vec3.z) * percent, + }; + let particle_command = format!( + "/particle dust {r} {g} {b} {size} {start_x} {start_y} {start_z} {delta_x} {delta_y} {delta_z} 0 {count}", + size = 1, + start_x = pos.x, + start_y = pos.y, + start_z = pos.z, + delta_x = 0, + delta_y = 0, + delta_z = 0, + count = 1 + ); + chat_events.send(SendChatEvent { + entity, + content: particle_command, + }); + } + + start = movement.target; + } + } +} diff --git a/azalea/src/pathfinder/goals.rs b/azalea/src/pathfinder/goals.rs index 9c01d486f..3f8c79932 100644 --- a/azalea/src/pathfinder/goals.rs +++ b/azalea/src/pathfinder/goals.rs @@ -1,12 +1,21 @@ +//! The goals that a pathfinder can try to reach. + use std::f32::consts::SQRT_2; use azalea_core::position::{BlockPos, Vec3}; +use azalea_world::ChunkStorage; + +use super::costs::{COST_HEURISTIC, FALL_N_BLOCKS_COST, JUMP_ONE_BLOCK_COST}; -use super::{ - costs::{FALL_N_BLOCKS_COST, JUMP_ONE_BLOCK_COST}, - Goal, -}; +pub trait Goal { + #[must_use] + fn heuristic(&self, n: BlockPos) -> f32; + #[must_use] + fn success(&self, n: BlockPos) -> bool; +} +/// Move to the given block position. +#[derive(Debug)] pub struct BlockPosGoal(pub BlockPos); impl Goal for BlockPosGoal { fn heuristic(&self, n: BlockPos) -> f32 { @@ -36,9 +45,11 @@ fn xz_heuristic(dx: f32, dz: f32) -> f32 { diagonal = z; } - diagonal * SQRT_2 + straight + (diagonal * SQRT_2 + straight) * COST_HEURISTIC } +/// Move to the given block position, ignoring the y axis. +#[derive(Debug)] pub struct XZGoal { pub x: i32, pub z: i32, @@ -62,6 +73,8 @@ fn y_heuristic(dy: f32) -> f32 { } } +/// Move to the given y coordinate. +#[derive(Debug)] pub struct YGoal { pub y: i32, } @@ -75,6 +88,8 @@ impl Goal for YGoal { } } +/// Get within the given radius of the given position. +#[derive(Debug)] pub struct RadiusGoal { pub pos: Vec3, pub radius: f32, @@ -96,6 +111,8 @@ impl Goal for RadiusGoal { } } +/// Do the opposite of the given goal. +#[derive(Debug)] pub struct InverseGoal(pub T); impl Goal for InverseGoal { fn heuristic(&self, n: BlockPos) -> f32 { @@ -106,6 +123,8 @@ impl Goal for InverseGoal { } } +/// Do either of the given goals, whichever is closer. +#[derive(Debug)] pub struct OrGoal(pub T, pub U); impl Goal for OrGoal { fn heuristic(&self, n: BlockPos) -> f32 { @@ -116,6 +135,24 @@ impl Goal for OrGoal { } } +/// Do any of the given goals, whichever is closest. +#[derive(Debug)] +pub struct OrGoals(pub Vec); +impl Goal for OrGoals { + fn heuristic(&self, n: BlockPos) -> f32 { + self.0 + .iter() + .map(|goal| goal.heuristic(n)) + .min_by(|a, b| a.partial_cmp(b).unwrap()) + .unwrap_or(f32::INFINITY) + } + fn success(&self, n: BlockPos) -> bool { + self.0.iter().any(|goal| goal.success(n)) + } +} + +/// Try to reach both of the given goals. +#[derive(Debug)] pub struct AndGoal(pub T, pub U); impl Goal for AndGoal { fn heuristic(&self, n: BlockPos) -> f32 { @@ -125,3 +162,52 @@ impl Goal for AndGoal { self.0.success(n) && self.1.success(n) } } + +/// Try to reach all of the given goals. +#[derive(Debug)] +pub struct AndGoals(pub Vec); +impl Goal for AndGoals { + fn heuristic(&self, n: BlockPos) -> f32 { + self.0 + .iter() + .map(|goal| goal.heuristic(n)) + .max_by(|a, b| a.partial_cmp(b).unwrap()) + .unwrap_or(f32::INFINITY) + } + fn success(&self, n: BlockPos) -> bool { + self.0.iter().all(|goal| goal.success(n)) + } +} + +/// Move to a position where we can reach the given block. +#[derive(Debug)] +pub struct ReachBlockPosGoal { + pub pos: BlockPos, + pub chunk_storage: ChunkStorage, +} +impl Goal for ReachBlockPosGoal { + fn heuristic(&self, n: BlockPos) -> f32 { + BlockPosGoal(self.pos).heuristic(n) + } + fn success(&self, n: BlockPos) -> bool { + // only do the expensive check if we're close enough + let max_pick_range = 6; + let actual_pick_range = 4.5; + + let distance = (self.pos - n).length_sqr(); + if distance > max_pick_range * max_pick_range { + return false; + } + + let eye_position = n.to_vec3_floored() + Vec3::new(0.5, 1.62, 0.5); + let look_direction = crate::direction_looking_at(&eye_position, &self.pos.center()); + let block_hit_result = azalea_client::interact::pick( + &look_direction, + &eye_position, + &self.chunk_storage, + actual_pick_range, + ); + + block_hit_result.block_pos == self.pos + } +} diff --git a/azalea/src/pathfinder/mining.rs b/azalea/src/pathfinder/mining.rs index d5977973b..1bc08c436 100644 --- a/azalea/src/pathfinder/mining.rs +++ b/azalea/src/pathfinder/mining.rs @@ -1,30 +1,109 @@ -use azalea_block::BlockState; +use std::{cell::UnsafeCell, ops::RangeInclusive}; + +use azalea_block::{BlockState, BlockStates}; use azalea_inventory::Menu; use nohash_hasher::IntMap; use crate::auto_tool::best_tool_in_hotbar_for_block; +use super::costs::BLOCK_BREAK_ADDITIONAL_PENALTY; + pub struct MiningCache { - block_state_id_costs: IntMap, - inventory_menu: Menu, + block_state_id_costs: UnsafeCell>, + inventory_menu: Option, + + water_block_state_range: RangeInclusive, + lava_block_state_range: RangeInclusive, + + falling_blocks: Vec, } impl MiningCache { - pub fn new(inventory_menu: Menu) -> Self { + pub fn new(inventory_menu: Option) -> Self { + let water_block_states = BlockStates::from(azalea_registry::Block::Water); + let lava_block_states = BlockStates::from(azalea_registry::Block::Lava); + + let mut water_block_state_range_min = u32::MAX; + let mut water_block_state_range_max = u32::MIN; + for state in water_block_states { + water_block_state_range_min = water_block_state_range_min.min(state.id); + water_block_state_range_max = water_block_state_range_max.max(state.id); + } + let water_block_state_range = water_block_state_range_min..=water_block_state_range_max; + + let mut lava_block_state_range_min = u32::MAX; + let mut lava_block_state_range_max = u32::MIN; + for state in lava_block_states { + lava_block_state_range_min = lava_block_state_range_min.min(state.id); + lava_block_state_range_max = lava_block_state_range_max.max(state.id); + } + let lava_block_state_range = lava_block_state_range_min..=lava_block_state_range_max; + + let mut falling_blocks: Vec = vec![ + azalea_registry::Block::Sand.into(), + azalea_registry::Block::RedSand.into(), + azalea_registry::Block::Gravel.into(), + azalea_registry::Block::Anvil.into(), + azalea_registry::Block::ChippedAnvil.into(), + azalea_registry::Block::DamagedAnvil.into(), + // concrete powders + azalea_registry::Block::WhiteConcretePowder.into(), + azalea_registry::Block::OrangeConcretePowder.into(), + azalea_registry::Block::MagentaConcretePowder.into(), + azalea_registry::Block::LightBlueConcretePowder.into(), + azalea_registry::Block::YellowConcretePowder.into(), + azalea_registry::Block::LimeConcretePowder.into(), + azalea_registry::Block::PinkConcretePowder.into(), + azalea_registry::Block::GrayConcretePowder.into(), + azalea_registry::Block::LightGrayConcretePowder.into(), + azalea_registry::Block::CyanConcretePowder.into(), + azalea_registry::Block::PurpleConcretePowder.into(), + azalea_registry::Block::BlueConcretePowder.into(), + azalea_registry::Block::BrownConcretePowder.into(), + azalea_registry::Block::GreenConcretePowder.into(), + azalea_registry::Block::RedConcretePowder.into(), + azalea_registry::Block::BlackConcretePowder.into(), + ]; + falling_blocks.sort_unstable_by_key(|block| block.id); + Self { - block_state_id_costs: IntMap::default(), + block_state_id_costs: UnsafeCell::new(IntMap::default()), inventory_menu, + water_block_state_range, + lava_block_state_range, + falling_blocks, } } - pub fn cost_for(&mut self, block: BlockState) -> f32 { - if let Some(cost) = self.block_state_id_costs.get(&block.id) { + pub fn cost_for(&self, block: BlockState) -> f32 { + let Some(inventory_menu) = &self.inventory_menu else { + return f32::INFINITY; + }; + + // SAFETY: mining is single-threaded, so this is safe + let block_state_id_costs = unsafe { &mut *self.block_state_id_costs.get() }; + + if let Some(cost) = block_state_id_costs.get(&block.id) { *cost } else { - let best_tool_result = best_tool_in_hotbar_for_block(block, &self.inventory_menu); - let cost = 1. / best_tool_result.percentage_per_tick; - self.block_state_id_costs.insert(block.id, cost); + let best_tool_result = best_tool_in_hotbar_for_block(block, inventory_menu); + let mut cost = 1. / best_tool_result.percentage_per_tick; + + cost += BLOCK_BREAK_ADDITIONAL_PENALTY; + + block_state_id_costs.insert(block.id, cost); cost } } + + pub fn is_liquid(&self, block: BlockState) -> bool { + self.water_block_state_range.contains(&block.id) + || self.lava_block_state_range.contains(&block.id) + } + + pub fn is_falling_block(&self, block: BlockState) -> bool { + self.falling_blocks + .binary_search_by_key(&block.id, |block| block.id) + .is_ok() + } } diff --git a/azalea/src/pathfinder/mod.rs b/azalea/src/pathfinder/mod.rs index db5080047..9fd769e64 100644 --- a/azalea/src/pathfinder/mod.rs +++ b/azalea/src/pathfinder/mod.rs @@ -1,8 +1,10 @@ -//! A pathfinding plugin to make bots navigate the world. A lot of this code is -//! based on [Baritone](https://github.com/cabaletta/baritone). +//! A pathfinding plugin to make bots able to traverse the world. +//! +//! Much of this code is based on [Baritone](https://github.com/cabaletta/baritone). pub mod astar; pub mod costs; +mod debug; pub mod goals; pub mod mining; pub mod moves; @@ -23,22 +25,21 @@ use crate::ecs::{ }; use crate::pathfinder::moves::PathfinderCtx; use crate::pathfinder::world::CachedWorld; -use azalea_client::chat::SendChatEvent; -use azalea_client::inventory::{InventoryComponent, InventorySet}; +use azalea_client::inventory::{InventoryComponent, InventorySet, SetSelectedHotbarSlotEvent}; +use azalea_client::mining::{Mining, StartMiningBlockEvent}; use azalea_client::movement::MoveEventsSet; -use azalea_client::{StartSprintEvent, StartWalkEvent}; -use azalea_core::position::{BlockPos, Vec3}; +use azalea_client::{InstanceHolder, StartSprintEvent, StartWalkEvent}; +use azalea_core::position::BlockPos; +use azalea_core::tick::GameTick; use azalea_entity::metadata::Player; use azalea_entity::LocalEntity; use azalea_entity::{Physics, Position}; use azalea_physics::PhysicsSet; use azalea_world::{InstanceContainer, InstanceName}; -use bevy_app::{FixedUpdate, PreUpdate, Update}; -use bevy_ecs::event::Events; +use bevy_app::{PreUpdate, Update}; use bevy_ecs::prelude::Event; use bevy_ecs::query::Changed; use bevy_ecs::schedule::IntoSystemConfigs; -use bevy_ecs::system::{Local, ResMut}; use bevy_tasks::{AsyncComputeTaskPool, Task}; use futures_lite::future; use std::collections::VecDeque; @@ -47,6 +48,9 @@ use std::sync::Arc; use std::time::{Duration, Instant}; use tracing::{debug, error, info, trace, warn}; +use self::debug::debug_render_path_with_particles; +pub use self::debug::PathfinderDebugParticles; +use self::goals::Goal; use self::mining::MiningCache; use self::moves::{ExecuteCtx, IsReachedCtx, SuccessorsFn}; @@ -58,9 +62,9 @@ impl Plugin for PathfinderPlugin { .add_event::() .add_event::() .add_systems( - // putting systems in the FixedUpdate schedule makes them run every Minecraft tick + // putting systems in the GameTick schedule makes them run every Minecraft tick // (every 50 milliseconds). - FixedUpdate, + GameTick, ( timeout_movement, check_node_reached, @@ -92,11 +96,12 @@ impl Plugin for PathfinderPlugin { } /// A component that makes this client able to pathfind. -#[derive(Component, Default)] +#[derive(Component, Default, Clone)] pub struct Pathfinder { pub goal: Option>, pub successors_fn: Option, pub is_calculating: bool, + pub allow_mining: bool, pub goto_id: Arc, } @@ -119,6 +124,9 @@ pub struct GotoEvent { /// The function that's used for checking what moves are possible. Usually /// `pathfinder::moves::default_move` pub successors_fn: SuccessorsFn, + + /// Whether the bot is allowed to break blocks while pathfinding. + pub allow_mining: bool, } #[derive(Event, Clone)] pub struct PathFoundEvent { @@ -127,6 +135,7 @@ pub struct PathFoundEvent { pub path: Option>>, pub is_partial: bool, pub successors_fn: SuccessorsFn, + pub allow_mining: bool, } #[allow(clippy::type_complexity)] @@ -141,6 +150,7 @@ fn add_default_pathfinder( pub trait PathfinderClientExt { fn goto(&self, goal: impl Goal + Send + Sync + 'static); + fn goto_without_mining(&self, goal: impl Goal + Send + Sync + 'static); fn stop_pathfinding(&self); } @@ -157,6 +167,18 @@ impl PathfinderClientExt for azalea_client::Client { entity: self.entity, goal: Arc::new(goal), successors_fn: moves::default_move, + allow_mining: true, + }); + } + + /// Same as [`goto`](Self::goto). but the bot won't break any blocks while + /// executing the path. + fn goto_without_mining(&self, goal: impl Goal + Send + Sync + 'static) { + self.ecs.lock().send_event(GotoEvent { + entity: self.entity, + goal: Arc::new(goal), + successors_fn: moves::default_move, + allow_mining: false, }); } @@ -190,19 +212,25 @@ fn goto_listener( .get_mut(event.entity) .expect("Called goto on an entity that's not in the world"); + if event.goal.success(BlockPos::from(position)) { + // we're already at the goal, nothing to do + pathfinder.goal = None; + pathfinder.successors_fn = None; + pathfinder.is_calculating = false; + continue; + } + // we store the goal so it can be recalculated later if necessary pathfinder.goal = Some(event.goal.clone()); pathfinder.successors_fn = Some(event.successors_fn); pathfinder.is_calculating = true; + pathfinder.allow_mining = event.allow_mining; let start = if let Some(executing_path) = executing_path - && let Some(final_node) = executing_path.path.back() { + && let Some(final_node) = executing_path.path.back() + { // if we're currently pathfinding and got a goto event, start a little ahead - executing_path - .path - .get(20) - .unwrap_or(final_node) - .target + executing_path.path.get(20).unwrap_or(final_node).target } else { BlockPos::from(position) }; @@ -222,7 +250,13 @@ fn goto_listener( let goto_id_atomic = pathfinder.goto_id.clone(); let goto_id = goto_id_atomic.fetch_add(1, atomic::Ordering::Relaxed) + 1; - let mining_cache = MiningCache::new(inventory.inventory_menu.clone()); + + let allow_mining = event.allow_mining; + let mining_cache = MiningCache::new(if allow_mining { + Some(inventory.inventory_menu.clone()) + } else { + None + }); let task = thread_pool.spawn(async move { debug!("start: {start:?}"); @@ -250,7 +284,11 @@ fn goto_listener( debug!("partial: {partial:?}"); let duration = end_time - start_time; if partial { - info!("Pathfinder took {duration:?} (incomplete path)"); + if movements.is_empty() { + info!("Pathfinder took {duration:?} (empty path)"); + } else { + info!("Pathfinder took {duration:?} (incomplete path)"); + } // wait a bit so it's not a busy loop std::thread::sleep(Duration::from_millis(100)); } else { @@ -291,10 +329,11 @@ fn goto_listener( path: Some(path), is_partial, successors_fn, + allow_mining, }) }); - commands.spawn(ComputePath(task)); + commands.entity(event.entity).insert(ComputePath(task)); } } @@ -344,7 +383,11 @@ fn path_found_listener( .expect("Entity tried to pathfind but the entity isn't in a valid world"); let successors_fn: moves::SuccessorsFn = event.successors_fn; let cached_world = CachedWorld::new(world_lock); - let mining_cache = MiningCache::new(inventory.inventory_menu.clone()); + let mining_cache = MiningCache::new(if event.allow_mining { + Some(inventory.inventory_menu.clone()) + } else { + None + }); let successors = |pos: BlockPos| { call_successors_fn(&cached_world, &mining_cache, successors_fn, pos) }; @@ -401,8 +444,21 @@ fn path_found_listener( } } -fn timeout_movement(mut query: Query<(&Pathfinder, &mut ExecutingPath, &Position)>) { - for (pathfinder, mut executing_path, position) in &mut query { +fn timeout_movement( + mut query: Query<(&Pathfinder, &mut ExecutingPath, &Position, Option<&Mining>)>, +) { + for (pathfinder, mut executing_path, position, mining) in &mut query { + // don't timeout if we're mining + if let Some(mining) = mining { + // also make sure we're close enough to the block that's being mined + if mining.pos.distance_to_sqr(&BlockPos::from(position)) < 6_i32.pow(2) { + // also reset the last_node_reached_at so we don't timeout after we finish + // mining + executing_path.last_node_reached_at = Instant::now(); + continue; + } + } + if executing_path.last_node_reached_at.elapsed() > Duration::from_secs(2) && !pathfinder.is_calculating && !executing_path.path.is_empty() @@ -537,7 +593,11 @@ fn check_for_path_obstruction( // obstruction check (the path we're executing isn't possible anymore) let cached_world = CachedWorld::new(world_lock); - let mining_cache = MiningCache::new(inventory.inventory_menu.clone()); + let mining_cache = MiningCache::new(if pathfinder.allow_mining { + Some(inventory.inventory_menu.clone()) + } else { + None + }); let successors = |pos: BlockPos| call_successors_fn(&cached_world, &mining_cache, successors_fn, pos); @@ -582,6 +642,7 @@ fn recalculate_near_end_of_path( entity, goal, successors_fn, + allow_mining: pathfinder.allow_mining, }); pathfinder.is_calculating = true; @@ -616,14 +677,27 @@ fn recalculate_near_end_of_path( } } +#[allow(clippy::type_complexity)] fn tick_execute_path( - mut query: Query<(Entity, &mut ExecutingPath, &Position, &Physics)>, + mut query: Query<( + Entity, + &mut ExecutingPath, + &Position, + &Physics, + Option<&Mining>, + &InstanceHolder, + &InventoryComponent, + )>, mut look_at_events: EventWriter, mut sprint_events: EventWriter, mut walk_events: EventWriter, mut jump_events: EventWriter, + mut start_mining_events: EventWriter, + mut set_selected_hotbar_slot_events: EventWriter, ) { - for (entity, executing_path, position, physics) in &mut query { + for (entity, executing_path, position, physics, mining, instance_holder, inventory_component) in + &mut query + { if let Some(movement) = executing_path.path.front() { let ctx = ExecuteCtx { entity, @@ -631,10 +705,16 @@ fn tick_execute_path( position: **position, start: executing_path.last_reached_node, physics, + is_currently_mining: mining.is_some(), + instance: instance_holder.instance.clone(), + menu: inventory_component.inventory_menu.clone(), + look_at_events: &mut look_at_events, sprint_events: &mut sprint_events, walk_events: &mut walk_events, jump_events: &mut jump_events, + start_mining_events: &mut start_mining_events, + set_selected_hotbar_slot_events: &mut set_selected_hotbar_slot_events, }; trace!("executing move"); (movement.data.execute)(ctx); @@ -654,6 +734,7 @@ fn recalculate_if_has_goal_but_no_path( entity, goal, successors_fn: pathfinder.successors_fn.unwrap(), + allow_mining: pathfinder.allow_mining, }); pathfinder.is_calculating = true; } @@ -677,6 +758,9 @@ fn handle_stop_pathfinding_event( mut commands: Commands, ) { for event in events.read() { + // stop computing any path that's being computed + commands.entity(event.entity).remove::(); + let Ok((mut pathfinder, mut executing_path)) = query.get_mut(event.entity) else { continue; }; @@ -684,13 +768,19 @@ fn handle_stop_pathfinding_event( if event.force { executing_path.path.clear(); executing_path.queued_path = None; + } else { + // switch to an empty path as soon as it can + executing_path.queued_path = Some(VecDeque::new()); + // make sure it doesn't recalculate + executing_path.is_path_partial = false; + } + + if executing_path.path.is_empty() { walk_events.send(StartWalkEvent { entity: event.entity, direction: WalkDirection::None, }); commands.entity(event.entity).remove::(); - } else { - executing_path.queued_path = Some(VecDeque::new()); } } } @@ -711,101 +801,6 @@ fn stop_pathfinding_on_instance_change( } } -/// A component that makes bots run /particle commands while pathfinding to show -/// where they're going. This requires the bots to have server operator -/// permissions, and it'll make them spam *a lot* of commands. -/// -/// ``` -/// # use azalea::prelude::*; -/// # use azalea::pathfinder::PathfinderDebugParticles; -/// # #[derive(Component, Clone, Default)] -/// # pub struct State; -/// -/// async fn handle(mut bot: Client, event: azalea::Event, state: State) -> anyhow::Result<()> { -/// match event { -/// azalea::Event::Init => { -/// bot.ecs -/// .lock() -/// .entity_mut(bot.entity) -/// .insert(PathfinderDebugParticles); -/// } -/// _ => {} -/// } -/// Ok(()) -/// } -/// ``` -#[derive(Component)] -pub struct PathfinderDebugParticles; - -fn debug_render_path_with_particles( - mut query: Query<(Entity, &ExecutingPath), With>, - // chat_events is Option because the tests don't have SendChatEvent - // and we have to use ResMut because bevy doesn't support Option - chat_events: Option>>, - mut tick_count: Local, -) { - let Some(mut chat_events) = chat_events else { - return; - }; - if *tick_count >= 2 { - *tick_count = 0; - } else { - *tick_count += 1; - return; - } - for (entity, executing_path) in &mut query { - if executing_path.path.is_empty() { - continue; - } - - let mut start = executing_path.last_reached_node; - for (i, movement) in executing_path.path.iter().enumerate() { - // /particle dust 0 1 1 1 ~ ~ ~ 0 0 0.2 0 100 - - let end = movement.target; - - let start_vec3 = start.center(); - let end_vec3 = end.center(); - - let step_count = (start_vec3.distance_to_sqr(&end_vec3).sqrt() * 4.0) as usize; - - let (r, g, b): (f64, f64, f64) = if i == 0 { (0., 1., 0.) } else { (0., 1., 1.) }; - - // interpolate between the start and end positions - for i in 0..step_count { - let percent = i as f64 / step_count as f64; - let pos = Vec3 { - x: start_vec3.x + (end_vec3.x - start_vec3.x) * percent, - y: start_vec3.y + (end_vec3.y - start_vec3.y) * percent, - z: start_vec3.z + (end_vec3.z - start_vec3.z) * percent, - }; - let particle_command = format!( - "/particle dust {r} {g} {b} {size} {start_x} {start_y} {start_z} {delta_x} {delta_y} {delta_z} 0 {count}", - size = 1, - start_x = pos.x, - start_y = pos.y, - start_z = pos.z, - delta_x = 0, - delta_y = 0, - delta_z = 0, - count = 1 - ); - chat_events.send(SendChatEvent { - entity, - content: particle_command, - }); - } - - start = movement.target; - } - } -} - -pub trait Goal { - fn heuristic(&self, n: BlockPos) -> f32; - fn success(&self, n: BlockPos) -> bool; -} - /// Checks whether the path has been obstructed, and returns Some(index) if it /// has been. The index is of the first obstructed node. fn check_path_obstructed( @@ -904,6 +899,7 @@ mod tests { entity: simulation.entity, goal: Arc::new(BlockPosGoal(end_pos)), successors_fn: moves::default_move, + allow_mining: false, }); simulation } diff --git a/azalea/src/pathfinder/moves/basic.rs b/azalea/src/pathfinder/moves/basic.rs index 957e24c60..54a6dc6a8 100644 --- a/azalea/src/pathfinder/moves/basic.rs +++ b/azalea/src/pathfinder/moves/basic.rs @@ -16,17 +16,20 @@ pub fn basic_move(ctx: &mut PathfinderCtx, node: BlockPos) { descend_move(ctx, node); diagonal_move(ctx, node); descend_forward_1_move(ctx, node); + downward_move(ctx, node); } fn forward_move(ctx: &mut PathfinderCtx, pos: BlockPos) { for dir in CardinalDirection::iter() { let offset = BlockPos::new(dir.x(), 0, dir.z()); - if !ctx.world.is_standable(pos + offset) { + let mut cost = SPRINT_ONE_BLOCK_COST; + + let break_cost = ctx.world.cost_for_standing(pos + offset, ctx.mining_cache); + if break_cost == f32::INFINITY { continue; } - - let cost = SPRINT_ONE_BLOCK_COST; + cost += break_cost; ctx.edges.push(Edge { movement: astar::Movement { @@ -43,6 +46,14 @@ fn forward_move(ctx: &mut PathfinderCtx, pos: BlockPos) { fn execute_forward_move(mut ctx: ExecuteCtx) { let center = ctx.target.center(); + + if ctx.mine_while_at_start(ctx.target.up(1)) { + return; + } + if ctx.mine_while_at_start(ctx.target) { + return; + } + ctx.look_at(center); ctx.sprint(SprintDirection::Forward); } @@ -51,14 +62,22 @@ fn ascend_move(ctx: &mut PathfinderCtx, pos: BlockPos) { for dir in CardinalDirection::iter() { let offset = BlockPos::new(dir.x(), 1, dir.z()); - if !ctx.world.is_block_passable(pos.up(2)) { + let break_cost_1 = ctx + .world + .cost_for_breaking_block(pos.up(2), ctx.mining_cache); + if break_cost_1 == f32::INFINITY { continue; } - if !ctx.world.is_standable(pos + offset) { + let break_cost_2 = ctx.world.cost_for_standing(pos + offset, ctx.mining_cache); + if break_cost_2 == f32::INFINITY { continue; } - let cost = SPRINT_ONE_BLOCK_COST + JUMP_PENALTY + *JUMP_ONE_BLOCK_COST; + let cost = SPRINT_ONE_BLOCK_COST + + JUMP_PENALTY + + *JUMP_ONE_BLOCK_COST + + break_cost_1 + + break_cost_2; ctx.edges.push(Edge { movement: astar::Movement { @@ -81,6 +100,16 @@ fn execute_ascend_move(mut ctx: ExecuteCtx) { .. } = ctx; + if ctx.mine_while_at_start(start.up(2)) { + return; + } + if ctx.mine_while_at_start(target) { + return; + } + if ctx.mine_while_at_start(target.up(1)) { + return; + } + let target_center = target.center(); ctx.look_at(target_center); @@ -123,19 +152,39 @@ fn descend_move(ctx: &mut PathfinderCtx, pos: BlockPos) { for dir in CardinalDirection::iter() { let dir_delta = BlockPos::new(dir.x(), 0, dir.z()); let new_horizontal_position = pos + dir_delta; - let fall_distance = ctx.world.fall_distance(new_horizontal_position); - if fall_distance == 0 || fall_distance > 3 { + + let break_cost_1 = ctx + .world + .cost_for_passing(new_horizontal_position, ctx.mining_cache); + if break_cost_1 == f32::INFINITY { continue; } - let new_position = new_horizontal_position.down(fall_distance as i32); - // check whether 3 blocks vertically forward are passable - if !ctx.world.is_passable(new_horizontal_position) { + let mut fall_distance = ctx.world.fall_distance(new_horizontal_position); + if fall_distance > 3 { continue; } - // check whether we can stand on the target position - if !ctx.world.is_standable(new_position) { - continue; + + if fall_distance == 0 { + // if the fall distance is 0, set it to 1 so we try mining + fall_distance = 1 + } + + let new_position = new_horizontal_position.down(fall_distance as i32); + + // only mine if we're descending 1 block + let break_cost_2; + if fall_distance == 1 { + break_cost_2 = ctx.world.cost_for_standing(new_position, ctx.mining_cache); + if break_cost_2 == f32::INFINITY { + continue; + } + } else { + // check whether we can stand on the target position + if !ctx.world.is_standable(new_position) { + continue; + } + break_cost_2 = 0.; } let cost = WALK_OFF_BLOCK_COST @@ -145,9 +194,11 @@ fn descend_move(ctx: &mut PathfinderCtx, pos: BlockPos) { .copied() // avoid panicking if we fall more than the size of FALL_N_BLOCKS_COST // probably not possible but just in case - .unwrap_or(f32::MAX), + .unwrap_or(f32::INFINITY), CENTER_AFTER_FALL_COST, - ); + ) + + break_cost_1 + + break_cost_2; ctx.edges.push(Edge { movement: astar::Movement { @@ -169,6 +220,12 @@ fn execute_descend_move(mut ctx: ExecuteCtx) { .. } = ctx; + for i in (0..=(start.y - target.y + 1)).rev() { + if ctx.mine_while_at_start(target.up(i)) { + return; + } + } + let start_center = start.center(); let center = target.center(); @@ -249,7 +306,7 @@ fn descend_forward_1_move(ctx: &mut PathfinderCtx, pos: BlockPos) { .copied() // avoid panicking if we fall more than the size of FALL_N_BLOCKS_COST // probably not possible but just in case - .unwrap_or(f32::MAX), + .unwrap_or(f32::INFINITY), CENTER_AFTER_FALL_COST, ); @@ -310,3 +367,53 @@ fn execute_diagonal_move(mut ctx: ExecuteCtx) { ctx.look_at(target_center); ctx.sprint(SprintDirection::Forward); } + +/// Go directly down, usually by mining. +fn downward_move(ctx: &mut PathfinderCtx, pos: BlockPos) { + // make sure we land on a solid block after breaking the one below us + if !ctx.world.is_block_solid(pos.down(2)) { + return; + } + + let break_cost = ctx + .world + .cost_for_breaking_block(pos.down(1), ctx.mining_cache); + if break_cost == f32::INFINITY { + return; + } + + let cost = FALL_N_BLOCKS_COST[1] + break_cost; + + ctx.edges.push(Edge { + movement: astar::Movement { + target: pos.down(1), + data: MoveData { + execute: &execute_downward_move, + is_reached: &default_is_reached, + }, + }, + cost, + }) +} +fn execute_downward_move(mut ctx: ExecuteCtx) { + let ExecuteCtx { + target, position, .. + } = ctx; + + let target_center = target.center(); + + let horizontal_distance_from_target = + (target_center - position).horizontal_distance_sqr().sqrt(); + + if horizontal_distance_from_target > 0.25 { + ctx.look_at(target_center); + ctx.walk(WalkDirection::Forward); + } else if ctx.mine_while_at_start(target) { + ctx.walk(WalkDirection::None); + } else if BlockPos::from(position) != target { + ctx.look_at(target_center); + ctx.walk(WalkDirection::Forward); + } else { + ctx.walk(WalkDirection::None); + } +} diff --git a/azalea/src/pathfinder/moves/mod.rs b/azalea/src/pathfinder/moves/mod.rs index e5b837ea2..bb10b1928 100644 --- a/azalea/src/pathfinder/moves/mod.rs +++ b/azalea/src/pathfinder/moves/mod.rs @@ -1,14 +1,24 @@ pub mod basic; pub mod parkour; -use std::fmt::Debug; - -use crate::{JumpEvent, LookAtEvent}; - -use super::{astar, mining::MiningCache, world::CachedWorld}; -use azalea_client::{SprintDirection, StartSprintEvent, StartWalkEvent, WalkDirection}; +use std::{fmt::Debug, sync::Arc}; + +use crate::{auto_tool::best_tool_in_hotbar_for_block, JumpEvent, LookAtEvent}; + +use super::{ + astar, + mining::MiningCache, + world::{is_block_state_passable, CachedWorld}, +}; +use azalea_client::{ + inventory::SetSelectedHotbarSlotEvent, mining::StartMiningBlockEvent, SprintDirection, + StartSprintEvent, StartWalkEvent, WalkDirection, +}; use azalea_core::position::{BlockPos, Vec3}; +use azalea_inventory::Menu; +use azalea_world::Instance; use bevy_ecs::{entity::Entity, event::EventWriter}; +use parking_lot::RwLock; type Edge = astar::Edge; @@ -35,7 +45,7 @@ impl Debug for MoveData { } } -pub struct ExecuteCtx<'w1, 'w2, 'w3, 'w4, 'a> { +pub struct ExecuteCtx<'w1, 'w2, 'w3, 'w4, 'w5, 'w6, 'a> { pub entity: Entity, /// The node that we're trying to reach. pub target: BlockPos, @@ -43,14 +53,19 @@ pub struct ExecuteCtx<'w1, 'w2, 'w3, 'w4, 'a> { pub start: BlockPos, pub position: Vec3, pub physics: &'a azalea_entity::Physics, + pub is_currently_mining: bool, + pub instance: Arc>, + pub menu: Menu, pub look_at_events: &'a mut EventWriter<'w1, LookAtEvent>, pub sprint_events: &'a mut EventWriter<'w2, StartSprintEvent>, pub walk_events: &'a mut EventWriter<'w3, StartWalkEvent>, pub jump_events: &'a mut EventWriter<'w4, JumpEvent>, + pub start_mining_events: &'a mut EventWriter<'w5, StartMiningBlockEvent>, + pub set_selected_hotbar_slot_events: &'a mut EventWriter<'w6, SetSelectedHotbarSlotEvent>, } -impl ExecuteCtx<'_, '_, '_, '_, '_> { +impl ExecuteCtx<'_, '_, '_, '_, '_, '_, '_> { pub fn look_at(&mut self, position: Vec3) { self.look_at_events.send(LookAtEvent { entity: self.entity, @@ -63,6 +78,13 @@ impl ExecuteCtx<'_, '_, '_, '_, '_> { }); } + pub fn look_at_exact(&mut self, position: Vec3) { + self.look_at_events.send(LookAtEvent { + entity: self.entity, + position, + }); + } + pub fn sprint(&mut self, direction: SprintDirection) { self.sprint_events.send(StartSprintEvent { entity: self.entity, @@ -82,6 +104,76 @@ impl ExecuteCtx<'_, '_, '_, '_, '_> { entity: self.entity, }); } + + /// Returns whether this block could be mined. + pub fn should_mine(&mut self, block: BlockPos) -> bool { + let block_state = self + .instance + .read() + .get_block_state(&block) + .unwrap_or_default(); + if is_block_state_passable(block_state) { + // block is already passable, no need to mine it + return false; + } + + true + } + + /// Mine the block at the given position. Returns whether the block is being + /// mined. + pub fn mine(&mut self, block: BlockPos) -> bool { + let block_state = self + .instance + .read() + .get_block_state(&block) + .unwrap_or_default(); + if is_block_state_passable(block_state) { + // block is already passable, no need to mine it + return false; + } + + let best_tool_result = best_tool_in_hotbar_for_block(block_state, &self.menu); + + self.set_selected_hotbar_slot_events + .send(SetSelectedHotbarSlotEvent { + entity: self.entity, + slot: best_tool_result.index as u8, + }); + + self.is_currently_mining = true; + + self.walk(WalkDirection::None); + self.look_at_exact(block.center()); + self.start_mining_events.send(StartMiningBlockEvent { + entity: self.entity, + position: block, + }); + + true + } + + /// Mine the given block, but make sure the player is standing at the start + /// of the current node first. + pub fn mine_while_at_start(&mut self, block: BlockPos) -> bool { + let horizontal_distance_from_start = (self.start.center() - self.position) + .horizontal_distance_sqr() + .sqrt(); + let at_start_position = + BlockPos::from(self.position) == self.start && horizontal_distance_from_start < 0.25; + + if self.should_mine(block) { + if at_start_position { + self.mine(block); + } else { + self.look_at(self.start.center()); + self.walk(WalkDirection::Forward); + } + true + } else { + false + } + } } pub struct IsReachedCtx<'a> { diff --git a/azalea/src/pathfinder/simulation.rs b/azalea/src/pathfinder/simulation.rs index 0cffb4161..ab3b340e8 100644 --- a/azalea/src/pathfinder/simulation.rs +++ b/azalea/src/pathfinder/simulation.rs @@ -1,18 +1,19 @@ //! Simulate the Minecraft world, currently only used for tests. -use std::{sync::Arc, time::Duration}; +use std::sync::Arc; -use azalea_client::{inventory::InventoryComponent, PhysicsState}; -use azalea_core::{position::Vec3, resource_location::ResourceLocation}; +use azalea_client::{ + inventory::InventoryComponent, packet_handling::game::SendPacketEvent, PhysicsState, +}; +use azalea_core::{position::Vec3, resource_location::ResourceLocation, tick::GameTick}; use azalea_entity::{ - attributes::AttributeInstance, metadata::Sprinting, Attributes, EntityDimensions, Physics, - Position, + attributes::AttributeInstance, Attributes, EntityDimensions, Physics, Position, }; -use azalea_world::{ChunkStorage, Instance, InstanceContainer, InstanceName, MinecraftEntityId}; -use bevy_app::{App, FixedUpdate}; +use azalea_world::{ChunkStorage, Instance, InstanceContainer, MinecraftEntityId, PartialInstance}; +use bevy_app::App; use bevy_ecs::prelude::*; -use bevy_time::{Fixed, Time}; use parking_lot::RwLock; +use uuid::Uuid; #[derive(Bundle, Clone)] pub struct SimulatedPlayerBundle { @@ -68,44 +69,52 @@ impl Simulation { super::PathfinderPlugin, crate::BotPlugin, azalea_client::task_pool::TaskPoolPlugin::default(), + // for mining + azalea_client::inventory::InventoryPlugin, + azalea_client::mining::MinePlugin, + azalea_client::interact::InteractPlugin, )) - // make sure it doesn't do fixed ticks without us telling it to - .insert_resource(Time::::from_duration(Duration::MAX)) .insert_resource(InstanceContainer { instances: [(instance_name.clone(), Arc::downgrade(&instance.clone()))] .iter() .cloned() .collect(), }) - .add_event::(); + .add_event::(); app.edit_schedule(bevy_app::Main, |schedule| { schedule.set_executor_kind(bevy_ecs::schedule::ExecutorKind::SingleThreaded); }); - let entity = app - .world - .spawn(( - MinecraftEntityId(0), - InstanceName(instance_name), - azalea_entity::LocalEntity, - azalea_entity::Jumping::default(), - azalea_entity::LookDirection::default(), - Sprinting(true), - azalea_entity::metadata::Player, - azalea_entity::EyeHeight::new(player.physics.dimensions.height * 0.85), - player, - )) - .id(); + let mut entity = app.world.spawn(( + MinecraftEntityId(0), + azalea_entity::LocalEntity, + azalea_entity::metadata::PlayerMetadataBundle::default(), + azalea_entity::EntityBundle::new( + Uuid::nil(), + *player.position, + azalea_registry::EntityKind::Player, + instance_name, + ), + azalea_client::InstanceHolder { + // partial_instance is never actually used by the pathfinder so + partial_instance: Arc::new(RwLock::new(PartialInstance::default())), + instance: instance.clone(), + }, + InventoryComponent::default(), + )); + entity.insert(player); + + let entity_id = entity.id(); Self { app, - entity, + entity: entity_id, _instance: instance, } } pub fn tick(&mut self) { - self.app.world.run_schedule(FixedUpdate); + self.app.world.run_schedule(GameTick); self.app.update(); } pub fn position(&self) -> Vec3 { diff --git a/azalea/src/pathfinder/world.rs b/azalea/src/pathfinder/world.rs index f73a6641b..a5a273fb2 100644 --- a/azalea/src/pathfinder/world.rs +++ b/azalea/src/pathfinder/world.rs @@ -11,6 +11,9 @@ use azalea_core::{ use azalea_physics::collision::BlockWithShape; use azalea_world::Instance; use parking_lot::RwLock; +use rustc_hash::FxHashMap; + +use super::mining::MiningCache; /// An efficient representation of the world used for the pathfinder. pub struct CachedWorld { @@ -20,8 +23,11 @@ pub struct CachedWorld { // we store `PalettedContainer`s instead of `Chunk`s or `Section`s because it doesn't contain // any unnecessary data like heightmaps or biomes. cached_chunks: RefCell)>>, + last_chunk_cache_index: RefCell>, cached_blocks: UnsafeCell, + + cached_mining_costs: RefCell>, } #[derive(Default)] @@ -82,7 +88,9 @@ impl CachedWorld { min_y, world_lock, cached_chunks: Default::default(), + last_chunk_cache_index: Default::default(), cached_blocks: Default::default(), + cached_mining_costs: Default::default(), } } @@ -100,24 +108,50 @@ impl CachedWorld { section_pos: ChunkSectionPos, f: impl FnOnce(&azalea_world::palette::PalettedContainer) -> T, ) -> Option { + if section_pos.y * 16 < self.min_y { + // y position is out of bounds + return None; + } + let chunk_pos = ChunkPos::from(section_pos); let section_index = azalea_world::chunk_storage::section_index(section_pos.y * 16, self.min_y) as usize; let mut cached_chunks = self.cached_chunks.borrow_mut(); - // get section from cache - if let Some(sections) = cached_chunks.iter().find_map(|(pos, sections)| { - if *pos == chunk_pos { - Some(sections) - } else { - None + // optimization: avoid doing the iter lookup if the last chunk we looked up is + // the same + if let Some(last_chunk_cache_index) = *self.last_chunk_cache_index.borrow() { + if cached_chunks[last_chunk_cache_index].0 == chunk_pos { + // don't bother with the iter lookup + let sections = &cached_chunks[last_chunk_cache_index].1; + if section_index >= sections.len() { + // y position is out of bounds + return None; + }; + let section: &azalea_world::palette::PalettedContainer = §ions[section_index]; + return Some(f(section)); } - }) { + } + + // get section from cache + if let Some((chunk_index, sections)) = + cached_chunks + .iter() + .enumerate() + .find_map(|(i, (pos, sections))| { + if *pos == chunk_pos { + Some((i, sections)) + } else { + None + } + }) + { if section_index >= sections.len() { // y position is out of bounds return None; }; + *self.last_chunk_cache_index.borrow_mut() = Some(chunk_index); let section: &azalea_world::palette::PalettedContainer = §ions[section_index]; return Some(f(section)); } @@ -206,18 +240,189 @@ impl CachedWorld { solid } + /// Returns how much it costs to break this block. Returns 0 if the block is + /// already passable. + pub fn cost_for_breaking_block(&self, pos: BlockPos, mining_cache: &MiningCache) -> f32 { + let mut cached_mining_costs = self.cached_mining_costs.borrow_mut(); + + if let Some(&cost) = cached_mining_costs.get(&pos) { + return cost; + } + + let cost = self.uncached_cost_for_breaking_block(pos, mining_cache); + cached_mining_costs.insert(pos, cost); + cost + } + + fn uncached_cost_for_breaking_block(&self, pos: BlockPos, mining_cache: &MiningCache) -> f32 { + if self.is_block_passable(pos) { + // if the block is passable then it doesn't need to be broken + return 0.; + } + + let (section_pos, section_block_pos) = + (ChunkSectionPos::from(pos), ChunkSectionBlockPos::from(pos)); + + // we use this as an optimization to avoid getting the section again if the + // block is in the same section + let up_is_in_same_section = section_block_pos.y != 15; + let north_is_in_same_section = section_block_pos.z != 0; + let east_is_in_same_section = section_block_pos.x != 15; + let south_is_in_same_section = section_block_pos.z != 15; + let west_is_in_same_section = section_block_pos.x != 0; + + let Some(mining_cost) = self.with_section(section_pos, |section| { + let block_state = + BlockState::try_from(section.get_at_index(u16::from(section_block_pos) as usize)) + .unwrap_or_default(); + let mining_cost = mining_cache.cost_for(block_state); + + if mining_cost == f32::INFINITY { + // the block is unbreakable + return f32::INFINITY; + } + + // if there's a falling block or liquid above this block, abort + if up_is_in_same_section { + let up_block = BlockState::try_from( + section.get_at_index(u16::from(section_block_pos.up(1)) as usize), + ) + .unwrap_or_default(); + if mining_cache.is_liquid(up_block) || mining_cache.is_falling_block(up_block) { + return f32::INFINITY; + } + } + + // if there's a liquid to the north of this block, abort + if north_is_in_same_section { + let north_block = BlockState::try_from( + section.get_at_index(u16::from(section_block_pos.north(1)) as usize), + ) + .unwrap_or_default(); + if mining_cache.is_liquid(north_block) { + return f32::INFINITY; + } + } + + // liquid to the east + if east_is_in_same_section { + let east_block = BlockState::try_from( + section.get_at_index(u16::from(section_block_pos.east(1)) as usize), + ) + .unwrap_or_default(); + if mining_cache.is_liquid(east_block) { + return f32::INFINITY; + } + } + + // liquid to the south + if south_is_in_same_section { + let south_block = BlockState::try_from( + section.get_at_index(u16::from(section_block_pos.south(1)) as usize), + ) + .unwrap_or_default(); + if mining_cache.is_liquid(south_block) { + return f32::INFINITY; + } + } + + // liquid to the west + if west_is_in_same_section { + let west_block = BlockState::try_from( + section.get_at_index(u16::from(section_block_pos.west(1)) as usize), + ) + .unwrap_or_default(); + if mining_cache.is_liquid(west_block) { + return f32::INFINITY; + } + } + + // the block is probably safe to break, we'll have to check the adjacent blocks + // that weren't in the same section next though + mining_cost + }) else { + // the chunk isn't loaded + let cost = if self.is_block_solid(pos) { + // assume it's unbreakable if it's solid and out of render distance + f32::INFINITY + } else { + 0. + }; + return cost; + }; + + if mining_cost == f32::INFINITY { + // the block is unbreakable + return f32::INFINITY; + } + + let check_should_avoid_this_block = |pos: BlockPos, check: &dyn Fn(BlockState) -> bool| { + let block_state = self + .with_section(ChunkSectionPos::from(pos), |section| { + BlockState::try_from( + section.get_at_index(u16::from(ChunkSectionBlockPos::from(pos)) as usize), + ) + .unwrap_or_default() + }) + .unwrap_or_default(); + check(block_state) + }; + + // check the adjacent blocks that weren't in the same section + if !up_is_in_same_section + && check_should_avoid_this_block(pos.up(1), &|b| { + mining_cache.is_liquid(b) || mining_cache.is_falling_block(b) + }) + { + return f32::INFINITY; + } + if !north_is_in_same_section + && check_should_avoid_this_block(pos.north(1), &|b| mining_cache.is_liquid(b)) + { + return f32::INFINITY; + } + if !east_is_in_same_section + && check_should_avoid_this_block(pos.east(1), &|b| mining_cache.is_liquid(b)) + { + return f32::INFINITY; + } + if !south_is_in_same_section + && check_should_avoid_this_block(pos.south(1), &|b| mining_cache.is_liquid(b)) + { + return f32::INFINITY; + } + if !west_is_in_same_section + && check_should_avoid_this_block(pos.west(1), &|b| mining_cache.is_liquid(b)) + { + return f32::INFINITY; + } + + mining_cost + } + /// Whether this block and the block above are passable pub fn is_passable(&self, pos: BlockPos) -> bool { self.is_block_passable(pos) && self.is_block_passable(pos.up(1)) } + pub fn cost_for_passing(&self, pos: BlockPos, mining_cache: &MiningCache) -> f32 { + self.cost_for_breaking_block(pos, mining_cache) + + self.cost_for_breaking_block(pos.up(1), mining_cache) + } + /// Whether we can stand in this position. Checks if the block below is /// solid, and that the two blocks above that are passable. - pub fn is_standable(&self, pos: BlockPos) -> bool { self.is_block_solid(pos.down(1)) && self.is_passable(pos) } + pub fn cost_for_standing(&self, pos: BlockPos, mining_cache: &MiningCache) -> f32 { + if !self.is_block_solid(pos.down(1)) { + return f32::INFINITY; + } + self.cost_for_passing(pos, mining_cache) + } + /// Get the amount of air blocks until the next solid block below this one. pub fn fall_distance(&self, pos: BlockPos) -> u32 { let mut distance = 0; @@ -235,7 +440,10 @@ impl CachedWorld { } /// whether this block is passable -fn is_block_state_passable(block: BlockState) -> bool { +pub fn is_block_state_passable(block: BlockState) -> bool { + // i already tried optimizing this by having it cache in an IntMap/FxHashMap but + // it wasn't measurably faster + if block.is_air() { // fast path return true; @@ -243,13 +451,17 @@ fn is_block_state_passable(block: BlockState) -> bool { if !block.is_shape_empty() { return false; } - if block == azalea_registry::Block::Water.into() { + let registry_block = azalea_registry::Block::from(block); + if registry_block == azalea_registry::Block::Water { return false; } - if block.waterlogged() { + if block + .property::() + .unwrap_or_default() + { return false; } - if block == azalea_registry::Block::Lava.into() { + if registry_block == azalea_registry::Block::Lava { return false; } // block.waterlogged currently doesn't account for seagrass and some other water @@ -258,11 +470,18 @@ fn is_block_state_passable(block: BlockState) -> bool { return false; } + // don't walk into fire + if registry_block == azalea_registry::Block::Fire + || registry_block == azalea_registry::Block::SoulFire + { + return false; + } + true } /// whether this block has a solid hitbox (i.e. we can stand on it) -fn is_block_state_solid(block: BlockState) -> bool { +pub fn is_block_state_solid(block: BlockState) -> bool { if block.is_air() { // fast path return false; diff --git a/azalea/src/prelude.rs b/azalea/src/prelude.rs index 87cb0b53c..b4d66aaf9 100644 --- a/azalea/src/prelude.rs +++ b/azalea/src/prelude.rs @@ -9,3 +9,4 @@ pub use azalea_client::{Account, Client, Event}; // this is necessary to make the macros that reference bevy_ecs work pub use crate::ecs as bevy_ecs; pub use crate::ecs::{component::Component, system::Resource}; +pub use azalea_core::tick::GameTick; diff --git a/azalea/src/swarm/chat.rs b/azalea/src/swarm/chat.rs index 8ed5d7f5e..7425293bf 100644 --- a/azalea/src/swarm/chat.rs +++ b/azalea/src/swarm/chat.rs @@ -161,7 +161,7 @@ mod tests { fn make_test_app() -> App { let mut app = App::new(); // we add the events like this instead of with .add_event so we can have our own - // event mangement in drain_events + // event management in drain_events app.init_resource::>() .init_resource::>() .add_systems( diff --git a/azalea/src/swarm/mod.rs b/azalea/src/swarm/mod.rs index 3489c5e72..ff1a57029 100644 --- a/azalea/src/swarm/mod.rs +++ b/azalea/src/swarm/mod.rs @@ -7,22 +7,18 @@ pub mod prelude; use azalea_client::{ chat::ChatPacket, start_ecs_runner, Account, Client, DefaultPlugins, Event, JoinError, }; -use azalea_protocol::{ - connect::{ConnectionError, Proxy}, - resolver::{self, ResolverError}, - ServerAddress, -}; +use azalea_protocol::{resolver, ServerAddress, connect::Proxy}; use azalea_world::InstanceContainer; use bevy_app::{App, PluginGroup, PluginGroupBuilder, Plugins}; use bevy_ecs::{component::Component, entity::Entity, system::Resource, world::World}; use futures::future::{join_all, BoxFuture}; use parking_lot::{Mutex, RwLock}; +use rand::{seq::SliceRandom, thread_rng}; use std::{collections::HashMap, future::Future, net::SocketAddr, sync::Arc, time::Duration}; -use thiserror::Error; use tokio::sync::mpsc; use tracing::error; -use crate::{BoxHandleFn, DefaultBotPlugins, HandleFn, NoState}; +use crate::{BoxHandleFn, DefaultBotPlugins, HandleFn, NoState, StartError}; /// A swarm is a way to conveniently control many bots at once, while also /// being able to control bots at an individual level when desired. @@ -36,10 +32,12 @@ pub struct Swarm { pub ecs_lock: Arc>, bots: Arc>>, + proxies: Vec, + + // the address is public and mutable so plugins can change it + pub resolved_address: Arc>, + pub address: Arc>, - // bot_datas: Arc>>, - resolved_address: SocketAddr, - address: ServerAddress, pub instance_container: Arc>, bots_tx: mpsc::UnboundedSender<(Option, Client)>, @@ -54,25 +52,27 @@ where S: Send + Sync + Clone + Component + 'static, SS: Default + Send + Sync + Clone + Resource + 'static, { - app: App, + pub(crate) app: App, /// The accounts that are going to join the server. - accounts: Vec, + pub(crate) accounts: Vec, + /// The proxies used for the swarm, if any. If empty, no proxies will be used. + pub (crate) proxies: Vec, /// The individual bot states. This must be the same length as `accounts`, /// since each bot gets one state. - states: Vec, + pub(crate) states: Vec, /// The state for the overall swarm. - swarm_state: SS, + pub(crate) swarm_state: SS, /// The function that's called every time a bot receives an [`Event`]. - handler: Option>, + pub(crate) handler: Option>, /// The function that's called every time the swarm receives a /// [`SwarmEvent`]. - swarm_handler: Option>, + pub(crate) swarm_handler: Option>, /// How long we should wait between each bot joining the server. Set to /// None to have every bot connect at the same time. None is different than /// a duration of 0, since if a duration is present the bots will wait for /// the previous one to be ready. - join_delay: Option, + pub(crate) join_delay: Option, } impl SwarmBuilder { /// Start creating the swarm. @@ -116,6 +116,7 @@ impl SwarmBuilder { // the schedules won't run until [`Self::start`] is called. app: App::new(), accounts: Vec::new(), + proxies: Vec::new(), states: Vec::new(), swarm_state: NoSwarmState, handler: None, @@ -213,6 +214,7 @@ where handler: self.handler, app: self.app, accounts: self.accounts, + proxies: self.proxies, states: self.states, swarm_state: SS::default(), swarm_handler: Some(Box::new(move |swarm, event, state| { @@ -233,6 +235,9 @@ where /// Use [`Self::add_account`] to only add one account. If you want the /// clients to have different default states, add them one at a time with /// [`Self::add_account_with_state`]. + /// + /// By default every account will join at the same time, you can add a delay + /// with [`Self::join_delay`]. #[must_use] pub fn add_accounts(mut self, accounts: Vec) -> Self where @@ -264,6 +269,16 @@ where self } + pub fn add_proxy(mut self, proxy: Proxy) -> Self { + self.proxies.push(proxy); + self + } + + pub fn add_proxies(mut self, proxies: Vec) -> Self { + self.proxies.extend(proxies); + self + } + /// Set the swarm state instead of initializing defaults. #[must_use] pub fn set_swarm_state(mut self, swarm_state: SS) -> Self { @@ -296,7 +311,7 @@ where /// that implements `TryInto`. /// /// [`ServerAddress`]: azalea_protocol::ServerAddress - pub async fn start(self, address: impl TryInto) -> Result<(), SwarmStartError> { + pub async fn start(self, address: impl TryInto) -> Result { assert_eq!( self.accounts.len(), self.states.len(), @@ -306,7 +321,7 @@ where // convert the TryInto into a ServerAddress let address: ServerAddress = match address.try_into() { Ok(address) => address, - Err(_) => return Err(SwarmStartError::InvalidAddress), + Err(_) => return Err(StartError::InvalidAddress), }; // resolve the address @@ -318,16 +333,22 @@ where let (bots_tx, mut bots_rx) = mpsc::unbounded_channel(); let (swarm_tx, mut swarm_rx) = mpsc::unbounded_channel(); + swarm_tx.send(SwarmEvent::Init).unwrap(); + let (run_schedule_sender, run_schedule_receiver) = mpsc::unbounded_channel(); + + let main_schedule_label = self.app.main_schedule_label; + let ecs_lock = start_ecs_runner(self.app, run_schedule_receiver, run_schedule_sender.clone()); let swarm = Swarm { ecs_lock: ecs_lock.clone(), bots: Arc::new(Mutex::new(HashMap::new())), + proxies: self.proxies, - resolved_address, - address, + resolved_address: Arc::new(RwLock::new(resolved_address)), + address: Arc::new(RwLock::new(address)), instance_container, bots_tx, @@ -336,7 +357,14 @@ where run_schedule_sender, }; - ecs_lock.lock().insert_resource(swarm.clone()); + + // run the main schedule so the startup systems run + { + let mut ecs = ecs_lock.lock(); + ecs.insert_resource(swarm.clone()); + ecs.run_schedule(main_schedule_label); + ecs.clear_trackers(); + } // SwarmBuilder (self) isn't Send so we have to take all the things we need out // of it @@ -345,13 +373,11 @@ where let accounts = self.accounts.clone(); let states = self.states.clone(); - let join_task = tokio::spawn(async move { + tokio::spawn(async move { if let Some(join_delay) = join_delay { // if there's a join delay, then join one by one for (account, state) in accounts.iter().zip(states) { - swarm_clone - .add_with_exponential_backoff(account, None, state) - .await; + swarm_clone.add_and_retry_forever(account, state).await; tokio::time::sleep(join_delay).await; } } else { @@ -364,12 +390,14 @@ where .map(move |(account, state)| async { swarm_borrow .clone() - .add_with_exponential_backoff(account, None, state) + .add_and_retry_forever(account, state) .await; }), ) .await; } + + swarm_tx.send(SwarmEvent::Login).unwrap(); }); let swarm_state = self.swarm_state; @@ -408,9 +436,9 @@ where } } - join_task.abort(); - - Ok(()) + unreachable!( + "bots_rx.recv() should never be None because the bots_tx channel is never closed" + ); } } @@ -441,16 +469,6 @@ pub type SwarmHandleFn = fn(Swarm, SwarmEvent, SS) -> Fut; pub type BoxSwarmHandleFn = Box BoxFuture<'static, Result<(), anyhow::Error>> + Send>; -#[derive(Error, Debug)] -pub enum SwarmStartError { - #[error("Invalid address")] - InvalidAddress, - #[error(transparent)] - ResolveAddress(#[from] ResolverError), - #[error("Join error: {0}")] - Join(#[from] azalea_client::JoinError), -} - /// Make a bot [`Swarm`]. /// /// [`Swarm`]: struct.Swarm.html @@ -467,7 +485,7 @@ pub enum SwarmStartError { /// struct SwarmState {} /// /// #[tokio::main] -/// async fn main() -> anyhow::Result<()> { +/// async fn main() { /// let mut accounts = Vec::new(); /// let mut states = Vec::new(); /// @@ -476,16 +494,14 @@ pub enum SwarmStartError { /// states.push(State::default()); /// } /// -/// loop { -/// let e = SwarmBuilder::new() -/// .add_accounts(accounts.clone()) -/// .set_handler(handle) -/// .set_swarm_handler(swarm_handle) -/// .join_delay(Duration::from_millis(1000)) -/// .start("localhost") -/// .await; -/// println!("{e:?}"); -/// } +/// SwarmBuilder::new() +/// .add_accounts(accounts.clone()) +/// .set_handler(handle) +/// .set_swarm_handler(swarm_handle) +/// .join_delay(Duration::from_millis(1000)) +/// .start("localhost") +/// .await +/// .unwrap(); /// } /// /// async fn handle(bot: Client, event: Event, _state: State) -> anyhow::Result<()> { @@ -527,17 +543,14 @@ impl Swarm { proxy: Option, state: S, ) -> Result { - // tx is moved to the bot so it can send us events - // rx is used to receive events from the bot - // An event that causes the schedule to run. This is only used internally. - // let (run_schedule_sender, run_schedule_receiver) = mpsc::unbounded_channel(); - // let ecs_lock = start_ecs_runner(run_schedule_receiver, - // run_schedule_sender.clone()); + let address = self.address.read().clone(); + let resolved_address = *self.resolved_address.read(); + let (bot, mut rx) = Client::start_client( self.ecs_lock.clone(), account, - &self.address, - &self.resolved_address, + &address, + &resolved_address, proxy, self.run_schedule_sender.clone(), ) @@ -577,17 +590,17 @@ impl Swarm { /// Add a new account to the swarm, retrying if it couldn't join. This will /// run forever until the bot joins or the task is aborted. /// - /// Exponential backoff means if it fails joining it will initially wait 10 - /// seconds, then 20, then 40, up to 2 minutes. - pub async fn add_with_exponential_backoff( + /// This does exponential backoff (though very limited), starting at 5 + /// seconds and doubling up to 15 seconds. + pub async fn add_and_retry_forever( &mut self, account: &Account, - proxy: Option, state: S, ) -> Client { let mut disconnects = 0; loop { - match self.add(account, proxy.clone(), state.clone()).await { + let proxy = self.proxies.choose(&mut thread_rng()).map(|p| p.clone()); + match self.add(account, proxy, state.clone()).await { Ok(bot) => return bot, Err(e) => { disconnects += 1; @@ -629,12 +642,6 @@ impl IntoIterator for Swarm { } } -impl From for SwarmStartError { - fn from(e: ConnectionError) -> Self { - SwarmStartError::from(JoinError::from(e)) - } -} - /// This plugin group will add all the default plugins necessary for swarms to /// work. pub struct DefaultSwarmPlugins; diff --git a/codegen/lib/code/blocks.py b/codegen/lib/code/blocks.py index 3af19fa83..f96b11166 100755 --- a/codegen/lib/code/blocks.py +++ b/codegen/lib/code/blocks.py @@ -71,9 +71,9 @@ def generate_blocks(blocks_burger: dict, blocks_report: dict, pixlyzer_block_dat # }, property_name = property_struct_names_to_names[property_struct_name] - # if the only variants are true and false, we can just make it a normal boolean + # if the only variants are true and false, we make it unit struct with a boolean instead of an enum if property_variants == ['true', 'false']: - property_shape_code = 'bool' + property_shape_code = f'{property_struct_name}(bool)' else: property_shape_code = f'{property_struct_name} {{\n' for variant in property_variants: @@ -119,7 +119,7 @@ def generate_blocks(blocks_burger: dict, blocks_report: dict, pixlyzer_block_dat if is_boolean_property: # if it's a boolean, keep the type lowercase # (so it's either `true` or `false`) - property_default_type = property_default + property_default_type = f'{property_struct_name}({property_default})' else: property_default_type = f'{property_struct_name}::{to_camel_case(property_default)}' @@ -208,6 +208,10 @@ def get_property_struct_name(property: Optional[dict], block_data_burger: dict, if property is None: return ''.join(map(to_camel_case, property_variants)) + if property_variants == ['true', 'false']: + # booleans are weird, so just return the string name minecraft uses + return to_camel_case(property['name']) + for class_name in [block_data_burger['class']] + block_data_burger['super']: property_name = mappings.get_field( class_name, property['field_name']) diff --git a/codegen/lib/code/utils.py b/codegen/lib/code/utils.py index cb835ecbf..f4f9ca82d 100755 --- a/codegen/lib/code/utils.py +++ b/codegen/lib/code/utils.py @@ -76,6 +76,9 @@ def burger_type_to_rust_type(burger_type, field_name: Optional[str] = None, inst field_type_rs = 'todo!()' elif burger_type == 'Iterator': field_type_rs = 'todo!()' + elif burger_type == 'Object': + # depends on context + field_type_rs = 'todo!()' elif burger_type == 'enum': if not instruction or not mappings or not obfuscated_class_name: field_type_rs = 'todo!("enum")' diff --git a/codegen/lib/code/version.py b/codegen/lib/code/version.py index 35779af6c..c2053da3c 100755 --- a/codegen/lib/code/version.py +++ b/codegen/lib/code/version.py @@ -3,7 +3,7 @@ import os README_DIR = get_dir_location('../README.md') -VERSION_REGEX = r'\*Currently supported Minecraft version: `(.*)`.\*' +VERSION_REGEX = r'\_Currently supported Minecraft version: `(.*)`.\_' def get_version_id() -> str: @@ -35,7 +35,7 @@ def set_version_id(version_id: str) -> None: def get_protocol_version() -> str: # azalea-protocol/src/packets/mod.rs - # pub const PROTOCOL_VERSION: u32 = 758; + # pub const PROTOCOL_VERSION: i32 = 758; with open(get_dir_location('../azalea-protocol/src/packets/mod.rs'), 'r') as f: mod_rs = f.read().splitlines() for line in mod_rs: @@ -50,7 +50,7 @@ def set_protocol_version(protocol_version: str) -> None: mod_rs = f.read().splitlines() for i, line in enumerate(mod_rs): if line.strip().startswith('pub const PROTOCOL_VERSION'): - mod_rs[i] = f'pub const PROTOCOL_VERSION: u32 = {protocol_version};' + mod_rs[i] = f'pub const PROTOCOL_VERSION: i32 = {protocol_version};' break else: raise Exception( diff --git a/codegen/migrate.py b/codegen/migrate.py index 6e51c2df2..860d860ee 100755 --- a/codegen/migrate.py +++ b/codegen/migrate.py @@ -124,7 +124,7 @@ new_version_id, 'blocks') lib.code.blocks.generate_blocks( - block_states_burger, block_states_report, new_ordered_blocks, new_mappings) + block_states_burger, block_states_report, pixlyzer_block_datas, new_ordered_blocks, new_mappings) lib.code.shapes.generate_block_shapes( pixlyzer_block_datas, shape_datas['shapes'], shape_datas['aabbs'], block_states_report, block_states_burger, new_mappings)