aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock780
-rwxr-xr-xCargo.toml11
-rwxr-xr-xazalea-auth/README.md2
-rwxr-xr-xazalea-auth/src/game_profile.rs2
-rwxr-xr-xazalea-block/azalea-block-macros/src/lib.rs15
-rwxr-xr-xazalea-brigadier/src/command_dispatcher.rs6
-rwxr-xr-xazalea-brigadier/src/exceptions/builtin_exceptions.rs2
-rwxr-xr-xazalea-brigadier/src/suggestion/mod.rs6
-rwxr-xr-xazalea-brigadier/src/suggestion/suggestions.rs8
-rwxr-xr-xazalea-brigadier/src/tree/mod.rs6
-rw-r--r--azalea-buf/azalea-buf-macros/src/read.rs11
-rw-r--r--azalea-buf/azalea-buf-macros/src/write.rs9
-rwxr-xr-xazalea-chat/README.md8
-rwxr-xr-xazalea-chat/src/base_component.rs4
-rwxr-xr-xazalea-chat/src/component.rs104
-rwxr-xr-xazalea-chat/src/lib.rs2
-rwxr-xr-xazalea-chat/src/text_component.rs23
-rwxr-xr-xazalea-chat/src/translatable_component.rs29
-rwxr-xr-xazalea-chat/tests/integration_test.rs8
-rw-r--r--azalea-client/Cargo.toml26
-rw-r--r--azalea-client/examples/echo.rs49
-rwxr-xr-xazalea-client/src/account.rs2
-rwxr-xr-xazalea-client/src/chat.rs25
-rw-r--r--azalea-client/src/client.rs1135
-rw-r--r--azalea-client/src/entity_query.rs100
-rw-r--r--azalea-client/src/events.rs189
-rw-r--r--azalea-client/src/lib.rs24
-rw-r--r--azalea-client/src/local_player.rs164
-rw-r--r--azalea-client/src/movement.rs415
-rw-r--r--azalea-client/src/packet_handling.rs935
-rwxr-xr-xazalea-client/src/player.rs32
-rw-r--r--azalea-client/src/plugins.rs144
-rw-r--r--azalea-client/src/task_pool.rs177
-rwxr-xr-xazalea-core/Cargo.toml12
-rwxr-xr-xazalea-core/src/delta.rs1
-rwxr-xr-xazalea-core/src/particle/mod.rs1
-rwxr-xr-xazalea-core/src/position.rs14
-rwxr-xr-xazalea-core/src/resource_location.rs3
-rw-r--r--azalea-ecs/Cargo.toml13
-rwxr-xr-xazalea-ecs/azalea-ecs-macros/Cargo.toml15
-rw-r--r--azalea-ecs/azalea-ecs-macros/src/component.rs125
-rw-r--r--azalea-ecs/azalea-ecs-macros/src/fetch.rs466
-rwxr-xr-xazalea-ecs/azalea-ecs-macros/src/lib.rs523
-rw-r--r--azalea-ecs/azalea-ecs-macros/src/utils/attrs.rs45
-rw-r--r--azalea-ecs/azalea-ecs-macros/src/utils/mod.rs224
-rw-r--r--azalea-ecs/azalea-ecs-macros/src/utils/shape.rs21
-rw-r--r--azalea-ecs/azalea-ecs-macros/src/utils/symbol.rs35
-rw-r--r--azalea-ecs/src/lib.rs144
-rwxr-xr-xazalea-nbt/src/encode.rs24
-rwxr-xr-xazalea-nbt/src/tag.rs11
-rw-r--r--azalea-physics/Cargo.toml9
-rwxr-xr-xazalea-physics/src/collision/discrete_voxel_shape.rs2
-rw-r--r--azalea-physics/src/collision/mod.rs304
-rwxr-xr-xazalea-physics/src/collision/shape.rs2
-rw-r--r--azalea-physics/src/collision/world_collisions.rs39
-rw-r--r--azalea-physics/src/lib.rs571
-rw-r--r--azalea-protocol/Cargo.toml1
-rwxr-xr-xazalea-protocol/azalea-protocol-macros/src/lib.rs12
-rwxr-xr-xazalea-protocol/src/packets/game/clientbound_add_entity_packet.rs48
-rwxr-xr-xazalea-protocol/src/packets/game/clientbound_add_player_packet.rs26
-rwxr-xr-xazalea-protocol/src/packets/game/clientbound_award_stats_packet.rs4
-rwxr-xr-xazalea-protocol/src/packets/game/clientbound_block_entity_data_packet.rs2
-rwxr-xr-xazalea-protocol/src/packets/game/clientbound_boss_event_packet.rs8
-rwxr-xr-xazalea-protocol/src/packets/game/clientbound_chat_preview_packet.rs4
-rwxr-xr-xazalea-protocol/src/packets/game/clientbound_command_suggestions_packet.rs6
-rwxr-xr-xazalea-protocol/src/packets/game/clientbound_commands_packet.rs2
-rwxr-xr-xazalea-protocol/src/packets/game/clientbound_disconnect_packet.rs4
-rw-r--r--azalea-protocol/src/packets/game/clientbound_disguised_chat_packet.rs4
-rwxr-xr-xazalea-protocol/src/packets/game/clientbound_map_item_data_packet.rs4
-rwxr-xr-xazalea-protocol/src/packets/game/clientbound_open_screen_packet.rs4
-rw-r--r--azalea-protocol/src/packets/game/clientbound_player_chat_packet.rs24
-rwxr-xr-xazalea-protocol/src/packets/game/clientbound_player_combat_kill_packet.rs4
-rw-r--r--azalea-protocol/src/packets/game/clientbound_player_info_update_packet.rs6
-rwxr-xr-xazalea-protocol/src/packets/game/clientbound_resource_pack_packet.rs4
-rwxr-xr-xazalea-protocol/src/packets/game/clientbound_server_data_packet.rs4
-rwxr-xr-xazalea-protocol/src/packets/game/clientbound_set_action_bar_text_packet.rs4
-rwxr-xr-xazalea-protocol/src/packets/game/clientbound_set_objective_packet.rs4
-rwxr-xr-xazalea-protocol/src/packets/game/clientbound_set_player_team_packet.rs8
-rwxr-xr-xazalea-protocol/src/packets/game/clientbound_set_subtitle_text_packet.rs4
-rwxr-xr-xazalea-protocol/src/packets/game/clientbound_set_title_text_packet.rs4
-rwxr-xr-xazalea-protocol/src/packets/game/clientbound_system_chat_packet.rs4
-rwxr-xr-xazalea-protocol/src/packets/game/clientbound_tab_list_packet.rs6
-rwxr-xr-xazalea-protocol/src/packets/game/clientbound_teleport_entity_packet.rs5
-rwxr-xr-xazalea-protocol/src/packets/game/clientbound_update_advancements_packet.rs10
-rwxr-xr-xazalea-protocol/src/packets/game/serverbound_set_jigsaw_block_packet.rs3
-rwxr-xr-xazalea-protocol/src/packets/login/clientbound_login_disconnect_packet.rs4
-rwxr-xr-xazalea-protocol/src/packets/status/clientbound_status_response_packet.rs4
-rwxr-xr-xazalea-registry/Cargo.toml7
-rwxr-xr-xazalea-registry/azalea-registry-macros/src/lib.rs2
-rwxr-xr-xazalea-registry/src/lib.rs60
-rw-r--r--azalea-world/Cargo.toml16
-rwxr-xr-xazalea-world/src/chunk_storage.rs123
-rw-r--r--azalea-world/src/container.rs60
-rw-r--r--azalea-world/src/entity/attributes.rs7
-rwxr-xr-xazalea-world/src/entity/data.rs23
-rwxr-xr-xazalea-world/src/entity/dimensions.rs15
-rw-r--r--azalea-world/src/entity/metadata.rs17494
-rw-r--r--azalea-world/src/entity/mod.rs478
-rw-r--r--azalea-world/src/entity_info.rs357
-rwxr-xr-xazalea-world/src/entity_storage.rs416
-rw-r--r--azalea-world/src/lib.rs8
-rwxr-xr-xazalea-world/src/palette.rs2
-rw-r--r--azalea-world/src/world.rs372
-rw-r--r--azalea/Cargo.toml24
-rwxr-xr-xazalea/README.md83
-rwxr-xr-xazalea/examples/craft_dig_straight_down.rs6
-rwxr-xr-xazalea/examples/echo.rs2
-rw-r--r--azalea/examples/mine_a_chunk.rs2
-rwxr-xr-xazalea/examples/potatobot/autoeat.rs3
-rwxr-xr-xazalea/examples/pvp.rs16
-rw-r--r--azalea/src/bot.rs131
-rw-r--r--azalea/src/lib.rs209
-rw-r--r--azalea/src/pathfinder/mod.rs341
-rw-r--r--azalea/src/pathfinder/moves.rs136
-rw-r--r--azalea/src/pathfinder/mtdstarlite.rs8
-rw-r--r--azalea/src/prelude.rs6
-rw-r--r--azalea/src/start.rs136
-rw-r--r--azalea/src/swarm/chat.rs344
-rw-r--r--azalea/src/swarm/events.rs45
-rw-r--r--azalea/src/swarm/mod.rs614
-rw-r--r--azalea/src/swarm/plugins.rs135
-rw-r--r--bot/src/main.rs133
-rw-r--r--codegen/lib/code/entity.py649
-rwxr-xr-xcodegen/lib/code/registry.py7
-rwxr-xr-xcodegen/lib/code/utils.py4
125 files changed, 17605 insertions, 12157 deletions
diff --git a/Cargo.lock b/Cargo.lock
index d7502e43..03f77a2e 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -4,9 +4,9 @@ version = 3
[[package]]
name = "addr2line"
-version = "0.17.0"
+version = "0.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b9ecd88a8c8378ca913a680cd98f0f13ac67383d35993f86c90a70e3f137816b"
+checksum = "a76fd60b23679b7d19bd066031410fb7e458ccc5e958eb5c325888ce4baedc97"
dependencies = [
"gimli",
]
@@ -30,9 +30,20 @@ dependencies = [
[[package]]
name = "ahash"
-version = "0.8.2"
+version = "0.7.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47"
+dependencies = [
+ "getrandom",
+ "once_cell",
+ "version_check",
+]
+
+[[package]]
+name = "ahash"
+version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bf6ccdb167abbf410dcb915cabd428929d7f6a04980b54a11f26a39f1c7f7107"
+checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f"
dependencies = [
"cfg-if",
"getrandom",
@@ -52,9 +63,20 @@ dependencies = [
[[package]]
name = "anyhow"
-version = "1.0.66"
+version = "1.0.68"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "216261ddc8289130e551ddcd5ce8a064710c0d064a4d2895c67151c92b5443f6"
+checksum = "2cb2f989d18dd141ab8ae82f64d1a8cdd37e0840f73a406896cf5e99502fab61"
+
+[[package]]
+name = "async-channel"
+version = "1.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cf46fee83e5ccffc220104713af3292ff9bc7c64c7de289f66dae8e38d826833"
+dependencies = [
+ "concurrent-queue 2.1.0",
+ "event-listener",
+ "futures-core",
+]
[[package]]
name = "async-compression"
@@ -70,10 +92,34 @@ dependencies = [
]
[[package]]
+name = "async-executor"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "17adb73da160dfb475c183343c8cccd80721ea5a605d3eb57125f0a7b7a92d0b"
+dependencies = [
+ "async-lock",
+ "async-task",
+ "concurrent-queue 2.1.0",
+ "fastrand",
+ "futures-lite",
+ "slab",
+]
+
+[[package]]
+name = "async-lock"
+version = "2.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c8101efe8695a6c17e02911402145357e718ac92d3ff88ae8419e84b1707b685"
+dependencies = [
+ "event-listener",
+ "futures-lite",
+]
+
+[[package]]
name = "async-recursion"
-version = "1.0.0"
+version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2cda8f4bcc10624c4e85bc66b3f452cca98cfa5ca002dc83a16aad2367641bea"
+checksum = "3b015a331cc64ebd1774ba119538573603427eaace0a1950c423ab971f903796"
dependencies = [
"proc-macro2",
"quote",
@@ -81,10 +127,16 @@ dependencies = [
]
[[package]]
+name = "async-task"
+version = "4.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7a40729d2133846d9ed0ea60a8b9541bccddab49cd30f0715a1da672fe9a2524"
+
+[[package]]
name = "async-trait"
-version = "0.1.59"
+version = "0.1.63"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "31e6e93155431f3931513b243d371981bb2770112b370c82745a1d19d2f99364"
+checksum = "eff18d764974428cf3a9328e23fc5c986f5fbed46e6cd4cdf42544df5d297ec1"
dependencies = [
"proc-macro2",
"quote",
@@ -118,11 +170,17 @@ dependencies = [
"azalea-chat",
"azalea-client",
"azalea-core",
+ "azalea-ecs",
"azalea-physics",
"azalea-protocol",
+ "azalea-registry",
"azalea-world",
+ "bevy_tasks",
+ "derive_more",
"env_logger 0.10.0",
"futures",
+ "futures-lite",
+ "iyes_loopless",
"log",
"nohash-hasher",
"num-traits",
@@ -221,9 +279,17 @@ dependencies = [
"azalea-chat",
"azalea-core",
"azalea-crypto",
+ "azalea-ecs",
"azalea-physics",
"azalea-protocol",
+ "azalea-registry",
"azalea-world",
+ "bevy_tasks",
+ "bevy_time",
+ "derive_more",
+ "env_logger 0.9.3",
+ "futures",
+ "iyes_loopless",
"log",
"nohash-hasher",
"once_cell",
@@ -242,6 +308,7 @@ dependencies = [
"azalea-buf",
"azalea-chat",
"azalea-nbt",
+ "bevy_ecs",
"uuid",
]
@@ -261,6 +328,27 @@ dependencies = [
]
[[package]]
+name = "azalea-ecs"
+version = "0.5.0"
+dependencies = [
+ "azalea-ecs-macros",
+ "bevy_app",
+ "bevy_ecs",
+ "iyes_loopless",
+ "tokio",
+]
+
+[[package]]
+name = "azalea-ecs-macros"
+version = "0.5.0"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+ "toml 0.7.0",
+]
+
+[[package]]
name = "azalea-language"
version = "0.5.0"
dependencies = [
@@ -273,7 +361,7 @@ dependencies = [
name = "azalea-nbt"
version = "0.5.0"
dependencies = [
- "ahash",
+ "ahash 0.8.3",
"azalea-buf",
"byteorder",
"criterion",
@@ -290,7 +378,10 @@ version = "0.5.0"
dependencies = [
"azalea-block",
"azalea-core",
+ "azalea-ecs",
+ "azalea-registry",
"azalea-world",
+ "iyes_loopless",
"once_cell",
"parking_lot",
"uuid",
@@ -314,6 +405,7 @@ dependencies = [
"azalea-protocol-macros",
"azalea-registry",
"azalea-world",
+ "bevy_ecs",
"byteorder",
"bytes",
"flate2",
@@ -347,6 +439,7 @@ version = "0.5.0"
dependencies = [
"azalea-buf",
"azalea-registry-macros",
+ "enum-as-inner",
]
[[package]]
@@ -366,11 +459,15 @@ dependencies = [
"azalea-buf",
"azalea-chat",
"azalea-core",
+ "azalea-ecs",
"azalea-nbt",
"azalea-registry",
+ "derive_more",
"enum-as-inner",
+ "iyes_loopless",
"log",
"nohash-hasher",
+ "once_cell",
"parking_lot",
"thiserror",
"uuid",
@@ -378,24 +475,200 @@ dependencies = [
[[package]]
name = "backtrace"
-version = "0.3.66"
+version = "0.3.67"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cab84319d616cfb654d03394f38ab7e6f0919e181b1b57e1fd15e7fb4077d9a7"
+checksum = "233d376d6d185f2a3093e58f283f60f880315b6c60075b01f36b3b85154564ca"
dependencies = [
"addr2line",
"cc",
"cfg-if",
"libc",
- "miniz_oxide 0.5.4",
+ "miniz_oxide",
"object",
"rustc-demangle",
]
[[package]]
name = "base64"
-version = "0.13.1"
+version = "0.21.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8"
+checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a"
+
+[[package]]
+name = "bevy_app"
+version = "0.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "536e4d0018347478545ed8b6cb6e57b9279ee984868e81b7c0e78e0fb3222e42"
+dependencies = [
+ "bevy_derive",
+ "bevy_ecs",
+ "bevy_reflect",
+ "bevy_utils",
+ "downcast-rs",
+ "wasm-bindgen",
+ "web-sys",
+]
+
+[[package]]
+name = "bevy_derive"
+version = "0.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7baf73c58d41c353c6fd08e6764a2e7420c9f19e8227b391c50981db6d0282a6"
+dependencies = [
+ "bevy_macro_utils",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "bevy_ecs"
+version = "0.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d4c071d7c6bc9801253485e05d0c257284150de755391902746837ba21c0cf74"
+dependencies = [
+ "async-channel",
+ "bevy_ecs_macros",
+ "bevy_ptr",
+ "bevy_reflect",
+ "bevy_tasks",
+ "bevy_utils",
+ "downcast-rs",
+ "event-listener",
+ "fixedbitset",
+ "fxhash",
+ "serde",
+ "thread_local",
+]
+
+[[package]]
+name = "bevy_ecs_macros"
+version = "0.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c15bd45438eeb681ad74f2d205bb07a5699f98f9524462a30ec764afab2742ce"
+dependencies = [
+ "bevy_macro_utils",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "bevy_macro_utils"
+version = "0.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "022bb69196deeea691b6997414af85bbd7f2b34a8914c4aa7a7ff4dfa44f7677"
+dependencies = [
+ "quote",
+ "syn",
+ "toml 0.5.11",
+]
+
+[[package]]
+name = "bevy_math"
+version = "0.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d434c77ab766c806ed9062ef8a7285b3b02b47df51f188d4496199c3ac062eaf"
+dependencies = [
+ "glam",
+ "serde",
+]
+
+[[package]]
+name = "bevy_ptr"
+version = "0.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8ec44f7655039546bc5d34d98de877083473f3e9b2b81d560c528d6d74d3eff4"
+
+[[package]]
+name = "bevy_reflect"
+version = "0.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6deae303a7f69dc243b2fa35b5e193cc920229f448942080c8eb2dbd9de6d37a"
+dependencies = [
+ "bevy_math",
+ "bevy_ptr",
+ "bevy_reflect_derive",
+ "bevy_utils",
+ "downcast-rs",
+ "erased-serde",
+ "glam",
+ "once_cell",
+ "parking_lot",
+ "serde",
+ "smallvec",
+ "thiserror",
+]
+
+[[package]]
+name = "bevy_reflect_derive"
+version = "0.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a2bf4cb9cd5acb4193f890f36cb63679f1502e2de025e66a63b194b8b133d018"
+dependencies = [
+ "bevy_macro_utils",
+ "bit-set",
+ "proc-macro2",
+ "quote",
+ "syn",
+ "uuid",
+]
+
+[[package]]
+name = "bevy_tasks"
+version = "0.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "680b16b53df9c9f24681dd95f4d772d83760bd19adf8bca00f358a3aad997853"
+dependencies = [
+ "async-channel",
+ "async-executor",
+ "async-task",
+ "concurrent-queue 1.2.4",
+ "futures-lite",
+ "once_cell",
+ "wasm-bindgen-futures",
+]
+
+[[package]]
+name = "bevy_time"
+version = "0.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1a5c38a6d3ea929c7f81e6adf5a6c62cf7e8c40f5106c2174d6057e9d8ea624d"
+dependencies = [
+ "bevy_app",
+ "bevy_ecs",
+ "bevy_reflect",
+ "bevy_utils",
+ "crossbeam-channel",
+]
+
+[[package]]
+name = "bevy_utils"
+version = "0.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "16750aae52cd35bd7b60eb61cee883420b250e11b4a290b8d44b2b2941795739"
+dependencies = [
+ "ahash 0.7.6",
+ "getrandom",
+ "hashbrown",
+ "instant",
+ "tracing",
+ "uuid",
+]
+
+[[package]]
+name = "bit-set"
+version = "0.5.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1"
+dependencies = [
+ "bit-vec",
+]
+
+[[package]]
+name = "bit-vec"
+version = "0.6.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb"
[[package]]
name = "bitflags"
@@ -440,9 +713,15 @@ dependencies = [
[[package]]
name = "bumpalo"
-version = "3.11.1"
+version = "3.12.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535"
+
+[[package]]
+name = "bytemuck"
+version = "1.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "572f695136211188308f16ad2ca5c851a712c464060ae6974944458eb83880ba"
+checksum = "c041d3eab048880cb0b86b256447da3f18859a163c3b8d8893f4e6368abe6393"
[[package]]
name = "byteorder"
@@ -457,6 +736,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dfb24e866b15a1af2a1b663f10c6b6b8f397a84aadb828f12e5b289ec23a3a3c"
[[package]]
+name = "cache-padded"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c1db59621ec70f09c5e9b597b220c7a2b43611f4710dc03ceb8748637775692c"
+
+[[package]]
name = "cast"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -464,9 +749,9 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5"
[[package]]
name = "cc"
-version = "1.0.77"
+version = "1.0.78"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e9f73505338f7d905b19d18738976aae232eb46b8efc15554ffc56deb5d9ebe4"
+checksum = "a20104e2335ce8a659d6dd92a51a767a0c062599c73b343fd152cb401e828c3d"
[[package]]
name = "cfb8"
@@ -515,6 +800,30 @@ dependencies = [
]
[[package]]
+name = "concurrent-queue"
+version = "1.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "af4780a44ab5696ea9e28294517f1fffb421a83a25af521333c838635509db9c"
+dependencies = [
+ "cache-padded",
+]
+
+[[package]]
+name = "concurrent-queue"
+version = "2.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c278839b831783b70278b14df4d45e1beb1aad306c07bb796637de9a0e323e8e"
+dependencies = [
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "convert_case"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e"
+
+[[package]]
name = "core-foundation"
version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -661,9 +970,22 @@ dependencies = [
[[package]]
name = "data-encoding"
-version = "2.3.2"
+version = "2.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "23d8666cb01533c39dde32bcbab8e227b4ed6679b2c925eba05feabea39508fb"
+
+[[package]]
+name = "derive_more"
+version = "0.99.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3ee2393c4a91429dffb4bedf19f4d6abf27d8a732c8ce4980305d782e5426d57"
+checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321"
+dependencies = [
+ "convert_case",
+ "proc-macro2",
+ "quote",
+ "rustc_version",
+ "syn",
+]
[[package]]
name = "digest"
@@ -676,6 +998,12 @@ dependencies = [
]
[[package]]
+name = "downcast-rs"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650"
+
+[[package]]
name = "either"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -729,6 +1057,15 @@ dependencies = [
]
[[package]]
+name = "erased-serde"
+version = "0.3.24"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e4ca605381c017ec7a5fef5e548f1cfaa419ed0f6df6367339300db74c92aa7d"
+dependencies = [
+ "serde",
+]
+
+[[package]]
name = "errno"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -750,6 +1087,12 @@ dependencies = [
]
[[package]]
+name = "event-listener"
+version = "2.5.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0"
+
+[[package]]
name = "fastrand"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -771,7 +1114,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a8a2db397cb1c8772f31494cb8917e48cd1e64f0fa7efac59fbd741a0a8ce841"
dependencies = [
"crc32fast",
- "miniz_oxide 0.6.2",
+ "miniz_oxide",
]
[[package]]
@@ -853,6 +1196,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "00f5fb52a06bdcadeb54e8d3671f8888a39697dcb0b81b23b55174030427f4eb"
[[package]]
+name = "futures-lite"
+version = "1.12.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7694489acd39452c77daa48516b894c153f192c3578d5a839b62c58099fcbf48"
+dependencies = [
+ "fastrand",
+ "futures-core",
+ "futures-io",
+ "memchr",
+ "parking",
+ "pin-project-lite",
+ "waker-fn",
+]
+
+[[package]]
name = "futures-macro"
version = "0.3.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -894,6 +1252,15 @@ dependencies = [
]
[[package]]
+name = "fxhash"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c"
+dependencies = [
+ "byteorder",
+]
+
+[[package]]
name = "generic-array"
version = "0.14.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -910,15 +1277,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31"
dependencies = [
"cfg-if",
+ "js-sys",
"libc",
"wasi",
+ "wasm-bindgen",
]
[[package]]
name = "gimli"
-version = "0.26.2"
+version = "0.27.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "221996f774192f0f718773def8201c4ae31f02616a54ccfc2d358bb0e5cefdec"
+
+[[package]]
+name = "glam"
+version = "0.22.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "22030e2c5a68ec659fde1e949a745124b48e6fa8b045b7ed5bd1fe4ccc5c4e5d"
+checksum = "12f597d56c1bd55a811a1be189459e8fad2bbc272616375602443bdfb37fa774"
+dependencies = [
+ "bytemuck",
+ "serde",
+]
[[package]]
name = "h2"
@@ -950,6 +1329,10 @@ name = "hashbrown"
version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
+dependencies = [
+ "ahash 0.7.6",
+ "serde",
+]
[[package]]
name = "heck"
@@ -983,7 +1366,7 @@ checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399"
dependencies = [
"bytes",
"fnv",
- "itoa 1.0.4",
+ "itoa 1.0.5",
]
[[package]]
@@ -1030,7 +1413,7 @@ dependencies = [
"http-body",
"httparse",
"httpdate",
- "itoa 1.0.4",
+ "itoa 1.0.5",
"pin-project-lite",
"socket2",
"tokio",
@@ -1099,6 +1482,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c"
dependencies = [
"cfg-if",
+ "js-sys",
+ "wasm-bindgen",
+ "web-sys",
]
[[package]]
@@ -1108,14 +1494,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7d6c6f8c91b4b9ed43484ad1a938e393caf35960fce7f82a040497207bd8e9e"
dependencies = [
"libc",
- "windows-sys 0.42.0",
+ "windows-sys",
]
[[package]]
name = "ipnet"
-version = "2.5.1"
+version = "2.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f88c5561171189e69df9d98bcf18fd5f9558300f7ea7b801eb8a0fd748bd8745"
+checksum = "30e22bd8629359895450b59ea7a776c850561b96a3b1d31321c1949d9e6c9146"
[[package]]
name = "is-terminal"
@@ -1126,7 +1512,7 @@ dependencies = [
"hermit-abi 0.2.6",
"io-lifetimes",
"rustix",
- "windows-sys 0.42.0",
+ "windows-sys",
]
[[package]]
@@ -1146,9 +1532,21 @@ checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4"
[[package]]
name = "itoa"
-version = "1.0.4"
+version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4217ad341ebadf8d8e724e264f13e593e0648f5b3e94b3896a5df283be015ecc"
+checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440"
+
+[[package]]
+name = "iyes_loopless"
+version = "0.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c47fd2cbdb1d7f295c25e6bfccfd78a84b6eef3055bc9f01b34ae861721b01ee"
+dependencies = [
+ "bevy_app",
+ "bevy_ecs",
+ "bevy_time",
+ "bevy_utils",
+]
[[package]]
name = "js-sys"
@@ -1167,9 +1565,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
-version = "0.2.138"
+version = "0.2.139"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "db6d7e329c562c5dfab7a46a2afabc8b987ab9a4834c9d1ca04dc54c1546cef8"
+checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79"
[[package]]
name = "linked-hash-map"
@@ -1213,9 +1611,9 @@ dependencies = [
[[package]]
name = "matches"
-version = "0.1.9"
+version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f"
+checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5"
[[package]]
name = "memchr"
@@ -1240,15 +1638,6 @@ checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d"
[[package]]
name = "miniz_oxide"
-version = "0.5.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "96590ba8f175222643a85693f33d26e9c8a015f599c216509b1a6894af675d34"
-dependencies = [
- "adler",
-]
-
-[[package]]
-name = "miniz_oxide"
version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa"
@@ -1265,7 +1654,7 @@ dependencies = [
"libc",
"log",
"wasi",
- "windows-sys 0.42.0",
+ "windows-sys",
]
[[package]]
@@ -1293,6 +1682,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451"
[[package]]
+name = "nom8"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ae01545c9c7fc4486ab7debaf2aad7003ac19431791868fb2e8066df97fad2f8"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
name = "nu-ansi-term"
version = "0.46.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1329,9 +1727,9 @@ dependencies = [
[[package]]
name = "num-complex"
-version = "0.4.2"
+version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7ae39348c8bc5fbd7f40c727a9925f03517afd2ab27d46702108b6a7e5414c19"
+checksum = "02e0d21255c828d6f128a1e41534206671e8c3ea0c62f32291e808dc82cff17d"
dependencies = [
"num-traits",
]
@@ -1391,19 +1789,19 @@ dependencies = [
[[package]]
name = "num_cpus"
-version = "1.14.0"
+version = "1.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f6058e64324c71e02bc2b150e4f3bc8286db6c83092132ffa3f6b1eab0f9def5"
+checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b"
dependencies = [
- "hermit-abi 0.1.19",
+ "hermit-abi 0.2.6",
"libc",
]
[[package]]
name = "object"
-version = "0.29.0"
+version = "0.30.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "21158b2c33aa6d4561f1c0a6ea283ca92bc54802a93b263e910746d679a7eb53"
+checksum = "ea86265d3d3dcb6a27fc51bd29a4bf387fae9d2986b823079d4986af253eb439"
dependencies = [
"memchr",
]
@@ -1422,9 +1820,9 @@ checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575"
[[package]]
name = "openssl"
-version = "0.10.43"
+version = "0.10.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "020433887e44c27ff16365eaa2d380547a94544ad509aff6eb5b6e3e0b27b376"
+checksum = "b102428fd03bc5edf97f62620f7298614c45cedf287c271e7ed450bbaf83f2e1"
dependencies = [
"bitflags",
"cfg-if",
@@ -1454,9 +1852,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
[[package]]
name = "openssl-sys"
-version = "0.9.78"
+version = "0.9.80"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "07d5c8cb6e57b3a3612064d7b18b117912b4ce70955c2504d4b741c9e244b132"
+checksum = "23bbbf7854cd45b83958ebe919f0e8e516793727652e27fda10a8384cfc790b7"
dependencies = [
"autocfg",
"cc",
@@ -1472,6 +1870,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
[[package]]
+name = "parking"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "427c3892f9e783d91cc128285287e70a59e206ca452770ece88a76f7a3eddd72"
+
+[[package]]
name = "parking_lot"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1483,9 +1887,9 @@ dependencies = [
[[package]]
name = "parking_lot_core"
-version = "0.9.5"
+version = "0.9.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7ff9f3fef3968a3ec5945535ed654cb38ff72d7495a25619e2247fb15a2ed9ba"
+checksum = "ba1ef8814b5c993410bb3adfad7a5ed269563e4a2f90c41f5d85be7fb47133bf"
dependencies = [
"backtrace",
"cfg-if",
@@ -1494,7 +1898,7 @@ dependencies = [
"redox_syscall",
"smallvec",
"thread-id",
- "windows-sys 0.42.0",
+ "windows-sys",
]
[[package]]
@@ -1577,18 +1981,18 @@ dependencies = [
[[package]]
name = "proc-macro2"
-version = "1.0.47"
+version = "1.0.50"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725"
+checksum = "6ef7d57beacfaf2d8aee5937dab7b7f28de3cb8b1828479bb5de2a7106f2bae2"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
-version = "1.0.21"
+version = "1.0.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179"
+checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b"
dependencies = [
"proc-macro2",
]
@@ -1625,20 +2029,19 @@ dependencies = [
[[package]]
name = "rayon"
-version = "1.6.0"
+version = "1.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1e060280438193c554f654141c9ea9417886713b7acd75974c85b18a69a88e0b"
+checksum = "6db3a213adf02b3bcfd2d3846bb41cb22857d131789e01df434fb7e7bc0759b7"
dependencies = [
- "crossbeam-deque",
"either",
"rayon-core",
]
[[package]]
name = "rayon-core"
-version = "1.10.1"
+version = "1.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cac410af5d00ab6884528b4ab69d1e8e146e8d471201800fa1b4524126de6ad3"
+checksum = "356a0625f1954f730c0201cdab48611198dc6ce21f4acff55089b5a78e6e835b"
dependencies = [
"crossbeam-channel",
"crossbeam-deque",
@@ -1657,9 +2060,9 @@ dependencies = [
[[package]]
name = "regex"
-version = "1.7.0"
+version = "1.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e076559ef8e241f2ae3479e36f97bd5741c0330689e217ad51ce2c76808b868a"
+checksum = "48aaa5748ba571fb95cd2c85c09f629215d3a6ece942baa100950af03a34f733"
dependencies = [
"aho-corasick",
"memchr",
@@ -1689,9 +2092,9 @@ dependencies = [
[[package]]
name = "reqwest"
-version = "0.11.13"
+version = "0.11.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "68cc60575865c7831548863cc02356512e3f1dc2f3f82cb837d7fc4cc8f3c97c"
+checksum = "21eed90ec8570952d53b772ecf8f206aa1ec9a3d76b2521c56c42973f2d91ee9"
dependencies = [
"base64",
"bytes",
@@ -1742,6 +2145,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342"
[[package]]
+name = "rustc_version"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366"
+dependencies = [
+ "semver",
+]
+
+[[package]]
name = "rustix"
version = "0.36.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1752,14 +2164,14 @@ dependencies = [
"io-lifetimes",
"libc",
"linux-raw-sys",
- "windows-sys 0.42.0",
+ "windows-sys",
]
[[package]]
name = "ryu"
-version = "1.0.11"
+version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09"
+checksum = "7b4b9743ed687d4b4bcedf9ff5eaa7398495ae14e61cba0a295704edbc7decde"
[[package]]
name = "same-file"
@@ -1772,12 +2184,11 @@ dependencies = [
[[package]]
name = "schannel"
-version = "0.1.20"
+version = "0.1.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "88d6731146462ea25d9244b2ed5fd1d716d25c52e4d54aa4fb0f3c4e9854dbe2"
+checksum = "713cfb06c7059f3588fb8044c0fad1d09e3c01d225e25b9220dbfdcf16dbb1b3"
dependencies = [
- "lazy_static",
- "windows-sys 0.36.1",
+ "windows-sys",
]
[[package]]
@@ -1788,9 +2199,9 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
[[package]]
name = "security-framework"
-version = "2.7.0"
+version = "2.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2bc1bb97804af6631813c55739f771071e0f2ed33ee20b68c86ec505d906356c"
+checksum = "7c4437699b6d34972de58652c68b98cb5b53a4199ab126db8e20ec8ded29a721"
dependencies = [
"bitflags",
"core-foundation",
@@ -1801,19 +2212,25 @@ dependencies = [
[[package]]
name = "security-framework-sys"
-version = "2.6.1"
+version = "2.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0160a13a177a45bfb43ce71c01580998474f556ad854dcbca936dd2841a5c556"
+checksum = "31c9bb296072e961fcbd8853511dd39c2d8be2deb1e17c6860b1d30732b323b4"
dependencies = [
"core-foundation-sys",
"libc",
]
[[package]]
+name = "semver"
+version = "1.0.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "58bc9567378fc7690d6b2addae4e60ac2eeea07becb2c64b9f218b53865cba2a"
+
+[[package]]
name = "serde"
-version = "1.0.149"
+version = "1.0.152"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "256b9932320c590e707b94576e3cc1f7c9024d0ee6612dfbcf1cb106cbe8e055"
+checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb"
dependencies = [
"serde_derive",
]
@@ -1830,9 +2247,9 @@ dependencies = [
[[package]]
name = "serde_derive"
-version = "1.0.149"
+version = "1.0.152"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b4eae9b04cbffdfd550eb462ed33bc6a1b68c935127d008b27444d08380f94e4"
+checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e"
dependencies = [
"proc-macro2",
"quote",
@@ -1841,23 +2258,32 @@ dependencies = [
[[package]]
name = "serde_json"
-version = "1.0.89"
+version = "1.0.91"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "020ff22c755c2ed3f8cf162dbb41a7268d934702f3ed3631656ea597e08fc3db"
+checksum = "877c235533714907a8c2464236f5c4b2a17262ef1bd71f38f35ea592c8da6883"
dependencies = [
- "itoa 1.0.4",
+ "itoa 1.0.5",
"ryu",
"serde",
]
[[package]]
+name = "serde_spanned"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2c68e921cef53841b8925c2abadd27c9b891d9613bdc43d6b823062866df38e8"
+dependencies = [
+ "serde",
+]
+
+[[package]]
name = "serde_urlencoded"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd"
dependencies = [
"form_urlencoded",
- "itoa 1.0.4",
+ "itoa 1.0.5",
"ryu",
"serde",
]
@@ -1917,6 +2343,9 @@ name = "smallvec"
version = "1.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0"
+dependencies = [
+ "serde",
+]
[[package]]
name = "socket2"
@@ -1930,9 +2359,9 @@ dependencies = [
[[package]]
name = "syn"
-version = "1.0.105"
+version = "1.0.107"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "60b9b43d45702de4c839cb9b51d9f529c5dd26a4aff255b42b1ebc03e88ee908"
+checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5"
dependencies = [
"proc-macro2",
"quote",
@@ -1955,9 +2384,9 @@ dependencies = [
[[package]]
name = "termcolor"
-version = "1.1.3"
+version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755"
+checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6"
dependencies = [
"winapi-util",
]
@@ -1973,18 +2402,18 @@ dependencies = [
[[package]]
name = "thiserror"
-version = "1.0.37"
+version = "1.0.38"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "10deb33631e3c9018b9baf9dcbbc4f737320d2b576bac10f6aefa048fa407e3e"
+checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
-version = "1.0.37"
+version = "1.0.38"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "982d17546b47146b28f7c22e3d08465f6b8903d0ea13c1660d9d84a6e7adcdbb"
+checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f"
dependencies = [
"proc-macro2",
"quote",
@@ -2038,9 +2467,9 @@ checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c"
[[package]]
name = "tokio"
-version = "1.24.2"
+version = "1.25.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "597a12a59981d9e3c38d216785b0c37399f6e415e8d0712047620f189371b0bb"
+checksum = "c8e00990ebabbe4c14c08aca901caed183ecd5c09562a12c824bb53d3c3fd3af"
dependencies = [
"autocfg",
"bytes",
@@ -2053,7 +2482,7 @@ dependencies = [
"signal-hook-registry",
"socket2",
"tokio-macros",
- "windows-sys 0.42.0",
+ "windows-sys",
]
[[package]]
@@ -2092,6 +2521,49 @@ dependencies = [
]
[[package]]
+name = "toml"
+version = "0.5.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "toml"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2f560bc7fb3eb31f5eee1340c68a2160cad39605b7b9c9ec32045ddbdee13b85"
+dependencies = [
+ "serde",
+ "serde_spanned",
+ "toml_datetime",
+ "toml_edit",
+]
+
+[[package]]
+name = "toml_datetime"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "886f31a9b85b6182cabd4d8b07df3b451afcc216563748201490940d2a28ed36"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "toml_edit"
+version = "0.19.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "233d8716cdc5d20ec88a18a839edaf545edc71efa4a5ff700ef4a102c26cd8fa"
+dependencies = [
+ "indexmap",
+ "nom8",
+ "serde",
+ "serde_spanned",
+ "toml_datetime",
+]
+
+[[package]]
name = "tower-service"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2200,9 +2672,9 @@ dependencies = [
[[package]]
name = "try-lock"
-version = "0.2.3"
+version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642"
+checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed"
[[package]]
name = "typemap_rev"
@@ -2218,15 +2690,15 @@ checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba"
[[package]]
name = "unicode-bidi"
-version = "0.3.8"
+version = "0.3.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992"
+checksum = "d54675592c1dbefd78cbd98db9bacd89886e1ca50692a0692baefffdeb92dd58"
[[package]]
name = "unicode-ident"
-version = "1.0.5"
+version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3"
+checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc"
[[package]]
name = "unicode-normalization"
@@ -2260,6 +2732,7 @@ version = "1.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "422ee0de9031b5b948b97a8fc04e3aa35230001a722ddd27943e0be31564ce4c"
dependencies = [
+ "getrandom",
"serde",
]
@@ -2282,6 +2755,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
[[package]]
+name = "waker-fn"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca"
+
+[[package]]
name = "walkdir"
version = "2.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2417,103 +2896,60 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows-sys"
-version = "0.36.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2"
-dependencies = [
- "windows_aarch64_msvc 0.36.1",
- "windows_i686_gnu 0.36.1",
- "windows_i686_msvc 0.36.1",
- "windows_x86_64_gnu 0.36.1",
- "windows_x86_64_msvc 0.36.1",
-]
-
-[[package]]
-name = "windows-sys"
version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7"
dependencies = [
"windows_aarch64_gnullvm",
- "windows_aarch64_msvc 0.42.0",
- "windows_i686_gnu 0.42.0",
- "windows_i686_msvc 0.42.0",
- "windows_x86_64_gnu 0.42.0",
+ "windows_aarch64_msvc",
+ "windows_i686_gnu",
+ "windows_i686_msvc",
+ "windows_x86_64_gnu",
"windows_x86_64_gnullvm",
- "windows_x86_64_msvc 0.42.0",
+ "windows_x86_64_msvc",
]
[[package]]
name = "windows_aarch64_gnullvm"
-version = "0.42.0"
+version = "0.42.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "41d2aa71f6f0cbe00ae5167d90ef3cfe66527d6f613ca78ac8024c3ccab9a19e"
+checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608"
[[package]]
name = "windows_aarch64_msvc"
-version = "0.36.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47"
-
-[[package]]
-name = "windows_aarch64_msvc"
-version = "0.42.0"
+version = "0.42.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4"
+checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7"
[[package]]
name = "windows_i686_gnu"
-version = "0.36.1"
+version = "0.42.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6"
-
-[[package]]
-name = "windows_i686_gnu"
-version = "0.42.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7"
+checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640"
[[package]]
name = "windows_i686_msvc"
-version = "0.36.1"
+version = "0.42.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024"
-
-[[package]]
-name = "windows_i686_msvc"
-version = "0.42.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246"
+checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605"
[[package]]
name = "windows_x86_64_gnu"
-version = "0.36.1"
+version = "0.42.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1"
-
-[[package]]
-name = "windows_x86_64_gnu"
-version = "0.42.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bf7b1b21b5362cbc318f686150e5bcea75ecedc74dd157d874d754a2ca44b0ed"
+checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45"
[[package]]
name = "windows_x86_64_gnullvm"
-version = "0.42.0"
+version = "0.42.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028"
+checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463"
[[package]]
name = "windows_x86_64_msvc"
-version = "0.36.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680"
-
-[[package]]
-name = "windows_x86_64_msvc"
-version = "0.42.0"
+version = "0.42.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5"
+checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd"
[[package]]
name = "winreg"
diff --git a/Cargo.toml b/Cargo.toml
index 8a984e81..6c3ed34c 100755
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -16,6 +16,7 @@ members = [
"azalea-buf",
"azalea-physics",
"azalea-registry",
+ "azalea-ecs",
]
[profile.release]
@@ -24,19 +25,9 @@ debug = true
# decoding packets takes forever if we don't do this
[profile.dev.package.azalea-crypto]
opt-level = 3
-[profile.dev.package.cipher]
-opt-level = 3
[profile.dev.package.cfb8]
opt-level = 3
[profile.dev.package.aes]
opt-level = 3
-[profile.dev.package.crypto-common]
-opt-level = 3
-[profile.dev.package.generic-array]
-opt-level = 3
-[profile.dev.package.typenum]
-opt-level = 3
-[profile.dev.package.inout]
-opt-level = 3
[profile.dev.package.flate2]
opt-level = 3
diff --git a/azalea-auth/README.md b/azalea-auth/README.md
index 568a9f88..a35fee1f 100755
--- a/azalea-auth/README.md
+++ b/azalea-auth/README.md
@@ -4,7 +4,7 @@ A port of Mojang's Authlib and launcher authentication.
# Examples
-```
+```no_run
use std::path::PathBuf;
#[tokio::main]
diff --git a/azalea-auth/src/game_profile.rs b/azalea-auth/src/game_profile.rs
index 12815821..6a34a87b 100755
--- a/azalea-auth/src/game_profile.rs
+++ b/azalea-auth/src/game_profile.rs
@@ -5,7 +5,9 @@ use uuid::Uuid;
#[derive(McBuf, Debug, Clone, Default, Eq, PartialEq)]
pub struct GameProfile {
+ /// The UUID of the player.
pub uuid: Uuid,
+ /// The username of the player.
pub name: String,
pub properties: HashMap<String, ProfilePropertyValue>,
}
diff --git a/azalea-block/azalea-block-macros/src/lib.rs b/azalea-block/azalea-block-macros/src/lib.rs
index 8b26122c..bbd98619 100755
--- a/azalea-block/azalea-block-macros/src/lib.rs
+++ b/azalea-block/azalea-block-macros/src/lib.rs
@@ -73,7 +73,7 @@ impl Parse for PropertyWithNameAndDefault {
is_enum = true;
property_type = first_ident;
let variant = input.parse::<Ident>()?;
- property_default.extend(quote! { ::#variant })
+ property_default.extend(quote! { ::#variant });
} else if first_ident_string == "true" || first_ident_string == "false" {
property_type = Ident::new("bool", first_ident.span());
} else {
@@ -388,7 +388,7 @@ pub fn make_block_states(input: TokenStream) -> TokenStream {
// Ident::new(&property.to_string(), proc_macro2::Span::call_site());
block_struct_fields.extend(quote! {
pub #name: #struct_name,
- })
+ });
}
let block_name_pascal_case = Ident::new(
@@ -420,8 +420,7 @@ pub fn make_block_states(input: TokenStream) -> TokenStream {
combination
.iter()
.map(|v| v[0..1].to_uppercase() + &v[1..])
- .collect::<Vec<String>>()
- .join("")
+ .collect::<String>()
),
proc_macro2::Span::call_site(),
);
@@ -509,20 +508,20 @@ pub fn make_block_states(input: TokenStream) -> TokenStream {
..
} in properties_with_name
{
- block_default_fields.extend(quote! {#name: #property_default,})
+ block_default_fields.extend(quote! { #name: #property_default, });
}
let block_behavior = &block.behavior;
let block_id = block.name.to_string();
- let from_block_to_state_match = if !block.properties_and_defaults.is_empty() {
+ let from_block_to_state_match = if block.properties_and_defaults.is_empty() {
+ quote! { BlockState::#block_name_pascal_case }
+ } else {
quote! {
match b {
#from_block_to_state_match_inner
}
}
- } else {
- quote! { BlockState::#block_name_pascal_case }
};
let block_struct = quote! {
diff --git a/azalea-brigadier/src/command_dispatcher.rs b/azalea-brigadier/src/command_dispatcher.rs
index bb9bfb28..273b1681 100755
--- a/azalea-brigadier/src/command_dispatcher.rs
+++ b/azalea-brigadier/src/command_dispatcher.rs
@@ -136,7 +136,7 @@ impl<S> CommandDispatcher<S> {
return Ordering::Greater;
};
Ordering::Equal
- })
+ });
}
let best_potential = potentials.into_iter().next().unwrap();
return Ok(best_potential);
@@ -195,7 +195,7 @@ impl<S> CommandDispatcher<S> {
let mut node = self.root.clone();
for name in path {
if let Some(child) = node.clone().borrow().child(name) {
- node = child
+ node = child;
} else {
return None;
}
@@ -228,7 +228,7 @@ impl<S> CommandDispatcher<S> {
let mut next: Vec<CommandContext<S>> = vec![];
while !contexts.is_empty() {
- for context in contexts.iter() {
+ for context in &contexts {
let child = &context.child;
if let Some(child) = child {
forked |= child.forks;
diff --git a/azalea-brigadier/src/exceptions/builtin_exceptions.rs b/azalea-brigadier/src/exceptions/builtin_exceptions.rs
index b96b37bf..e60c697c 100755
--- a/azalea-brigadier/src/exceptions/builtin_exceptions.rs
+++ b/azalea-brigadier/src/exceptions/builtin_exceptions.rs
@@ -88,7 +88,7 @@ impl fmt::Debug for BuiltInExceptions {
BuiltInExceptions::ReaderInvalidBool { value } => {
write!(
f,
- "Invalid bool, expected true or false but found '{value}'",
+ "Invalid bool, expected true or false but found '{value}'"
)
}
BuiltInExceptions::ReaderInvalidInt { value } => {
diff --git a/azalea-brigadier/src/suggestion/mod.rs b/azalea-brigadier/src/suggestion/mod.rs
index 114a4c47..592c674c 100755
--- a/azalea-brigadier/src/suggestion/mod.rs
+++ b/azalea-brigadier/src/suggestion/mod.rs
@@ -4,7 +4,7 @@ use crate::context::StringRange;
#[cfg(feature = "azalea-buf")]
use azalea_buf::McBufWritable;
#[cfg(feature = "azalea-buf")]
-use azalea_chat::Component;
+use azalea_chat::FormattedText;
#[cfg(feature = "azalea-buf")]
use std::io::Write;
pub use suggestions::*;
@@ -31,7 +31,7 @@ impl<M: Clone> Suggestion<M> {
}
result.push_str(&self.text);
if self.range.end() < input.len() {
- result.push_str(&input[self.range.end()..])
+ result.push_str(&input[self.range.end()..]);
}
result
@@ -58,7 +58,7 @@ impl<M: Clone> Suggestion<M> {
}
#[cfg(feature = "azalea-buf")]
-impl McBufWritable for Suggestion<Component> {
+impl McBufWritable for Suggestion<FormattedText> {
fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
self.text.write_into(buf)?;
self.tooltip.write_into(buf)?;
diff --git a/azalea-brigadier/src/suggestion/suggestions.rs b/azalea-brigadier/src/suggestion/suggestions.rs
index 06ef9661..2a8b5e9e 100755
--- a/azalea-brigadier/src/suggestion/suggestions.rs
+++ b/azalea-brigadier/src/suggestion/suggestions.rs
@@ -5,7 +5,7 @@ use azalea_buf::{
BufReadError, McBuf, McBufReadable, McBufVarReadable, McBufVarWritable, McBufWritable,
};
#[cfg(feature = "azalea-buf")]
-use azalea_chat::Component;
+use azalea_chat::FormattedText;
#[cfg(feature = "azalea-buf")]
use std::io::{Cursor, Write};
use std::{collections::HashSet, hash::Hash};
@@ -68,12 +68,12 @@ impl<M> Default for Suggestions<M> {
}
#[cfg(feature = "azalea-buf")]
-impl McBufReadable for Suggestions<Component> {
+impl McBufReadable for Suggestions<FormattedText> {
fn read_from(buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError> {
#[derive(McBuf)]
struct StandaloneSuggestion {
pub text: String,
- pub tooltip: Option<Component>,
+ pub tooltip: Option<FormattedText>,
}
let start = u32::var_read_from(buf)? as usize;
@@ -97,7 +97,7 @@ impl McBufReadable for Suggestions<Component> {
}
#[cfg(feature = "azalea-buf")]
-impl McBufWritable for Suggestions<Component> {
+impl McBufWritable for Suggestions<FormattedText> {
fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
(self.range.start() as u32).var_write_into(buf)?;
(self.range.length() as u32).var_write_into(buf)?;
diff --git a/azalea-brigadier/src/tree/mod.rs b/azalea-brigadier/src/tree/mod.rs
index 59b79b5d..bb6af68d 100755
--- a/azalea-brigadier/src/tree/mod.rs
+++ b/azalea-brigadier/src/tree/mod.rs
@@ -65,7 +65,9 @@ impl<S> CommandNode<S> {
pub fn get_relevant_nodes(&self, input: &mut StringReader) -> Vec<Rc<RefCell<CommandNode<S>>>> {
let literals = &self.literals;
- if !literals.is_empty() {
+ if literals.is_empty() {
+ self.arguments.values().cloned().collect()
+ } else {
let cursor = input.cursor();
while input.can_read() && input.peek() != ' ' {
input.skip();
@@ -83,8 +85,6 @@ impl<S> CommandNode<S> {
} else {
self.arguments.values().cloned().collect()
}
- } else {
- self.arguments.values().cloned().collect()
}
}
diff --git a/azalea-buf/azalea-buf-macros/src/read.rs b/azalea-buf/azalea-buf-macros/src/read.rs
index b72e2bf5..82fd1ce8 100644
--- a/azalea-buf/azalea-buf-macros/src/read.rs
+++ b/azalea-buf/azalea-buf-macros/src/read.rs
@@ -39,9 +39,8 @@ fn read_named_fields(
pub fn create_impl_mcbufreadable(ident: &Ident, data: &Data) -> proc_macro2::TokenStream {
match data {
syn::Data::Struct(syn::DataStruct { fields, .. }) => {
- let FieldsNamed { named, .. } = match fields {
- syn::Fields::Named(f) => f,
- _ => panic!("#[derive(McBuf)] can only be used on structs with named fields"),
+ let syn::Fields::Named(FieldsNamed { named, .. }) = fields else {
+ panic!("#[derive(McBuf)] can only be used on structs with named fields")
};
let (read_fields, read_field_names) = read_named_fields(named);
@@ -69,7 +68,7 @@ pub fn create_impl_mcbufreadable(ident: &Ident, data: &Data) -> proc_macro2::Tok
variant_discrim = match &d.1 {
syn::Expr::Lit(e) => match &e.lit {
syn::Lit::Int(i) => i.base10_parse().unwrap(),
- _ => panic!("Error parsing enum discriminant as int (is {e:?})",),
+ _ => panic!("Error parsing enum discriminant as int (is {e:?})"),
},
syn::Expr::Unary(_) => {
panic!("Negative enum discriminants are not supported")
@@ -102,11 +101,11 @@ pub fn create_impl_mcbufreadable(ident: &Ident, data: &Data) -> proc_macro2::Tok
if f.attrs.iter().any(|attr| attr.path.is_ident("var")) {
reader_code.extend(quote! {
Self::#variant_name(azalea_buf::McBufVarReadable::var_read_from(buf)?),
- })
+ });
} else {
reader_code.extend(quote! {
Self::#variant_name(azalea_buf::McBufReadable::read_from(buf)?),
- })
+ });
}
}
quote! { Ok(#reader_code) }
diff --git a/azalea-buf/azalea-buf-macros/src/write.rs b/azalea-buf/azalea-buf-macros/src/write.rs
index 67647c06..b491a550 100644
--- a/azalea-buf/azalea-buf-macros/src/write.rs
+++ b/azalea-buf/azalea-buf-macros/src/write.rs
@@ -40,9 +40,8 @@ fn write_named_fields(
pub fn create_impl_mcbufwritable(ident: &Ident, data: &Data) -> proc_macro2::TokenStream {
match data {
syn::Data::Struct(syn::DataStruct { fields, .. }) => {
- let FieldsNamed { named, .. } = match fields {
- syn::Fields::Named(f) => f,
- _ => panic!("#[derive(McBuf)] can only be used on structs with named fields"),
+ let syn::Fields::Named(FieldsNamed { named, .. }) = fields else {
+ panic!("#[derive(McBuf)] can only be used on structs with named fields")
};
let write_fields =
@@ -137,11 +136,11 @@ pub fn create_impl_mcbufwritable(ident: &Ident, data: &Data) -> proc_macro2::Tok
if f.attrs.iter().any(|attr| attr.path.is_ident("var")) {
writers_code.extend(quote! {
azalea_buf::McBufVarWritable::var_write_into(#param_ident, buf)?;
- })
+ });
} else {
writers_code.extend(quote! {
azalea_buf::McBufWritable::write_into(#param_ident, buf)?;
- })
+ });
}
}
match_arms.extend(quote! {
diff --git a/azalea-chat/README.md b/azalea-chat/README.md
index 1cf52878..e79d273a 100755
--- a/azalea-chat/README.md
+++ b/azalea-chat/README.md
@@ -5,9 +5,9 @@ Things for working with Minecraft formatted text components.
# Examples
```
-// convert a Minecraft component JSON into colored text that can be printed to the terminal.
+// convert a Minecraft formatted text JSON into colored text that can be printed to the terminal.
-use azalea_chat::Component;
+use azalea_chat::FormattedText;
use serde_json::Value;
use serde::Deserialize;
@@ -15,9 +15,9 @@ let j: Value = serde_json::from_str(
r#"{"text": "hello","color": "red","bold": true}"#
)
.unwrap();
-let component = Component::deserialize(&j).unwrap();
+let text = FormattedText::deserialize(&j).unwrap();
assert_eq!(
- component.to_ansi(),
+ text.to_ansi(),
"\u{1b}[1m\u{1b}[38;2;255;85;85mhello\u{1b}[m"
);
```
diff --git a/azalea-chat/src/base_component.rs b/azalea-chat/src/base_component.rs
index ab4f5e5d..43b35aef 100755
--- a/azalea-chat/src/base_component.rs
+++ b/azalea-chat/src/base_component.rs
@@ -1,11 +1,11 @@
-use crate::{style::Style, Component};
+use crate::{style::Style, FormattedText};
use serde::Serialize;
#[derive(Clone, Debug, PartialEq, Serialize)]
pub struct BaseComponent {
// implements mutablecomponent
#[serde(skip_serializing_if = "Vec::is_empty")]
- pub siblings: Vec<Component>,
+ pub siblings: Vec<FormattedText>,
#[serde(flatten)]
pub style: Style,
}
diff --git a/azalea-chat/src/component.rs b/azalea-chat/src/component.rs
index 14ec0d0c..59e11bbf 100755
--- a/azalea-chat/src/component.rs
+++ b/azalea-chat/src/component.rs
@@ -17,7 +17,7 @@ use std::{
/// A chat component, basically anything you can see in chat.
#[derive(Clone, Debug, PartialEq, Serialize)]
#[serde(untagged)]
-pub enum Component {
+pub enum FormattedText {
Text(TextComponent),
Translatable(TranslatableComponent),
}
@@ -28,7 +28,7 @@ pub static DEFAULT_STYLE: Lazy<Style> = Lazy::new(|| Style {
});
/// A chat component
-impl Component {
+impl FormattedText {
pub fn get_base_mut(&mut self) -> &mut BaseComponent {
match self {
Self::Text(c) => &mut c.base,
@@ -44,14 +44,16 @@ impl Component {
}
/// Add a component as a sibling of this one
- fn append(&mut self, sibling: Component) {
+ fn append(&mut self, sibling: FormattedText) {
self.get_base_mut().siblings.push(sibling);
}
/// Get the "separator" component from the json
- fn parse_separator(json: &serde_json::Value) -> Result<Option<Component>, serde_json::Error> {
+ fn parse_separator(
+ json: &serde_json::Value,
+ ) -> Result<Option<FormattedText>, serde_json::Error> {
if json.get("separator").is_some() {
- return Ok(Some(Component::deserialize(
+ return Ok(Some(FormattedText::deserialize(
json.get("separator").unwrap(),
)?));
}
@@ -62,16 +64,17 @@ impl Component {
/// [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 [`Component::to_ansi_custom_style`]
- /// with a default [`Style`] colored white.
+ /// This is technically a shortcut for
+ /// [`FormattedText::to_ansi_custom_style`] with a default [`Style`]
+ /// colored white.
///
/// # Examples
///
/// ```rust
- /// use azalea_chat::Component;
+ /// use azalea_chat::FormattedText;
/// use serde::de::Deserialize;
///
- /// let component = Component::deserialize(&serde_json::json!({
+ /// let component = FormattedText::deserialize(&serde_json::json!({
/// "text": "Hello, world!",
/// "color": "red",
/// })).unwrap();
@@ -86,7 +89,7 @@ impl Component {
/// Convert this component into an
/// [ANSI string](https://en.wikipedia.org/wiki/ANSI_escape_code).
///
- /// This is the same as [`Component::to_ansi`], but you can specify a
+ /// 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 {
// this contains the final string will all the ansi escape codes
@@ -117,12 +120,12 @@ impl Component {
}
}
-impl IntoIterator for Component {
+impl IntoIterator for FormattedText {
/// Recursively call the function for every component in this component
fn into_iter(self) -> Self::IntoIter {
let base = self.get_base();
let siblings = base.siblings.clone();
- let mut v: Vec<Component> = Vec::with_capacity(siblings.len() + 1);
+ let mut v: Vec<FormattedText> = Vec::with_capacity(siblings.len() + 1);
v.push(self);
for sibling in siblings {
v.extend(sibling.into_iter());
@@ -131,11 +134,11 @@ impl IntoIterator for Component {
v.into_iter()
}
- type Item = Component;
+ type Item = FormattedText;
type IntoIter = std::vec::IntoIter<Self::Item>;
}
-impl<'de> Deserialize<'de> for Component {
+impl<'de> Deserialize<'de> for FormattedText {
fn deserialize<D>(de: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
@@ -143,11 +146,11 @@ impl<'de> Deserialize<'de> for Component {
let json: serde_json::Value = serde::Deserialize::deserialize(de)?;
// we create a component that we might add siblings to
- let mut component: Component;
+ let mut component: FormattedText;
// if it's primitive, make it a text component
if !json.is_array() && !json.is_object() {
- return Ok(Component::Text(TextComponent::new(
+ return Ok(FormattedText::Text(TextComponent::new(
json.as_str().unwrap_or("").to_string(),
)));
}
@@ -155,7 +158,7 @@ impl<'de> Deserialize<'de> for Component {
else if json.is_object() {
if let Some(text) = json.get("text") {
let text = text.as_str().unwrap_or("").to_string();
- component = Component::Text(TextComponent::new(text));
+ component = FormattedText::Text(TextComponent::new(text));
} else if let Some(translate) = json.get("translate") {
let translate = translate
.as_str()
@@ -170,8 +173,8 @@ impl<'de> Deserialize<'de> for Component {
// 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 = Component::deserialize(item).map_err(de::Error::custom)?;
- if let Component::Text(text_component) = c {
+ let c = FormattedText::deserialize(item).map_err(de::Error::custom)?;
+ if let FormattedText::Text(text_component) = c {
if text_component.base.siblings.is_empty()
&& text_component.base.style.is_empty()
{
@@ -179,16 +182,19 @@ impl<'de> Deserialize<'de> for Component {
continue;
}
}
- with_array.push(StringOrComponent::Component(
- Component::deserialize(item).map_err(de::Error::custom)?,
+ with_array.push(StringOrComponent::FormattedText(
+ FormattedText::deserialize(item).map_err(de::Error::custom)?,
));
}
- component =
- Component::Translatable(TranslatableComponent::new(translate, with_array));
+ component = FormattedText::Translatable(TranslatableComponent::new(
+ translate, with_array,
+ ));
} else {
// if it doesn't have a "with", just have the with_array be empty
- component =
- Component::Translatable(TranslatableComponent::new(translate, Vec::new()));
+ component = FormattedText::Translatable(TranslatableComponent::new(
+ translate,
+ Vec::new(),
+ ));
}
} else if let Some(score) = json.get("score") {
// object = GsonHelper.getAsJsonObject(jsonObject, "score");
@@ -210,14 +216,13 @@ impl<'de> Deserialize<'de> for Component {
"keybind text components aren't yet supported",
));
} else {
- let _nbt = if let Some(nbt) = json.get("nbt") {
- nbt
- } else {
+ let Some(_nbt) = json.get("nbt") else {
return Err(de::Error::custom(
- format!("Don't know how to turn {json} into a Component").as_str(),
+ format!("Don't know how to turn {json} into a FormattedText").as_str(),
));
};
- let _separator = Component::parse_separator(&json).map_err(de::Error::custom)?;
+ let _separator =
+ FormattedText::parse_separator(&json).map_err(de::Error::custom)?;
let _interpret = match json.get("interpret") {
Some(v) => v.as_bool().ok_or(Some(false)).unwrap(),
@@ -229,16 +234,15 @@ impl<'de> Deserialize<'de> for Component {
));
}
if let Some(extra) = json.get("extra") {
- let extra = match extra.as_array() {
- Some(r) => r,
- None => return Err(de::Error::custom("Extra isn't an array")),
+ let Some(extra) = extra.as_array() else {
+ return Err(de::Error::custom("Extra isn't an array"));
};
if extra.is_empty() {
return Err(de::Error::custom("Unexpected empty array of components"));
}
for extra_component in extra {
let sibling =
- Component::deserialize(extra_component).map_err(de::Error::custom)?;
+ FormattedText::deserialize(extra_component).map_err(de::Error::custom)?;
component.append(sibling);
}
}
@@ -251,16 +255,18 @@ impl<'de> Deserialize<'de> for Component {
// ok so it's not an object, if it's an array deserialize every item
else if !json.is_array() {
return Err(de::Error::custom(
- format!("Don't know how to turn {json} into a Component").as_str(),
+ format!("Don't know how to turn {json} into a FormattedText").as_str(),
));
}
let json_array = json.as_array().unwrap();
// the first item in the array is the one that we're gonna return, the others
// are siblings
- let mut component = Component::deserialize(&json_array[0]).map_err(de::Error::custom)?;
+ let mut component =
+ FormattedText::deserialize(&json_array[0]).map_err(de::Error::custom)?;
for i in 1..json_array.len() {
component.append(
- Component::deserialize(json_array.get(i).unwrap()).map_err(de::Error::custom)?,
+ FormattedText::deserialize(json_array.get(i).unwrap())
+ .map_err(de::Error::custom)?,
);
}
Ok(component)
@@ -268,18 +274,18 @@ impl<'de> Deserialize<'de> for Component {
}
#[cfg(feature = "azalea-buf")]
-impl McBufReadable for Component {
+impl McBufReadable for FormattedText {
fn read_from(buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError> {
let string = String::read_from(buf)?;
- debug!("Component string: {}", string);
+ debug!("FormattedText string: {}", string);
let json: serde_json::Value = serde_json::from_str(string.as_str())?;
- let component = Component::deserialize(json)?;
+ let component = FormattedText::deserialize(json)?;
Ok(component)
}
}
#[cfg(feature = "azalea-buf")]
-impl McBufWritable for Component {
+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)?;
@@ -287,31 +293,31 @@ impl McBufWritable for Component {
}
}
-impl From<String> for Component {
+impl From<String> for FormattedText {
fn from(s: String) -> Self {
- Component::Text(TextComponent {
+ FormattedText::Text(TextComponent {
text: s,
base: BaseComponent::default(),
})
}
}
-impl From<&str> for Component {
+impl From<&str> for FormattedText {
fn from(s: &str) -> Self {
Self::from(s.to_string())
}
}
-impl Display for Component {
+impl Display for FormattedText {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
- Component::Text(c) => c.fmt(f),
- Component::Translatable(c) => c.fmt(f),
+ FormattedText::Text(c) => c.fmt(f),
+ FormattedText::Translatable(c) => c.fmt(f),
}
}
}
-impl Default for Component {
+impl Default for FormattedText {
fn default() -> Self {
- Component::Text(TextComponent::default())
+ FormattedText::Text(TextComponent::default())
}
}
diff --git a/azalea-chat/src/lib.rs b/azalea-chat/src/lib.rs
index 97a2580d..d6ff7285 100755
--- a/azalea-chat/src/lib.rs
+++ b/azalea-chat/src/lib.rs
@@ -6,4 +6,4 @@ pub mod style;
pub mod text_component;
pub mod translatable_component;
-pub use component::Component;
+pub use component::FormattedText;
diff --git a/azalea-chat/src/text_component.rs b/azalea-chat/src/text_component.rs
index 44bcbcf1..42932d0e 100755
--- a/azalea-chat/src/text_component.rs
+++ b/azalea-chat/src/text_component.rs
@@ -1,4 +1,4 @@
-use crate::{base_component::BaseComponent, style::ChatFormatting, Component};
+use crate::{base_component::BaseComponent, style::ChatFormatting, FormattedText};
use serde::{ser::SerializeMap, Serialize, Serializer, __private::ser::FlatMapSerializer};
use std::fmt::Display;
@@ -26,7 +26,7 @@ impl Serialize for TextComponent {
const LEGACY_FORMATTING_CODE_SYMBOL: char = '§';
-/// Convert a legacy color code string into a Component
+/// Convert a legacy color code string into a FormattedText
/// Technically in Minecraft this is done when displaying the text, but AFAIK
/// it's the same as just doing it in TextComponent
pub fn legacy_color_code_to_text_component(legacy_color_code: &str) -> TextComponent {
@@ -41,12 +41,9 @@ pub fn legacy_color_code_to_text_component(legacy_color_code: &str) -> TextCompo
while i < legacy_color_code.chars().count() {
if legacy_color_code.chars().nth(i).unwrap() == LEGACY_FORMATTING_CODE_SYMBOL {
let formatting_code = legacy_color_code.chars().nth(i + 1);
- let formatting_code = match formatting_code {
- Some(formatting_code) => formatting_code,
- None => {
- i += 1;
- continue;
- }
+ let Some(formatting_code) = formatting_code else {
+ i += 1;
+ continue;
};
if let Some(formatter) = ChatFormatting::from_code(formatting_code) {
if components.is_empty() || !components.last().unwrap().text.is_empty() {
@@ -98,18 +95,18 @@ impl TextComponent {
}
}
- fn get(self) -> Component {
- Component::Text(self)
+ fn get(self) -> FormattedText {
+ FormattedText::Text(self)
}
}
impl Display for TextComponent {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
// this contains the final string will all the ansi escape codes
- for component in Component::Text(self.clone()).into_iter() {
+ for component in FormattedText::Text(self.clone()).into_iter() {
let component_text = match &component {
- Component::Text(c) => c.text.to_string(),
- Component::Translatable(c) => c.read()?.to_string(),
+ FormattedText::Text(c) => c.text.to_string(),
+ FormattedText::Translatable(c) => c.read()?.to_string(),
};
f.write_str(&component_text)?;
diff --git a/azalea-chat/src/translatable_component.rs b/azalea-chat/src/translatable_component.rs
index 7819d5ff..edbb5a6d 100755
--- a/azalea-chat/src/translatable_component.rs
+++ b/azalea-chat/src/translatable_component.rs
@@ -1,7 +1,7 @@
use std::fmt::{self, Display, Formatter};
use crate::{
- base_component::BaseComponent, style::Style, text_component::TextComponent, Component,
+ base_component::BaseComponent, style::Style, text_component::TextComponent, FormattedText,
};
use serde::{ser::SerializeMap, Serialize, Serializer, __private::ser::FlatMapSerializer};
@@ -9,7 +9,7 @@ use serde::{ser::SerializeMap, Serialize, Serializer, __private::ser::FlatMapSer
#[serde(untagged)]
pub enum StringOrComponent {
String(String),
- Component(Component),
+ FormattedText(FormattedText),
}
/// A message whose content depends on the client's language.
@@ -42,7 +42,7 @@ impl TranslatableComponent {
}
}
- /// Convert the key and args to a Component.
+ /// Convert the key and args to a FormattedText.
pub fn read(&self) -> Result<TextComponent, fmt::Error> {
let template = azalea_language::get(&self.key).unwrap_or(&self.key);
// decode the % things
@@ -57,12 +57,9 @@ impl TranslatableComponent {
while i < template.len() {
if template.chars().nth(i).unwrap() == '%' {
- let char_after = match template.chars().nth(i + 1) {
- Some(c) => c,
- None => {
- built_text.push(template.chars().nth(i).unwrap());
- break;
- }
+ let Some(char_after) = template.chars().nth(i + 1) else {
+ built_text.push(template.chars().nth(i).unwrap());
+ break;
};
i += 1;
match char_after {
@@ -111,7 +108,7 @@ impl TranslatableComponent {
built_text.push(template.chars().nth(i).unwrap());
}
- i += 1
+ i += 1;
}
if components.is_empty() {
@@ -122,7 +119,7 @@ impl TranslatableComponent {
Ok(TextComponent {
base: BaseComponent {
- siblings: components.into_iter().map(Component::Text).collect(),
+ siblings: components.into_iter().map(FormattedText::Text).collect(),
style: Style::default(),
},
text: "".to_string(),
@@ -133,10 +130,10 @@ impl TranslatableComponent {
impl Display for TranslatableComponent {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
// this contains the final string will all the ansi escape codes
- for component in Component::Translatable(self.clone()).into_iter() {
+ for component in FormattedText::Translatable(self.clone()).into_iter() {
let component_text = match &component {
- Component::Text(c) => c.text.to_string(),
- Component::Translatable(c) => c.read()?.to_string(),
+ FormattedText::Text(c) => c.text.to_string(),
+ FormattedText::Translatable(c) => c.read()?.to_string(),
};
f.write_str(&component_text)?;
@@ -150,7 +147,7 @@ impl Display for StringOrComponent {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> {
match self {
StringOrComponent::String(s) => write!(f, "{s}"),
- StringOrComponent::Component(c) => write!(f, "{c}"),
+ StringOrComponent::FormattedText(c) => write!(f, "{c}"),
}
}
}
@@ -159,7 +156,7 @@ impl From<StringOrComponent> for TextComponent {
fn from(soc: StringOrComponent) -> Self {
match soc {
StringOrComponent::String(s) => TextComponent::new(s),
- StringOrComponent::Component(c) => TextComponent::new(c.to_string()),
+ StringOrComponent::FormattedText(c) => TextComponent::new(c.to_string()),
}
}
}
diff --git a/azalea-chat/tests/integration_test.rs b/azalea-chat/tests/integration_test.rs
index 35fab8d3..122df538 100755
--- a/azalea-chat/tests/integration_test.rs
+++ b/azalea-chat/tests/integration_test.rs
@@ -1,6 +1,6 @@
use azalea_chat::{
style::{Ansi, ChatFormatting, TextColor},
- Component,
+ FormattedText,
};
use serde::Deserialize;
use serde_json::Value;
@@ -15,7 +15,7 @@ fn basic_ansi_test() {
}"#,
)
.unwrap();
- let component = Component::deserialize(&j).unwrap();
+ let component = FormattedText::deserialize(&j).unwrap();
assert_eq!(
component.to_ansi(),
"\u{1b}[1m\u{1b}[38;2;255;85;85mhello\u{1b}[m"
@@ -51,7 +51,7 @@ fn complex_ansi_test() {
]"##,
)
.unwrap();
- let component = Component::deserialize(&j).unwrap();
+ let component = FormattedText::deserialize(&j).unwrap();
assert_eq!(
component.to_ansi(),
format!(
@@ -70,6 +70,6 @@ fn complex_ansi_test() {
#[test]
fn component_from_string() {
let j: Value = serde_json::from_str("\"foo\"").unwrap();
- let component = Component::deserialize(&j).unwrap();
+ let component = FormattedText::deserialize(&j).unwrap();
assert_eq!(component.to_ansi(), "foo");
}
diff --git a/azalea-client/Cargo.toml b/azalea-client/Cargo.toml
index 01ffc601..8a807212 100644
--- a/azalea-client/Cargo.toml
+++ b/azalea-client/Cargo.toml
@@ -11,14 +11,21 @@ version = "0.5.0"
[dependencies]
anyhow = "1.0.59"
async-trait = "0.1.58"
-azalea-auth = {path = "../azalea-auth", version = "0.5.0" }
-azalea-block = {path = "../azalea-block", version = "0.5.0" }
-azalea-chat = {path = "../azalea-chat", version = "0.5.0" }
-azalea-core = {path = "../azalea-core", version = "0.5.0" }
-azalea-crypto = {path = "../azalea-crypto", version = "0.5.0" }
-azalea-physics = {path = "../azalea-physics", version = "0.5.0" }
-azalea-protocol = {path = "../azalea-protocol", version = "0.5.0" }
-azalea-world = {path = "../azalea-world", version = "0.5.0" }
+azalea-auth = {path = "../azalea-auth", version = "0.5.0"}
+azalea-block = {path = "../azalea-block", version = "0.5.0"}
+azalea-chat = {path = "../azalea-chat", version = "0.5.0"}
+azalea-core = {path = "../azalea-core", version = "0.5.0"}
+azalea-crypto = {path = "../azalea-crypto", version = "0.5.0"}
+azalea-ecs = {path = "../azalea-ecs", version = "0.5.0"}
+azalea-physics = {path = "../azalea-physics", version = "0.5.0"}
+azalea-protocol = {path = "../azalea-protocol", version = "0.5.0"}
+azalea-registry = {path = "../azalea-registry", version = "0.5.0"}
+azalea-world = {path = "../azalea-world", version = "0.5.0"}
+bevy_tasks = "0.9.1"
+bevy_time = "0.9.1"
+derive_more = {version = "0.99.17", features = ["deref", "deref_mut"]}
+futures = "0.3.25"
+iyes_loopless = "0.9.1"
log = "0.4.17"
nohash-hasher = "0.2.0"
once_cell = "1.16.0"
@@ -28,3 +35,6 @@ thiserror = "^1.0.34"
tokio = {version = "^1.24.2", features = ["sync"]}
typemap_rev = "0.3.0"
uuid = "^1.1.2"
+
+[dev-dependencies]
+env_logger = "0.9.1"
diff --git a/azalea-client/examples/echo.rs b/azalea-client/examples/echo.rs
new file mode 100644
index 00000000..f37cd904
--- /dev/null
+++ b/azalea-client/examples/echo.rs
@@ -0,0 +1,49 @@
+//! A simple bot that repeats chat messages sent by other players.
+
+use azalea_client::{Account, Client, Event};
+
+#[tokio::main]
+async fn main() {
+ env_logger::init();
+ // deadlock detection, you can safely delete this block if you're not trying to
+ // debug deadlocks in azalea
+ {
+ use parking_lot::deadlock;
+ use std::thread;
+ use std::time::Duration;
+ thread::spawn(move || loop {
+ thread::sleep(Duration::from_secs(10));
+ let deadlocks = deadlock::check_deadlock();
+ if deadlocks.is_empty() {
+ continue;
+ }
+ println!("{} deadlocks detected", deadlocks.len());
+ for (i, threads) in deadlocks.iter().enumerate() {
+ println!("Deadlock #{i}");
+ for t in threads {
+ println!("Thread Id {:#?}", t.thread_id());
+ println!("{:#?}", t.backtrace());
+ }
+ }
+ });
+ }
+
+ let account = Account::offline("bot");
+ // or let account = Account::microsoft("email").await;
+
+ let (client, mut rx) = Client::join(&account, "localhost").await.unwrap();
+
+ while let Some(event) = rx.recv().await {
+ match &event {
+ Event::Chat(m) => {
+ if let (Some(sender), content) = m.split_sender_and_content() {
+ if sender == client.profile.name {
+ continue; // ignore our own messages
+ }
+ client.chat(&content);
+ };
+ }
+ _ => {}
+ }
+ }
+}
diff --git a/azalea-client/src/account.rs b/azalea-client/src/account.rs
index 79feb1a7..3c2c7d1b 100755
--- a/azalea-client/src/account.rs
+++ b/azalea-client/src/account.rs
@@ -28,7 +28,7 @@ pub struct Account {
/// The access token for authentication. You can obtain one of these
/// manually from azalea-auth.
///
- /// This is an Arc<Mutex> so it can be modified by [`Self::refresh`].
+ /// This is an `Arc<Mutex>` so it can be modified by [`Self::refresh`].
pub access_token: Option<Arc<Mutex<String>>>,
/// Only required for online-mode accounts.
pub uuid: Option<Uuid>,
diff --git a/azalea-client/src/chat.rs b/azalea-client/src/chat.rs
index 91dcf63e..3fa0ceec 100755
--- a/azalea-client/src/chat.rs
+++ b/azalea-client/src/chat.rs
@@ -1,7 +1,6 @@
//! Implementations of chat-related features.
-use crate::Client;
-use azalea_chat::Component;
+use azalea_chat::FormattedText;
use azalea_protocol::packets::game::{
clientbound_player_chat_packet::ClientboundPlayerChatPacket,
clientbound_system_chat_packet::ClientboundSystemChatPacket,
@@ -14,6 +13,8 @@ use std::{
};
use uuid::Uuid;
+use crate::client::Client;
+
/// A chat packet, either a system message or a chat message.
#[derive(Debug, Clone, PartialEq)]
pub enum ChatPacket {
@@ -30,7 +31,7 @@ macro_rules! regex {
impl ChatPacket {
/// Get the message shown in chat for this packet.
- pub fn message(&self) -> Component {
+ pub fn message(&self) -> FormattedText {
match self {
ChatPacket::System(p) => p.content.clone(),
ChatPacket::Player(p) => p.message(),
@@ -94,7 +95,7 @@ impl ChatPacket {
/// convenience function for testing.
pub fn new(message: &str) -> Self {
ChatPacket::System(Arc::new(ClientboundSystemChatPacket {
- content: Component::from(message),
+ content: FormattedText::from(message),
overlay: false,
}))
}
@@ -105,7 +106,7 @@ impl Client {
/// not the command packet. The [`Client::chat`] function handles checking
/// whether the message is a command and using the proper packet for you,
/// so you should use that instead.
- pub async fn send_chat_packet(&self, message: &str) -> Result<(), std::io::Error> {
+ pub fn send_chat_packet(&self, message: &str) {
// TODO: chat signing
// let signature = sign_message();
let packet = ServerboundChatPacket {
@@ -121,12 +122,12 @@ impl Client {
last_seen_messages: LastSeenMessagesUpdate::default(),
}
.get();
- self.write_packet(packet).await
+ self.write_packet(packet);
}
/// Send a command packet to the server. The `command` argument should not
/// include the slash at the front.
- pub async fn send_command_packet(&self, command: &str) -> Result<(), std::io::Error> {
+ pub fn send_command_packet(&self, command: &str) {
// TODO: chat signing
let packet = ServerboundChatCommandPacket {
command: command.to_string(),
@@ -141,7 +142,7 @@ impl Client {
last_seen_messages: LastSeenMessagesUpdate::default(),
}
.get();
- self.write_packet(packet).await
+ self.write_packet(packet);
}
/// Send a message in chat.
@@ -149,15 +150,15 @@ impl Client {
/// ```rust,no_run
/// # use azalea_client::{Client, Event};
/// # async fn handle(bot: Client, event: Event) -> anyhow::Result<()> {
- /// bot.chat("Hello, world!").await.unwrap();
+ /// bot.chat("Hello, world!");
/// # Ok(())
/// # }
/// ```
- pub async fn chat(&self, message: &str) -> Result<(), std::io::Error> {
+ pub fn chat(&self, message: &str) {
if let Some(command) = message.strip_prefix('/') {
- self.send_command_packet(command).await
+ self.send_command_packet(command);
} else {
- self.send_chat_packet(message).await
+ self.send_chat_packet(message);
}
}
}
diff --git a/azalea-client/src/client.rs b/azalea-client/src/client.rs
index 125facda..bc1d8d62 100644
--- a/azalea-client/src/client.rs
+++ b/azalea-client/src/client.rs
@@ -1,18 +1,32 @@
pub use crate::chat::ChatPacket;
-use crate::{movement::WalkDirection, plugins::PluginStates, Account, PlayerInfo};
+use crate::{
+ events::{Event, EventPlugin, LocalPlayerEvents},
+ local_player::{
+ death_event, update_in_loaded_chunk, GameProfileComponent, LocalPlayer, PhysicsState,
+ },
+ movement::{local_player_ai_step, send_position, sprint_listener, walk_listener},
+ packet_handling::{self, PacketHandlerPlugin},
+ player::retroactively_add_game_profile_component,
+ task_pool::TaskPoolPlugin,
+ Account, PlayerInfo, StartSprintEvent, StartWalkEvent,
+};
+
use azalea_auth::{game_profile::GameProfile, sessionserver::ClientSessionServerError};
-use azalea_chat::Component;
-use azalea_core::{ChunkPos, ResourceLocation, Vec3};
+use azalea_chat::FormattedText;
+use azalea_ecs::{
+ app::{App, Plugin, PluginGroup, PluginGroupBuilder},
+ component::Component,
+ entity::Entity,
+ schedule::{IntoSystemDescriptor, Schedule, Stage, SystemSet},
+ AppTickExt,
+};
+use azalea_ecs::{ecs::Ecs, TickPlugin};
+use azalea_physics::PhysicsPlugin;
use azalea_protocol::{
- connect::{Connection, ConnectionError, ReadConnection, WriteConnection},
+ connect::{Connection, ConnectionError},
packets::{
game::{
- clientbound_player_combat_kill_packet::ClientboundPlayerCombatKillPacket,
- serverbound_accept_teleportation_packet::ServerboundAcceptTeleportationPacket,
serverbound_client_information_packet::ServerboundClientInformationPacket,
- serverbound_custom_payload_packet::ServerboundCustomPayloadPacket,
- serverbound_keep_alive_packet::ServerboundKeepAlivePacket,
- serverbound_move_player_pos_rot_packet::ServerboundMovePlayerPosRotPacket,
ClientboundGamePacket, ServerboundGamePacket,
},
handshake::{
@@ -26,109 +40,40 @@ use azalea_protocol::{
},
ConnectionProtocol, PROTOCOL_VERSION,
},
- read::ReadPacketError,
resolver, ServerAddress,
};
-use azalea_world::{
- entity::{metadata, Entity, EntityData, EntityMetadata},
- PartialWorld, WeakWorld, WeakWorldContainer,
-};
-use log::{debug, error, info, trace, warn};
+use azalea_world::{EntityPlugin, Local, PartialWorld, World, WorldContainer};
+use log::{debug, error};
use parking_lot::{Mutex, RwLock};
-use std::{
- any,
- backtrace::Backtrace,
- collections::HashMap,
- fmt::Debug,
- io::{self, Cursor},
- sync::Arc,
-};
+use std::{collections::HashMap, fmt::Debug, io, net::SocketAddr, sync::Arc};
use thiserror::Error;
-use tokio::{
- sync::mpsc::{self, Receiver, Sender},
- task::JoinHandle,
- time::{self},
-};
+use tokio::{sync::mpsc, time};
use uuid::Uuid;
pub type ClientInformation = ServerboundClientInformationPacket;
-/// Something that happened in-game, such as a tick passing or chat message
-/// being sent.
-///
-/// Note: Events are sent before they're processed, so for example game ticks
-/// happen at the beginning of a tick before anything has happened.
-#[derive(Debug, Clone)]
-pub enum Event {
- /// Happens right after the bot switches into the Game state, but before
- /// it's actually spawned. This can be useful for setting the client
- /// information with `Client::set_client_information`, so the packet
- /// doesn't have to be sent twice.
- Init,
- /// The client is now in the world. Fired when we receive a login packet.
- Login,
- /// A chat message was sent in the game chat.
- Chat(ChatPacket),
- /// Happens 20 times per second, but only when the world is loaded.
- Tick,
- Packet(Arc<ClientboundGamePacket>),
- /// A player joined the game (or more specifically, was added to the tab
- /// list).
- AddPlayer(PlayerInfo),
- /// A player left the game (or maybe is still in the game and was just
- /// removed from the tab list).
- RemovePlayer(PlayerInfo),
- /// A player was updated in the tab list (gamemode, display
- /// name, or latency changed).
- UpdatePlayer(PlayerInfo),
- /// The client player died in-game.
- Death(Option<Arc<ClientboundPlayerCombatKillPacket>>),
-}
-
-/// A player that you control that is currently in a Minecraft server.
+/// Client has the things that a user interacting with the library will want.
+/// Things that a player in the world will want to know are in [`LocalPlayer`].
#[derive(Clone)]
pub struct Client {
+ /// The [`GameProfile`] for our client. This contains your username, UUID,
+ /// and skin data.
+ ///
+ /// This is immutable; the server cannot change it. To get the username and
+ /// skin the server chose for you, get your player from
+ /// [`Self::players`].
pub profile: GameProfile,
- pub read_conn: Arc<tokio::sync::Mutex<ReadConnection<ClientboundGamePacket>>>,
- pub write_conn: Arc<tokio::sync::Mutex<WriteConnection<ServerboundGamePacket>>>,
- pub entity_id: Arc<RwLock<u32>>,
- /// The world that this client has access to. This supports shared worlds.
+ /// The entity for this client in the ECS.
+ pub entity: Entity,
+ /// The world that this client is in.
pub world: Arc<RwLock<PartialWorld>>,
- /// A container of world names to worlds. If we're not using a shared world
- /// (i.e. not a swarm), then this will only contain data about the world
- /// we're currently in.
- world_container: Arc<RwLock<WeakWorldContainer>>,
- pub world_name: Arc<RwLock<Option<ResourceLocation>>>,
- pub physics_state: Arc<Mutex<PhysicsState>>,
- pub client_information: Arc<RwLock<ClientInformation>>,
- pub dead: Arc<Mutex<bool>>,
- /// Plugins are a way for other crates to add custom functionality to the
- /// client and keep state. If you're not making a plugin and you're using
- /// the `azalea` crate. you can ignore this field.
- pub plugins: Arc<PluginStates>,
- /// A map of player uuids to their information in the tab list
- pub players: Arc<RwLock<HashMap<Uuid, PlayerInfo>>>,
- tasks: Arc<Mutex<Vec<JoinHandle<()>>>>,
-}
-
-#[derive(Default)]
-pub struct PhysicsState {
- /// Minecraft only sends a movement packet either after 20 ticks or if the
- /// player moved enough. This is that tick counter.
- pub position_remainder: u32,
- pub was_sprinting: bool,
- // Whether we're going to try to start sprinting this tick. Equivalent to
- // holding down ctrl for a tick.
- pub trying_to_sprint: bool,
- pub move_direction: WalkDirection,
- pub forward_impulse: f32,
- pub left_impulse: f32,
+ /// The entity component system. You probably don't need to access this
+ /// directly. Note that if you're using a shared world (i.e. a swarm), this
+ /// will contain all entities in all worlds.
+ pub ecs: Arc<Mutex<Ecs>>,
}
-/// Whether we should ignore errors when decoding packets.
-const IGNORE_ERRORS: bool = !cfg!(debug_assertions);
-
/// An error that happened while joining the server.
#[derive(Error, Debug)]
pub enum JoinError {
@@ -147,54 +92,21 @@ pub enum JoinError {
#[error("Couldn't refresh access token: {0}")]
Auth(#[from] azalea_auth::AuthError),
#[error("Disconnected: {reason}")]
- Disconnect { reason: Component },
-}
-
-#[derive(Error, Debug)]
-pub enum HandleError {
- #[error("{0}")]
- Poison(String),
- #[error(transparent)]
- Io(#[from] io::Error),
- #[error(transparent)]
- Other(#[from] anyhow::Error),
- #[error("{0}")]
- Send(#[from] mpsc::error::SendError<Event>),
+ Disconnect { reason: FormattedText },
}
impl Client {
/// Create a new client from the given GameProfile, Connection, and World.
/// You should only use this if you want to change these fields from the
/// defaults, otherwise use [`Client::join`].
- pub fn new(
- profile: GameProfile,
- conn: Connection<ClientboundGamePacket, ServerboundGamePacket>,
- world_container: Option<Arc<RwLock<WeakWorldContainer>>>,
- ) -> Self {
- let (read_conn, write_conn) = conn.into_split();
- let (read_conn, write_conn) = (
- Arc::new(tokio::sync::Mutex::new(read_conn)),
- Arc::new(tokio::sync::Mutex::new(write_conn)),
- );
-
+ pub fn new(profile: GameProfile, entity: Entity, ecs: Arc<Mutex<Ecs>>) -> Self {
Self {
profile,
- read_conn,
- write_conn,
// default our id to 0, it'll be set later
- entity_id: Arc::new(RwLock::new(0)),
+ entity,
world: Arc::new(RwLock::new(PartialWorld::default())),
- world_container: world_container
- .unwrap_or_else(|| Arc::new(RwLock::new(WeakWorldContainer::new()))),
- world_name: Arc::new(RwLock::new(None)),
- physics_state: Arc::new(Mutex::new(PhysicsState::default())),
- client_information: Arc::new(RwLock::new(ClientInformation::default())),
- dead: Arc::new(Mutex::new(false)),
- // The plugins can be modified by the user by replacing the plugins
- // field right after this. No Mutex so the user doesn't need to .lock().
- plugins: Arc::new(PluginStates::default()),
- players: Arc::new(RwLock::new(HashMap::new())),
- tasks: Arc::new(Mutex::new(Vec::new())),
+
+ ecs,
}
}
@@ -213,34 +125,90 @@ impl Client {
/// async fn main() -> Result<(), Box<dyn std::error::Error>> {
/// let account = Account::offline("bot");
/// let (client, rx) = Client::join(&account, "localhost").await?;
- /// client.chat("Hello, world!").await?;
- /// client.disconnect().await?;
+ /// client.chat("Hello, world!");
+ /// client.disconnect();
/// Ok(())
/// }
/// ```
pub async fn join(
account: &Account,
address: impl TryInto<ServerAddress>,
- ) -> Result<(Self, Receiver<Event>), JoinError> {
+ ) -> Result<(Self, mpsc::UnboundedReceiver<Event>), JoinError> {
let address: ServerAddress = address.try_into().map_err(|_| JoinError::InvalidAddress)?;
let resolved_address = resolver::resolve_address(&address).await?;
- let conn = Connection::new(&resolved_address).await?;
- let (conn, game_profile) = Self::handshake(conn, account, &address).await?;
+ // An event that causes the schedule to run. This is only used internally.
+ let (run_schedule_sender, run_schedule_receiver) = mpsc::channel(1);
+ let app = init_ecs_app();
+ let ecs_lock = start_ecs(app, run_schedule_receiver, run_schedule_sender.clone());
+
+ Self::start_client(
+ ecs_lock,
+ account,
+ &address,
+ &resolved_address,
+ run_schedule_sender,
+ )
+ .await
+ }
+
+ /// Create a [`Client`] when you already have the ECS made with
+ /// [`start_ecs`]. You'd usually want to use [`Self::join`] instead.
+ pub async fn start_client(
+ ecs_lock: Arc<Mutex<Ecs>>,
+ account: &Account,
+ address: &ServerAddress,
+ resolved_address: &SocketAddr,
+ run_schedule_sender: mpsc::Sender<()>,
+ ) -> Result<(Self, mpsc::UnboundedReceiver<Event>), JoinError> {
+ let conn = Connection::new(resolved_address).await?;
+ let (conn, game_profile) = Self::handshake(conn, account, address).await?;
+ let (read_conn, write_conn) = conn.into_split();
- // The buffer has to be 1 to avoid a bug where if it lags events are
- // received a bit later instead of the instant they were fired.
- // That bug especially causes issues with the pathfinder.
- let (tx, rx) = mpsc::channel(1);
+ let (tx, rx) = mpsc::unbounded_channel();
+
+ let mut ecs = ecs_lock.lock();
+
+ // Make the ecs entity for this client
+ let entity_mut = ecs.spawn_empty();
+ let entity = entity_mut.id();
// we got the GameConnection, so the server is now connected :)
- let client = Client::new(game_profile, conn, None);
+ let client = Client::new(game_profile.clone(), entity, ecs_lock.clone());
- tx.send(Event::Init).await.expect("Failed to send event");
+ let (packet_writer_sender, packet_writer_receiver) = mpsc::unbounded_channel();
- // just start up the game loop and we're ready!
+ let mut local_player = crate::local_player::LocalPlayer::new(
+ entity,
+ packet_writer_sender,
+ // default to an empty world, it'll be set correctly later when we
+ // get the login packet
+ Arc::new(RwLock::new(World::default())),
+ );
- client.start_tasks(tx);
+ // start receiving packets
+ let packet_receiver = packet_handling::PacketReceiver {
+ packets: Arc::new(Mutex::new(Vec::new())),
+ run_schedule_sender: run_schedule_sender.clone(),
+ };
+
+ let read_packets_task = tokio::spawn(packet_receiver.clone().read_task(read_conn));
+ let write_packets_task = tokio::spawn(
+ packet_receiver
+ .clone()
+ .write_task(write_conn, packet_writer_receiver),
+ );
+ local_player.tasks.push(read_packets_task);
+ local_player.tasks.push(write_packets_task);
+
+ ecs.entity_mut(entity).insert((
+ local_player,
+ packet_receiver,
+ GameProfileComponent(game_profile),
+ PhysicsState::default(),
+ Local,
+ LocalPlayerEvents(tx),
+ ));
Ok((client, rx))
}
@@ -369,712 +337,61 @@ impl Client {
}
/// Write a packet directly to the server.
- pub async fn write_packet(&self, packet: ServerboundGamePacket) -> Result<(), std::io::Error> {
- self.write_conn.lock().await.write(packet).await?;
- Ok(())
+ pub fn write_packet(&self, packet: ServerboundGamePacket) {
+ self.local_player_mut(&mut self.ecs.lock())
+ .write_packet(packet);
}
- /// Disconnect this client from the server, ending all tasks.
- pub async fn disconnect(&self) -> Result<(), std::io::Error> {
- if let Err(e) = self.write_conn.lock().await.shutdown().await {
- warn!(
- "Error shutting down connection, but it might be fine: {}",
- e
- );
- }
- let tasks = self.tasks.lock();
- for task in tasks.iter() {
- task.abort();
- }
- Ok(())
+ /// Disconnect this client from the server by ending all tasks.
+ ///
+ /// The OwnedReadHalf for the TCP connection is in one of the tasks, so it
+ /// automatically closes the connection when that's dropped.
+ pub fn disconnect(&self) {
+ self.local_player_mut(&mut self.ecs.lock()).disconnect();
}
- /// Start the protocol and game tick loop.
- #[doc(hidden)]
- pub fn start_tasks(&self, tx: Sender<Event>) {
- // if you get an error right here that means you're doing something with locks
- // wrong read the error to see where the issue is
- // you might be able to just drop the lock or put it in its own scope to fix
-
- let mut tasks = self.tasks.lock();
- tasks.push(tokio::spawn(Client::protocol_loop(
- self.clone(),
- tx.clone(),
- )));
- tasks.push(tokio::spawn(Client::game_tick_loop(self.clone(), tx)));
+ pub fn local_player<'a>(&'a self, ecs: &'a mut Ecs) -> &'a LocalPlayer {
+ self.query::<&LocalPlayer>(ecs)
}
-
- async fn protocol_loop(client: Client, tx: Sender<Event>) {
- loop {
- let r = client.read_conn.lock().await.read().await;
- match r {
- Ok(packet) => match Self::handle(&packet, &client, &tx).await {
- Ok(_) => {}
- Err(e) => {
- error!("Error handling packet: {}", e);
- if !IGNORE_ERRORS {
- panic!("Error handling packet: {e}");
- }
- }
- },
- Err(e) => {
- let e = *e;
- if let ReadPacketError::ConnectionClosed = e {
- info!("Connection closed");
- if let Err(e) = client.disconnect().await {
- error!("Error shutting down connection: {:?}", e);
- }
- break;
- }
- let default_backtrace = Backtrace::capture();
- if IGNORE_ERRORS {
- let backtrace =
- any::request_ref::<Backtrace>(&e).unwrap_or(&default_backtrace);
- warn!("{e}\n{backtrace}");
- match e {
- ReadPacketError::FrameSplitter { .. } => panic!("Error: {e:?}"),
- _ => continue,
- }
- } else {
- let backtrace =
- any::request_ref::<Backtrace>(&e).unwrap_or(&default_backtrace);
- panic!("{e}\n{backtrace}")
- }
- }
- };
- }
+ pub fn local_player_mut<'a>(
+ &'a self,
+ ecs: &'a mut Ecs,
+ ) -> azalea_ecs::ecs::Mut<'a, LocalPlayer> {
+ self.query::<&mut LocalPlayer>(ecs)
}
- async fn handle(
- packet: &ClientboundGamePacket,
- client: &Client,
- tx: &Sender<Event>,
- ) -> Result<(), HandleError> {
- let packet = Arc::new(packet.clone());
- tx.send(Event::Packet(packet.clone())).await?;
- match &*packet {
- ClientboundGamePacket::Login(p) => {
- debug!("Got login packet");
-
- {
- // // write p into login.txt
- // std::io::Write::write_all(
- // &mut std::fs::File::create("login.txt").unwrap(),
- // format!("{:#?}", p).as_bytes(),
- // )
- // .unwrap();
-
- // TODO: have registry_holder be a struct because this sucks rn
- // best way would be to add serde support to azalea-nbt
-
- let registry_holder = p
- .registry_holder
- .as_compound()
- .expect("Registry holder is not a compound")
- .get("")
- .expect("No \"\" tag")
- .as_compound()
- .expect("\"\" tag is not a compound");
- let dimension_types = registry_holder
- .get("minecraft:dimension_type")
- .expect("No dimension_type tag")
- .as_compound()
- .expect("dimension_type is not a compound")
- .get("value")
- .expect("No dimension_type value")
- .as_list()
- .expect("dimension_type value is not a list");
- let dimension_type = dimension_types
- .iter()
- .find(|t| {
- t.as_compound()
- .expect("dimension_type value is not a compound")
- .get("name")
- .expect("No name tag")
- .as_string()
- .expect("name is not a string")
- == p.dimension_type.to_string()
- })
- .unwrap_or_else(|| {
- panic!("No dimension_type with name {}", p.dimension_type)
- })
- .as_compound()
- .unwrap()
- .get("element")
- .expect("No element tag")
- .as_compound()
- .expect("element is not a compound");
- let height = (*dimension_type
- .get("height")
- .expect("No height tag")
- .as_int()
- .expect("height tag is not an int"))
- .try_into()
- .expect("height is not a u32");
- let min_y = *dimension_type
- .get("min_y")
- .expect("No min_y tag")
- .as_int()
- .expect("min_y tag is not an int");
-
- let world_name = p.dimension.clone();
-
- *client.world_name.write() = Some(world_name.clone());
- // add this world to the world_container (or don't if it's already there)
- let weak_world = client
- .world_container
- .write()
- .insert(world_name, height, min_y);
- // set the loaded_world to an empty world
- // (when we add chunks or entities those will be in the world_container)
- let mut world_lock = client.world.write();
- *world_lock = PartialWorld::new(
- client.client_information.read().view_distance.into(),
- weak_world,
- Some(p.player_id),
- );
-
- let entity = EntityData::new(
- client.profile.uuid,
- Vec3::default(),
- EntityMetadata::Player(metadata::Player::default()),
- );
- // make it so other entities don't update this entity in a shared world
- world_lock.add_entity(p.player_id, entity);
-
- *client.entity_id.write() = p.player_id;
- }
-
- // send the client information that we have set
- let client_information_packet: ClientInformation =
- client.client_information.read().clone();
- log::debug!(
- "Sending client information because login: {:?}",
- client_information_packet
- );
- client.write_packet(client_information_packet.get()).await?;
-
- // brand
- client
- .write_packet(
- ServerboundCustomPayloadPacket {
- identifier: ResourceLocation::new("brand").unwrap(),
- // they don't have to know :)
- data: "vanilla".into(),
- }
- .get(),
- )
- .await?;
-
- tx.send(Event::Login).await?;
- }
- ClientboundGamePacket::SetChunkCacheRadius(p) => {
- debug!("Got set chunk cache radius packet {:?}", p);
- }
- ClientboundGamePacket::CustomPayload(p) => {
- debug!("Got custom payload packet {:?}", p);
- }
- ClientboundGamePacket::ChangeDifficulty(p) => {
- debug!("Got difficulty packet {:?}", p);
- }
- ClientboundGamePacket::Commands(_p) => {
- debug!("Got declare commands packet");
- }
- ClientboundGamePacket::PlayerAbilities(p) => {
- debug!("Got player abilities packet {:?}", p);
- }
- ClientboundGamePacket::SetCarriedItem(p) => {
- debug!("Got set carried item packet {:?}", p);
- }
- ClientboundGamePacket::UpdateTags(_p) => {
- debug!("Got update tags packet");
- }
- ClientboundGamePacket::Disconnect(p) => {
- debug!("Got disconnect packet {:?}", p);
- client.disconnect().await?;
- }
- ClientboundGamePacket::UpdateRecipes(_p) => {
- debug!("Got update recipes packet");
- }
- ClientboundGamePacket::EntityEvent(_p) => {
- // debug!("Got entity event packet {:?}", p);
- }
- ClientboundGamePacket::Recipe(_p) => {
- debug!("Got recipe packet");
- }
- ClientboundGamePacket::PlayerPosition(p) => {
- // TODO: reply with teleport confirm
- debug!("Got player position packet {:?}", p);
-
- let (new_pos, y_rot, x_rot) = {
- let player_entity_id = *client.entity_id.read();
-
- let mut world_lock = client.world.write();
-
- let mut player_entity = world_lock.entity_mut(player_entity_id).unwrap();
-
- let delta_movement = player_entity.delta;
-
- let is_x_relative = p.relative_arguments.x;
- let is_y_relative = p.relative_arguments.y;
- let is_z_relative = p.relative_arguments.z;
-
- let (delta_x, new_pos_x) = if is_x_relative {
- player_entity.last_pos.x += p.x;
- (delta_movement.x, player_entity.pos().x + p.x)
- } else {
- player_entity.last_pos.x = p.x;
- (0.0, p.x)
- };
- let (delta_y, new_pos_y) = if is_y_relative {
- player_entity.last_pos.y += p.y;
- (delta_movement.y, player_entity.pos().y + p.y)
- } else {
- player_entity.last_pos.y = p.y;
- (0.0, p.y)
- };
- let (delta_z, new_pos_z) = if is_z_relative {
- player_entity.last_pos.z += p.z;
- (delta_movement.z, player_entity.pos().z + p.z)
- } else {
- player_entity.last_pos.z = p.z;
- (0.0, p.z)
- };
-
- let mut y_rot = p.y_rot;
- let mut x_rot = p.x_rot;
- if p.relative_arguments.x_rot {
- x_rot += player_entity.x_rot;
- }
- if p.relative_arguments.y_rot {
- y_rot += player_entity.y_rot;
- }
-
- player_entity.delta = Vec3 {
- x: delta_x,
- y: delta_y,
- z: delta_z,
- };
- player_entity.set_rotation(y_rot, x_rot);
- // TODO: minecraft sets "xo", "yo", and "zo" here but idk what that means
- // so investigate that ig
- let new_pos = Vec3 {
- x: new_pos_x,
- y: new_pos_y,
- z: new_pos_z,
- };
- world_lock
- .set_entity_pos(player_entity_id, new_pos)
- .expect("The player entity should always exist");
-
- (new_pos, y_rot, x_rot)
- };
-
- client
- .write_packet(ServerboundAcceptTeleportationPacket { id: p.id }.get())
- .await?;
- client
- .write_packet(
- ServerboundMovePlayerPosRotPacket {
- x: new_pos.x,
- y: new_pos.y,
- z: new_pos.z,
- y_rot,
- x_rot,
- // this is always false
- on_ground: false,
- }
- .get(),
- )
- .await?;
- }
- ClientboundGamePacket::PlayerInfoUpdate(p) => {
- debug!("Got player info packet {:?}", p);
- let mut events = Vec::new();
- {
- let mut players_lock = client.players.write();
- for updated_info in &p.entries {
- // add the new player maybe
- if p.actions.add_player {
- let player_info = PlayerInfo {
- profile: updated_info.profile.clone(),
- uuid: updated_info.profile.uuid,
- gamemode: updated_info.game_mode,
- latency: updated_info.latency,
- display_name: updated_info.display_name.clone(),
- };
- players_lock.insert(updated_info.profile.uuid, player_info.clone());
- events.push(Event::AddPlayer(player_info));
- } else if let Some(info) = players_lock.get_mut(&updated_info.profile.uuid)
- {
- // `else if` because the block for add_player above
- // already sets all the fields
- if p.actions.update_game_mode {
- info.gamemode = updated_info.game_mode;
- }
- if p.actions.update_latency {
- info.latency = updated_info.latency;
- }
- if p.actions.update_display_name {
- info.display_name = updated_info.display_name.clone();
- }
- events.push(Event::UpdatePlayer(info.clone()));
- } else {
- warn!(
- "Ignoring PlayerInfoUpdate for unknown player {}",
- updated_info.profile.uuid
- );
- }
- }
- }
- for event in events {
- tx.send(event).await?;
- }
- }
- ClientboundGamePacket::PlayerInfoRemove(p) => {
- let mut events = Vec::new();
- {
- let mut players_lock = client.players.write();
- for uuid in &p.profile_ids {
- if let Some(info) = players_lock.remove(uuid) {
- events.push(Event::RemovePlayer(info));
- }
- }
- }
- for event in events {
- tx.send(event).await?;
- }
- }
- ClientboundGamePacket::SetChunkCacheCenter(p) => {
- debug!("Got chunk cache center packet {:?}", p);
- client
- .world
- .write()
- .update_view_center(&ChunkPos::new(p.x, p.z));
- }
- ClientboundGamePacket::LevelChunkWithLight(p) => {
- // debug!("Got chunk with light packet {} {}", p.x, p.z);
- let pos = ChunkPos::new(p.x, p.z);
-
- // OPTIMIZATION: if we already know about the chunk from the
- // shared world (and not ourselves), then we don't need to
- // parse it again. This is only used when we have a shared
- // world, since we check that the chunk isn't currently owned
- // by this client.
- let shared_has_chunk = client.world.read().get_chunk(&pos).is_some();
- let this_client_has_chunk = client
- .world
- .read()
- .chunk_storage
- .limited_get(&pos)
- .is_some();
- if shared_has_chunk && !this_client_has_chunk {
- trace!(
- "Skipping parsing chunk {:?} because we already know about it",
- pos
- );
- return Ok(());
- }
-
- // let chunk = Chunk::read_with_world_height(&mut p.chunk_data);
- // debug("chunk {:?}")
- if let Err(e) = client
- .world
- .write()
- .replace_with_packet_data(&pos, &mut Cursor::new(&p.chunk_data.data))
- {
- error!("Couldn't set chunk data: {}", e);
- }
- }
- ClientboundGamePacket::LightUpdate(_p) => {
- // debug!("Got light update packet {:?}", p);
- }
- ClientboundGamePacket::AddEntity(p) => {
- debug!("Got add entity packet {:?}", p);
- let entity = EntityData::from(p);
- client.world.write().add_entity(p.id, entity);
- }
- ClientboundGamePacket::SetEntityData(p) => {
- debug!("Got set entity data packet {:?}", p);
- let mut world = client.world.write();
- if let Some(mut entity) = world.entity_mut(p.id) {
- entity.apply_metadata(&p.packed_items.0);
- } else {
- // warn!("Server sent an entity data packet for an entity id
- // ({}) that we don't know about", p.id);
- }
- }
- ClientboundGamePacket::UpdateAttributes(_p) => {
- // debug!("Got update attributes packet {:?}", p);
- }
- ClientboundGamePacket::SetEntityMotion(_p) => {
- // debug!("Got entity velocity packet {:?}", p);
- }
- ClientboundGamePacket::SetEntityLink(p) => {
- debug!("Got set entity link packet {:?}", p);
- }
- ClientboundGamePacket::AddPlayer(p) => {
- debug!("Got add player packet {:?}", p);
- let entity = EntityData::from(p);
- client.world.write().add_entity(p.id, entity);
- }
- ClientboundGamePacket::InitializeBorder(p) => {
- debug!("Got initialize border packet {:?}", p);
- }
- ClientboundGamePacket::SetTime(p) => {
- debug!("Got set time packet {:?}", p);
- }
- ClientboundGamePacket::SetDefaultSpawnPosition(p) => {
- debug!("Got set default spawn position packet {:?}", p);
- }
- ClientboundGamePacket::ContainerSetContent(p) => {
- debug!("Got container set content packet {:?}", p);
- }
- ClientboundGamePacket::SetHealth(p) => {
- debug!("Got set health packet {:?}", p);
- if p.health == 0.0 {
- // we can't define a variable here with client.dead.lock()
- // because of https://github.com/rust-lang/rust/issues/57478
- if !*client.dead.lock() {
- *client.dead.lock() = true;
- tx.send(Event::Death(None)).await?;
- }
- }
- }
- ClientboundGamePacket::SetExperience(p) => {
- debug!("Got set experience packet {:?}", p);
- }
- ClientboundGamePacket::TeleportEntity(p) => {
- let mut world_lock = client.world.write();
- let _ = world_lock.set_entity_pos(
- p.id,
- Vec3 {
- x: p.x,
- y: p.y,
- z: p.z,
- },
- );
- }
- ClientboundGamePacket::UpdateAdvancements(p) => {
- debug!("Got update advancements packet {:?}", p);
- }
- ClientboundGamePacket::RotateHead(_p) => {
- // debug!("Got rotate head packet {:?}", p);
- }
- ClientboundGamePacket::MoveEntityPos(p) => {
- let mut world_lock = client.world.write();
-
- let _ = world_lock.move_entity_with_delta(p.entity_id, &p.delta);
- }
- ClientboundGamePacket::MoveEntityPosRot(p) => {
- let mut world_lock = client.world.write();
-
- let _ = world_lock.move_entity_with_delta(p.entity_id, &p.delta);
- }
- ClientboundGamePacket::MoveEntityRot(_p) => {
- // debug!("Got move entity rot packet {:?}", p);
- }
- ClientboundGamePacket::KeepAlive(p) => {
- debug!("Got keep alive packet {:?}", p);
- client
- .write_packet(ServerboundKeepAlivePacket { id: p.id }.get())
- .await?;
- }
- ClientboundGamePacket::RemoveEntities(p) => {
- debug!("Got remove entities packet {:?}", p);
- }
- ClientboundGamePacket::PlayerChat(p) => {
- debug!("Got player chat packet {:?}", p);
- tx.send(Event::Chat(ChatPacket::Player(Arc::new(p.clone()))))
- .await?;
- }
- ClientboundGamePacket::SystemChat(p) => {
- debug!("Got system chat packet {:?}", p);
- tx.send(Event::Chat(ChatPacket::System(Arc::new(p.clone()))))
- .await?;
- }
- ClientboundGamePacket::Sound(_p) => {
- // debug!("Got sound packet {:?}", p);
- }
- ClientboundGamePacket::LevelEvent(p) => {
- debug!("Got level event packet {:?}", p);
- }
- ClientboundGamePacket::BlockUpdate(p) => {
- debug!("Got block update packet {:?}", p);
- let mut world = client.world.write();
- world.set_block_state(&p.pos, p.block_state);
- }
- ClientboundGamePacket::Animate(p) => {
- debug!("Got animate packet {:?}", p);
- }
- ClientboundGamePacket::SectionBlocksUpdate(p) => {
- debug!("Got section blocks update packet {:?}", p);
- let mut world = client.world.write();
- for state in &p.states {
- world.set_block_state(&(p.section_pos + state.pos.clone()), state.state);
- }
- }
- ClientboundGamePacket::GameEvent(p) => {
- debug!("Got game event packet {:?}", p);
- }
- ClientboundGamePacket::LevelParticles(p) => {
- debug!("Got level particles packet {:?}", p);
- }
- ClientboundGamePacket::ServerData(p) => {
- debug!("Got server data packet {:?}", p);
- }
- ClientboundGamePacket::SetEquipment(p) => {
- debug!("Got set equipment packet {:?}", p);
- }
- ClientboundGamePacket::UpdateMobEffect(p) => {
- debug!("Got update mob effect packet {:?}", p);
- }
- ClientboundGamePacket::AddExperienceOrb(_) => {}
- ClientboundGamePacket::AwardStats(_) => {}
- ClientboundGamePacket::BlockChangedAck(_) => {}
- ClientboundGamePacket::BlockDestruction(_) => {}
- ClientboundGamePacket::BlockEntityData(_) => {}
- ClientboundGamePacket::BlockEvent(_) => {}
- ClientboundGamePacket::BossEvent(_) => {}
- ClientboundGamePacket::CommandSuggestions(_) => {}
- ClientboundGamePacket::ContainerSetData(_) => {}
- ClientboundGamePacket::ContainerSetSlot(_) => {}
- ClientboundGamePacket::Cooldown(_) => {}
- ClientboundGamePacket::CustomChatCompletions(_) => {}
- ClientboundGamePacket::DeleteChat(_) => {}
- ClientboundGamePacket::Explode(_) => {}
- ClientboundGamePacket::ForgetLevelChunk(_) => {}
- ClientboundGamePacket::HorseScreenOpen(_) => {}
- ClientboundGamePacket::MapItemData(_) => {}
- ClientboundGamePacket::MerchantOffers(_) => {}
- ClientboundGamePacket::MoveVehicle(_) => {}
- ClientboundGamePacket::OpenBook(_) => {}
- ClientboundGamePacket::OpenScreen(_) => {}
- ClientboundGamePacket::OpenSignEditor(_) => {}
- ClientboundGamePacket::Ping(_) => {}
- ClientboundGamePacket::PlaceGhostRecipe(_) => {}
- ClientboundGamePacket::PlayerCombatEnd(_) => {}
- ClientboundGamePacket::PlayerCombatEnter(_) => {}
- ClientboundGamePacket::PlayerCombatKill(p) => {
- debug!("Got player kill packet {:?}", p);
- if *client.entity_id.read() == p.player_id {
- // we can't define a variable here with client.dead.lock()
- // because of https://github.com/rust-lang/rust/issues/57478
- if !*client.dead.lock() {
- *client.dead.lock() = true;
- tx.send(Event::Death(Some(Arc::new(p.clone())))).await?;
- }
- }
- }
- ClientboundGamePacket::PlayerLookAt(_) => {}
- ClientboundGamePacket::RemoveMobEffect(_) => {}
- ClientboundGamePacket::ResourcePack(_) => {}
- ClientboundGamePacket::Respawn(p) => {
- debug!("Got respawn packet {:?}", p);
- // Sets clients dead state to false.
- let mut dead_lock = client.dead.lock();
- *dead_lock = false;
- }
- ClientboundGamePacket::SelectAdvancementsTab(_) => {}
- ClientboundGamePacket::SetActionBarText(_) => {}
- ClientboundGamePacket::SetBorderCenter(_) => {}
- ClientboundGamePacket::SetBorderLerpSize(_) => {}
- ClientboundGamePacket::SetBorderSize(_) => {}
- ClientboundGamePacket::SetBorderWarningDelay(_) => {}
- ClientboundGamePacket::SetBorderWarningDistance(_) => {}
- ClientboundGamePacket::SetCamera(_) => {}
- ClientboundGamePacket::SetDisplayObjective(_) => {}
- ClientboundGamePacket::SetObjective(_) => {}
- ClientboundGamePacket::SetPassengers(_) => {}
- ClientboundGamePacket::SetPlayerTeam(_) => {}
- ClientboundGamePacket::SetScore(_) => {}
- ClientboundGamePacket::SetSimulationDistance(_) => {}
- ClientboundGamePacket::SetSubtitleText(_) => {}
- ClientboundGamePacket::SetTitleText(_) => {}
- ClientboundGamePacket::SetTitlesAnimation(_) => {}
- ClientboundGamePacket::SoundEntity(_) => {}
- ClientboundGamePacket::StopSound(_) => {}
- ClientboundGamePacket::TabList(_) => {}
- ClientboundGamePacket::TagQuery(_) => {}
- ClientboundGamePacket::TakeItemEntity(_) => {}
- ClientboundGamePacket::DisguisedChat(_) => {}
- ClientboundGamePacket::UpdateEnabledFeatures(_) => {}
- ClientboundGamePacket::ContainerClose(_) => {}
- }
-
- Ok(())
- }
-
- /// Runs game_tick every 50 milliseconds.
- async fn game_tick_loop(mut client: Client, tx: Sender<Event>) {
- let mut game_tick_interval = time::interval(time::Duration::from_millis(50));
- // TODO: Minecraft bursts up to 10 ticks and then skips, we should too
- game_tick_interval.set_missed_tick_behavior(time::MissedTickBehavior::Burst);
- loop {
- game_tick_interval.tick().await;
- Self::game_tick(&mut client, &tx).await;
- }
- }
-
- /// Runs every 50 milliseconds.
- async fn game_tick(client: &mut Client, tx: &Sender<Event>) {
- // return if there's no chunk at the player's position
-
- {
- let world_lock = client.world();
- let player_entity_id = *client.entity_id.read();
- let player_entity = world_lock.entity(player_entity_id);
- let Some(player_entity) = player_entity else {
- return;
- };
- let player_chunk_pos: ChunkPos = player_entity.pos().into();
- if world_lock.get_chunk(&player_chunk_pos).is_none() {
- return;
- }
- }
-
- tx.send(Event::Tick)
- .await
- .expect("Sending tick event should never fail");
-
- // TODO: if we're a passenger, send the required packets
-
- if let Err(e) = client.send_position().await {
- warn!("Error sending position: {:?}", e);
- }
- client.ai_step();
-
- // TODO: minecraft does ambient sounds here
+ /// Get a component from this client. This will clone the component and
+ /// return it.
+ pub fn component<T: Component + Clone>(&self) -> T {
+ self.query::<&T>(&mut self.ecs.lock()).clone()
}
/// Get a reference to our (potentially shared) world.
///
- /// This gets the [`WeakWorld`] from our world container. If it's a normal
+ /// This gets the [`World`] from our world container. If it's a normal
/// client, then it'll be the same as the world the client has loaded.
/// If the client using a shared world, then the shared world will be a
/// superset of the client's world.
- pub fn world(&self) -> Arc<WeakWorld> {
- self.world.read().shared.clone()
- }
-
- /// Returns the entity associated to the player.
- pub fn entity(&self) -> Entity<Arc<WeakWorld>> {
- let entity_id = *self.entity_id.read();
+ pub fn world(&self) -> Arc<RwLock<World>> {
+ let mut ecs = self.ecs.lock();
+
+ let world_name = {
+ let local_player = self.local_player(&mut ecs);
+ local_player
+ .world_name
+ .as_ref()
+ .expect("World name must be known if we're doing Client::world")
+ .clone()
+ };
- let world = self.world();
- let entity_data = world
- .entity_storage
- .read()
- .get_by_id(entity_id)
- .expect("Player entity should be in the given world");
- let entity_ptr = unsafe { entity_data.as_ptr() };
- Entity::new(world, entity_id, entity_ptr)
+ let world_container = ecs.resource::<WorldContainer>();
+ world_container.get(&world_name).unwrap()
}
/// Returns whether we have a received the login packet yet.
pub fn logged_in(&self) -> bool {
// the login packet tells us the world name
- self.world_name.read().is_some()
+ self.local_player(&mut self.ecs.lock()).world_name.is_some()
}
/// Tell the server we changed our game options (i.e. render distance, main
@@ -1097,34 +414,156 @@ impl Client {
client_information: ServerboundClientInformationPacket,
) -> Result<(), std::io::Error> {
{
- let mut client_information_lock = self.client_information.write();
- *client_information_lock = client_information;
+ self.local_player_mut(&mut self.ecs.lock())
+ .client_information = client_information;
}
if self.logged_in() {
- let client_information_packet = {
- let client_information = self.client_information.read();
- client_information.clone().get()
- };
+ let client_information_packet = self
+ .local_player(&mut self.ecs.lock())
+ .client_information
+ .clone()
+ .get();
log::debug!(
"Sending client information (already logged in): {:?}",
client_information_packet
);
- self.write_packet(client_information_packet).await?;
+ self.write_packet(client_information_packet);
}
Ok(())
}
- /// Get your player entity's metadata. You can use this to get your health,
- /// xp score, and other useful information.
- pub fn metadata(&self) -> metadata::Player {
- self.entity().metadata.clone().into_player().unwrap()
+ /// Get a HashMap of all the players in the tab list.
+ pub fn players(&mut self) -> HashMap<Uuid, PlayerInfo> {
+ self.local_player(&mut self.ecs.lock()).players.clone()
+ }
+}
+
+pub struct AzaleaPlugin;
+impl Plugin for AzaleaPlugin {
+ fn build(&self, app: &mut App) {
+ app.add_event::<StartWalkEvent>()
+ .add_event::<StartSprintEvent>();
+
+ app.add_plugins(DefaultPlugins);
+
+ app.add_tick_system_set(
+ SystemSet::new()
+ .with_system(send_position)
+ .with_system(update_in_loaded_chunk)
+ .with_system(
+ local_player_ai_step
+ .before("ai_step")
+ .after("sprint_listener"),
+ ),
+ );
+
+ // fire the Death event when the player dies.
+ app.add_system(death_event.after("tick").after("packet"));
+
+ // walk and sprint event listeners
+ app.add_system(walk_listener.label("walk_listener").before("travel"))
+ .add_system(
+ sprint_listener
+ .label("sprint_listener")
+ .before("travel")
+ .before("walk_listener"),
+ );
+
+ // add GameProfileComponent when we get an AddPlayerEvent
+ app.add_system(
+ retroactively_add_game_profile_component
+ .after("tick")
+ .after("packet"),
+ );
+
+ app.init_resource::<WorldContainer>();
+ }
+}
+
+/// Create the [`App`]. This won't actually run anything yet.
+///
+/// Note that you usually only need this if you're creating a client manually,
+/// otherwise use [`Client::join`].
+///
+/// Use [`start_ecs`] to actually start running the app and then
+/// [`Client::start_client`] to add a client to the ECS and make it join a
+/// server.
+#[doc(hidden)]
+pub fn init_ecs_app() -> App {
+ // if you get an error right here that means you're doing something with locks
+ // wrong read the error to see where the issue is
+ // you might be able to just drop the lock or put it in its own scope to fix
+
+ let mut app = App::new();
+ app.add_plugin(AzaleaPlugin);
+ app
+}
+
+/// Start running the ECS loop! You must create your `App` from [`init_ecs_app`]
+/// first.
+#[doc(hidden)]
+pub fn start_ecs(
+ app: App,
+ run_schedule_receiver: mpsc::Receiver<()>,
+ run_schedule_sender: mpsc::Sender<()>,
+) -> Arc<Mutex<Ecs>> {
+ // all resources should have been added by now so we can take the ecs from the
+ // app
+ let ecs = Arc::new(Mutex::new(app.world));
+
+ tokio::spawn(run_schedule_loop(
+ ecs.clone(),
+ app.schedule,
+ run_schedule_receiver,
+ ));
+ tokio::spawn(tick_run_schedule_loop(run_schedule_sender));
+
+ ecs
+}
+
+async fn run_schedule_loop(
+ ecs: Arc<Mutex<Ecs>>,
+ mut schedule: Schedule,
+ mut run_schedule_receiver: mpsc::Receiver<()>,
+) {
+ loop {
+ // whenever we get an event from run_schedule_receiver, run the schedule
+ run_schedule_receiver.recv().await;
+ schedule.run(&mut ecs.lock());
+ }
+}
+
+/// Send an event to run the schedule every 50 milliseconds. It will stop when
+/// the receiver is dropped.
+pub async fn tick_run_schedule_loop(run_schedule_sender: mpsc::Sender<()>) {
+ let mut game_tick_interval = time::interval(time::Duration::from_millis(50));
+ // TODO: Minecraft bursts up to 10 ticks and then skips, we should too
+ game_tick_interval.set_missed_tick_behavior(time::MissedTickBehavior::Burst);
+
+ loop {
+ game_tick_interval.tick().await;
+ if let Err(e) = run_schedule_sender.send(()).await {
+ println!("tick_run_schedule_loop error: {e}");
+ // the sender is closed so end the task
+ return;
+ }
}
}
-impl<T> From<std::sync::PoisonError<T>> for HandleError {
- fn from(e: std::sync::PoisonError<T>) -> Self {
- HandleError::Poison(e.to_string())
+/// This plugin group will add all the default plugins necessary for Azalea to
+/// work.
+pub struct DefaultPlugins;
+
+impl PluginGroup for DefaultPlugins {
+ fn build(self) -> PluginGroupBuilder {
+ PluginGroupBuilder::start::<Self>()
+ .add(TickPlugin::default())
+ .add(PacketHandlerPlugin)
+ .add(EntityPlugin)
+ .add(PhysicsPlugin)
+ .add(EventPlugin)
+ .add(TaskPoolPlugin::default())
}
}
diff --git a/azalea-client/src/entity_query.rs b/azalea-client/src/entity_query.rs
new file mode 100644
index 00000000..0e486741
--- /dev/null
+++ b/azalea-client/src/entity_query.rs
@@ -0,0 +1,100 @@
+use std::sync::Arc;
+
+use azalea_ecs::{
+ component::Component,
+ ecs::Ecs,
+ entity::Entity,
+ query::{ROQueryItem, ReadOnlyWorldQuery, WorldQuery},
+};
+use parking_lot::Mutex;
+
+use crate::Client;
+
+impl Client {
+ /// A convenience function for getting components of our player's entity.
+ pub fn query<'w, Q: WorldQuery>(&self, ecs: &'w mut Ecs) -> <Q as WorldQuery>::Item<'w> {
+ ecs.query::<Q>()
+ .get_mut(ecs, self.entity)
+ .expect("Our client is missing a required component.")
+ }
+
+ /// Return a lightweight [`Entity`] for the entity that matches the given
+ /// predicate function.
+ ///
+ /// You can then use [`Self::entity_component`] to get components from this
+ /// entity.
+ ///
+ /// # Example
+ /// Note that this will very likely change in the future.
+ /// ```
+ /// use azalea_client::{Client, GameProfileComponent};
+ /// use azalea_ecs::query::With;
+ /// use azalea_world::entity::{Position, metadata::Player};
+ ///
+ /// # fn example(mut bot: Client, sender_name: String) {
+ /// let entity = bot.entity_by::<With<Player>, (&GameProfileComponent,)>(
+ /// |profile: &&GameProfileComponent| profile.name == sender_name,
+ /// );
+ /// if let Some(entity) = entity {
+ /// let position = bot.entity_component::<Position>(entity);
+ /// // ...
+ /// }
+ /// # }
+ /// ```
+ pub fn entity_by<F: ReadOnlyWorldQuery, Q: ReadOnlyWorldQuery>(
+ &mut self,
+ predicate: impl EntityPredicate<Q, F>,
+ ) -> Option<Entity> {
+ predicate.find(self.ecs.clone())
+ }
+
+ /// Get a component from an entity. Note that this will return an owned type
+ /// (i.e. not a reference) so it may be expensive for larger types.
+ ///
+ /// If you're trying to get a component for this client, use
+ /// [`Self::component`].
+ pub fn entity_component<Q: Component + Clone>(&mut self, entity: Entity) -> Q {
+ let mut ecs = self.ecs.lock();
+ let mut q = ecs.query::<&Q>();
+ let components = q
+ .get(&ecs, entity)
+ .expect("Entity components must be present in Client::entity)components.");
+ components.clone()
+ }
+}
+
+pub trait EntityPredicate<Q: ReadOnlyWorldQuery, Filter: ReadOnlyWorldQuery> {
+ fn find(&self, ecs_lock: Arc<Mutex<Ecs>>) -> Option<Entity>;
+}
+impl<F, Q, Filter> EntityPredicate<(Q,), Filter> for F
+where
+ F: Fn(&ROQueryItem<Q>) -> bool,
+ Q: ReadOnlyWorldQuery,
+ Filter: ReadOnlyWorldQuery,
+{
+ fn find(&self, ecs_lock: Arc<Mutex<Ecs>>) -> Option<Entity> {
+ let mut ecs = ecs_lock.lock();
+ let mut query = ecs.query_filtered::<(Entity, Q), Filter>();
+ let entity = query.iter(&ecs).find(|(_, q)| (self)(q)).map(|(e, _)| e);
+
+ entity
+ }
+}
+
+// impl<'a, F, Q1, Q2> EntityPredicate<'a, (Q1, Q2)> for F
+// where
+// F: Fn(&<Q1 as WorldQuery>::Item<'_>, &<Q2 as WorldQuery>::Item<'_>) ->
+// bool, Q1: ReadOnlyWorldQuery,
+// Q2: ReadOnlyWorldQuery,
+// {
+// fn find(&self, ecs: &mut Ecs) -> Option<Entity> {
+// // (self)(query)
+// let mut query = ecs.query_filtered::<(Entity, Q1, Q2), ()>();
+// let entity = query
+// .iter(ecs)
+// .find(|(_, q1, q2)| (self)(q1, q2))
+// .map(|(e, _, _)| e);
+
+// entity
+// }
+// }
diff --git a/azalea-client/src/events.rs b/azalea-client/src/events.rs
new file mode 100644
index 00000000..f8b9f434
--- /dev/null
+++ b/azalea-client/src/events.rs
@@ -0,0 +1,189 @@
+//! Defines the [`Event`] enum and makes those events trigger when they're sent
+//! in the ECS.
+
+use std::sync::Arc;
+
+use azalea_ecs::{
+ app::{App, Plugin},
+ component::Component,
+ event::EventReader,
+ query::{Added, Changed},
+ system::Query,
+ AppTickExt,
+};
+use azalea_protocol::packets::game::{
+ clientbound_player_combat_kill_packet::ClientboundPlayerCombatKillPacket, ClientboundGamePacket,
+};
+use azalea_world::entity::MinecraftEntityId;
+use derive_more::{Deref, DerefMut};
+use tokio::sync::mpsc;
+
+use crate::{
+ packet_handling::{
+ AddPlayerEvent, ChatReceivedEvent, DeathEvent, PacketReceiver, RemovePlayerEvent,
+ UpdatePlayerEvent,
+ },
+ ChatPacket, PlayerInfo,
+};
+
+// (for contributors):
+// HOW TO ADD A NEW (packet based) EVENT:
+// - make a struct that contains an entity field and a data field (look in
+// packet_handling.rs for examples, also you should end the struct name with
+// "Event")
+// - the entity field is the local player entity that's receiving the event
+// - in packet_handling, you always have a variable called player_entity that
+// you can use
+// - add the event struct in the `impl Plugin for PacketHandlerPlugin`
+// - to get the event writer, you have to get an
+// EventWriter<SomethingHappenedEvent> from the SystemState (the convention is
+// to end your variable with the word "events", like "something_events")
+//
+// - then here in this file, add it to the Event enum
+// - and make an event listener system/function like the other ones and put the
+// function in the `impl Plugin for EventPlugin`
+
+/// Something that happened in-game, such as a tick passing or chat message
+/// being sent.
+///
+/// Note: Events are sent before they're processed, so for example game ticks
+/// happen at the beginning of a tick before anything has happened.
+#[derive(Debug, Clone)]
+pub enum Event {
+ /// Happens right after the bot switches into the Game state, but before
+ /// it's actually spawned. This can be useful for setting the client
+ /// information with `Client::set_client_information`, so the packet
+ /// doesn't have to be sent twice.
+ Init,
+ /// The client is now in the world. Fired when we receive a login packet.
+ Login,
+ /// A chat message was sent in the game chat.
+ Chat(ChatPacket),
+ /// Happens 20 times per second, but only when the world is loaded.
+ Tick,
+ Packet(Arc<ClientboundGamePacket>),
+ /// A player joined the game (or more specifically, was added to the tab
+ /// list).
+ AddPlayer(PlayerInfo),
+ /// A player left the game (or maybe is still in the game and was just
+ /// removed from the tab list).
+ RemovePlayer(PlayerInfo),
+ /// A player was updated in the tab list (gamemode, display
+ /// name, or latency changed).
+ UpdatePlayer(PlayerInfo),
+ /// The client player died in-game.
+ Death(Option<Arc<ClientboundPlayerCombatKillPacket>>),
+}
+
+/// A component that contains an event sender for events that are only
+/// received by local players. The receiver for this is returned by
+/// [`Client::start_client`].
+///
+/// [`Client::start_client`]: crate::Client::start_client
+#[derive(Component, Deref, DerefMut)]
+pub struct LocalPlayerEvents(pub mpsc::UnboundedSender<Event>);
+
+pub struct EventPlugin;
+impl Plugin for EventPlugin {
+ fn build(&self, app: &mut App) {
+ app.add_system(chat_listener)
+ .add_system(login_listener)
+ .add_system(init_listener)
+ .add_system(packet_listener)
+ .add_system(add_player_listener)
+ .add_system(update_player_listener)
+ .add_system(remove_player_listener)
+ .add_system(death_listener)
+ .add_tick_system(tick_listener);
+ }
+}
+
+// when LocalPlayerEvents is added, it means the client just started
+fn init_listener(query: Query<&LocalPlayerEvents, Added<LocalPlayerEvents>>) {
+ for local_player_events in &query {
+ local_player_events.send(Event::Init).unwrap();
+ }
+}
+
+// when MinecraftEntityId is added, it means the player is now in the world
+fn login_listener(query: Query<&LocalPlayerEvents, Added<MinecraftEntityId>>) {
+ for local_player_events in &query {
+ local_player_events.send(Event::Login).unwrap();
+ }
+}
+
+fn chat_listener(query: Query<&LocalPlayerEvents>, mut events: EventReader<ChatReceivedEvent>) {
+ for event in events.iter() {
+ let local_player_events = query
+ .get(event.entity)
+ .expect("Non-localplayer entities shouldn't be able to receive chat events");
+ local_player_events
+ .send(Event::Chat(event.packet.clone()))
+ .unwrap();
+ }
+}
+
+fn tick_listener(query: Query<&LocalPlayerEvents>) {
+ for local_player_events in &query {
+ local_player_events.send(Event::Tick).unwrap();
+ }
+}
+
+fn packet_listener(query: Query<(&LocalPlayerEvents, &PacketReceiver), Changed<PacketReceiver>>) {
+ for (local_player_events, packet_receiver) in &query {
+ for packet in packet_receiver.packets.lock().iter() {
+ local_player_events
+ .send(Event::Packet(packet.clone().into()))
+ .unwrap();
+ }
+ }
+}
+
+fn add_player_listener(query: Query<&LocalPlayerEvents>, mut events: EventReader<AddPlayerEvent>) {
+ for event in events.iter() {
+ let local_player_events = query
+ .get(event.entity)
+ .expect("Non-localplayer entities shouldn't be able to receive add player events");
+ local_player_events
+ .send(Event::AddPlayer(event.info.clone()))
+ .unwrap();
+ }
+}
+
+fn update_player_listener(
+ query: Query<&LocalPlayerEvents>,
+ mut events: EventReader<UpdatePlayerEvent>,
+) {
+ for event in events.iter() {
+ let local_player_events = query
+ .get(event.entity)
+ .expect("Non-localplayer entities shouldn't be able to receive add player events");
+ local_player_events
+ .send(Event::UpdatePlayer(event.info.clone()))
+ .unwrap();
+ }
+}
+
+fn remove_player_listener(
+ query: Query<&LocalPlayerEvents>,
+ mut events: EventReader<RemovePlayerEvent>,
+) {
+ for event in events.iter() {
+ let local_player_events = query
+ .get(event.entity)
+ .expect("Non-localplayer entities shouldn't be able to receive add player events");
+ local_player_events
+ .send(Event::RemovePlayer(event.info.clone()))
+ .unwrap();
+ }
+}
+
+fn death_listener(query: Query<&LocalPlayerEvents>, mut events: EventReader<DeathEvent>) {
+ for event in events.iter() {
+ if let Ok(local_player_events) = query.get(event.entity) {
+ local_player_events
+ .send(Event::Death(event.packet.clone().map(|p| p.into())))
+ .unwrap();
+ }
+ }
+}
diff --git a/azalea-client/src/lib.rs b/azalea-client/src/lib.rs
index f2952248..d46516be 100644
--- a/azalea-client/src/lib.rs
+++ b/azalea-client/src/lib.rs
@@ -9,27 +9,25 @@
#![allow(incomplete_features)]
#![feature(trait_upcasting)]
#![feature(error_generic_member_access)]
+#![feature(type_alias_impl_trait)]
mod account;
mod chat;
mod client;
+mod entity_query;
+mod events;
mod get_mc_dir;
+mod local_player;
mod movement;
+pub mod packet_handling;
pub mod ping;
mod player;
-mod plugins;
+mod task_pool;
pub use account::Account;
-pub use client::{ChatPacket, Client, ClientInformation, Event, JoinError, PhysicsState};
-pub use movement::{SprintDirection, WalkDirection};
+pub use azalea_ecs as ecs;
+pub use client::{init_ecs_app, start_ecs, ChatPacket, Client, ClientInformation, JoinError};
+pub use events::Event;
+pub use local_player::{GameProfileComponent, LocalPlayer};
+pub use movement::{SprintDirection, StartSprintEvent, StartWalkEvent, WalkDirection};
pub use player::PlayerInfo;
-pub use plugins::{Plugin, PluginState, PluginStates, Plugins};
-
-#[cfg(test)]
-mod tests {
- #[test]
- fn it_works() {
- let result = 2 + 2;
- assert_eq!(result, 4);
- }
-}
diff --git a/azalea-client/src/local_player.rs b/azalea-client/src/local_player.rs
new file mode 100644
index 00000000..0165a5f5
--- /dev/null
+++ b/azalea-client/src/local_player.rs
@@ -0,0 +1,164 @@
+use std::{collections::HashMap, io, sync::Arc};
+
+use azalea_auth::game_profile::GameProfile;
+use azalea_core::{ChunkPos, ResourceLocation};
+use azalea_ecs::component::Component;
+use azalea_ecs::entity::Entity;
+use azalea_ecs::{query::Added, system::Query};
+use azalea_protocol::packets::game::ServerboundGamePacket;
+use azalea_world::{
+ entity::{self, Dead},
+ PartialWorld, World,
+};
+use derive_more::{Deref, DerefMut};
+use parking_lot::RwLock;
+use thiserror::Error;
+use tokio::{sync::mpsc, task::JoinHandle};
+use uuid::Uuid;
+
+use crate::{
+ events::{Event, LocalPlayerEvents},
+ ClientInformation, PlayerInfo, WalkDirection,
+};
+
+/// A player that you control that is currently in a Minecraft server.
+#[derive(Component)]
+pub struct LocalPlayer {
+ pub packet_writer: mpsc::UnboundedSender<ServerboundGamePacket>,
+
+ pub client_information: ClientInformation,
+ /// A map of player uuids to their information in the tab list
+ pub players: HashMap<Uuid, PlayerInfo>,
+
+ /// The partial world is the world this client currently has loaded. It has
+ /// a limited render distance.
+ pub partial_world: Arc<RwLock<PartialWorld>>,
+ /// The world is the combined [`PartialWorld`]s of all clients in the same
+ /// world. (Only relevant if you're using a shared world, i.e. a swarm)
+ pub world: Arc<RwLock<World>>,
+ pub world_name: Option<ResourceLocation>,
+
+ /// A list of async tasks that are running and will stop running when this
+ /// LocalPlayer is dropped or disconnected with [`Self::disconnect`]
+ pub(crate) tasks: Vec<JoinHandle<()>>,
+}
+
+/// Component for entities that can move and sprint. Usually only in
+/// [`LocalPlayer`] entities.
+#[derive(Default, Component)]
+pub struct PhysicsState {
+ /// Minecraft only sends a movement packet either after 20 ticks or if the
+ /// player moved enough. This is that tick counter.
+ pub position_remainder: u32,
+ pub was_sprinting: bool,
+ // Whether we're going to try to start sprinting this tick. Equivalent to
+ // holding down ctrl for a tick.
+ pub trying_to_sprint: bool,
+
+ pub move_direction: WalkDirection,
+ pub forward_impulse: f32,
+ pub left_impulse: f32,
+}
+
+/// A component only present in players that contains the [`GameProfile`] (which
+/// you can use to get a player's name).
+///
+/// Note that it's possible for this to be missing in a player if the server
+/// never sent the player info for them (though this is uncommon).
+#[derive(Component, Clone, Debug, Deref, DerefMut)]
+pub struct GameProfileComponent(pub GameProfile);
+
+/// Marks a [`LocalPlayer`] that's in a loaded chunk. This is updated at the
+/// beginning of every tick.
+#[derive(Component)]
+pub struct LocalPlayerInLoadedChunk;
+
+impl LocalPlayer {
+ /// Create a new `LocalPlayer`.
+ pub fn new(
+ entity: Entity,
+ packet_writer: mpsc::UnboundedSender<ServerboundGamePacket>,
+ world: Arc<RwLock<World>>,
+ ) -> Self {
+ let client_information = ClientInformation::default();
+
+ LocalPlayer {
+ packet_writer,
+
+ client_information: ClientInformation::default(),
+ players: HashMap::new(),
+
+ world,
+ partial_world: Arc::new(RwLock::new(PartialWorld::new(
+ client_information.view_distance.into(),
+ Some(entity),
+ ))),
+ world_name: None,
+
+ tasks: Vec::new(),
+ }
+ }
+
+ /// Spawn a task to write a packet directly to the server.
+ pub fn write_packet(&mut self, packet: ServerboundGamePacket) {
+ self.packet_writer
+ .send(packet)
+ .expect("write_packet shouldn't be able to be called if the connection is closed");
+ }
+
+ /// Disconnect this client from the server by ending all tasks.
+ ///
+ /// The OwnedReadHalf for the TCP connection is in one of the tasks, so it
+ /// automatically closes the connection when that's dropped.
+ pub fn disconnect(&self) {
+ for task in &self.tasks {
+ task.abort();
+ }
+ }
+}
+
+/// Update the [`LocalPlayerInLoadedChunk`] component for all [`LocalPlayer`]s.
+pub fn update_in_loaded_chunk(
+ mut commands: azalea_ecs::system::Commands,
+ query: Query<(Entity, &LocalPlayer, &entity::Position)>,
+) {
+ for (entity, local_player, position) in &query {
+ let player_chunk_pos = ChunkPos::from(position);
+ let in_loaded_chunk = local_player
+ .world
+ .read()
+ .chunks
+ .get(&player_chunk_pos)
+ .is_some();
+ if in_loaded_chunk {
+ commands.entity(entity).insert(LocalPlayerInLoadedChunk);
+ } else {
+ commands.entity(entity).remove::<LocalPlayerInLoadedChunk>();
+ }
+ }
+}
+
+/// Send the "Death" event for [`LocalPlayer`]s that died with no reason.
+pub fn death_event(query: Query<&LocalPlayerEvents, Added<Dead>>) {
+ for local_player_events in &query {
+ local_player_events.send(Event::Death(None)).unwrap();
+ }
+}
+
+#[derive(Error, Debug)]
+pub enum HandlePacketError {
+ #[error("{0}")]
+ Poison(String),
+ #[error(transparent)]
+ Io(#[from] io::Error),
+ #[error(transparent)]
+ Other(#[from] anyhow::Error),
+ #[error("{0}")]
+ Send(#[from] mpsc::error::SendError<Event>),
+}
+
+impl<T> From<std::sync::PoisonError<T>> for HandlePacketError {
+ fn from(e: std::sync::PoisonError<T>) -> Self {
+ HandlePacketError::Poison(e.to_string())
+ }
+}
diff --git a/azalea-client/src/movement.rs b/azalea-client/src/movement.rs
index d9cab1d4..8d6faabe 100644
--- a/azalea-client/src/movement.rs
+++ b/azalea-client/src/movement.rs
@@ -1,9 +1,7 @@
-use std::backtrace::Backtrace;
-
-use crate::Client;
-use azalea_core::Vec3;
-use azalea_physics::collision::{MovableEntity, MoverType};
-use azalea_physics::HasPhysics;
+use crate::client::Client;
+use crate::local_player::{LocalPlayer, LocalPlayerInLoadedChunk, PhysicsState};
+use azalea_ecs::entity::Entity;
+use azalea_ecs::{event::EventReader, query::With, system::Query};
use azalea_protocol::packets::game::serverbound_player_command_packet::ServerboundPlayerCommandPacket;
use azalea_protocol::packets::game::{
serverbound_move_player_pos_packet::ServerboundMovePlayerPosPacket,
@@ -11,7 +9,11 @@ use azalea_protocol::packets::game::{
serverbound_move_player_rot_packet::ServerboundMovePlayerRotPacket,
serverbound_move_player_status_only_packet::ServerboundMovePlayerStatusOnlyPacket,
};
-use azalea_world::MoveEntityError;
+use azalea_world::{
+ entity::{self, metadata::Sprinting, Attributes, Jumping, MinecraftEntityId},
+ MoveEntityError,
+};
+use std::backtrace::Backtrace;
use thiserror::Error;
#[derive(Error, Debug)]
@@ -33,24 +35,72 @@ impl From<MoveEntityError> for MovePlayerError {
}
impl Client {
- /// This gets called automatically every tick.
- pub(crate) async fn send_position(&mut self) -> Result<(), MovePlayerError> {
+ /// Set whether we're jumping. This acts as if you held space in
+ /// vanilla. If you want to jump once, use the `jump` function.
+ ///
+ /// If you're making a realistic client, calling this function every tick is
+ /// recommended.
+ pub fn set_jumping(&mut self, jumping: bool) {
+ let mut ecs = self.ecs.lock();
+ let mut jumping_mut = self.query::<&mut Jumping>(&mut ecs);
+ **jumping_mut = jumping;
+ }
+
+ /// Returns whether the player will try to jump next tick.
+ pub fn jumping(&self) -> bool {
+ let mut ecs = self.ecs.lock();
+ let jumping_ref = self.query::<&Jumping>(&mut ecs);
+ **jumping_ref
+ }
+
+ /// Sets your rotation. `y_rot` is yaw (looking to the side), `x_rot` is
+ /// pitch (looking up and down). You can get these numbers from the vanilla
+ /// f3 screen.
+ /// `y_rot` goes from -180 to 180, and `x_rot` goes from -90 to 90.
+ pub fn set_rotation(&mut self, y_rot: f32, x_rot: f32) {
+ let mut ecs = self.ecs.lock();
+ let mut physics = self.query::<&mut entity::Physics>(&mut ecs);
+
+ entity::set_rotation(&mut physics, y_rot, x_rot);
+ }
+}
+
+#[allow(clippy::type_complexity)]
+pub(crate) fn send_position(
+ mut query: Query<
+ (
+ &MinecraftEntityId,
+ &mut LocalPlayer,
+ &mut PhysicsState,
+ &entity::Position,
+ &mut entity::LastSentPosition,
+ &mut entity::Physics,
+ &entity::metadata::Sprinting,
+ ),
+ &LocalPlayerInLoadedChunk,
+ >,
+) {
+ for (
+ id,
+ mut local_player,
+ mut physics_state,
+ position,
+ mut last_sent_position,
+ mut physics,
+ sprinting,
+ ) in query.iter_mut()
+ {
+ local_player.send_sprinting_if_needed(id, sprinting, &mut physics_state);
+
let packet = {
- self.send_sprinting_if_needed().await?;
// TODO: the camera being able to be controlled by other entities isn't
// implemented yet if !self.is_controlled_camera() { return };
- let mut physics_state = self.physics_state.lock();
-
- let player_entity = self.entity();
- let player_pos = player_entity.pos();
- let player_old_pos = player_entity.last_pos;
-
- let x_delta = player_pos.x - player_old_pos.x;
- let y_delta = player_pos.y - player_old_pos.y;
- let z_delta = player_pos.z - player_old_pos.z;
- let y_rot_delta = (player_entity.y_rot - player_entity.y_rot_last) as f64;
- let x_rot_delta = (player_entity.x_rot - player_entity.x_rot_last) as f64;
+ let x_delta = position.x - last_sent_position.x;
+ let y_delta = position.y - last_sent_position.y;
+ let z_delta = position.z - last_sent_position.z;
+ let y_rot_delta = (physics.y_rot - physics.y_rot_last) as f64;
+ let x_rot_delta = (physics.x_rot - physics.x_rot_last) as f64;
physics_state.position_remainder += 1;
@@ -67,38 +117,38 @@ impl Client {
let packet = if sending_position && sending_rotation {
Some(
ServerboundMovePlayerPosRotPacket {
- x: player_pos.x,
- y: player_pos.y,
- z: player_pos.z,
- x_rot: player_entity.x_rot,
- y_rot: player_entity.y_rot,
- on_ground: player_entity.on_ground,
+ x: position.x,
+ y: position.y,
+ z: position.z,
+ x_rot: physics.x_rot,
+ y_rot: physics.y_rot,
+ on_ground: physics.on_ground,
}
.get(),
)
} else if sending_position {
Some(
ServerboundMovePlayerPosPacket {
- x: player_pos.x,
- y: player_pos.y,
- z: player_pos.z,
- on_ground: player_entity.on_ground,
+ x: position.x,
+ y: position.y,
+ z: position.z,
+ on_ground: physics.on_ground,
}
.get(),
)
} else if sending_rotation {
Some(
ServerboundMovePlayerRotPacket {
- x_rot: player_entity.x_rot,
- y_rot: player_entity.y_rot,
- on_ground: player_entity.on_ground,
+ x_rot: physics.x_rot,
+ y_rot: physics.y_rot,
+ on_ground: physics.on_ground,
}
.get(),
)
- } else if player_entity.last_on_ground != player_entity.on_ground {
+ } else if physics.last_on_ground != physics.on_ground {
Some(
ServerboundMovePlayerStatusOnlyPacket {
- on_ground: player_entity.on_ground,
+ on_ground: physics.on_ground,
}
.get(),
)
@@ -106,131 +156,56 @@ impl Client {
None
};
- drop(player_entity);
- let mut player_entity = self.entity();
-
if sending_position {
- player_entity.last_pos = *player_entity.pos();
+ **last_sent_position = **position;
physics_state.position_remainder = 0;
}
if sending_rotation {
- player_entity.y_rot_last = player_entity.y_rot;
- player_entity.x_rot_last = player_entity.x_rot;
+ physics.y_rot_last = physics.y_rot;
+ physics.x_rot_last = physics.x_rot;
}
- player_entity.last_on_ground = player_entity.on_ground;
+ physics.last_on_ground = physics.on_ground;
// minecraft checks for autojump here, but also autojump is bad so
packet
};
if let Some(packet) = packet {
- self.write_packet(packet).await?;
+ local_player.write_packet(packet);
}
-
- Ok(())
}
+}
- async fn send_sprinting_if_needed(&mut self) -> Result<(), MovePlayerError> {
- let is_sprinting = self.entity().metadata.sprinting;
- let was_sprinting = self.physics_state.lock().was_sprinting;
- if is_sprinting != was_sprinting {
- let sprinting_action = if is_sprinting {
+impl LocalPlayer {
+ fn send_sprinting_if_needed(
+ &mut self,
+ id: &MinecraftEntityId,
+ sprinting: &entity::metadata::Sprinting,
+ physics_state: &mut PhysicsState,
+ ) {
+ let was_sprinting = physics_state.was_sprinting;
+ if **sprinting != was_sprinting {
+ let sprinting_action = if **sprinting {
azalea_protocol::packets::game::serverbound_player_command_packet::Action::StartSprinting
} else {
azalea_protocol::packets::game::serverbound_player_command_packet::Action::StopSprinting
};
- let player_entity_id = self.entity().id;
self.write_packet(
ServerboundPlayerCommandPacket {
- id: player_entity_id,
+ id: **id,
action: sprinting_action,
data: 0,
}
.get(),
- )
- .await?;
- self.physics_state.lock().was_sprinting = is_sprinting;
- }
-
- Ok(())
- }
-
- // Set our current position to the provided Vec3, potentially clipping through
- // blocks.
- pub async fn set_position(&mut self, new_pos: Vec3) -> Result<(), MovePlayerError> {
- let player_entity_id = *self.entity_id.read();
- let mut world_lock = self.world.write();
-
- world_lock.set_entity_pos(player_entity_id, new_pos)?;
-
- Ok(())
- }
-
- pub async fn move_entity(&mut self, movement: &Vec3) -> Result<(), MovePlayerError> {
- let mut world_lock = self.world.write();
- let player_entity_id = *self.entity_id.read();
-
- let mut entity = world_lock
- .entity_mut(player_entity_id)
- .ok_or(MovePlayerError::PlayerNotInWorld(Backtrace::capture()))?;
- log::trace!(
- "move entity bounding box: {} {:?}",
- entity.id,
- entity.bounding_box
- );
-
- entity.move_colliding(&MoverType::Own, movement)?;
-
- Ok(())
- }
-
- /// Makes the bot do one physics tick. Note that this is already handled
- /// automatically by the client.
- pub fn ai_step(&mut self) {
- self.tick_controls(None);
-
- // server ai step
- {
- let mut player_entity = self.entity();
-
- let physics_state = self.physics_state.lock();
- player_entity.xxa = physics_state.left_impulse;
- player_entity.zza = physics_state.forward_impulse;
- }
-
- // TODO: food data and abilities
- // let has_enough_food_to_sprint = self.food_data().food_level ||
- // self.abilities().may_fly;
- let has_enough_food_to_sprint = true;
-
- // TODO: double tapping w to sprint i think
-
- let trying_to_sprint = self.physics_state.lock().trying_to_sprint;
-
- if !self.sprinting()
- && (
- // !self.is_in_water()
- // || self.is_underwater() &&
- self.has_enough_impulse_to_start_sprinting()
- && has_enough_food_to_sprint
- // && !self.using_item()
- // && !self.has_effect(MobEffects.BLINDNESS)
- && trying_to_sprint
- )
- {
- self.set_sprinting(true);
+ );
+ physics_state.was_sprinting = **sprinting;
}
-
- let mut player_entity = self.entity();
- player_entity.ai_step();
}
/// Update the impulse from self.move_direction. The multipler is used for
/// sneaking.
- pub(crate) fn tick_controls(&mut self, multiplier: Option<f32>) {
- let mut physics_state = self.physics_state.lock();
-
+ pub(crate) fn tick_controls(multiplier: Option<f32>, physics_state: &mut PhysicsState) {
let mut forward_impulse: f32 = 0.;
let mut left_impulse: f32 = 0.;
let move_direction = physics_state.move_direction;
@@ -262,7 +237,54 @@ impl Client {
physics_state.left_impulse *= multiplier;
}
}
+}
+
+/// Makes the bot do one physics tick. Note that this is already handled
+/// automatically by the client.
+pub fn local_player_ai_step(
+ mut query: Query<
+ (
+ &mut PhysicsState,
+ &mut entity::Physics,
+ &mut entity::metadata::Sprinting,
+ &mut entity::Attributes,
+ ),
+ With<LocalPlayerInLoadedChunk>,
+ >,
+) {
+ for (mut physics_state, mut physics, mut sprinting, mut attributes) in query.iter_mut() {
+ LocalPlayer::tick_controls(None, &mut physics_state);
+
+ // server ai step
+ physics.xxa = physics_state.left_impulse;
+ physics.zza = physics_state.forward_impulse;
+
+ // TODO: food data and abilities
+ // let has_enough_food_to_sprint = self.food_data().food_level ||
+ // self.abilities().may_fly;
+ let has_enough_food_to_sprint = true;
+
+ // TODO: double tapping w to sprint i think
+
+ let trying_to_sprint = physics_state.trying_to_sprint;
+
+ if !**sprinting
+ && (
+ // !self.is_in_water()
+ // || self.is_underwater() &&
+ has_enough_impulse_to_start_sprinting(&physics_state)
+ && has_enough_food_to_sprint
+ // && !self.using_item()
+ // && !self.has_effect(MobEffects.BLINDNESS)
+ && trying_to_sprint
+ )
+ {
+ set_sprinting(true, &mut sprinting, &mut attributes);
+ }
+ }
+}
+impl Client {
/// Start walking in the given direction. To sprint, use
/// [`Client::sprint`]. To stop walking, call walk with
/// `WalkDirection::None`.
@@ -280,12 +302,11 @@ impl Client {
/// # }
/// ```
pub fn walk(&mut self, direction: WalkDirection) {
- {
- let mut physics_state = self.physics_state.lock();
- physics_state.move_direction = direction;
- }
-
- self.set_sprinting(false);
+ let mut ecs = self.ecs.lock();
+ ecs.send_event(StartWalkEvent {
+ entity: self.entity,
+ direction,
+ });
}
/// Start sprinting in the given direction. To stop moving, call
@@ -304,71 +325,81 @@ impl Client {
/// # }
/// ```
pub fn sprint(&mut self, direction: SprintDirection) {
- let mut physics_state = self.physics_state.lock();
- physics_state.move_direction = WalkDirection::from(direction);
- physics_state.trying_to_sprint = true;
+ let mut ecs = self.ecs.lock();
+ ecs.send_event(StartSprintEvent {
+ entity: self.entity,
+ direction,
+ });
}
+}
- // Whether we're currently sprinting.
- pub fn sprinting(&self) -> bool {
- self.entity().metadata.sprinting
- }
+pub struct StartWalkEvent {
+ pub entity: Entity,
+ pub direction: WalkDirection,
+}
- /// Change whether we're sprinting by adding an attribute modifier to the
- /// player. You should use the [`walk`] and [`sprint`] methods instead.
- /// Returns if the operation was successful.
- fn set_sprinting(&mut self, sprinting: bool) -> bool {
- let mut player_entity = self.entity();
- player_entity.metadata.sprinting = sprinting;
- if sprinting {
- player_entity
- .attributes
- .speed
- .insert(azalea_world::entity::attributes::sprinting_modifier())
- .is_ok()
- } else {
- player_entity
- .attributes
- .speed
- .remove(&azalea_world::entity::attributes::sprinting_modifier().uuid)
- .is_none()
+/// Start walking in the given direction. To sprint, use
+/// [`Client::sprint`]. To stop walking, call walk with
+/// `WalkDirection::None`.
+pub fn walk_listener(
+ mut events: EventReader<StartWalkEvent>,
+ mut query: Query<(&mut PhysicsState, &mut Sprinting, &mut Attributes)>,
+) {
+ for event in events.iter() {
+ if let Ok((mut physics_state, mut sprinting, mut attributes)) = query.get_mut(event.entity)
+ {
+ physics_state.move_direction = event.direction;
+ set_sprinting(false, &mut sprinting, &mut attributes);
}
}
+}
- /// Set whether we're jumping. This acts as if you held space in
- /// vanilla. If you want to jump once, use the `jump` function.
- ///
- /// If you're making a realistic client, calling this function every tick is
- /// recommended.
- pub fn set_jumping(&mut self, jumping: bool) {
- let mut player_entity = self.entity();
- player_entity.jumping = jumping;
- }
-
- /// Returns whether the player will try to jump next tick.
- pub fn jumping(&self) -> bool {
- let player_entity = self.entity();
- player_entity.jumping
+pub struct StartSprintEvent {
+ pub entity: Entity,
+ pub direction: SprintDirection,
+}
+/// Start sprinting in the given direction.
+pub fn sprint_listener(
+ mut query: Query<&mut PhysicsState>,
+ mut events: EventReader<StartSprintEvent>,
+) {
+ for event in events.iter() {
+ if let Ok(mut physics_state) = query.get_mut(event.entity) {
+ physics_state.move_direction = WalkDirection::from(event.direction);
+ physics_state.trying_to_sprint = true;
+ }
}
+}
- /// Sets your rotation. `y_rot` is yaw (looking to the side), `x_rot` is
- /// pitch (looking up and down). You can get these numbers from the vanilla
- /// f3 screen.
- /// `y_rot` goes from -180 to 180, and `x_rot` goes from -90 to 90.
- pub fn set_rotation(&mut self, y_rot: f32, x_rot: f32) {
- let mut player_entity = self.entity();
- player_entity.set_rotation(y_rot, x_rot);
+/// Change whether we're sprinting by adding an attribute modifier to the
+/// player. You should use the [`walk`] and [`sprint`] methods instead.
+/// Returns if the operation was successful.
+fn set_sprinting(
+ sprinting: bool,
+ currently_sprinting: &mut Sprinting,
+ attributes: &mut Attributes,
+) -> bool {
+ **currently_sprinting = sprinting;
+ if sprinting {
+ attributes
+ .speed
+ .insert(entity::attributes::sprinting_modifier())
+ .is_ok()
+ } else {
+ attributes
+ .speed
+ .remove(&entity::attributes::sprinting_modifier().uuid)
+ .is_none()
}
+}
- // Whether the player is moving fast enough to be able to start sprinting.
- fn has_enough_impulse_to_start_sprinting(&self) -> bool {
- // if self.underwater() {
- // self.has_forward_impulse()
- // } else {
- let physics_state = self.physics_state.lock();
- physics_state.forward_impulse > 0.8
- // }
- }
+// Whether the player is moving fast enough to be able to start sprinting.
+fn has_enough_impulse_to_start_sprinting(physics_state: &PhysicsState) -> bool {
+ // if self.underwater() {
+ // self.has_forward_impulse()
+ // } else {
+ physics_state.forward_impulse > 0.8
+ // }
}
#[derive(Clone, Copy, Debug, Default)]
diff --git a/azalea-client/src/packet_handling.rs b/azalea-client/src/packet_handling.rs
new file mode 100644
index 00000000..db2c3c45
--- /dev/null
+++ b/azalea-client/src/packet_handling.rs
@@ -0,0 +1,935 @@
+use std::{collections::HashSet, io::Cursor, sync::Arc};
+
+use azalea_core::{ChunkPos, ResourceLocation, Vec3};
+use azalea_ecs::{
+ app::{App, Plugin},
+ component::Component,
+ ecs::Ecs,
+ entity::Entity,
+ event::EventWriter,
+ query::Changed,
+ schedule::{IntoSystemDescriptor, SystemSet},
+ system::{Commands, Query, ResMut, SystemState},
+};
+use azalea_protocol::{
+ connect::{ReadConnection, WriteConnection},
+ packets::game::{
+ clientbound_player_combat_kill_packet::ClientboundPlayerCombatKillPacket,
+ serverbound_accept_teleportation_packet::ServerboundAcceptTeleportationPacket,
+ serverbound_custom_payload_packet::ServerboundCustomPayloadPacket,
+ serverbound_keep_alive_packet::ServerboundKeepAlivePacket,
+ serverbound_move_player_pos_rot_packet::ServerboundMovePlayerPosRotPacket,
+ ClientboundGamePacket, ServerboundGamePacket,
+ },
+};
+use azalea_world::{
+ entity::{
+ metadata::{apply_metadata, Health, PlayerMetadataBundle},
+ set_rotation, Dead, EntityBundle, EntityKind, LastSentPosition, MinecraftEntityId, Physics,
+ PlayerBundle, Position,
+ },
+ LoadedBy, PartialWorld, RelativeEntityUpdate, WorldContainer,
+};
+use log::{debug, error, trace, warn};
+use parking_lot::Mutex;
+use tokio::sync::mpsc;
+
+use crate::{
+ local_player::{GameProfileComponent, LocalPlayer},
+ ChatPacket, ClientInformation, PlayerInfo,
+};
+
+pub struct PacketHandlerPlugin;
+
+impl Plugin for PacketHandlerPlugin {
+ fn build(&self, app: &mut App) {
+ app.add_system_set(
+ SystemSet::new().with_system(handle_packets.label("packet").before("tick")),
+ )
+ .add_event::<AddPlayerEvent>()
+ .add_event::<RemovePlayerEvent>()
+ .add_event::<UpdatePlayerEvent>()
+ .add_event::<ChatReceivedEvent>()
+ .add_event::<DeathEvent>();
+ }
+}
+
+/// A player joined the game (or more specifically, was added to the tab
+/// list of a local player).
+#[derive(Debug)]
+pub struct AddPlayerEvent {
+ /// The local player entity that received this event.
+ pub entity: Entity,
+ pub info: PlayerInfo,
+}
+/// A player left the game (or maybe is still in the game and was just
+/// removed from the tab list of a local player).
+#[derive(Debug)]
+pub struct RemovePlayerEvent {
+ /// The local player entity that received this event.
+ pub entity: Entity,
+ pub info: PlayerInfo,
+}
+/// A player was updated in the tab list of a local player (gamemode, display
+/// name, or latency changed).
+#[derive(Debug)]
+pub struct UpdatePlayerEvent {
+ /// The local player entity that received this event.
+ pub entity: Entity,
+ pub info: PlayerInfo,
+}
+
+/// A client received a chat message packet.
+#[derive(Debug)]
+pub struct ChatReceivedEvent {
+ pub entity: Entity,
+ pub packet: ChatPacket,
+}
+
+/// Event for when an entity dies. dies. If it's a local player and there's a
+/// reason in the death screen, the [`ClientboundPlayerCombatKillPacket`] will
+/// be included.
+pub struct DeathEvent {
+ pub entity: Entity,
+ pub packet: Option<ClientboundPlayerCombatKillPacket>,
+}
+
+/// Something that receives packets from the server.
+#[derive(Component, Clone)]
+pub struct PacketReceiver {
+ pub packets: Arc<Mutex<Vec<ClientboundGamePacket>>>,
+ pub run_schedule_sender: mpsc::Sender<()>,
+}
+
+fn handle_packets(ecs: &mut Ecs) {
+ let mut events_owned = Vec::new();
+
+ {
+ let mut system_state: SystemState<
+ Query<(Entity, &PacketReceiver), Changed<PacketReceiver>>,
+ > = SystemState::new(ecs);
+ let query = system_state.get(ecs);
+ for (player_entity, packet_events) in &query {
+ let mut packets = packet_events.packets.lock();
+ if !packets.is_empty() {
+ events_owned.push((player_entity, packets.clone()));
+ // clear the packets right after we read them
+ packets.clear();
+ }
+ }
+ }
+
+ for (player_entity, packets) in events_owned {
+ for packet in &packets {
+ match packet {
+ ClientboundGamePacket::Login(p) => {
+ debug!("Got login packet");
+
+ #[allow(clippy::type_complexity)]
+ let mut system_state: SystemState<(
+ Commands,
+ Query<(&mut LocalPlayer, &GameProfileComponent)>,
+ ResMut<WorldContainer>,
+ )> = SystemState::new(ecs);
+ let (mut commands, mut query, mut world_container) = system_state.get_mut(ecs);
+ let (mut local_player, game_profile) = query.get_mut(player_entity).unwrap();
+
+ {
+ // TODO: have registry_holder be a struct because this sucks rn
+ // best way would be to add serde support to azalea-nbt
+
+ let registry_holder = p
+ .registry_holder
+ .as_compound()
+ .expect("Registry holder is not a compound")
+ .get("")
+ .expect("No \"\" tag")
+ .as_compound()
+ .expect("\"\" tag is not a compound");
+ let dimension_types = registry_holder
+ .get("minecraft:dimension_type")
+ .expect("No dimension_type tag")
+ .as_compound()
+ .expect("dimension_type is not a compound")
+ .get("value")
+ .expect("No dimension_type value")
+ .as_list()
+ .expect("dimension_type value is not a list");
+ let dimension_type = dimension_types
+ .iter()
+ .find(|t| {
+ t.as_compound()
+ .expect("dimension_type value is not a compound")
+ .get("name")
+ .expect("No name tag")
+ .as_string()
+ .expect("name is not a string")
+ == p.dimension_type.to_string()
+ })
+ .unwrap_or_else(|| {
+ panic!("No dimension_type with name {}", p.dimension_type)
+ })
+ .as_compound()
+ .unwrap()
+ .get("element")
+ .expect("No element tag")
+ .as_compound()
+ .expect("element is not a compound");
+ let height = (*dimension_type
+ .get("height")
+ .expect("No height tag")
+ .as_int()
+ .expect("height tag is not an int"))
+ .try_into()
+ .expect("height is not a u32");
+ let min_y = *dimension_type
+ .get("min_y")
+ .expect("No min_y tag")
+ .as_int()
+ .expect("min_y tag is not an int");
+
+ let world_name = p.dimension.clone();
+
+ local_player.world_name = Some(world_name.clone());
+ // add this world to the world_container (or don't if it's already
+ // there)
+ let weak_world = world_container.insert(world_name.clone(), height, min_y);
+ // set the partial_world to an empty world
+ // (when we add chunks or entities those will be in the
+ // world_container)
+
+ *local_player.partial_world.write() = PartialWorld::new(
+ local_player.client_information.view_distance.into(),
+ // this argument makes it so other clients don't update this
+ // player entity
+ // in a shared world
+ Some(player_entity),
+ );
+ local_player.world = weak_world;
+
+ let player_bundle = PlayerBundle {
+ entity: EntityBundle::new(
+ game_profile.uuid,
+ Vec3::default(),
+ azalea_registry::EntityKind::Player,
+ world_name,
+ ),
+ metadata: PlayerMetadataBundle::default(),
+ };
+ // insert our components into the ecs :)
+ commands
+ .entity(player_entity)
+ .insert((MinecraftEntityId(p.player_id), player_bundle));
+ }
+
+ // send the client information that we have set
+ let client_information_packet: ClientInformation =
+ local_player.client_information.clone();
+ log::debug!(
+ "Sending client information because login: {:?}",
+ client_information_packet
+ );
+ local_player.write_packet(client_information_packet.get());
+
+ // brand
+ local_player.write_packet(
+ ServerboundCustomPayloadPacket {
+ identifier: ResourceLocation::new("brand").unwrap(),
+ // they don't have to know :)
+ data: "vanilla".into(),
+ }
+ .get(),
+ );
+
+ system_state.apply(ecs);
+ }
+ ClientboundGamePacket::SetChunkCacheRadius(p) => {
+ debug!("Got set chunk cache radius packet {:?}", p);
+ }
+ ClientboundGamePacket::CustomPayload(p) => {
+ debug!("Got custom payload packet {:?}", p);
+ }
+ ClientboundGamePacket::ChangeDifficulty(p) => {
+ debug!("Got difficulty packet {:?}", p);
+ }
+ ClientboundGamePacket::Commands(_p) => {
+ debug!("Got declare commands packet");
+ }
+ ClientboundGamePacket::PlayerAbilities(p) => {
+ debug!("Got player abilities packet {:?}", p);
+ }
+ ClientboundGamePacket::SetCarriedItem(p) => {
+ debug!("Got set carried item packet {:?}", p);
+ }
+ ClientboundGamePacket::UpdateTags(_p) => {
+ debug!("Got update tags packet");
+ }
+ ClientboundGamePacket::Disconnect(p) => {
+ debug!("Got disconnect packet {:?}", p);
+ let mut system_state: SystemState<Query<&LocalPlayer>> = SystemState::new(ecs);
+ let query = system_state.get(ecs);
+ let local_player = query.get(player_entity).unwrap();
+ local_player.disconnect();
+ }
+ ClientboundGamePacket::UpdateRecipes(_p) => {
+ debug!("Got update recipes packet");
+ }
+ ClientboundGamePacket::EntityEvent(_p) => {
+ // debug!("Got entity event packet {:?}", p);
+ }
+ ClientboundGamePacket::Recipe(_p) => {
+ debug!("Got recipe packet");
+ }
+ ClientboundGamePacket::PlayerPosition(p) => {
+ // TODO: reply with teleport confirm
+ debug!("Got player position packet {:?}", p);
+
+ let mut system_state: SystemState<
+ Query<(
+ &mut LocalPlayer,
+ &mut Physics,
+ &mut Position,
+ &mut LastSentPosition,
+ )>,
+ > = SystemState::new(ecs);
+ let mut query = system_state.get_mut(ecs);
+ let Ok((mut local_player, mut physics, mut position, mut last_sent_position)) =
+ query.get_mut(player_entity) else {
+ continue;
+ };
+
+ let delta_movement = physics.delta;
+
+ let is_x_relative = p.relative_arguments.x;
+ let is_y_relative = p.relative_arguments.y;
+ let is_z_relative = p.relative_arguments.z;
+
+ let (delta_x, new_pos_x) = if is_x_relative {
+ last_sent_position.x += p.x;
+ (delta_movement.x, position.x + p.x)
+ } else {
+ last_sent_position.x = p.x;
+ (0.0, p.x)
+ };
+ let (delta_y, new_pos_y) = if is_y_relative {
+ last_sent_position.y += p.y;
+ (delta_movement.y, position.y + p.y)
+ } else {
+ last_sent_position.y = p.y;
+ (0.0, p.y)
+ };
+ let (delta_z, new_pos_z) = if is_z_relative {
+ last_sent_position.z += p.z;
+ (delta_movement.z, position.z + p.z)
+ } else {
+ last_sent_position.z = p.z;
+ (0.0, p.z)
+ };
+
+ let mut y_rot = p.y_rot;
+ let mut x_rot = p.x_rot;
+ if p.relative_arguments.x_rot {
+ x_rot += physics.x_rot;
+ }
+ if p.relative_arguments.y_rot {
+ y_rot += physics.y_rot;
+ }
+
+ physics.delta = Vec3 {
+ x: delta_x,
+ y: delta_y,
+ z: delta_z,
+ };
+ // we call a function instead of setting the fields ourself since the
+ // function makes sure the rotations stay in their
+ // ranges
+ set_rotation(&mut physics, y_rot, x_rot);
+ // TODO: minecraft sets "xo", "yo", and "zo" here but idk what that means
+ // so investigate that ig
+ let new_pos = Vec3 {
+ x: new_pos_x,
+ y: new_pos_y,
+ z: new_pos_z,
+ };
+
+ **position = new_pos;
+
+ local_player
+ .write_packet(ServerboundAcceptTeleportationPacket { id: p.id }.get());
+ local_player.write_packet(
+ ServerboundMovePlayerPosRotPacket {
+ x: new_pos.x,
+ y: new_pos.y,
+ z: new_pos.z,
+ y_rot,
+ x_rot,
+ // this is always false
+ on_ground: false,
+ }
+ .get(),
+ );
+ }
+ ClientboundGamePacket::PlayerInfoUpdate(p) => {
+ debug!("Got player info packet {:?}", p);
+
+ let mut system_state: SystemState<(
+ Query<&mut LocalPlayer>,
+ EventWriter<AddPlayerEvent>,
+ EventWriter<UpdatePlayerEvent>,
+ )> = SystemState::new(ecs);
+ let (mut query, mut add_player_events, mut update_player_events) =
+ system_state.get_mut(ecs);
+ let mut local_player = query.get_mut(player_entity).unwrap();
+
+ for updated_info in &p.entries {
+ // add the new player maybe
+ if p.actions.add_player {
+ let info = PlayerInfo {
+ profile: updated_info.profile.clone(),
+ uuid: updated_info.profile.uuid,
+ gamemode: updated_info.game_mode,
+ latency: updated_info.latency,
+ display_name: updated_info.display_name.clone(),
+ };
+ local_player
+ .players
+ .insert(updated_info.profile.uuid, info.clone());
+ add_player_events.send(AddPlayerEvent {
+ entity: player_entity,
+ info: info.clone(),
+ });
+ } else if let Some(info) =
+ local_player.players.get_mut(&updated_info.profile.uuid)
+ {
+ // `else if` because the block for add_player above
+ // already sets all the fields
+ if p.actions.update_game_mode {
+ info.gamemode = updated_info.game_mode;
+ }
+ if p.actions.update_latency {
+ info.latency = updated_info.latency;
+ }
+ if p.actions.update_display_name {
+ info.display_name = updated_info.display_name.clone();
+ }
+ update_player_events.send(UpdatePlayerEvent {
+ entity: player_entity,
+ info: info.clone(),
+ });
+ } else {
+ warn!(
+ "Ignoring PlayerInfoUpdate for unknown player {}",
+ updated_info.profile.uuid
+ );
+ }
+ }
+ }
+ ClientboundGamePacket::PlayerInfoRemove(p) => {
+ let mut system_state: SystemState<(
+ Query<&mut LocalPlayer>,
+ EventWriter<RemovePlayerEvent>,
+ )> = SystemState::new(ecs);
+ let (mut query, mut remove_player_events) = system_state.get_mut(ecs);
+ let mut local_player = query.get_mut(player_entity).unwrap();
+
+ for uuid in &p.profile_ids {
+ if let Some(info) = local_player.players.remove(uuid) {
+ remove_player_events.send(RemovePlayerEvent {
+ entity: player_entity,
+ info,
+ });
+ }
+ }
+ }
+ ClientboundGamePacket::SetChunkCacheCenter(p) => {
+ debug!("Got chunk cache center packet {:?}", p);
+
+ let mut system_state: SystemState<Query<&mut LocalPlayer>> =
+ SystemState::new(ecs);
+ let mut query = system_state.get_mut(ecs);
+ let local_player = query.get_mut(player_entity).unwrap();
+ let mut partial_world = local_player.partial_world.write();
+
+ partial_world.chunks.view_center = ChunkPos::new(p.x, p.z);
+ }
+ ClientboundGamePacket::LevelChunkWithLight(p) => {
+ debug!("Got chunk with light packet {} {}", p.x, p.z);
+ let pos = ChunkPos::new(p.x, p.z);
+
+ let mut system_state: SystemState<Query<&mut LocalPlayer>> =
+ SystemState::new(ecs);
+ let mut query = system_state.get_mut(ecs);
+ let local_player = query.get_mut(player_entity).unwrap();
+
+ // OPTIMIZATION: if we already know about the chunk from the
+ // shared world (and not ourselves), then we don't need to
+ // parse it again. This is only used when we have a shared
+ // world, since we check that the chunk isn't currently owned
+ // by this client.
+ let shared_chunk = local_player.world.read().chunks.get(&pos);
+ let this_client_has_chunk = local_player
+ .partial_world
+ .read()
+ .chunks
+ .limited_get(&pos)
+ .is_some();
+
+ let mut world = local_player.world.write();
+ let mut partial_world = local_player.partial_world.write();
+
+ if !this_client_has_chunk {
+ if let Some(shared_chunk) = shared_chunk {
+ trace!(
+ "Skipping parsing chunk {:?} because we already know about it",
+ pos
+ );
+ partial_world.chunks.set_with_shared_reference(
+ &pos,
+ Some(shared_chunk.clone()),
+ &mut world.chunks,
+ );
+ continue;
+ }
+ }
+
+ if let Err(e) = partial_world.chunks.replace_with_packet_data(
+ &pos,
+ &mut Cursor::new(&p.chunk_data.data),
+ &mut world.chunks,
+ ) {
+ error!("Couldn't set chunk data: {}", e);
+ }
+ }
+ ClientboundGamePacket::LightUpdate(_p) => {
+ // debug!("Got light update packet {:?}", p);
+ }
+ ClientboundGamePacket::AddEntity(p) => {
+ debug!("Got add entity packet {:?}", p);
+
+ let mut system_state: SystemState<(Commands, Query<&mut LocalPlayer>)> =
+ SystemState::new(ecs);
+ let (mut commands, mut query) = system_state.get_mut(ecs);
+ let local_player = query.get_mut(player_entity).unwrap();
+
+ if let Some(world_name) = &local_player.world_name {
+ let bundle = p.as_entity_bundle(world_name.clone());
+ let mut entity_commands = commands.spawn((
+ MinecraftEntityId(p.id),
+ LoadedBy(HashSet::from([player_entity])),
+ bundle,
+ ));
+ // the bundle doesn't include the default entity metadata so we add that
+ // separately
+ p.apply_metadata(&mut entity_commands);
+ } else {
+ warn!("got add player packet but we haven't gotten a login packet yet");
+ }
+
+ system_state.apply(ecs);
+ }
+ ClientboundGamePacket::SetEntityData(p) => {
+ debug!("Got set entity data packet {:?}", p);
+
+ let mut system_state: SystemState<(
+ Commands,
+ Query<&mut LocalPlayer>,
+ Query<&EntityKind>,
+ )> = SystemState::new(ecs);
+ let (mut commands, mut query, entity_kind_query) = system_state.get_mut(ecs);
+ let local_player = query.get_mut(player_entity).unwrap();
+
+ let world = local_player.world.read();
+ let entity = world.entity_by_id(&MinecraftEntityId(p.id));
+ drop(world);
+
+ if let Some(entity) = entity {
+ let entity_kind = entity_kind_query.get(entity).unwrap();
+ let mut entity_commands = commands.entity(entity);
+ if let Err(e) = apply_metadata(
+ &mut entity_commands,
+ **entity_kind,
+ (*p.packed_items).clone(),
+ ) {
+ warn!("{e}");
+ }
+ } else {
+ warn!("Server sent an entity data packet for an entity id ({}) that we don't know about", p.id);
+ }
+
+ system_state.apply(ecs);
+ }
+ ClientboundGamePacket::UpdateAttributes(_p) => {
+ // debug!("Got update attributes packet {:?}", p);
+ }
+ ClientboundGamePacket::SetEntityMotion(_p) => {
+ // debug!("Got entity velocity packet {:?}", p);
+ }
+ ClientboundGamePacket::SetEntityLink(p) => {
+ debug!("Got set entity link packet {:?}", p);
+ }
+ ClientboundGamePacket::AddPlayer(p) => {
+ debug!("Got add player packet {:?}", p);
+
+ let mut system_state: SystemState<(Commands, Query<&mut LocalPlayer>)> =
+ SystemState::new(ecs);
+ let (mut commands, mut query) = system_state.get_mut(ecs);
+ let local_player = query.get_mut(player_entity).unwrap();
+
+ if let Some(world_name) = &local_player.world_name {
+ let bundle = p.as_player_bundle(world_name.clone());
+ let mut spawned = commands.spawn((
+ MinecraftEntityId(p.id),
+ LoadedBy(HashSet::from([player_entity])),
+ bundle,
+ ));
+
+ if let Some(player_info) = local_player.players.get(&p.uuid) {
+ spawned.insert(GameProfileComponent(player_info.profile.clone()));
+ }
+ } else {
+ warn!("got add player packet but we haven't gotten a login packet yet");
+ }
+
+ system_state.apply(ecs);
+ }
+ ClientboundGamePacket::InitializeBorder(p) => {
+ debug!("Got initialize border packet {:?}", p);
+ }
+ ClientboundGamePacket::SetTime(_p) => {
+ // debug!("Got set time packet {:?}", p);
+ }
+ ClientboundGamePacket::SetDefaultSpawnPosition(p) => {
+ debug!("Got set default spawn position packet {:?}", p);
+ }
+ ClientboundGamePacket::ContainerSetContent(p) => {
+ debug!("Got container set content packet {:?}", p);
+ }
+ ClientboundGamePacket::SetHealth(p) => {
+ debug!("Got set health packet {:?}", p);
+
+ let mut system_state: SystemState<(
+ Query<&mut Health>,
+ EventWriter<DeathEvent>,
+ )> = SystemState::new(ecs);
+ let (mut query, mut death_events) = system_state.get_mut(ecs);
+ let mut health = query.get_mut(player_entity).unwrap();
+
+ if p.health == 0. && **health != 0. {
+ death_events.send(DeathEvent {
+ entity: player_entity,
+ packet: None,
+ });
+ }
+
+ **health = p.health;
+
+ // the `Dead` component is added by the `update_dead` system
+ // in azalea-world and then the `dead_event` system fires
+ // the Death event.
+ }
+ ClientboundGamePacket::SetExperience(p) => {
+ debug!("Got set experience packet {:?}", p);
+ }
+ ClientboundGamePacket::TeleportEntity(p) => {
+ let mut system_state: SystemState<(Commands, Query<&mut LocalPlayer>)> =
+ SystemState::new(ecs);
+ let (mut commands, mut query) = system_state.get_mut(ecs);
+ let local_player = query.get_mut(player_entity).unwrap();
+
+ let world = local_player.world.read();
+ let entity = world.entity_by_id(&MinecraftEntityId(p.id));
+ drop(world);
+
+ if let Some(entity) = entity {
+ let new_position = p.position;
+ commands.add(RelativeEntityUpdate {
+ entity,
+ partial_world: local_player.partial_world.clone(),
+ update: Box::new(move |entity| {
+ let mut position = entity.get_mut::<Position>().unwrap();
+ **position = new_position;
+ }),
+ });
+ } else {
+ warn!("Got teleport entity packet for unknown entity id {}", p.id);
+ }
+
+ system_state.apply(ecs);
+ }
+ ClientboundGamePacket::UpdateAdvancements(p) => {
+ debug!("Got update advancements packet {:?}", p);
+ }
+ ClientboundGamePacket::RotateHead(_p) => {
+ // debug!("Got rotate head packet {:?}", p);
+ }
+ ClientboundGamePacket::MoveEntityPos(p) => {
+ let mut system_state: SystemState<(Commands, Query<&LocalPlayer>)> =
+ SystemState::new(ecs);
+ let (mut commands, mut query) = system_state.get_mut(ecs);
+ let local_player = query.get_mut(player_entity).unwrap();
+
+ let world = local_player.world.read();
+ let entity = world.entity_by_id(&MinecraftEntityId(p.entity_id));
+ drop(world);
+
+ if let Some(entity) = entity {
+ let delta = p.delta.clone();
+ commands.add(RelativeEntityUpdate {
+ entity,
+ partial_world: local_player.partial_world.clone(),
+ update: Box::new(move |entity_mut| {
+ let mut position = entity_mut.get_mut::<Position>().unwrap();
+ **position = position.with_delta(&delta);
+ }),
+ });
+ } else {
+ warn!(
+ "Got move entity pos packet for unknown entity id {}",
+ p.entity_id
+ );
+ }
+
+ system_state.apply(ecs);
+ }
+ ClientboundGamePacket::MoveEntityPosRot(p) => {
+ let mut system_state: SystemState<(Commands, Query<&mut LocalPlayer>)> =
+ SystemState::new(ecs);
+ let (mut commands, mut query) = system_state.get_mut(ecs);
+ let local_player = query.get_mut(player_entity).unwrap();
+
+ let world = local_player.world.read();
+ let entity = world.entity_by_id(&MinecraftEntityId(p.entity_id));
+ drop(world);
+
+ if let Some(entity) = entity {
+ let delta = p.delta.clone();
+ commands.add(RelativeEntityUpdate {
+ entity,
+ partial_world: local_player.partial_world.clone(),
+ update: Box::new(move |entity_mut| {
+ let mut position = entity_mut.get_mut::<Position>().unwrap();
+ **position = position.with_delta(&delta);
+ }),
+ });
+ } else {
+ warn!(
+ "Got move entity pos rot packet for unknown entity id {}",
+ p.entity_id
+ );
+ }
+
+ system_state.apply(ecs);
+ }
+
+ ClientboundGamePacket::MoveEntityRot(_p) => {
+ // debug!("Got move entity rot packet {:?}", p);
+ }
+ ClientboundGamePacket::KeepAlive(p) => {
+ debug!("Got keep alive packet {p:?} for {player_entity:?}");
+
+ let mut system_state: SystemState<Query<&mut LocalPlayer>> =
+ SystemState::new(ecs);
+ let mut query = system_state.get_mut(ecs);
+ let mut local_player = query.get_mut(player_entity).unwrap();
+
+ local_player.write_packet(ServerboundKeepAlivePacket { id: p.id }.get());
+ debug!("Sent keep alive packet {p:?} for {player_entity:?}");
+ }
+ ClientboundGamePacket::RemoveEntities(p) => {
+ debug!("Got remove entities packet {:?}", p);
+ }
+ ClientboundGamePacket::PlayerChat(p) => {
+ debug!("Got player chat packet {:?}", p);
+
+ let mut system_state: SystemState<EventWriter<ChatReceivedEvent>> =
+ SystemState::new(ecs);
+ let mut chat_events = system_state.get_mut(ecs);
+
+ chat_events.send(ChatReceivedEvent {
+ entity: player_entity,
+ packet: ChatPacket::Player(Arc::new(p.clone())),
+ });
+ }
+ ClientboundGamePacket::SystemChat(p) => {
+ debug!("Got system chat packet {:?}", p);
+
+ let mut system_state: SystemState<EventWriter<ChatReceivedEvent>> =
+ SystemState::new(ecs);
+ let mut chat_events = system_state.get_mut(ecs);
+
+ chat_events.send(ChatReceivedEvent {
+ entity: player_entity,
+ packet: ChatPacket::System(Arc::new(p.clone())),
+ });
+ }
+ ClientboundGamePacket::Sound(_p) => {
+ // debug!("Got sound packet {:?}", p);
+ }
+ ClientboundGamePacket::LevelEvent(p) => {
+ debug!("Got level event packet {:?}", p);
+ }
+ ClientboundGamePacket::BlockUpdate(p) => {
+ debug!("Got block update packet {:?}", p);
+
+ let mut system_state: SystemState<Query<&mut LocalPlayer>> =
+ SystemState::new(ecs);
+ let mut query = system_state.get_mut(ecs);
+ let local_player = query.get_mut(player_entity).unwrap();
+
+ let world = local_player.world.write();
+
+ world.chunks.set_block_state(&p.pos, p.block_state);
+ }
+ ClientboundGamePacket::Animate(p) => {
+ debug!("Got animate packet {:?}", p);
+ }
+ ClientboundGamePacket::SectionBlocksUpdate(p) => {
+ debug!("Got section blocks update packet {:?}", p);
+ let mut system_state: SystemState<Query<&mut LocalPlayer>> =
+ SystemState::new(ecs);
+ let mut query = system_state.get_mut(ecs);
+ let local_player = query.get_mut(player_entity).unwrap();
+
+ let world = local_player.world.write();
+
+ for state in &p.states {
+ world
+ .chunks
+ .set_block_state(&(p.section_pos + state.pos.clone()), state.state);
+ }
+ }
+ ClientboundGamePacket::GameEvent(p) => {
+ debug!("Got game event packet {:?}", p);
+ }
+ ClientboundGamePacket::LevelParticles(p) => {
+ debug!("Got level particles packet {:?}", p);
+ }
+ ClientboundGamePacket::ServerData(p) => {
+ debug!("Got server data packet {:?}", p);
+ }
+ ClientboundGamePacket::SetEquipment(p) => {
+ debug!("Got set equipment packet {:?}", p);
+ }
+ ClientboundGamePacket::UpdateMobEffect(p) => {
+ debug!("Got update mob effect packet {:?}", p);
+ }
+ ClientboundGamePacket::AddExperienceOrb(_) => {}
+ ClientboundGamePacket::AwardStats(_) => {}
+ ClientboundGamePacket::BlockChangedAck(_) => {}
+ ClientboundGamePacket::BlockDestruction(_) => {}
+ ClientboundGamePacket::BlockEntityData(_) => {}
+ ClientboundGamePacket::BlockEvent(_) => {}
+ ClientboundGamePacket::BossEvent(_) => {}
+ ClientboundGamePacket::CommandSuggestions(_) => {}
+ ClientboundGamePacket::ContainerSetData(_) => {}
+ ClientboundGamePacket::ContainerSetSlot(_) => {}
+ ClientboundGamePacket::Cooldown(_) => {}
+ ClientboundGamePacket::CustomChatCompletions(_) => {}
+ ClientboundGamePacket::DeleteChat(_) => {}
+ ClientboundGamePacket::Explode(_) => {}
+ ClientboundGamePacket::ForgetLevelChunk(_) => {}
+ ClientboundGamePacket::HorseScreenOpen(_) => {}
+ ClientboundGamePacket::MapItemData(_) => {}
+ ClientboundGamePacket::MerchantOffers(_) => {}
+ ClientboundGamePacket::MoveVehicle(_) => {}
+ ClientboundGamePacket::OpenBook(_) => {}
+ ClientboundGamePacket::OpenScreen(_) => {}
+ ClientboundGamePacket::OpenSignEditor(_) => {}
+ ClientboundGamePacket::Ping(_) => {}
+ ClientboundGamePacket::PlaceGhostRecipe(_) => {}
+ ClientboundGamePacket::PlayerCombatEnd(_) => {}
+ ClientboundGamePacket::PlayerCombatEnter(_) => {}
+ ClientboundGamePacket::PlayerCombatKill(p) => {
+ debug!("Got player kill packet {:?}", p);
+
+ #[allow(clippy::type_complexity)]
+ let mut system_state: SystemState<(
+ Commands,
+ Query<(&MinecraftEntityId, Option<&Dead>)>,
+ EventWriter<DeathEvent>,
+ )> = SystemState::new(ecs);
+ let (mut commands, mut query, mut death_events) = system_state.get_mut(ecs);
+ let (entity_id, dead) = query.get_mut(player_entity).unwrap();
+
+ if **entity_id == p.player_id && dead.is_none() {
+ commands.entity(player_entity).insert(Dead);
+ death_events.send(DeathEvent {
+ entity: player_entity,
+ packet: Some(p.clone()),
+ });
+ }
+
+ system_state.apply(ecs);
+ }
+ ClientboundGamePacket::PlayerLookAt(_) => {}
+ ClientboundGamePacket::RemoveMobEffect(_) => {}
+ ClientboundGamePacket::ResourcePack(_) => {}
+ ClientboundGamePacket::Respawn(p) => {
+ debug!("Got respawn packet {:?}", p);
+
+ let mut system_state: SystemState<Commands> = SystemState::new(ecs);
+ let mut commands = system_state.get(ecs);
+
+ // Remove the Dead marker component from the player.
+ commands.entity(player_entity).remove::<Dead>();
+
+ system_state.apply(ecs);
+ }
+ ClientboundGamePacket::SelectAdvancementsTab(_) => {}
+ ClientboundGamePacket::SetActionBarText(_) => {}
+ ClientboundGamePacket::SetBorderCenter(_) => {}
+ ClientboundGamePacket::SetBorderLerpSize(_) => {}
+ ClientboundGamePacket::SetBorderSize(_) => {}
+ ClientboundGamePacket::SetBorderWarningDelay(_) => {}
+ ClientboundGamePacket::SetBorderWarningDistance(_) => {}
+ ClientboundGamePacket::SetCamera(_) => {}
+ ClientboundGamePacket::SetDisplayObjective(_) => {}
+ ClientboundGamePacket::SetObjective(_) => {}
+ ClientboundGamePacket::SetPassengers(_) => {}
+ ClientboundGamePacket::SetPlayerTeam(_) => {}
+ ClientboundGamePacket::SetScore(_) => {}
+ ClientboundGamePacket::SetSimulationDistance(_) => {}
+ ClientboundGamePacket::SetSubtitleText(_) => {}
+ ClientboundGamePacket::SetTitleText(_) => {}
+ ClientboundGamePacket::SetTitlesAnimation(_) => {}
+ ClientboundGamePacket::SoundEntity(_) => {}
+ ClientboundGamePacket::StopSound(_) => {}
+ ClientboundGamePacket::TabList(_) => {}
+ ClientboundGamePacket::TagQuery(_) => {}
+ ClientboundGamePacket::TakeItemEntity(_) => {}
+ ClientboundGamePacket::DisguisedChat(_) => {}
+ ClientboundGamePacket::UpdateEnabledFeatures(_) => {}
+ ClientboundGamePacket::ContainerClose(_) => {}
+ }
+ }
+ }
+}
+
+impl PacketReceiver {
+ /// Loop that reads from the connection and adds the packets to the queue +
+ /// runs the schedule.
+ pub async fn read_task(self, mut read_conn: ReadConnection<ClientboundGamePacket>) {
+ while let Ok(packet) = read_conn.read().await {
+ self.packets.lock().push(packet);
+ // tell the client to run all the systems
+ self.run_schedule_sender.send(()).await.unwrap();
+ }
+ }
+
+ /// Consume the [`ServerboundGamePacket`] queue and actually write the
+ /// packets to the server. It's like this so writing packets doesn't need to
+ /// be awaited.
+ pub async fn write_task(
+ self,
+ mut write_conn: WriteConnection<ServerboundGamePacket>,
+ mut write_receiver: mpsc::UnboundedReceiver<ServerboundGamePacket>,
+ ) {
+ while let Some(packet) = write_receiver.recv().await {
+ if let Err(err) = write_conn.write(packet).await {
+ error!("Disconnecting because we couldn't write a packet: {err}.");
+ break;
+ };
+ }
+ // receiver is automatically closed when it's dropped
+ }
+}
diff --git a/azalea-client/src/player.rs b/azalea-client/src/player.rs
index 650fb58c..00b6b25e 100755
--- a/azalea-client/src/player.rs
+++ b/azalea-client/src/player.rs
@@ -1,13 +1,14 @@
use azalea_auth::game_profile::GameProfile;
-use azalea_chat::Component;
+use azalea_chat::FormattedText;
use azalea_core::GameType;
-use azalea_world::PartialWorld;
+use azalea_ecs::{
+ event::EventReader,
+ system::{Commands, Res},
+};
+use azalea_world::EntityInfos;
use uuid::Uuid;
-/// Something that has a world associated to it. this is usually a `Client`.
-pub trait WorldHaver {
- fn world(&self) -> &PartialWorld;
-}
+use crate::{packet_handling::AddPlayerEvent, GameProfileComponent};
/// A player in the tab list.
#[derive(Debug, Clone)]
@@ -18,5 +19,22 @@ pub struct PlayerInfo {
pub gamemode: GameType,
pub latency: i32,
/// The player's display name in the tab list.
- pub display_name: Option<Component>,
+ pub display_name: Option<FormattedText>,
+}
+
+/// Add a [`GameProfileComponent`] when an [`AddPlayerEvent`] is received.
+/// Usually the `GameProfileComponent` will be added from the
+/// `ClientboundGamePacket::AddPlayer` handler though.
+pub fn retroactively_add_game_profile_component(
+ mut commands: Commands,
+ mut events: EventReader<AddPlayerEvent>,
+ entity_infos: Res<EntityInfos>,
+) {
+ for event in events.iter() {
+ if let Some(entity) = entity_infos.get_entity_by_uuid(&event.info.uuid) {
+ commands
+ .entity(entity)
+ .insert(GameProfileComponent(event.info.profile.clone()));
+ }
+ }
}
diff --git a/azalea-client/src/plugins.rs b/azalea-client/src/plugins.rs
deleted file mode 100644
index 93641906..00000000
--- a/azalea-client/src/plugins.rs
+++ /dev/null
@@ -1,144 +0,0 @@
-use crate::{Client, Event};
-use async_trait::async_trait;
-use nohash_hasher::NoHashHasher;
-use std::{
- any::{Any, TypeId},
- collections::HashMap,
- hash::BuildHasherDefault,
-};
-
-type U64Hasher = BuildHasherDefault<NoHashHasher<u64>>;
-
-// kind of based on https://docs.rs/http/latest/src/http/extensions.rs.html
-#[derive(Clone, Default)]
-pub struct PluginStates {
- map: Option<HashMap<TypeId, Box<dyn PluginState>, U64Hasher>>,
-}
-
-/// A map of PluginState TypeIds to AnyPlugin objects. This can then be built
-/// into a [`PluginStates`] object to get a fresh new state based on this
-/// plugin.
-///
-/// If you're using the azalea crate, you should generate this from the
-/// `plugins!` macro.
-#[derive(Clone, Default)]
-pub struct Plugins {
- map: Option<HashMap<TypeId, Box<dyn AnyPlugin>, U64Hasher>>,
-}
-
-impl PluginStates {
- pub fn get<T: PluginState>(&self) -> Option<&T> {
- self.map
- .as_ref()
- .and_then(|map| map.get(&TypeId::of::<T>()))
- .and_then(|boxed| (boxed.as_ref() as &dyn Any).downcast_ref::<T>())
- }
-}
-
-impl Plugins {
- /// Create a new empty set of plugins.
- pub fn new() -> Self {
- Self::default()
- }
-
- /// Add a new plugin to this set.
- pub fn add<T: Plugin + Clone>(&mut self, plugin: T) {
- if self.map.is_none() {
- self.map = Some(HashMap::with_hasher(BuildHasherDefault::default()));
- }
- self.map
- .as_mut()
- .unwrap()
- .insert(TypeId::of::<T::State>(), Box::new(plugin));
- }
-
- /// Build our plugin states from this set of plugins. Note that if you're
- /// using `azalea` you'll probably never need to use this as it's called
- /// for you.
- pub fn build(self) -> PluginStates {
- let mut map = HashMap::with_hasher(BuildHasherDefault::default());
- for (id, plugin) in self.map.unwrap().into_iter() {
- map.insert(id, plugin.build());
- }
- PluginStates { map: Some(map) }
- }
-}
-
-impl IntoIterator for PluginStates {
- type Item = Box<dyn PluginState>;
- type IntoIter = std::vec::IntoIter<Self::Item>;
-
- /// Iterate over the plugin states.
- fn into_iter(self) -> Self::IntoIter {
- self.map
- .map(|map| map.into_values().collect::<Vec<_>>())
- .unwrap_or_default()
- .into_iter()
- }
-}
-
-/// A `PluginState` keeps the current state of a plugin for a client. All the
-/// fields must be atomic. Unique `PluginState`s are built from [`Plugin`]s.
-#[async_trait]
-pub trait PluginState: Send + Sync + PluginStateClone + Any + 'static {
- async fn handle(self: Box<Self>, event: Event, bot: Client);
-}
-
-/// Plugins can keep their own personal state, listen to [`Event`]s, and add
-/// new functions to [`Client`].
-pub trait Plugin: Send + Sync + Any + 'static {
- type State: PluginState;
-
- fn build(&self) -> Self::State;
-}
-
-/// AnyPlugin is basically a Plugin but without the State associated type
-/// it has to exist so we can do a hashmap with Box<dyn AnyPlugin>
-#[doc(hidden)]
-pub trait AnyPlugin: Send + Sync + Any + AnyPluginClone + 'static {
- fn build(&self) -> Box<dyn PluginState>;
-}
-
-impl<S: PluginState, B: Plugin<State = S> + Clone> AnyPlugin for B {
- fn build(&self) -> Box<dyn PluginState> {
- Box::new(self.build())
- }
-}
-
-/// An internal trait that allows PluginState to be cloned.
-#[doc(hidden)]
-pub trait PluginStateClone {
- fn clone_box(&self) -> Box<dyn PluginState>;
-}
-impl<T> PluginStateClone for T
-where
- T: 'static + PluginState + Clone,
-{
- fn clone_box(&self) -> Box<dyn PluginState> {
- Box::new(self.clone())
- }
-}
-impl Clone for Box<dyn PluginState> {
- fn clone(&self) -> Self {
- self.clone_box()
- }
-}
-
-/// An internal trait that allows AnyPlugin to be cloned.
-#[doc(hidden)]
-pub trait AnyPluginClone {
- fn clone_box(&self) -> Box<dyn AnyPlugin>;
-}
-impl<T> AnyPluginClone for T
-where
- T: 'static + Plugin + Clone,
-{
- fn clone_box(&self) -> Box<dyn AnyPlugin> {
- Box::new(self.clone())
- }
-}
-impl Clone for Box<dyn AnyPlugin> {
- fn clone(&self) -> Self {
- self.clone_box()
- }
-}
diff --git a/azalea-client/src/task_pool.rs b/azalea-client/src/task_pool.rs
new file mode 100644
index 00000000..2a3afbbc
--- /dev/null
+++ b/azalea-client/src/task_pool.rs
@@ -0,0 +1,177 @@
+//! Borrowed from `bevy_core`.
+
+use azalea_ecs::{
+ app::{App, Plugin},
+ schedule::IntoSystemDescriptor,
+ system::Resource,
+};
+use bevy_tasks::{AsyncComputeTaskPool, ComputeTaskPool, IoTaskPool, TaskPoolBuilder};
+
+/// Setup of default task pools: `AsyncComputeTaskPool`, `ComputeTaskPool`,
+/// `IoTaskPool`.
+#[derive(Default)]
+pub struct TaskPoolPlugin {
+ /// Options for the [`TaskPool`](bevy_tasks::TaskPool) created at
+ /// application start.
+ pub task_pool_options: TaskPoolOptions,
+}
+
+impl Plugin for TaskPoolPlugin {
+ fn build(&self, app: &mut App) {
+ // Setup the default bevy task pools
+ self.task_pool_options.create_default_pools();
+
+ #[cfg(not(target_arch = "wasm32"))]
+ app.add_system_to_stage(
+ azalea_ecs::app::CoreStage::Last,
+ bevy_tasks::tick_global_task_pools_on_main_thread.at_end(),
+ );
+ }
+}
+
+/// Helper for configuring and creating the default task pools. For end-users
+/// who want full control, set up [`TaskPoolPlugin`](super::TaskPoolPlugin)
+#[derive(Clone, Resource)]
+pub struct TaskPoolOptions {
+ /// If the number of physical cores is less than min_total_threads, force
+ /// using min_total_threads
+ pub min_total_threads: usize,
+ /// If the number of physical cores is greater than max_total_threads, force
+ /// using max_total_threads
+ pub max_total_threads: usize,
+
+ /// Used to determine number of IO threads to allocate
+ pub io: TaskPoolThreadAssignmentPolicy,
+ /// Used to determine number of async compute threads to allocate
+ pub async_compute: TaskPoolThreadAssignmentPolicy,
+ /// Used to determine number of compute threads to allocate
+ pub compute: TaskPoolThreadAssignmentPolicy,
+}
+
+impl Default for TaskPoolOptions {
+ fn default() -> Self {
+ TaskPoolOptions {
+ // By default, use however many cores are available on the system
+ min_total_threads: 1,
+ max_total_threads: std::usize::MAX,
+
+ // Use 25% of cores for IO, at least 1, no more than 4
+ io: TaskPoolThreadAssignmentPolicy {
+ min_threads: 1,
+ max_threads: 4,
+ percent: 0.25,
+ },
+
+ // Use 25% of cores for async compute, at least 1, no more than 4
+ async_compute: TaskPoolThreadAssignmentPolicy {
+ min_threads: 1,
+ max_threads: 4,
+ percent: 0.25,
+ },
+
+ // Use all remaining cores for compute (at least 1)
+ compute: TaskPoolThreadAssignmentPolicy {
+ min_threads: 1,
+ max_threads: std::usize::MAX,
+ percent: 1.0, // This 1.0 here means "whatever is left over"
+ },
+ }
+ }
+}
+
+impl TaskPoolOptions {
+ // /// Create a configuration that forces using the given number of threads.
+ // pub fn with_num_threads(thread_count: usize) -> Self {
+ // TaskPoolOptions {
+ // min_total_threads: thread_count,
+ // max_total_threads: thread_count,
+ // ..Default::default()
+ // }
+ // }
+
+ /// Inserts the default thread pools into the given resource map based on
+ /// the configured values
+ pub fn create_default_pools(&self) {
+ let total_threads = bevy_tasks::available_parallelism()
+ .clamp(self.min_total_threads, self.max_total_threads);
+
+ let mut remaining_threads = total_threads;
+
+ {
+ // Determine the number of IO threads we will use
+ let io_threads = self
+ .io
+ .get_number_of_threads(remaining_threads, total_threads);
+
+ remaining_threads = remaining_threads.saturating_sub(io_threads);
+
+ IoTaskPool::init(|| {
+ TaskPoolBuilder::default()
+ .num_threads(io_threads)
+ .thread_name("IO Task Pool".to_string())
+ .build()
+ });
+ }
+
+ {
+ // Determine the number of async compute threads we will use
+ let async_compute_threads = self
+ .async_compute
+ .get_number_of_threads(remaining_threads, total_threads);
+
+ remaining_threads = remaining_threads.saturating_sub(async_compute_threads);
+
+ AsyncComputeTaskPool::init(|| {
+ TaskPoolBuilder::default()
+ .num_threads(async_compute_threads)
+ .thread_name("Async Compute Task Pool".to_string())
+ .build()
+ });
+ }
+
+ {
+ // Determine the number of compute threads we will use
+ // This is intentionally last so that an end user can specify 1.0 as the percent
+ let compute_threads = self
+ .compute
+ .get_number_of_threads(remaining_threads, total_threads);
+
+ ComputeTaskPool::init(|| {
+ TaskPoolBuilder::default()
+ .num_threads(compute_threads)
+ .thread_name("Compute Task Pool".to_string())
+ .build()
+ });
+ }
+ }
+}
+
+/// Defines a simple way to determine how many threads to use given the number
+/// of remaining cores and number of total cores
+#[derive(Clone)]
+pub struct TaskPoolThreadAssignmentPolicy {
+ /// Force using at least this many threads
+ pub min_threads: usize,
+ /// Under no circumstance use more than this many threads for this pool
+ pub max_threads: usize,
+ /// Target using this percentage of total cores, clamped by min_threads and
+ /// max_threads. It is permitted to use 1.0 to try to use all remaining
+ /// threads
+ pub percent: f32,
+}
+
+impl TaskPoolThreadAssignmentPolicy {
+ /// Determine the number of threads to use for this task pool
+ fn get_number_of_threads(&self, remaining_threads: usize, total_threads: usize) -> usize {
+ assert!(self.percent >= 0.0);
+ let mut desired = (total_threads as f32 * self.percent).round() as usize;
+
+ // Limit ourselves to the number of cores available
+ desired = desired.min(remaining_threads);
+
+ // Clamp by min_threads, max_threads. (This may result in us using more threads
+ // than are available, this is intended. An example case where this
+ // might happen is a device with <= 2 threads.
+ desired.clamp(self.min_threads, self.max_threads)
+ }
+}
diff --git a/azalea-core/Cargo.toml b/azalea-core/Cargo.toml
index 9addac59..e102594d 100755
--- a/azalea-core/Cargo.toml
+++ b/azalea-core/Cargo.toml
@@ -3,13 +3,17 @@ description = "Miscellaneous things in Azalea."
edition = "2021"
license = "MIT"
name = "azalea-core"
-version = "0.5.0"
repository = "https://github.com/mat-1/azalea/tree/main/azalea-core"
+version = "0.5.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.5.0" }
-azalea-chat = {path = "../azalea-chat", version = "^0.5.0" }
-azalea-nbt = {path = "../azalea-nbt", version = "^0.5.0" }
+azalea-buf = {path = "../azalea-buf", version = "^0.5.0"}
+azalea-chat = {path = "../azalea-chat", version = "^0.5.0"}
+azalea-nbt = {path = "../azalea-nbt", version = "^0.5.0"}
+bevy_ecs = {version = "0.9.1", default-features = false, optional = true}
uuid = "^1.1.2"
+
+[features]
+bevy_ecs = ["dep:bevy_ecs"]
diff --git a/azalea-core/src/delta.rs b/azalea-core/src/delta.rs
index 4f730d14..ce49ab50 100755
--- a/azalea-core/src/delta.rs
+++ b/azalea-core/src/delta.rs
@@ -39,6 +39,7 @@ impl PositionDeltaTrait for PositionDelta8 {
}
impl Vec3 {
+ #[must_use]
pub fn with_delta(&self, delta: &dyn PositionDeltaTrait) -> Vec3 {
Vec3 {
x: self.x + delta.x(),
diff --git a/azalea-core/src/particle/mod.rs b/azalea-core/src/particle/mod.rs
index 44c7c2f5..8dc9f8c6 100755
--- a/azalea-core/src/particle/mod.rs
+++ b/azalea-core/src/particle/mod.rs
@@ -1,6 +1,7 @@
use crate::{BlockPos, Slot};
use azalea_buf::McBuf;
+#[cfg_attr(feature = "bevy_ecs", derive(bevy_ecs::component::Component))]
#[derive(Debug, Clone, McBuf, Default)]
pub struct Particle {
#[var]
diff --git a/azalea-core/src/position.rs b/azalea-core/src/position.rs
index ea54c60c..d04f58cc 100755
--- a/azalea-core/src/position.rs
+++ b/azalea-core/src/position.rs
@@ -1,5 +1,5 @@
use crate::ResourceLocation;
-use azalea_buf::{BufReadError, McBufReadable, McBufWritable};
+use azalea_buf::{BufReadError, McBuf, McBufReadable, McBufWritable};
use std::{
io::{Cursor, Write},
ops::{Add, AddAssign, Mul, Rem, Sub},
@@ -109,7 +109,7 @@ macro_rules! vec3_impl {
};
}
-#[derive(Clone, Copy, Debug, Default, PartialEq)]
+#[derive(Clone, Copy, Debug, Default, PartialEq, McBuf)]
pub struct Vec3 {
pub x: f64,
pub y: f64,
@@ -270,12 +270,22 @@ impl From<&Vec3> for BlockPos {
}
}
}
+impl From<Vec3> for BlockPos {
+ fn from(pos: Vec3) -> Self {
+ BlockPos::from(&pos)
+ }
+}
impl From<&Vec3> for ChunkPos {
fn from(pos: &Vec3) -> Self {
ChunkPos::from(&BlockPos::from(pos))
}
}
+impl From<Vec3> for ChunkPos {
+ fn from(pos: Vec3) -> Self {
+ ChunkPos::from(&pos)
+ }
+}
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;
diff --git a/azalea-core/src/resource_location.rs b/azalea-core/src/resource_location.rs
index e810b96e..4e25d00e 100755
--- a/azalea-core/src/resource_location.rs
+++ b/azalea-core/src/resource_location.rs
@@ -3,6 +3,9 @@
use azalea_buf::{BufReadError, McBufReadable, McBufWritable};
use std::io::{Cursor, Write};
+// TODO: make a `resourcelocation!("minecraft:overwolrd")` macro that checks if
+// it's correct at compile-time.
+
#[derive(Hash, Clone, PartialEq, Eq)]
pub struct ResourceLocation {
pub namespace: String,
diff --git a/azalea-ecs/Cargo.toml b/azalea-ecs/Cargo.toml
new file mode 100644
index 00000000..30c9676b
--- /dev/null
+++ b/azalea-ecs/Cargo.toml
@@ -0,0 +1,13 @@
+[package]
+edition = "2021"
+name = "azalea-ecs"
+version = "0.5.0"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+azalea-ecs-macros = {path = "./azalea-ecs-macros", version = "^0.5.0"}
+bevy_app = "0.9.1"
+bevy_ecs = {version = "0.9.1", default-features = false}
+iyes_loopless = "0.9.1"
+tokio = {version = "1.25.0", features = ["time"]}
diff --git a/azalea-ecs/azalea-ecs-macros/Cargo.toml b/azalea-ecs/azalea-ecs-macros/Cargo.toml
new file mode 100755
index 00000000..2301f2f1
--- /dev/null
+++ b/azalea-ecs/azalea-ecs-macros/Cargo.toml
@@ -0,0 +1,15 @@
+[package]
+description = "Azalea ECS Macros"
+edition = "2021"
+license = "MIT OR Apache-2.0"
+name = "azalea-ecs-macros"
+version = "0.5.0"
+
+[lib]
+proc-macro = true
+
+[dependencies]
+proc-macro2 = "1.0"
+quote = "1.0"
+syn = "1.0"
+toml = "0.7.0"
diff --git a/azalea-ecs/azalea-ecs-macros/src/component.rs b/azalea-ecs/azalea-ecs-macros/src/component.rs
new file mode 100644
index 00000000..306b64de
--- /dev/null
+++ b/azalea-ecs/azalea-ecs-macros/src/component.rs
@@ -0,0 +1,125 @@
+use crate::utils::{get_lit_str, Symbol};
+use proc_macro::TokenStream;
+use proc_macro2::{Span, TokenStream as TokenStream2};
+use quote::{quote, ToTokens};
+use syn::{parse_macro_input, parse_quote, DeriveInput, Error, Ident, Path, Result};
+
+use crate::utils;
+
+pub fn derive_resource(input: TokenStream) -> TokenStream {
+ let mut ast = parse_macro_input!(input as DeriveInput);
+ let azalea_ecs_path: Path = crate::azalea_ecs_path();
+
+ ast.generics
+ .make_where_clause()
+ .predicates
+ .push(parse_quote! { Self: Send + Sync + 'static });
+
+ let struct_name = &ast.ident;
+ let (impl_generics, type_generics, where_clause) = &ast.generics.split_for_impl();
+
+ TokenStream::from(quote! {
+ impl #impl_generics #azalea_ecs_path::system::BevyResource for #struct_name #type_generics #where_clause {
+ }
+ })
+}
+
+pub fn derive_component(input: TokenStream) -> TokenStream {
+ let mut ast = parse_macro_input!(input as DeriveInput);
+ let azalea_ecs_path: Path = crate::azalea_ecs_path();
+
+ let attrs = match parse_component_attr(&ast) {
+ Ok(attrs) => attrs,
+ Err(e) => return e.into_compile_error().into(),
+ };
+
+ let storage = storage_path(&azalea_ecs_path, attrs.storage);
+
+ ast.generics
+ .make_where_clause()
+ .predicates
+ .push(parse_quote! { Self: Send + Sync + 'static });
+
+ let struct_name = &ast.ident;
+ let (impl_generics, type_generics, where_clause) = &ast.generics.split_for_impl();
+
+ TokenStream::from(quote! {
+ impl #impl_generics #azalea_ecs_path::component::BevyComponent for #struct_name #type_generics #where_clause {
+ type Storage = #storage;
+ }
+ })
+}
+
+pub const COMPONENT: Symbol = Symbol("component");
+pub const STORAGE: Symbol = Symbol("storage");
+
+struct Attrs {
+ storage: StorageTy,
+}
+
+#[derive(Clone, Copy)]
+enum StorageTy {
+ Table,
+ SparseSet,
+}
+
+// values for `storage` attribute
+const TABLE: &str = "Table";
+const SPARSE_SET: &str = "SparseSet";
+
+fn parse_component_attr(ast: &DeriveInput) -> Result<Attrs> {
+ let meta_items = utils::parse_attrs(ast, COMPONENT)?;
+
+ let mut attrs = Attrs {
+ storage: StorageTy::Table,
+ };
+
+ for meta in meta_items {
+ use syn::{
+ Meta::NameValue,
+ NestedMeta::{Lit, Meta},
+ };
+ match meta {
+ Meta(NameValue(m)) if m.path == STORAGE => {
+ attrs.storage = match get_lit_str(STORAGE, &m.lit)?.value().as_str() {
+ TABLE => StorageTy::Table,
+ SPARSE_SET => StorageTy::SparseSet,
+ s => {
+ return Err(Error::new_spanned(
+ m.lit,
+ format!(
+ "Invalid storage type `{s}`, expected '{TABLE}' or '{SPARSE_SET}'."
+ ),
+ ))
+ }
+ };
+ }
+ Meta(meta_item) => {
+ return Err(Error::new_spanned(
+ meta_item.path(),
+ format!(
+ "unknown component attribute `{}`",
+ meta_item.path().into_token_stream()
+ ),
+ ));
+ }
+ Lit(lit) => {
+ return Err(Error::new_spanned(
+ lit,
+ "unexpected literal in component attribute",
+ ))
+ }
+ }
+ }
+
+ Ok(attrs)
+}
+
+fn storage_path(azalea_ecs_path: &Path, ty: StorageTy) -> TokenStream2 {
+ let typename = match ty {
+ StorageTy::Table => Ident::new("TableStorage", Span::call_site()),
+ StorageTy::SparseSet => Ident::new("SparseStorage", Span::call_site()),
+ };
+
+ quote! { #azalea_ecs_path::component::#typename }
+}
diff --git a/azalea-ecs/azalea-ecs-macros/src/fetch.rs b/azalea-ecs/azalea-ecs-macros/src/fetch.rs
new file mode 100644
index 00000000..8a6b93ba
--- /dev/null
+++ b/azalea-ecs/azalea-ecs-macros/src/fetch.rs
@@ -0,0 +1,466 @@
+use proc_macro::TokenStream;
+use proc_macro2::{Ident, Span};
+use quote::{quote, ToTokens};
+use syn::{
+ parse::{Parse, ParseStream},
+ parse_quote,
+ punctuated::Punctuated,
+ Attribute, Data, DataStruct, DeriveInput, Field, Fields,
+};
+
+use crate::azalea_ecs_path;
+
+#[derive(Default)]
+struct FetchStructAttributes {
+ pub is_mutable: bool,
+ pub derive_args: Punctuated<syn::NestedMeta, syn::token::Comma>,
+}
+
+static MUTABLE_ATTRIBUTE_NAME: &str = "mutable";
+static DERIVE_ATTRIBUTE_NAME: &str = "derive";
+
+mod field_attr_keywords {
+ syn::custom_keyword!(ignore);
+}
+
+pub static WORLD_QUERY_ATTRIBUTE_NAME: &str = "world_query";
+
+pub fn derive_world_query_impl(ast: DeriveInput) -> TokenStream {
+ let visibility = ast.vis;
+
+ let mut fetch_struct_attributes = FetchStructAttributes::default();
+ for attr in &ast.attrs {
+ if !attr
+ .path
+ .get_ident()
+ .map_or(false, |ident| ident == WORLD_QUERY_ATTRIBUTE_NAME)
+ {
+ continue;
+ }
+
+ attr.parse_args_with(|input: ParseStream| {
+ let meta = input.parse_terminated::<syn::Meta, syn::token::Comma>(syn::Meta::parse)?;
+ for meta in meta {
+ let ident = meta.path().get_ident().unwrap_or_else(|| {
+ panic!(
+ "Unrecognized attribute: `{}`",
+ meta.path().to_token_stream()
+ )
+ });
+ if ident == MUTABLE_ATTRIBUTE_NAME {
+ if let syn::Meta::Path(_) = meta {
+ fetch_struct_attributes.is_mutable = true;
+ } else {
+ panic!(
+ "The `{MUTABLE_ATTRIBUTE_NAME}` attribute is expected to have no value or arguments"
+ );
+ }
+ } else if ident == DERIVE_ATTRIBUTE_NAME {
+ if let syn::Meta::List(meta_list) = meta {
+ fetch_struct_attributes
+ .derive_args
+ .extend(meta_list.nested.iter().cloned());
+ } else {
+ panic!(
+ "Expected a structured list within the `{DERIVE_ATTRIBUTE_NAME}` attribute"
+ );
+ }
+ } else {
+ panic!(
+ "Unrecognized attribute: `{}`",
+ meta.path().to_token_stream()
+ );
+ }
+ }
+ Ok(())
+ })
+ .unwrap_or_else(|_| panic!("Invalid `{WORLD_QUERY_ATTRIBUTE_NAME}` attribute format"));
+ }
+
+ let path = azalea_ecs_path();
+
+ let user_generics = ast.generics.clone();
+ let (user_impl_generics, user_ty_generics, user_where_clauses) = user_generics.split_for_impl();
+ let user_generics_with_world = {
+ let mut generics = ast.generics.clone();
+ generics.params.insert(0, parse_quote!('__w));
+ generics
+ };
+ let (user_impl_generics_with_world, user_ty_generics_with_world, user_where_clauses_with_world) =
+ user_generics_with_world.split_for_impl();
+
+ let struct_name = ast.ident.clone();
+ let read_only_struct_name = if fetch_struct_attributes.is_mutable {
+ Ident::new(&format!("{struct_name}ReadOnly"), Span::call_site())
+ } else {
+ struct_name.clone()
+ };
+
+ let item_struct_name = Ident::new(&format!("{struct_name}Item"), Span::call_site());
+ let read_only_item_struct_name = if fetch_struct_attributes.is_mutable {
+ Ident::new(&format!("{struct_name}ReadOnlyItem"), Span::call_site())
+ } else {
+ item_struct_name.clone()
+ };
+
+ let fetch_struct_name = Ident::new(&format!("{struct_name}Fetch"), Span::call_site());
+ let read_only_fetch_struct_name = if fetch_struct_attributes.is_mutable {
+ Ident::new(&format!("{struct_name}ReadOnlyFetch"), Span::call_site())
+ } else {
+ fetch_struct_name.clone()
+ };
+
+ let state_struct_name = Ident::new(&format!("{struct_name}State"), Span::call_site());
+
+ let fields = match &ast.data {
+ Data::Struct(DataStruct {
+ fields: Fields::Named(fields),
+ ..
+ }) => &fields.named,
+ _ => panic!("Expected a struct with named fields"),
+ };
+
+ let mut ignored_field_attrs = Vec::new();
+ let mut ignored_field_visibilities = Vec::new();
+ let mut ignored_field_idents = Vec::new();
+ let mut ignored_field_types = Vec::new();
+ let mut field_attrs = Vec::new();
+ let mut field_visibilities = Vec::new();
+ let mut field_idents = Vec::new();
+ let mut field_types = Vec::new();
+ let mut read_only_field_types = Vec::new();
+
+ for field in fields {
+ let WorldQueryFieldInfo { is_ignored, attrs } = read_world_query_field_info(field);
+
+ let field_ident = field.ident.as_ref().unwrap().clone();
+ if is_ignored {
+ ignored_field_attrs.push(attrs);
+ ignored_field_visibilities.push(field.vis.clone());
+ ignored_field_idents.push(field_ident.clone());
+ ignored_field_types.push(field.ty.clone());
+ } else {
+ field_attrs.push(attrs);
+ field_visibilities.push(field.vis.clone());
+ field_idents.push(field_ident.clone());
+ let field_ty = field.ty.clone();
+ field_types.push(quote!(#field_ty));
+ read_only_field_types.push(quote!(<#field_ty as #path::query::WorldQuery>::ReadOnly));
+ }
+ }
+
+ let derive_args = &fetch_struct_attributes.derive_args;
+ // `#[derive()]` is valid syntax
+ let derive_macro_call = quote! { #[derive(#derive_args)] };
+
+ let impl_fetch = |is_readonly: bool| {
+ let struct_name = if is_readonly {
+ &read_only_struct_name
+ } else {
+ &struct_name
+ };
+ let item_struct_name = if is_readonly {
+ &read_only_item_struct_name
+ } else {
+ &item_struct_name
+ };
+ let fetch_struct_name = if is_readonly {
+ &read_only_fetch_struct_name
+ } else {
+ &fetch_struct_name
+ };
+
+ let field_types = if is_readonly {
+ &read_only_field_types
+ } else {
+ &field_types
+ };
+
+ quote! {
+ #derive_macro_call
+ #[doc = "Automatically generated [`WorldQuery`] item type for [`"]
+ #[doc = stringify!(#struct_name)]
+ #[doc = "`], returned when iterating over query results."]
+ #[automatically_derived]
+ #visibility struct #item_struct_name #user_impl_generics_with_world #user_where_clauses_with_world {
+ #(#(#field_attrs)* #field_visibilities #field_idents: <#field_types as #path::query::WorldQuery>::Item<'__w>,)*
+ #(#(#ignored_field_attrs)* #ignored_field_visibilities #ignored_field_idents: #ignored_field_types,)*
+ }
+
+ #[doc(hidden)]
+ #[doc = "Automatically generated internal [`WorldQuery`] fetch type for [`"]
+ #[doc = stringify!(#struct_name)]
+ #[doc = "`], used to define the world data accessed by this query."]
+ #[automatically_derived]
+ #visibility struct #fetch_struct_name #user_impl_generics_with_world #user_where_clauses_with_world {
+ #(#field_idents: <#field_types as #path::query::WorldQuery>::Fetch<'__w>,)*
+ #(#ignored_field_idents: #ignored_field_types,)*
+ }
+
+ // SAFETY: `update_component_access` and `update_archetype_component_access` are called on every field
+ unsafe impl #user_impl_generics #path::query::WorldQuery
+ for #struct_name #user_ty_generics #user_where_clauses {
+
+ type Item<'__w> = #item_struct_name #user_ty_generics_with_world;
+ type Fetch<'__w> = #fetch_struct_name #user_ty_generics_with_world;
+ type ReadOnly = #read_only_struct_name #user_ty_generics;
+ type State = #state_struct_name #user_ty_generics;
+
+ fn shrink<'__wlong: '__wshort, '__wshort>(
+ item: <#struct_name #user_ty_generics as #path::query::WorldQuery>::Item<'__wlong>
+ ) -> <#struct_name #user_ty_generics as #path::query::WorldQuery>::Item<'__wshort> {
+ #item_struct_name {
+ #(
+ #field_idents: <#field_types>::shrink(item.#field_idents),
+ )*
+ #(
+ #ignored_field_idents: item.#ignored_field_idents,
+ )*
+ }
+ }
+
+ unsafe fn init_fetch<'__w>(
+ _world: &'__w #path::world::World,
+ state: &Self::State,
+ _last_change_tick: u32,
+ _change_tick: u32
+ ) -> <Self as #path::query::WorldQuery>::Fetch<'__w> {
+ #fetch_struct_name {
+ #(#field_idents:
+ <#field_types>::init_fetch(
+ _world,
+ &state.#field_idents,
+ _last_change_tick,
+ _change_tick
+ ),
+ )*
+ #(#ignored_field_idents: Default::default(),)*
+ }
+ }
+
+ unsafe fn clone_fetch<'__w>(
+ _fetch: &<Self as #path::query::WorldQuery>::Fetch<'__w>
+ ) -> <Self as #path::query::WorldQuery>::Fetch<'__w> {
+ #fetch_struct_name {
+ #(
+ #field_idents: <#field_types>::clone_fetch(& _fetch. #field_idents),
+ )*
+ #(
+ #ignored_field_idents: Default::default(),
+ )*
+ }
+ }
+
+ const IS_DENSE: bool = true #(&& <#field_types>::IS_DENSE)*;
+
+ const IS_ARCHETYPAL: bool = true #(&& <#field_types>::IS_ARCHETYPAL)*;
+
+ /// SAFETY: we call `set_archetype` for each member that implements `Fetch`
+ #[inline]
+ unsafe fn set_archetype<'__w>(
+ _fetch: &mut <Self as #path::query::WorldQuery>::Fetch<'__w>,
+ _state: &Self::State,
+ _archetype: &'__w #path::archetype::Archetype,
+ _table: &'__w #path::storage::Table
+ ) {
+ #(<#field_types>::set_archetype(&mut _fetch.#field_idents, &_state.#field_idents, _archetype, _table);)*
+ }
+
+ /// SAFETY: we call `set_table` for each member that implements `Fetch`
+ #[inline]
+ unsafe fn set_table<'__w>(
+ _fetch: &mut <Self as #path::query::WorldQuery>::Fetch<'__w>,
+ _state: &Self::State,
+ _table: &'__w #path::storage::Table
+ ) {
+ #(<#field_types>::set_table(&mut _fetch.#field_idents, &_state.#field_idents, _table);)*
+ }
+
+ /// SAFETY: we call `fetch` for each member that implements `Fetch`.
+ #[inline(always)]
+ unsafe fn fetch<'__w>(
+ _fetch: &mut <Self as #path::query::WorldQuery>::Fetch<'__w>,
+ _entity: Entity,
+ _table_row: usize
+ ) -> <Self as #path::query::WorldQuery>::Item<'__w> {
+ Self::Item {
+ #(#field_idents: <#field_types>::fetch(&mut _fetch.#field_idents, _entity, _table_row),)*
+ #(#ignored_field_idents: Default::default(),)*
+ }
+ }
+
+ #[allow(unused_variables)]
+ #[inline(always)]
+ unsafe fn filter_fetch<'__w>(
+ _fetch: &mut <Self as #path::query::WorldQuery>::Fetch<'__w>,
+ _entity: Entity,
+ _table_row: usize
+ ) -> bool {
+ true #(&& <#field_types>::filter_fetch(&mut _fetch.#field_idents, _entity, _table_row))*
+ }
+
+ fn update_component_access(state: &Self::State, _access: &mut #path::query::FilteredAccess<#path::component::ComponentId>) {
+ #( <#field_types>::update_component_access(&state.#field_idents, _access); )*
+ }
+
+ fn update_archetype_component_access(
+ state: &Self::State,
+ _archetype: &#path::archetype::Archetype,
+ _access: &mut #path::query::Access<#path::archetype::ArchetypeComponentId>
+ ) {
+ #(
+ <#field_types>::update_archetype_component_access(&state.#field_idents, _archetype, _access);
+ )*
+ }
+
+ fn init_state(world: &mut #path::world::World) -> #state_struct_name #user_ty_generics {
+ #state_struct_name {
+ #(#field_idents: <#field_types>::init_state(world),)*
+ #(#ignored_field_idents: Default::default(),)*
+ }
+ }
+
+ fn matches_component_set(state: &Self::State, _set_contains_id: &impl Fn(#path::component::ComponentId) -> bool) -> bool {
+ true #(&& <#field_types>::matches_component_set(&state.#field_idents, _set_contains_id))*
+ }
+ }
+ }
+ };
+
+ let mutable_impl = impl_fetch(false);
+ let readonly_impl = if fetch_struct_attributes.is_mutable {
+ let world_query_impl = impl_fetch(true);
+ quote! {
+ #[doc(hidden)]
+ #[doc = "Automatically generated internal [`WorldQuery`] type for [`"]
+ #[doc = stringify!(#struct_name)]
+ #[doc = "`], used for read-only access."]
+ #[automatically_derived]
+ #visibility struct #read_only_struct_name #user_impl_generics #user_where_clauses {
+ #( #field_idents: #read_only_field_types, )*
+ #(#(#ignored_field_attrs)* #ignored_field_visibilities #ignored_field_idents: #ignored_field_types,)*
+ }
+
+ #world_query_impl
+ }
+ } else {
+ quote! {}
+ };
+
+ let read_only_asserts = if fetch_struct_attributes.is_mutable {
+ quote! {
+ // Double-check that the data fetched by `<_ as WorldQuery>::ReadOnly` is read-only.
+ // This is technically unnecessary as `<_ as WorldQuery>::ReadOnly: ReadOnlyWorldQuery`
+ // but to protect against future mistakes we assert the assoc type implements `ReadOnlyWorldQuery` anyway
+ #( assert_readonly::<#read_only_field_types>(); )*
+ }
+ } else {
+ quote! {
+ // Statically checks that the safety guarantee of `ReadOnlyWorldQuery` for `$fetch_struct_name` actually holds true.
+ // We need this to make sure that we don't compile `ReadOnlyWorldQuery` if our struct contains nested `WorldQuery`
+ // members that don't implement it. I.e.:
+ // ```
+ // #[derive(WorldQuery)]
+ // pub struct Foo { a: &'static mut MyComponent }
+ // ```
+ #( assert_readonly::<#field_types>(); )*
+ }
+ };
+
+ TokenStream::from(quote! {
+ #mutable_impl
+
+ #readonly_impl
+
+ #[doc(hidden)]
+ #[doc = "Automatically generated internal [`WorldQuery`] state type for [`"]
+ #[doc = stringify!(#struct_name)]
+ #[doc = "`], used for caching."]
+ #[automatically_derived]
+ #visibility struct #state_struct_name #user_impl_generics #user_where_clauses {
+ #(#field_idents: <#field_types as #path::query::WorldQuery>::State,)*
+ #(#ignored_field_idents: #ignored_field_types,)*
+ }
+
+ /// SAFETY: we assert fields are readonly below
+ unsafe impl #user_impl_generics #path::query::ReadOnlyWorldQuery
+ for #read_only_struct_name #user_ty_generics #user_where_clauses {}
+
+ #[allow(dead_code)]
+ const _: () = {
+ fn assert_readonly<T>()
+ where
+ T: #path::query::ReadOnlyWorldQuery,
+ {
+ }
+
+ // We generate a readonly assertion for every struct member.
+ fn assert_all #user_impl_generics_with_world () #user_where_clauses_with_world {
+ #read_only_asserts
+ }
+ };
+
+ // The original struct will most likely be left unused. As we don't want our users having
+ // to specify `#[allow(dead_code)]` for their custom queries, we are using this cursed
+ // workaround.
+ #[allow(dead_code)]
+ const _: () = {
+ fn dead_code_workaround #user_impl_generics (
+ q: #struct_name #user_ty_generics,
+ q2: #read_only_struct_name #user_ty_generics
+ ) #user_where_clauses {
+ #(q.#field_idents;)*
+ #(q.#ignored_field_idents;)*
+ #(q2.#field_idents;)*
+ #(q2.#ignored_field_idents;)*
+
+ }
+ };
+ })
+}
+
+struct WorldQueryFieldInfo {
+ /// Has `#[fetch(ignore)]` or `#[filter_fetch(ignore)]` attribute.
+ is_ignored: bool,
+ /// All field attributes except for `world_query` ones.
+ attrs: Vec<Attribute>,
+}
+
+fn read_world_query_field_info(field: &Field) -> WorldQueryFieldInfo {
+ let is_ignored = field
+ .attrs
+ .iter()
+ .find(|attr| {
+ attr.path
+ .get_ident()
+ .map_or(false, |ident| ident == WORLD_QUERY_ATTRIBUTE_NAME)
+ })
+ .map_or(false, |attr| {
+ let mut is_ignored = false;
+ attr.parse_args_with(|input: ParseStream| {
+ if input
+ .parse::<Option<field_attr_keywords::ignore>>()?
+ .is_some()
+ {
+ is_ignored = true;
+ }
+ Ok(())
+ })
+ .unwrap_or_else(|_| panic!("Invalid `{WORLD_QUERY_ATTRIBUTE_NAME}` attribute format"));
+
+ is_ignored
+ });
+
+ let attrs = field
+ .attrs
+ .iter()
+ .filter(|attr| {
+ attr.path
+ .get_ident()
+ .map_or(true, |ident| ident != WORLD_QUERY_ATTRIBUTE_NAME)
+ })
+ .cloned()
+ .collect();
+
+ WorldQueryFieldInfo { is_ignored, attrs }
+}
diff --git a/azalea-ecs/azalea-ecs-macros/src/lib.rs b/azalea-ecs/azalea-ecs-macros/src/lib.rs
new file mode 100755
index 00000000..09ccb094
--- /dev/null
+++ b/azalea-ecs/azalea-ecs-macros/src/lib.rs
@@ -0,0 +1,523 @@
+//! A fork of bevy_ecs_macros that uses azalea_ecs instead of bevy_ecs.
+
+extern crate proc_macro;
+
+mod component;
+mod fetch;
+pub(crate) mod utils;
+
+use crate::fetch::derive_world_query_impl;
+use proc_macro::TokenStream;
+use proc_macro2::Span;
+use quote::{format_ident, quote};
+use syn::{
+ parse::{Parse, ParseStream},
+ parse_macro_input,
+ punctuated::Punctuated,
+ spanned::Spanned,
+ token::Comma,
+ DeriveInput, Field, GenericParam, Ident, Index, LitInt, Meta, MetaList, NestedMeta, Result,
+ Token, TypeParam,
+};
+use utils::{derive_label, get_named_struct_fields, BevyManifest};
+
+struct AllTuples {
+ macro_ident: Ident,
+ start: usize,
+ end: usize,
+ idents: Vec<Ident>,
+}
+
+impl Parse for AllTuples {
+ fn parse(input: ParseStream) -> Result<Self> {
+ let macro_ident = input.parse::<Ident>()?;
+ input.parse::<Comma>()?;
+ let start = input.parse::<LitInt>()?.base10_parse()?;
+ input.parse::<Comma>()?;
+ let end = input.parse::<LitInt>()?.base10_parse()?;
+ input.parse::<Comma>()?;
+ let mut idents = vec![input.parse::<Ident>()?];
+ while input.parse::<Comma>().is_ok() {
+ idents.push(input.parse::<Ident>()?);
+ }
+
+ Ok(AllTuples {
+ macro_ident,
+ start,
+ end,
+ idents,
+ })
+ }
+}
+
+#[proc_macro]
+pub fn all_tuples(input: TokenStream) -> TokenStream {
+ let input = parse_macro_input!(input as AllTuples);
+ let len = input.end - input.start;
+ let mut ident_tuples = Vec::with_capacity(len);
+ for i in input.start..=input.end {
+ let idents = input
+ .idents
+ .iter()
+ .map(|ident| format_ident!("{}{}", ident, i));
+ if input.idents.len() < 2 {
+ ident_tuples.push(quote! {
+ #(#idents)*
+ });
+ } else {
+ ident_tuples.push(quote! {
+ (#(#idents),*)
+ });
+ }
+ }
+
+ let macro_ident = &input.macro_ident;
+ let invocations = (input.start..=input.end).map(|i| {
+ let ident_tuples = &ident_tuples[..i];
+ quote! {
+ #macro_ident!(#(#ident_tuples),*);
+ }
+ });
+ TokenStream::from(quote! {
+ #(
+ #invocations
+ )*
+ })
+}
+
+enum BundleFieldKind {
+ Component,
+ Ignore,
+}
+
+const BUNDLE_ATTRIBUTE_NAME: &str = "bundle";
+const BUNDLE_ATTRIBUTE_IGNORE_NAME: &str = "ignore";
+
+#[proc_macro_derive(Bundle, attributes(bundle))]
+pub fn derive_bundle(input: TokenStream) -> TokenStream {
+ let ast = parse_macro_input!(input as DeriveInput);
+ let ecs_path = azalea_ecs_path();
+
+ let named_fields = match get_named_struct_fields(&ast.data) {
+ Ok(fields) => &fields.named,
+ Err(e) => return e.into_compile_error().into(),
+ };
+
+ let mut field_kind = Vec::with_capacity(named_fields.len());
+
+ 'field_loop: for field in named_fields.iter() {
+ for attr in &field.attrs {
+ if attr.path.is_ident(BUNDLE_ATTRIBUTE_NAME) {
+ if let Ok(Meta::List(MetaList { nested, .. })) = attr.parse_meta() {
+ if let Some(&NestedMeta::Meta(Meta::Path(ref path))) = nested.first() {
+ if path.is_ident(BUNDLE_ATTRIBUTE_IGNORE_NAME) {
+ field_kind.push(BundleFieldKind::Ignore);
+ continue 'field_loop;
+ }
+
+ return syn::Error::new(
+ path.span(),
+ format!(
+ "Invalid bundle attribute. Use `{BUNDLE_ATTRIBUTE_IGNORE_NAME}`"
+ ),
+ )
+ .into_compile_error()
+ .into();
+ }
+
+ return syn::Error::new(attr.span(), format!("Invalid bundle attribute. Use `#[{BUNDLE_ATTRIBUTE_NAME}({BUNDLE_ATTRIBUTE_IGNORE_NAME})]`")).into_compile_error().into();
+ }
+ }
+ }
+
+ field_kind.push(BundleFieldKind::Component);
+ }
+
+ let field = named_fields
+ .iter()
+ .map(|field| field.ident.as_ref().unwrap())
+ .collect::<Vec<_>>();
+ let field_type = named_fields
+ .iter()
+ .map(|field| &field.ty)
+ .collect::<Vec<_>>();
+
+ let mut field_component_ids = Vec::new();
+ let mut field_get_components = Vec::new();
+ let mut field_from_components = Vec::new();
+ for ((field_type, field_kind), field) in
+ field_type.iter().zip(field_kind.iter()).zip(field.iter())
+ {
+ match field_kind {
+ BundleFieldKind::Component => {
+ field_component_ids.push(quote! {
+ <#field_type as #ecs_path::bundle::BevyBundle>::component_ids(components, storages, &mut *ids);
+ });
+ field_get_components.push(quote! {
+ self.#field.get_components(&mut *func);
+ });
+ field_from_components.push(quote! {
+ #field: <#field_type as #ecs_path::bundle::BevyBundle>::from_components(ctx, &mut *func),
+ });
+ }
+
+ BundleFieldKind::Ignore => {
+ field_from_components.push(quote! {
+ #field: ::std::default::Default::default(),
+ });
+ }
+ }
+ }
+ let generics = ast.generics;
+ let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
+ let struct_name = &ast.ident;
+
+ TokenStream::from(quote! {
+ /// SAFETY: ComponentId is returned in field-definition-order. [from_components] and [get_components] use field-definition-order
+ unsafe impl #impl_generics #ecs_path::bundle::BevyBundle for #struct_name #ty_generics #where_clause {
+ fn component_ids(
+ components: &mut #ecs_path::component::Components,
+ storages: &mut #ecs_path::storage::Storages,
+ ids: &mut impl FnMut(#ecs_path::component::ComponentId)
+ ){
+ #(#field_component_ids)*
+ }
+
+ #[allow(unused_variables, non_snake_case)]
+ unsafe fn from_components<__T, __F>(ctx: &mut __T, func: &mut __F) -> Self
+ where
+ __F: FnMut(&mut __T) -> #ecs_path::ptr::OwningPtr<'_>
+ {
+ Self {
+ #(#field_from_components)*
+ }
+ }
+
+ #[allow(unused_variables)]
+ fn get_components(self, func: &mut impl FnMut(#ecs_path::ptr::OwningPtr<'_>)) {
+ #(#field_get_components)*
+ }
+ }
+ })
+}
+
+fn get_idents(fmt_string: fn(usize) -> String, count: usize) -> Vec<Ident> {
+ (0..count)
+ .map(|i| Ident::new(&fmt_string(i), Span::call_site()))
+ .collect::<Vec<Ident>>()
+}
+
+#[proc_macro]
+pub fn impl_param_set(_input: TokenStream) -> TokenStream {
+ let mut tokens = TokenStream::new();
+ let max_params = 8;
+ let params = get_idents(|i| format!("P{i}"), max_params);
+ let params_fetch = get_idents(|i| format!("PF{i}"), max_params);
+ let metas = get_idents(|i| format!("m{i}"), max_params);
+ let mut param_fn_muts = Vec::new();
+ for (i, param) in params.iter().enumerate() {
+ let fn_name = Ident::new(&format!("p{i}"), Span::call_site());
+ let index = Index::from(i);
+ param_fn_muts.push(quote! {
+ pub fn #fn_name<'a>(&'a mut self) -> <#param::Fetch as SystemParamFetch<'a, 'a>>::Item {
+ // SAFETY: systems run without conflicts with other systems.
+ // Conflicting params in ParamSet are not accessible at the same time
+ // ParamSets are guaranteed to not conflict with other SystemParams
+ unsafe {
+ <#param::Fetch as SystemParamFetch<'a, 'a>>::get_param(&mut self.param_states.#index, &self.system_meta, self.world, self.change_tick)
+ }
+ }
+ });
+ }
+
+ for param_count in 1..=max_params {
+ let param = &params[0..param_count];
+ let param_fetch = &params_fetch[0..param_count];
+ let meta = &metas[0..param_count];
+ let param_fn_mut = &param_fn_muts[0..param_count];
+ tokens.extend(TokenStream::from(quote! {
+ impl<'w, 's, #(#param: SystemParam,)*> SystemParam for ParamSet<'w, 's, (#(#param,)*)>
+ {
+ type Fetch = ParamSetState<(#(#param::Fetch,)*)>;
+ }
+
+ // SAFETY: All parameters are constrained to ReadOnlyFetch, so World is only read
+
+ unsafe impl<#(#param_fetch: for<'w1, 's1> SystemParamFetch<'w1, 's1>,)*> ReadOnlySystemParamFetch for ParamSetState<(#(#param_fetch,)*)>
+ where #(#param_fetch: ReadOnlySystemParamFetch,)*
+ { }
+
+ // SAFETY: Relevant parameter ComponentId and ArchetypeComponentId access is applied to SystemMeta. If any ParamState conflicts
+ // with any prior access, a panic will occur.
+
+ unsafe impl<#(#param_fetch: for<'w1, 's1> SystemParamFetch<'w1, 's1>,)*> SystemParamState for ParamSetState<(#(#param_fetch,)*)>
+ {
+ fn init(world: &mut World, system_meta: &mut SystemMeta) -> Self {
+ #(
+ // Pretend to add each param to the system alone, see if it conflicts
+ let mut #meta = system_meta.clone();
+ #meta.component_access_set.clear();
+ #meta.archetype_component_access.clear();
+ #param_fetch::init(world, &mut #meta);
+ let #param = #param_fetch::init(world, &mut system_meta.clone());
+ )*
+ #(
+ system_meta
+ .component_access_set
+ .extend(#meta.component_access_set);
+ system_meta
+ .archetype_component_access
+ .extend(&#meta.archetype_component_access);
+ )*
+ ParamSetState((#(#param,)*))
+ }
+
+ fn new_archetype(&mut self, archetype: &Archetype, system_meta: &mut SystemMeta) {
+ let (#(#param,)*) = &mut self.0;
+ #(
+ #param.new_archetype(archetype, system_meta);
+ )*
+ }
+
+ fn apply(&mut self, world: &mut World) {
+ self.0.apply(world)
+ }
+ }
+
+
+
+ impl<'w, 's, #(#param_fetch: for<'w1, 's1> SystemParamFetch<'w1, 's1>,)*> SystemParamFetch<'w, 's> for ParamSetState<(#(#param_fetch,)*)>
+ {
+ type Item = ParamSet<'w, 's, (#(<#param_fetch as SystemParamFetch<'w, 's>>::Item,)*)>;
+
+ #[inline]
+ unsafe fn get_param(
+ state: &'s mut Self,
+ system_meta: &SystemMeta,
+ world: &'w World,
+ change_tick: u32,
+ ) -> Self::Item {
+ ParamSet {
+ param_states: &mut state.0,
+ system_meta: system_meta.clone(),
+ world,
+ change_tick,
+ }
+ }
+ }
+
+ impl<'w, 's, #(#param: SystemParam,)*> ParamSet<'w, 's, (#(#param,)*)>
+ {
+
+ #(#param_fn_mut)*
+ }
+ }));
+ }
+
+ tokens
+}
+
+#[derive(Default)]
+struct SystemParamFieldAttributes {
+ pub ignore: bool,
+}
+
+static SYSTEM_PARAM_ATTRIBUTE_NAME: &str = "system_param";
+
+/// Implement `SystemParam` to use a struct as a parameter in a system
+#[proc_macro_derive(SystemParam, attributes(system_param))]
+pub fn derive_system_param(input: TokenStream) -> TokenStream {
+ let ast = parse_macro_input!(input as DeriveInput);
+ let fields = match get_named_struct_fields(&ast.data) {
+ Ok(fields) => &fields.named,
+ Err(e) => return e.into_compile_error().into(),
+ };
+ let path = azalea_ecs_path();
+
+ let field_attributes = fields
+ .iter()
+ .map(|field| {
+ (
+ field,
+ field
+ .attrs
+ .iter()
+ .find(|a| *a.path.get_ident().as_ref().unwrap() == SYSTEM_PARAM_ATTRIBUTE_NAME)
+ .map_or_else(SystemParamFieldAttributes::default, |a| {
+ syn::custom_keyword!(ignore);
+ let mut attributes = SystemParamFieldAttributes::default();
+ a.parse_args_with(|input: ParseStream| {
+ if input.parse::<Option<ignore>>()?.is_some() {
+ attributes.ignore = true;
+ }
+ Ok(())
+ })
+ .expect("Invalid 'system_param' attribute format.");
+
+ attributes
+ }),
+ )
+ })
+ .collect::<Vec<(&Field, SystemParamFieldAttributes)>>();
+ let mut fields = Vec::new();
+ let mut field_indices = Vec::new();
+ let mut field_types = Vec::new();
+ let mut ignored_fields = Vec::new();
+ let mut ignored_field_types = Vec::new();
+ for (i, (field, attrs)) in field_attributes.iter().enumerate() {
+ if attrs.ignore {
+ ignored_fields.push(field.ident.as_ref().unwrap());
+ ignored_field_types.push(&field.ty);
+ } else {
+ fields.push(field.ident.as_ref().unwrap());
+ field_types.push(&field.ty);
+ field_indices.push(Index::from(i));
+ }
+ }
+
+ let generics = ast.generics;
+ let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
+
+ let lifetimeless_generics: Vec<_> = generics
+ .params
+ .iter()
+ .filter(|g| matches!(g, GenericParam::Type(_)))
+ .collect();
+
+ let mut punctuated_generics = Punctuated::<_, Token![,]>::new();
+ punctuated_generics.extend(lifetimeless_generics.iter().map(|g| match g {
+ GenericParam::Type(g) => GenericParam::Type(TypeParam {
+ default: None,
+ ..g.clone()
+ }),
+ _ => unreachable!(),
+ }));
+
+ let mut punctuated_generic_idents = Punctuated::<_, Token![,]>::new();
+ punctuated_generic_idents.extend(lifetimeless_generics.iter().map(|g| match g {
+ GenericParam::Type(g) => &g.ident,
+ _ => unreachable!(),
+ }));
+
+ let struct_name = &ast.ident;
+ let fetch_struct_visibility = &ast.vis;
+
+ TokenStream::from(quote! {
+ // We define the FetchState struct in an anonymous scope to avoid polluting the user namespace.
+ // The struct can still be accessed via SystemParam::Fetch, e.g. EventReaderState can be accessed via
+ // <EventReader<'static, 'static, T> as SystemParam>::Fetch
+ const _: () = {
+ impl #impl_generics #path::system::SystemParam for #struct_name #ty_generics #where_clause {
+ type Fetch = FetchState <(#(<#field_types as #path::system::SystemParam>::Fetch,)*), #punctuated_generic_idents>;
+ }
+
+ #[doc(hidden)]
+ #fetch_struct_visibility struct FetchState <TSystemParamState, #punctuated_generic_idents> {
+ state: TSystemParamState,
+ marker: std::marker::PhantomData<fn()->(#punctuated_generic_idents)>
+ }
+
+ unsafe impl<TSystemParamState: #path::system::SystemParamState, #punctuated_generics> #path::system::SystemParamState for FetchState <TSystemParamState, #punctuated_generic_idents> #where_clause {
+ fn init(world: &mut #path::world::World, system_meta: &mut #path::system::SystemMeta) -> Self {
+ Self {
+ state: TSystemParamState::init(world, system_meta),
+ marker: std::marker::PhantomData,
+ }
+ }
+
+ fn new_archetype(&mut self, archetype: &#path::archetype::Archetype, system_meta: &mut #path::system::SystemMeta) {
+ self.state.new_archetype(archetype, system_meta)
+ }
+
+ fn apply(&mut self, world: &mut #path::world::World) {
+ self.state.apply(world)
+ }
+ }
+
+ impl #impl_generics #path::system::SystemParamFetch<'w, 's> for FetchState <(#(<#field_types as #path::system::SystemParam>::Fetch,)*), #punctuated_generic_idents> #where_clause {
+ type Item = #struct_name #ty_generics;
+ unsafe fn get_param(
+ state: &'s mut Self,
+ system_meta: &#path::system::SystemMeta,
+ world: &'w #path::world::World,
+ change_tick: u32,
+ ) -> Self::Item {
+ #struct_name {
+ #(#fields: <<#field_types as #path::system::SystemParam>::Fetch as #path::system::SystemParamFetch>::get_param(&mut state.state.#field_indices, system_meta, world, change_tick),)*
+ #(#ignored_fields: <#ignored_field_types>::default(),)*
+ }
+ }
+ }
+
+ // Safety: The `ParamState` is `ReadOnlySystemParamFetch`, so this can only read from the `World`
+ unsafe impl<TSystemParamState: #path::system::SystemParamState + #path::system::ReadOnlySystemParamFetch, #punctuated_generics> #path::system::ReadOnlySystemParamFetch for FetchState <TSystemParamState, #punctuated_generic_idents> #where_clause {}
+ };
+ })
+}
+
+/// Implement `WorldQuery` to use a struct as a parameter in a query
+#[proc_macro_derive(WorldQuery, attributes(world_query))]
+pub fn derive_world_query(input: TokenStream) -> TokenStream {
+ let ast = parse_macro_input!(input as DeriveInput);
+ derive_world_query_impl(ast)
+}
+
+/// Generates an impl of the `SystemLabel` trait.
+///
+/// This works only for unit structs, or enums with only unit variants.
+/// You may force a struct or variant to behave as if it were fieldless with
+/// `#[system_label(ignore_fields)]`.
+#[proc_macro_derive(SystemLabel, attributes(system_label))]
+pub fn derive_system_label(input: TokenStream) -> TokenStream {
+ let input = parse_macro_input!(input as DeriveInput);
+ let mut trait_path = azalea_ecs_path();
+ trait_path.segments.push(format_ident!("schedule").into());
+ trait_path
+ .segments
+ .push(format_ident!("SystemLabel").into());
+ derive_label(input, &trait_path, "system_label")
+}
+
+/// Generates an impl of the `StageLabel` trait.
+///
+/// This works only for unit structs, or enums with only unit variants.
+/// You may force a struct or variant to behave as if it were fieldless with
+/// `#[stage_label(ignore_fields)]`.
+#[proc_macro_derive(StageLabel, attributes(stage_label))]
+pub fn derive_stage_label(input: TokenStream) -> TokenStream {
+ let input = parse_macro_input!(input as DeriveInput);
+ let mut trait_path = azalea_ecs_path();
+ trait_path.segments.push(format_ident!("schedule").into());
+ trait_path.segments.push(format_ident!("StageLabel").into());
+ derive_label(input, &trait_path, "stage_label")
+}
+
+/// Generates an impl of the `RunCriteriaLabel` trait.
+///
+/// This works only for unit structs, or enums with only unit variants.
+/// You may force a struct or variant to behave as if it were fieldless with
+/// `#[run_criteria_label(ignore_fields)]`.
+#[proc_macro_derive(RunCriteriaLabel, attributes(run_criteria_label))]
+pub fn derive_run_criteria_label(input: TokenStream) -> TokenStream {
+ let input = parse_macro_input!(input as DeriveInput);
+ let mut trait_path = azalea_ecs_path();
+ trait_path.segments.push(format_ident!("schedule").into());
+ trait_path
+ .segments
+ .push(format_ident!("RunCriteriaLabel").into());
+ derive_label(input, &trait_path, "run_criteria_label")
+}
+
+pub(crate) fn azalea_ecs_path() -> syn::Path {
+ BevyManifest::default().get_path("azalea_ecs")
+}
+
+#[proc_macro_derive(Resource)]
+pub fn derive_resource(input: TokenStream) -> TokenStream {
+ component::derive_resource(input)
+}
+
+#[proc_macro_derive(Component, attributes(component))]
+pub fn derive_component(input: TokenStream) -> TokenStream {
+ component::derive_component(input)
+}
diff --git a/azalea-ecs/azalea-ecs-macros/src/utils/attrs.rs b/azalea-ecs/azalea-ecs-macros/src/utils/attrs.rs
new file mode 100644
index 00000000..05f0712a
--- /dev/null
+++ b/azalea-ecs/azalea-ecs-macros/src/utils/attrs.rs
@@ -0,0 +1,45 @@
+#![allow(dead_code)]
+
+use syn::DeriveInput;
+
+use super::symbol::Symbol;
+
+pub fn parse_attrs(ast: &DeriveInput, attr_name: Symbol) -> syn::Result<Vec<syn::NestedMeta>> {
+ let mut list = Vec::new();
+ for attr in ast.attrs.iter().filter(|a| a.path == attr_name) {
+ match attr.parse_meta()? {
+ syn::Meta::List(meta) => list.extend(meta.nested.into_iter()),
+ other => {
+ return Err(syn::Error::new_spanned(
+ other,
+ format!("expected #[{attr_name}(...)]"),
+ ))
+ }
+ }
+ }
+ Ok(list)
+}
+
+pub fn get_lit_str(attr_name: Symbol, lit: &syn::Lit) -> syn::Result<&syn::LitStr> {
+ if let syn::Lit::Str(lit) = lit {
+ Ok(lit)
+ } else {
+ Err(syn::Error::new_spanned(
+ lit,
+ format!("expected {attr_name} attribute to be a string: `{attr_name} = \"...\"`"),
+ ))
+ }
+}
+
+pub fn get_lit_bool(attr_name: Symbol, lit: &syn::Lit) -> syn::Result<bool> {
+ if let syn::Lit::Bool(lit) = lit {
+ Ok(lit.value())
+ } else {
+ Err(syn::Error::new_spanned(
+ lit,
+ format!(
+ "expected {attr_name} attribute to be a bool value, `true` or `false`: `{attr_name} = ...`"
+ ),
+ ))
+ }
+}
diff --git a/azalea-ecs/azalea-ecs-macros/src/utils/mod.rs b/azalea-ecs/azalea-ecs-macros/src/utils/mod.rs
new file mode 100644
index 00000000..5fbba0e9
--- /dev/null
+++ b/azalea-ecs/azalea-ecs-macros/src/utils/mod.rs
@@ -0,0 +1,224 @@
+#![allow(dead_code)]
+
+extern crate proc_macro;
+
+mod attrs;
+mod shape;
+mod symbol;
+
+pub use attrs::*;
+pub use shape::*;
+pub use symbol::*;
+
+use proc_macro::TokenStream;
+use quote::{quote, quote_spanned};
+use std::{env, path::PathBuf};
+use syn::spanned::Spanned;
+use toml::{map::Map, Value};
+
+pub struct BevyManifest {
+ manifest: Map<String, Value>,
+}
+
+impl Default for BevyManifest {
+ fn default() -> Self {
+ Self {
+ manifest: env::var_os("CARGO_MANIFEST_DIR")
+ .map(PathBuf::from)
+ .map(|mut path| {
+ path.push("Cargo.toml");
+ let manifest = std::fs::read_to_string(path).unwrap();
+ toml::from_str(&manifest).unwrap()
+ })
+ .unwrap(),
+ }
+ }
+}
+
+impl BevyManifest {
+ pub fn maybe_get_path(&self, name: &str) -> Option<syn::Path> {
+ const AZALEA: &str = "azalea";
+ const BEVY_ECS: &str = "bevy_ecs";
+ const BEVY: &str = "bevy";
+
+ fn dep_package(dep: &Value) -> Option<&str> {
+ if dep.as_str().is_some() {
+ None
+ } else {
+ dep.as_table()
+ .unwrap()
+ .get("package")
+ .map(|name| name.as_str().unwrap())
+ }
+ }
+
+ let find_in_deps = |deps: &Map<String, Value>| -> Option<syn::Path> {
+ let package = if let Some(dep) = deps.get(name) {
+ return Some(Self::parse_str(dep_package(dep).unwrap_or(name)));
+ } else if let Some(dep) = deps.get(AZALEA) {
+ dep_package(dep).unwrap_or(AZALEA)
+ } else if let Some(dep) = deps.get(BEVY_ECS) {
+ dep_package(dep).unwrap_or(BEVY_ECS)
+ } else if let Some(dep) = deps.get(BEVY) {
+ dep_package(dep).unwrap_or(BEVY)
+ } else {
+ return None;
+ };
+
+ let mut path = Self::parse_str::<syn::Path>(package);
+ if let Some(module) = name.strip_prefix("azalea_") {
+ path.segments.push(Self::parse_str(module));
+ }
+ Some(path)
+ };
+
+ let deps = self
+ .manifest
+ .get("dependencies")
+ .map(|deps| deps.as_table().unwrap());
+ let deps_dev = self
+ .manifest
+ .get("dev-dependencies")
+ .map(|deps| deps.as_table().unwrap());
+
+ deps.and_then(find_in_deps)
+ .or_else(|| deps_dev.and_then(find_in_deps))
+ }
+
+ /// Returns the path for the crate with the given name.
+ ///
+ /// This is a convenience method for constructing a [manifest] and
+ /// calling the [`get_path`] method.
+ ///
+ /// This method should only be used where you just need the path and can't
+ /// cache the [manifest]. If caching is possible, it's recommended to create
+ /// the [manifest] yourself and use the [`get_path`] method.
+ ///
+ /// [`get_path`]: Self::get_path
+ /// [manifest]: Self
+ pub fn get_path_direct(name: &str) -> syn::Path {
+ Self::default().get_path(name)
+ }
+
+ pub fn get_path(&self, name: &str) -> syn::Path {
+ self.maybe_get_path(name)
+ .unwrap_or_else(|| Self::parse_str(name))
+ }
+
+ pub fn parse_str<T: syn::parse::Parse>(path: &str) -> T {
+ syn::parse(path.parse::<TokenStream>().unwrap()).unwrap()
+ }
+}
+
+/// Derive a label trait
+///
+/// # Args
+///
+/// - `input`: The [`syn::DeriveInput`] for struct that is deriving the label
+/// trait
+/// - `trait_path`: The path [`syn::Path`] to the label trait
+pub fn derive_label(
+ input: syn::DeriveInput,
+ trait_path: &syn::Path,
+ attr_name: &str,
+) -> TokenStream {
+ // return true if the variant specified is an `ignore_fields` attribute
+ fn is_ignore(attr: &syn::Attribute, attr_name: &str) -> bool {
+ if attr.path.get_ident().as_ref().unwrap() != &attr_name {
+ return false;
+ }
+
+ syn::custom_keyword!(ignore_fields);
+ attr.parse_args_with(|input: syn::parse::ParseStream| {
+ let ignore = input.parse::<Option<ignore_fields>>()?.is_some();
+ Ok(ignore)
+ })
+ .unwrap()
+ }
+
+ let ident = input.ident.clone();
+
+ let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
+ let mut where_clause = where_clause.cloned().unwrap_or_else(|| syn::WhereClause {
+ where_token: Default::default(),
+ predicates: Default::default(),
+ });
+ where_clause
+ .predicates
+ .push(syn::parse2(quote! { Self: 'static }).unwrap());
+
+ let as_str = match input.data {
+ syn::Data::Struct(d) => {
+ // see if the user tried to ignore fields incorrectly
+ if let Some(attr) = d
+ .fields
+ .iter()
+ .flat_map(|f| &f.attrs)
+ .find(|a| is_ignore(a, attr_name))
+ {
+ let err_msg = format!("`#[{attr_name}(ignore_fields)]` cannot be applied to fields individually: add it to the struct declaration");
+ return quote_spanned! {
+ attr.span() => compile_error!(#err_msg);
+ }
+ .into();
+ }
+ // Structs must either be fieldless, or explicitly ignore the fields.
+ let ignore_fields = input.attrs.iter().any(|a| is_ignore(a, attr_name));
+ if matches!(d.fields, syn::Fields::Unit) || ignore_fields {
+ let lit = ident.to_string();
+ quote! { #lit }
+ } else {
+ let err_msg = format!("Labels cannot contain data, unless explicitly ignored with `#[{attr_name}(ignore_fields)]`");
+ return quote_spanned! {
+ d.fields.span() => compile_error!(#err_msg);
+ }
+ .into();
+ }
+ }
+ syn::Data::Enum(d) => {
+ // check if the user put #[label(ignore_fields)] in the wrong place
+ if let Some(attr) = input.attrs.iter().find(|a| is_ignore(a, attr_name)) {
+ let err_msg = format!("`#[{attr_name}(ignore_fields)]` can only be applied to enum variants or struct declarations");
+ return quote_spanned! {
+ attr.span() => compile_error!(#err_msg);
+ }
+ .into();
+ }
+ let arms = d.variants.iter().map(|v| {
+ // Variants must either be fieldless, or explicitly ignore the fields.
+ let ignore_fields = v.attrs.iter().any(|a| is_ignore(a, attr_name));
+ if matches!(v.fields, syn::Fields::Unit) | ignore_fields {
+ let mut path = syn::Path::from(ident.clone());
+ path.segments.push(v.ident.clone().into());
+ let lit = format!("{ident}::{}", v.ident.clone());
+ quote! { #path { .. } => #lit }
+ } else {
+ let err_msg = format!("Label variants cannot contain data, unless explicitly ignored with `#[{attr_name}(ignore_fields)]`");
+ quote_spanned! {
+ v.fields.span() => _ => { compile_error!(#err_msg); }
+ }
+ }
+ });
+ quote! {
+ match self {
+ #(#arms),*
+ }
+ }
+ }
+ syn::Data::Union(_) => {
+ return quote_spanned! {
+ input.span() => compile_error!("Unions cannot be used as labels.");
+ }
+ .into();
+ }
+ };
+
+ (quote! {
+ impl #impl_generics #trait_path for #ident #ty_generics #where_clause {
+ fn as_str(&self) -> &'static str {
+ #as_str
+ }
+ }
+ })
+ .into()
+}
diff --git a/azalea-ecs/azalea-ecs-macros/src/utils/shape.rs b/azalea-ecs/azalea-ecs-macros/src/utils/shape.rs
new file mode 100644
index 00000000..98230749
--- /dev/null
+++ b/azalea-ecs/azalea-ecs-macros/src/utils/shape.rs
@@ -0,0 +1,21 @@
+use proc_macro::Span;
+use syn::{Data, DataStruct, Error, Fields, FieldsNamed};
+
+/// Get the fields of a data structure if that structure is a struct with named
+/// fields; otherwise, return a compile error that points to the site of the
+/// macro invocation.
+pub fn get_named_struct_fields(data: &syn::Data) -> syn::Result<&FieldsNamed> {
+ match data {
+ Data::Struct(DataStruct {
+ fields: Fields::Named(fields),
+ ..
+ }) => Ok(fields),
+ _ => Err(Error::new(
+ // This deliberately points to the call site rather than the structure
+ // body; marking the entire body as the source of the error makes it
+ // impossible to figure out which `derive` has a problem.
+ Span::call_site().into(),
+ "Only structs with named fields are supported",
+ )),
+ }
+}
diff --git a/azalea-ecs/azalea-ecs-macros/src/utils/symbol.rs b/azalea-ecs/azalea-ecs-macros/src/utils/symbol.rs
new file mode 100644
index 00000000..dc639f4d
--- /dev/null
+++ b/azalea-ecs/azalea-ecs-macros/src/utils/symbol.rs
@@ -0,0 +1,35 @@
+use std::fmt::{self, Display};
+use syn::{Ident, Path};
+
+#[derive(Copy, Clone)]
+pub struct Symbol(pub &'static str);
+
+impl PartialEq<Symbol> for Ident {
+ fn eq(&self, word: &Symbol) -> bool {
+ self == word.0
+ }
+}
+
+impl<'a> PartialEq<Symbol> for &'a Ident {
+ fn eq(&self, word: &Symbol) -> bool {
+ *self == word.0
+ }
+}
+
+impl PartialEq<Symbol> for Path {
+ fn eq(&self, word: &Symbol) -> bool {
+ self.is_ident(word.0)
+ }
+}
+
+impl<'a> PartialEq<Symbol> for &'a Path {
+ fn eq(&self, word: &Symbol) -> bool {
+ self.is_ident(word.0)
+ }
+}
+
+impl Display for Symbol {
+ fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+ formatter.write_str(self.0)
+ }
+}
diff --git a/azalea-ecs/src/lib.rs b/azalea-ecs/src/lib.rs
new file mode 100644
index 00000000..44579c5d
--- /dev/null
+++ b/azalea-ecs/src/lib.rs
@@ -0,0 +1,144 @@
+#![feature(trait_alias)]
+
+//! Re-export important parts of `bevy_ecs` and `bevy_app` and make them more
+//! compatible with Azalea.
+//!
+//! This is completely compatible with `bevy_ecs`, so it won't cause issues if
+//! you use plugins meant for Bevy.
+//!
+//! Changes:
+//! - Add [`TickPlugin`], [`TickStage`] and [`AppTickExt`]
+//! - Change the macros to use azalea/azalea_ecs instead of bevy/bevy_ecs
+//! - Rename bevy_ecs::world::World to azalea_ecs::ecs::Ecs
+//! - Re-export `bevy_app` in the `app` module.
+
+use std::time::{Duration, Instant};
+
+pub mod ecs {
+ pub use bevy_ecs::world::World as Ecs;
+ pub use bevy_ecs::world::{EntityMut, EntityRef, Mut};
+}
+pub mod component {
+ pub use azalea_ecs_macros::Component;
+ pub use bevy_ecs::component::{ComponentId, ComponentStorage, Components, TableStorage};
+
+ // we do this because re-exporting Component would re-export the macro as well,
+ // which is bad (since we have our own Component macro)
+ // instead, we have to do this so Component is a trait alias and the original
+ // impl-able trait is still available as BevyComponent
+ pub trait Component = bevy_ecs::component::Component;
+ pub use bevy_ecs::component::Component as BevyComponent;
+}
+pub mod bundle {
+ pub use azalea_ecs_macros::Bundle;
+ pub trait Bundle = bevy_ecs::bundle::Bundle;
+ pub use bevy_ecs::bundle::Bundle as BevyBundle;
+}
+pub mod system {
+ pub use azalea_ecs_macros::Resource;
+ pub use bevy_ecs::system::{
+ Command, Commands, EntityCommands, Query, Res, ResMut, SystemState,
+ };
+ pub trait Resource = bevy_ecs::system::Resource;
+ pub use bevy_ecs::system::Resource as BevyResource;
+}
+pub use bevy_app as app;
+pub use bevy_ecs::{entity, event, ptr, query, schedule, storage};
+
+use app::{App, CoreStage, Plugin};
+use bevy_ecs::schedule::*;
+use ecs::Ecs;
+
+pub struct TickPlugin {
+ /// How often a tick should happen. 50 milliseconds by default. Set to 0 to
+ /// tick every update.
+ pub tick_interval: Duration,
+}
+impl Plugin for TickPlugin {
+ fn build(&self, app: &mut App) {
+ app.add_stage_before(
+ CoreStage::Update,
+ TickLabel,
+ TickStage {
+ interval: self.tick_interval,
+ next_tick: Instant::now(),
+ stage: Box::new(SystemStage::parallel()),
+ },
+ );
+ }
+}
+impl Default for TickPlugin {
+ fn default() -> Self {
+ Self {
+ tick_interval: Duration::from_millis(50),
+ }
+ }
+}
+
+#[derive(StageLabel)]
+struct TickLabel;
+
+/// A [`Stage`] that runs every 50 milliseconds.
+pub struct TickStage {
+ pub interval: Duration,
+ pub next_tick: Instant,
+ stage: Box<dyn Stage>,
+}
+
+impl Stage for TickStage {
+ fn run(&mut self, ecs: &mut Ecs) {
+ // if the interval is 0, that means it runs every tick
+ if self.interval.is_zero() {
+ self.stage.run(ecs);
+ return;
+ }
+ // keep calling run until it's caught up
+ // TODO: Minecraft bursts up to 10 ticks and then skips, we should too (but
+ // check the source so we do it right)
+ while Instant::now() > self.next_tick {
+ self.next_tick += self.interval;
+ self.stage.run(ecs);
+ }
+ }
+}
+
+pub trait AppTickExt {
+ fn add_tick_system_set(&mut self, system_set: SystemSet) -> &mut App;
+ fn add_tick_system<Params>(&mut self, system: impl IntoSystemDescriptor<Params>) -> &mut App;
+}
+
+impl AppTickExt for App {
+ /// Adds a set of ECS systems that will run every 50 milliseconds.
+ ///
+ /// Note that you should NOT have `EventReader`s in tick systems, as this
+ /// will make them sometimes be missed.
+ fn add_tick_system_set(&mut self, system_set: SystemSet) -> &mut App {
+ let tick_stage = self
+ .schedule
+ .get_stage_mut::<TickStage>(TickLabel)
+ .expect("Tick Stage not found");
+ let stage = tick_stage
+ .stage
+ .downcast_mut::<SystemStage>()
+ .expect("Fixed Timestep sub-stage is not a SystemStage");
+ stage.add_system_set(system_set);
+ self
+ }
+
+ /// Adds a new ECS system that will run every 50 milliseconds.
+ ///
+ /// Note that you should NOT have `EventReader`s in tick systems, as this
+ /// will make them sometimes be missed.
+ fn add_tick_system<Params>(&mut self, system: impl IntoSystemDescriptor<Params>) -> &mut App {
+ let tick_stage = self
+ .schedule
+ .get_stage_mut::<TickStage>(TickLabel)
+ .expect("Tick Stage not found");
+ let stage = tick_stage
+ .stage
+ .downcast_mut::<SystemStage>()
+ .expect("Fixed Timestep sub-stage is not a SystemStage");
+ stage.add_system(system);
+ self
+ }
+}
diff --git a/azalea-nbt/src/encode.rs b/azalea-nbt/src/encode.rs
index a4df15c1..36109f35 100755
--- a/azalea-nbt/src/encode.rs
+++ b/azalea-nbt/src/encode.rs
@@ -26,62 +26,62 @@ fn write_compound(
Tag::Byte(value) => {
writer.write_u8(1)?;
write_string(writer, key)?;
- writer.write_i8(*value)?
+ writer.write_i8(*value)?;
}
Tag::Short(value) => {
writer.write_u8(2)?;
write_string(writer, key)?;
- writer.write_i16::<BE>(*value)?
+ writer.write_i16::<BE>(*value)?;
}
Tag::Int(value) => {
writer.write_u8(3)?;
write_string(writer, key)?;
- writer.write_i32::<BE>(*value)?
+ writer.write_i32::<BE>(*value)?;
}
Tag::Long(value) => {
writer.write_u8(4)?;
write_string(writer, key)?;
- writer.write_i64::<BE>(*value)?
+ writer.write_i64::<BE>(*value)?;
}
Tag::Float(value) => {
writer.write_u8(5)?;
write_string(writer, key)?;
- writer.write_f32::<BE>(*value)?
+ writer.write_f32::<BE>(*value)?;
}
Tag::Double(value) => {
writer.write_u8(6)?;
write_string(writer, key)?;
- writer.write_f64::<BE>(*value)?
+ writer.write_f64::<BE>(*value)?;
}
Tag::ByteArray(value) => {
writer.write_u8(7)?;
write_string(writer, key)?;
- write_bytearray(writer, value)?
+ write_bytearray(writer, value)?;
}
Tag::String(value) => {
writer.write_u8(8)?;
write_string(writer, key)?;
- write_string(writer, value)?
+ write_string(writer, value)?;
}
Tag::List(value) => {
writer.write_u8(9)?;
write_string(writer, key)?;
- write_list(writer, value)?
+ write_list(writer, value)?;
}
Tag::Compound(value) => {
writer.write_u8(10)?;
write_string(writer, key)?;
- write_compound(writer, value, true)?
+ write_compound(writer, value, true)?;
}
Tag::IntArray(value) => {
writer.write_u8(11)?;
write_string(writer, key)?;
- write_intarray(writer, value)?
+ write_intarray(writer, value)?;
}
Tag::LongArray(value) => {
writer.write_u8(12)?;
write_string(writer, key)?;
- write_longarray(writer, value)?
+ write_longarray(writer, value)?;
}
}
}
diff --git a/azalea-nbt/src/tag.rs b/azalea-nbt/src/tag.rs
index 65b152fa..a18a42b1 100755
--- a/azalea-nbt/src/tag.rs
+++ b/azalea-nbt/src/tag.rs
@@ -2,10 +2,11 @@ use ahash::AHashMap;
use serde::{Deserialize, Serialize};
/// An NBT value.
-#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
+#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, Default)]
#[serde(untagged)]
pub enum Tag {
- End, // 0
+ #[default]
+ End, // 0
Byte(i8), // 1
Short(i16), // 2
Int(i32), // 3
@@ -20,12 +21,6 @@ pub enum Tag {
LongArray(Vec<i64>), // 12
}
-impl Default for Tag {
- fn default() -> Self {
- Tag::End
- }
-}
-
impl Tag {
/// Get the numerical ID of the tag type.
#[inline]
diff --git a/azalea-physics/Cargo.toml b/azalea-physics/Cargo.toml
index eb8b9dfc..e0fc7f96 100644
--- a/azalea-physics/Cargo.toml
+++ b/azalea-physics/Cargo.toml
@@ -9,11 +9,14 @@ version = "0.5.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.5.0" }
-azalea-core = {path = "../azalea-core", version = "^0.5.0" }
-azalea-world = {path = "../azalea-world", version = "^0.5.0" }
+azalea-block = { path = "../azalea-block", version = "^0.5.0" }
+azalea-core = { path = "../azalea-core", version = "^0.5.0" }
+azalea-world = { path = "../azalea-world", version = "^0.5.0" }
+azalea-registry = { path = "../azalea-registry", version = "^0.5.0" }
+iyes_loopless = "0.9.1"
once_cell = "1.16.0"
parking_lot = "^0.12.1"
+azalea-ecs = { version = "0.5.0", path = "../azalea-ecs" }
[dev-dependencies]
uuid = "^1.1.2"
diff --git a/azalea-physics/src/collision/discrete_voxel_shape.rs b/azalea-physics/src/collision/discrete_voxel_shape.rs
index 51d45316..4a329398 100755
--- a/azalea-physics/src/collision/discrete_voxel_shape.rs
+++ b/azalea-physics/src/collision/discrete_voxel_shape.rs
@@ -64,7 +64,7 @@ impl DiscreteVoxelShape {
}
pub fn for_all_boxes(&self, consumer: impl IntLineConsumer, swap: bool) {
- BitSetDiscreteVoxelShape::for_all_boxes(self, consumer, swap)
+ BitSetDiscreteVoxelShape::for_all_boxes(self, consumer, swap);
}
}
diff --git a/azalea-physics/src/collision/mod.rs b/azalea-physics/src/collision/mod.rs
index 458303c5..7d934020 100644
--- a/azalea-physics/src/collision/mod.rs
+++ b/azalea-physics/src/collision/mod.rs
@@ -5,13 +5,15 @@ mod shape;
mod world_collisions;
use azalea_core::{Axis, Vec3, AABB, EPSILON};
-use azalea_world::entity::{Entity, EntityData};
-use azalea_world::{MoveEntityError, WeakWorld};
+use azalea_world::{
+ entity::{self},
+ MoveEntityError, World,
+};
pub use blocks::BlockWithShape;
pub use discrete_voxel_shape::*;
pub use shape::*;
-use std::ops::Deref;
-use world_collisions::CollisionGetter;
+
+use self::world_collisions::get_block_collisions;
pub enum MoverType {
Own,
@@ -21,192 +23,170 @@ pub enum MoverType {
Shulker,
}
-pub trait HasCollision {
- fn collide(&self, movement: &Vec3, entity: &EntityData) -> Vec3;
-}
+// private Vec3 collide(Vec3 var1) {
+// AABB var2 = this.getBoundingBox();
+// List var3 = this.level.getEntityCollisions(this,
+// var2.expandTowards(var1)); Vec3 var4 = var1.lengthSqr() == 0.0D ?
+// var1 : collideBoundingBox(this, var1, var2, this.level, var3);
+// boolean var5 = var1.x != var4.x;
+// boolean var6 = var1.y != var4.y;
+// boolean var7 = var1.z != var4.z;
+// boolean var8 = this.onGround || var6 && var1.y < 0.0D;
+// if (this.maxUpStep > 0.0F && var8 && (var5 || var7)) {
+// Vec3 var9 = collideBoundingBox(this, new Vec3(var1.x,
+// (double)this.maxUpStep, var1.z), var2, this.level, var3); Vec3
+// var10 = collideBoundingBox(this, new Vec3(0.0D, (double)this.maxUpStep,
+// 0.0D), var2.expandTowards(var1.x, 0.0D, var1.z), this.level, var3);
+// if (var10.y < (double)this.maxUpStep) {
+// Vec3 var11 = collideBoundingBox(this, new Vec3(var1.x, 0.0D,
+// var1.z), var2.move(var10), this.level, var3).add(var10); if
+// (var11.horizontalDistanceSqr() > var9.horizontalDistanceSqr()) {
+// var9 = var11;
+// }
+// }
+
+// if (var9.horizontalDistanceSqr() > var4.horizontalDistanceSqr()) {
+// return var9.add(collideBoundingBox(this, new Vec3(0.0D, -var9.y +
+// var1.y, 0.0D), var2.move(var9), this.level, var3)); }
+// }
+
+// return var4;
+// }
+fn collide(movement: &Vec3, world: &World, physics: &entity::Physics) -> Vec3 {
+ let entity_bounding_box = physics.bounding_box;
+ // TODO: get_entity_collisions
+ // let entity_collisions = world.get_entity_collisions(self,
+ // entity_bounding_box.expand_towards(movement));
+ let entity_collisions = Vec::new();
+ if movement.length_sqr() == 0.0 {
+ *movement
+ } else {
+ collide_bounding_box(movement, &entity_bounding_box, world, entity_collisions)
+ }
-pub trait MovableEntity {
- fn move_colliding(
- &mut self,
- mover_type: &MoverType,
- movement: &Vec3,
- ) -> Result<(), MoveEntityError>;
+ // TODO: stepping (for stairs and stuff)
+
+ // collided_movement
}
-impl<D: Deref<Target = WeakWorld>> HasCollision for D {
- // private Vec3 collide(Vec3 var1) {
- // AABB var2 = this.getBoundingBox();
- // List var3 = this.level.getEntityCollisions(this,
- // var2.expandTowards(var1)); Vec3 var4 = var1.lengthSqr() == 0.0D ?
- // var1 : collideBoundingBox(this, var1, var2, this.level, var3);
- // boolean var5 = var1.x != var4.x;
- // boolean var6 = var1.y != var4.y;
- // boolean var7 = var1.z != var4.z;
- // boolean var8 = this.onGround || var6 && var1.y < 0.0D;
- // if (this.maxUpStep > 0.0F && var8 && (var5 || var7)) {
- // Vec3 var9 = collideBoundingBox(this, new Vec3(var1.x,
- // (double)this.maxUpStep, var1.z), var2, this.level, var3); Vec3
- // var10 = collideBoundingBox(this, new Vec3(0.0D, (double)this.maxUpStep,
- // 0.0D), var2.expandTowards(var1.x, 0.0D, var1.z), this.level, var3);
- // if (var10.y < (double)this.maxUpStep) {
- // Vec3 var11 = collideBoundingBox(this, new Vec3(var1.x, 0.0D,
- // var1.z), var2.move(var10), this.level, var3).add(var10); if
- // (var11.horizontalDistanceSqr() > var9.horizontalDistanceSqr()) {
- // var9 = var11;
- // }
- // }
-
- // if (var9.horizontalDistanceSqr() > var4.horizontalDistanceSqr()) {
- // return var9.add(collideBoundingBox(this, new Vec3(0.0D, -var9.y +
- // var1.y, 0.0D), var2.move(var9), this.level, var3)); }
+/// Move an entity by a given delta, checking for collisions.
+pub fn move_colliding(
+ _mover_type: &MoverType,
+ movement: &Vec3,
+ world: &World,
+ position: &mut entity::Position,
+ physics: &mut entity::Physics,
+) -> Result<(), MoveEntityError> {
+ // TODO: do all these
+
+ // if self.no_physics {
+ // return;
+ // };
+
+ // if (var1 == MoverType.PISTON) {
+ // var2 = this.limitPistonMovement(var2);
+ // if (var2.equals(Vec3.ZERO)) {
+ // return;
// }
+ // }
- // return var4;
+ // if (this.stuckSpeedMultiplier.lengthSqr() > 1.0E-7D) {
+ // var2 = var2.multiply(this.stuckSpeedMultiplier);
+ // this.stuckSpeedMultiplier = Vec3.ZERO;
+ // this.setDeltaMovement(Vec3.ZERO);
// }
- fn collide(&self, movement: &Vec3, entity: &EntityData) -> Vec3 {
- let entity_bounding_box = entity.bounding_box;
- // TODO: get_entity_collisions
- // let entity_collisions = world.get_entity_collisions(self,
- // entity_bounding_box.expand_towards(movement));
- let entity_collisions = Vec::new();
- if movement.length_sqr() == 0.0 {
- *movement
- } else {
- collide_bounding_box(
- Some(entity),
- movement,
- &entity_bounding_box,
- self,
- entity_collisions,
- )
- }
- // TODO: stepping (for stairs and stuff)
+ // movement = this.maybeBackOffFromEdge(movement, moverType);
- // collided_movement
- }
-}
+ let collide_result = collide(movement, world, physics);
-impl<D: Deref<Target = WeakWorld>> MovableEntity for Entity<'_, D> {
- /// Move an entity by a given delta, checking for collisions.
- fn move_colliding(
- &mut self,
- _mover_type: &MoverType,
- movement: &Vec3,
- ) -> Result<(), MoveEntityError> {
- // TODO: do all these
-
- // if self.no_physics {
- // return;
- // };
-
- // if (var1 == MoverType.PISTON) {
- // var2 = this.limitPistonMovement(var2);
- // if (var2.equals(Vec3.ZERO)) {
- // return;
- // }
- // }
-
- // if (this.stuckSpeedMultiplier.lengthSqr() > 1.0E-7D) {
- // var2 = var2.multiply(this.stuckSpeedMultiplier);
- // this.stuckSpeedMultiplier = Vec3.ZERO;
- // this.setDeltaMovement(Vec3.ZERO);
- // }
-
- // movement = this.maybeBackOffFromEdge(movement, moverType);
-
- let collide_result = { self.world.collide(movement, self) };
-
- let move_distance = collide_result.length_sqr();
-
- if move_distance > EPSILON {
- // TODO: fall damage
-
- let new_pos = {
- let entity_pos = self.pos();
- Vec3 {
- x: entity_pos.x + collide_result.x,
- y: entity_pos.y + collide_result.y,
- z: entity_pos.z + collide_result.z,
- }
- };
-
- self.world.set_entity_pos(self.id, new_pos)?;
- }
+ let move_distance = collide_result.length_sqr();
+
+ if move_distance > EPSILON {
+ // TODO: fall damage
+
+ let new_pos = {
+ Vec3 {
+ x: position.x + collide_result.x,
+ y: position.y + collide_result.y,
+ z: position.z + collide_result.z,
+ }
+ };
- let x_collision = movement.x != collide_result.x;
- let z_collision = movement.z != collide_result.z;
- let horizontal_collision = x_collision || z_collision;
- let vertical_collision = movement.y != collide_result.y;
- let on_ground = vertical_collision && movement.y < 0.;
- self.on_ground = on_ground;
+ **position = new_pos;
+ }
- // TODO: minecraft checks for a "minor" horizontal collision here
+ let x_collision = movement.x != collide_result.x;
+ let z_collision = movement.z != collide_result.z;
+ let horizontal_collision = x_collision || z_collision;
+ let vertical_collision = movement.y != collide_result.y;
+ let on_ground = vertical_collision && movement.y < 0.;
+ physics.on_ground = on_ground;
- let _block_pos_below = self.on_pos_legacy();
- // let _block_state_below = self
- // .world
- // .get_block_state(&block_pos_below)
- // .expect("Couldn't get block state below");
+ // TODO: minecraft checks for a "minor" horizontal collision here
- // self.check_fall_damage(collide_result.y, on_ground, block_state_below,
- // block_pos_below);
+ let _block_pos_below = entity::on_pos_legacy(&world.chunks, position);
+ // let _block_state_below = self
+ // .world
+ // .get_block_state(&block_pos_below)
+ // .expect("Couldn't get block state below");
- // if self.isRemoved() { return; }
+ // self.check_fall_damage(collide_result.y, on_ground, block_state_below,
+ // block_pos_below);
- if horizontal_collision {
- let delta_movement = &self.delta;
- self.delta = Vec3 {
- x: if x_collision { 0. } else { delta_movement.x },
- y: delta_movement.y,
- z: if z_collision { 0. } else { delta_movement.z },
- }
- }
+ // if self.isRemoved() { return; }
- if vertical_collision {
- // blockBelow.updateEntityAfterFallOn(this.level, this);
- // the default implementation of updateEntityAfterFallOn sets the y movement to
- // 0
- self.delta.y = 0.;
+ if horizontal_collision {
+ let delta_movement = &physics.delta;
+ physics.delta = Vec3 {
+ x: if x_collision { 0. } else { delta_movement.x },
+ y: delta_movement.y,
+ z: if z_collision { 0. } else { delta_movement.z },
}
+ }
- if on_ground {
- // blockBelow.stepOn(this.level, blockPosBelow, blockStateBelow,
- // this);
- }
+ if vertical_collision {
+ // blockBelow.updateEntityAfterFallOn(this.level, this);
+ // the default implementation of updateEntityAfterFallOn sets the y movement to
+ // 0
+ physics.delta.y = 0.;
+ }
+
+ if on_ground {
+ // blockBelow.stepOn(this.level, blockPosBelow, blockStateBelow,
+ // this);
+ }
- // sounds
+ // sounds
- // this.tryCheckInsideBlocks();
+ // this.tryCheckInsideBlocks();
- // float var25 = this.getBlockSpeedFactor();
- // this.setDeltaMovement(this.getDeltaMovement().multiply((double)var25, 1.0D,
- // (double)var25)); if (this.level.getBlockStatesIfLoaded(this.
- // getBoundingBox().deflate(1.0E-6D)).noneMatch((var0) -> {
- // return var0.is(BlockTags.FIRE) || var0.is(Blocks.LAVA);
- // })) {
- // if (this.remainingFireTicks <= 0) {
- // this.setRemainingFireTicks(-this.getFireImmuneTicks());
- // }
+ // float var25 = this.getBlockSpeedFactor();
+ // this.setDeltaMovement(this.getDeltaMovement().multiply((double)var25, 1.0D,
+ // (double)var25)); if (this.level.getBlockStatesIfLoaded(this.
+ // getBoundingBox().deflate(1.0E-6D)).noneMatch((var0) -> {
+ // return var0.is(BlockTags.FIRE) || var0.is(Blocks.LAVA);
+ // })) {
+ // if (this.remainingFireTicks <= 0) {
+ // this.setRemainingFireTicks(-this.getFireImmuneTicks());
+ // }
- // if (this.wasOnFire && (this.isInPowderSnow ||
- // this.isInWaterRainOrBubble())) { this.
- // playEntityOnFireExtinguishedSound(); }
- // }
+ // if (this.wasOnFire && (this.isInPowderSnow ||
+ // this.isInWaterRainOrBubble())) { this.
+ // playEntityOnFireExtinguishedSound(); }
+ // }
- // if (this.isOnFire() && (this.isInPowderSnow || this.isInWaterRainOrBubble()))
- // { this.setRemainingFireTicks(-this.getFireImmuneTicks());
- // }
+ // if (this.isOnFire() && (this.isInPowderSnow || this.isInWaterRainOrBubble()))
+ // { this.setRemainingFireTicks(-this.getFireImmuneTicks());
+ // }
- Ok(())
- }
+ Ok(())
}
fn collide_bounding_box(
- entity: Option<&EntityData>,
movement: &Vec3,
entity_bounding_box: &AABB,
- world: &WeakWorld,
+ world: &World,
entity_collisions: Vec<VoxelShape>,
) -> Vec3 {
let mut collision_boxes: Vec<VoxelShape> = Vec::with_capacity(entity_collisions.len() + 1);
@@ -218,7 +198,7 @@ fn collide_bounding_box(
// TODO: world border
let block_collisions =
- world.get_block_collisions(entity, entity_bounding_box.expand_towards(movement));
+ get_block_collisions(world, entity_bounding_box.expand_towards(movement));
let block_collisions = block_collisions.collect::<Vec<_>>();
collision_boxes.extend(block_collisions);
collide_with_shapes(movement, *entity_bounding_box, &collision_boxes)
diff --git a/azalea-physics/src/collision/shape.rs b/azalea-physics/src/collision/shape.rs
index bb2ed2e5..cc184591 100755
--- a/azalea-physics/src/collision/shape.rs
+++ b/azalea-physics/src/collision/shape.rs
@@ -539,7 +539,7 @@ impl VoxelShape {
x_coords[var7 as usize],
y_coords[var8 as usize],
z_coords[var9 as usize],
- )
+ );
},
true,
);
diff --git a/azalea-physics/src/collision/world_collisions.rs b/azalea-physics/src/collision/world_collisions.rs
index 9e238bf9..c7b2a91b 100644
--- a/azalea-physics/src/collision/world_collisions.rs
+++ b/azalea-physics/src/collision/world_collisions.rs
@@ -1,34 +1,17 @@
+use super::Shapes;
use crate::collision::{BlockWithShape, VoxelShape, AABB};
use azalea_block::BlockState;
use azalea_core::{ChunkPos, ChunkSectionPos, Cursor3d, CursorIterationType, EPSILON};
-use azalea_world::entity::EntityData;
-use azalea_world::{Chunk, WeakWorld};
+use azalea_world::{Chunk, World};
use parking_lot::RwLock;
use std::sync::Arc;
-use super::Shapes;
-
-pub trait CollisionGetter {
- fn get_block_collisions<'a>(
- &'a self,
- entity: Option<&EntityData>,
- aabb: AABB,
- ) -> BlockCollisions<'a>;
-}
-
-impl CollisionGetter for WeakWorld {
- fn get_block_collisions<'a>(
- &'a self,
- entity: Option<&EntityData>,
- aabb: AABB,
- ) -> BlockCollisions<'a> {
- BlockCollisions::new(self, entity, aabb)
- }
+pub fn get_block_collisions(world: &World, aabb: AABB) -> BlockCollisions<'_> {
+ BlockCollisions::new(world, aabb)
}
pub struct BlockCollisions<'a> {
- pub world: &'a WeakWorld,
- // context: CollisionContext,
+ pub world: &'a World,
pub aabb: AABB,
pub entity_shape: VoxelShape,
pub cursor: Cursor3d,
@@ -36,8 +19,7 @@ pub struct BlockCollisions<'a> {
}
impl<'a> BlockCollisions<'a> {
- // TODO: the entity is stored in the context
- pub fn new(world: &'a WeakWorld, _entity: Option<&EntityData>, aabb: AABB) -> Self {
+ pub fn new(world: &'a World, aabb: AABB) -> Self {
let origin_x = (aabb.min_x - EPSILON) as i32 - 1;
let origin_y = (aabb.min_y - EPSILON) as i32 - 1;
let origin_z = (aabb.min_z - EPSILON) as i32 - 1;
@@ -75,7 +57,7 @@ impl<'a> BlockCollisions<'a> {
// return var7;
// }
- self.world.get_chunk(&chunk_pos)
+ self.world.chunks.get(&chunk_pos)
}
}
@@ -89,15 +71,14 @@ impl<'a> Iterator for BlockCollisions<'a> {
}
let chunk = self.get_chunk(item.pos.x, item.pos.z);
- let chunk = match chunk {
- Some(chunk) => chunk,
- None => continue,
+ let Some(chunk) = chunk else {
+ continue
};
let pos = item.pos;
let block_state: BlockState = chunk
.read()
- .get(&(&pos).into(), self.world.min_y())
+ .get(&(&pos).into(), self.world.chunks.min_y)
.unwrap_or(BlockState::Air);
// TODO: continue if self.only_suffocating_blocks and the block is not
diff --git a/azalea-physics/src/lib.rs b/azalea-physics/src/lib.rs
index 2409dff7..96bebd1a 100644
--- a/azalea-physics/src/lib.rs
+++ b/azalea-physics/src/lib.rs
@@ -5,24 +5,56 @@ pub mod collision;
use azalea_block::{Block, BlockState};
use azalea_core::{BlockPos, Vec3};
+use azalea_ecs::{
+ app::{App, Plugin},
+ entity::Entity,
+ event::{EventReader, EventWriter},
+ query::With,
+ schedule::{IntoSystemDescriptor, SystemSet},
+ system::{Query, Res},
+ AppTickExt,
+};
use azalea_world::{
- entity::{Entity, EntityData},
- WeakWorld,
+ entity::{
+ metadata::Sprinting, move_relative, Attributes, Jumping, Physics, Position, WorldName,
+ },
+ Local, World, WorldContainer,
};
-use collision::{MovableEntity, MoverType};
-use std::ops::Deref;
-
-pub trait HasPhysics {
- fn travel(&mut self, acceleration: &Vec3);
- fn ai_step(&mut self);
-
- fn jump_from_ground(&mut self);
+use collision::{move_colliding, MoverType};
+
+pub struct PhysicsPlugin;
+impl Plugin for PhysicsPlugin {
+ fn build(&self, app: &mut App) {
+ app.add_event::<ForceJumpEvent>()
+ .add_system(
+ force_jump_listener
+ .label("force_jump_listener")
+ .after("ai_step"),
+ )
+ .add_tick_system_set(
+ SystemSet::new()
+ .with_system(ai_step.label("ai_step"))
+ .with_system(
+ travel
+ .label("travel")
+ .after("ai_step")
+ .after("force_jump_listener"),
+ ),
+ );
+ }
}
-impl<D: Deref<Target = WeakWorld>> HasPhysics for Entity<'_, D> {
- /// Move the entity with the given acceleration while handling friction,
- /// gravity, collisions, and some other stuff.
- fn travel(&mut self, acceleration: &Vec3) {
+/// Move the entity with the given acceleration while handling friction,
+/// gravity, collisions, and some other stuff.
+fn travel(
+ mut query: Query<(&mut Physics, &mut Position, &Attributes, &WorldName), With<Local>>,
+ world_container: Res<WorldContainer>,
+) {
+ for (mut physics, mut position, attributes, world_name) in &mut query {
+ let world_lock = world_container
+ .get(world_name)
+ .expect("All entities should be in a valid world");
+ let world = world_lock.read();
// if !self.is_effective_ai() && !self.is_controlled_by_local_instance() {
// // this.calculateEntityAnimation(this, this instanceof FlyingAnimal);
// return;
@@ -37,24 +69,29 @@ impl<D: Deref<Target = WeakWorld>> HasPhysics for Entity<'_, D> {
// TODO: elytra
- let block_pos_below = get_block_pos_below_that_affects_movement(self);
+ let block_pos_below = get_block_pos_below_that_affects_movement(&position);
- let block_state_below = self
- .world
+ let block_state_below = world
+ .chunks
.get_block_state(&block_pos_below)
.unwrap_or(BlockState::Air);
let block_below: Box<dyn Block> = block_state_below.into();
let block_friction = block_below.behavior().friction;
- let inertia = if self.on_ground {
+ let inertia = if physics.on_ground {
block_friction * 0.91
} else {
0.91
};
// this applies the current delta
- let mut movement =
- handle_relative_friction_and_calculate_movement(self, acceleration, block_friction);
+ let mut movement = handle_relative_friction_and_calculate_movement(
+ block_friction,
+ &world,
+ &mut physics,
+ &mut position,
+ attributes,
+ );
movement.y -= gravity;
@@ -66,96 +103,132 @@ impl<D: Deref<Target = WeakWorld>> HasPhysics for Entity<'_, D> {
// if should_discard_friction(self) {
if false {
- self.delta = movement;
+ physics.delta = movement;
} else {
- self.delta = Vec3 {
+ physics.delta = Vec3 {
x: movement.x * inertia as f64,
y: movement.y * 0.98f64,
z: movement.z * inertia as f64,
};
}
}
+}
- /// applies air resistance, calls self.travel(), and some other random
- /// stuff.
- fn ai_step(&mut self) {
+/// applies air resistance, calls self.travel(), and some other random
+/// stuff.
+pub fn ai_step(
+ mut query: Query<
+ (Entity, &mut Physics, Option<&Jumping>),
+ With<Local>,
+ // TODO: ai_step should only run for players in loaded chunks
+ // With<LocalPlayerInLoadedChunk> maybe there should be an InLoadedChunk/InUnloadedChunk
+ // component?
+ >,
+ mut force_jump_events: EventWriter<ForceJumpEvent>,
+) {
+ for (entity, mut physics, jumping) in &mut query {
// vanilla does movement interpolation here, doesn't really matter much for a
// bot though
- if self.delta.x.abs() < 0.003 {
- self.delta.x = 0.;
+ if physics.delta.x.abs() < 0.003 {
+ physics.delta.x = 0.;
}
- if self.delta.y.abs() < 0.003 {
- self.delta.y = 0.;
+ if physics.delta.y.abs() < 0.003 {
+ physics.delta.y = 0.;
}
- if self.delta.z.abs() < 0.003 {
- self.delta.z = 0.;
+ if physics.delta.z.abs() < 0.003 {
+ physics.delta.z = 0.;
}
- if self.jumping {
- // TODO: jumping in liquids and jump delay
+ if let Some(jumping) = jumping {
+ if **jumping {
+ // TODO: jumping in liquids and jump delay
- if self.on_ground {
- self.jump_from_ground();
+ if physics.on_ground {
+ force_jump_events.send(ForceJumpEvent(entity));
+ }
}
}
- self.xxa *= 0.98;
- self.zza *= 0.98;
-
- self.travel(&Vec3 {
- x: self.xxa as f64,
- y: self.yya as f64,
- z: self.zza as f64,
- });
- // freezing
- // pushEntities
- // drowning damage
+ physics.xxa *= 0.98;
+ physics.zza *= 0.98;
+
+ // TODO: freezing, pushEntities, drowning damage (in their own systems,
+ // after `travel`)
}
+}
- fn jump_from_ground(&mut self) {
- let jump_power: f64 = jump_power(self) as f64 + jump_boost_power(self);
- let old_delta_movement = self.delta;
- self.delta = Vec3 {
- x: old_delta_movement.x,
- y: jump_power,
- z: old_delta_movement.z,
- };
- if self.metadata.sprinting {
- let y_rot = self.y_rot * 0.017453292;
- self.delta += Vec3 {
- x: (-f32::sin(y_rot) * 0.2) as f64,
- y: 0.,
- z: (f32::cos(y_rot) * 0.2) as f64,
+/// Jump even if we aren't on the ground.
+pub struct ForceJumpEvent(pub Entity);
+
+fn force_jump_listener(
+ mut query: Query<(&mut Physics, &Position, &Sprinting, &WorldName)>,
+ world_container: Res<WorldContainer>,
+ mut events: EventReader<ForceJumpEvent>,
+) {
+ for event in events.iter() {
+ if let Ok((mut physics, position, sprinting, world_name)) = query.get_mut(event.0) {
+ let world_lock = world_container
+ .get(world_name)
+ .expect("All entities should be in a valid world");
+ let world = world_lock.read();
+
+ let jump_power: f64 = jump_power(&world, position) as f64 + jump_boost_power();
+ let old_delta_movement = physics.delta;
+ physics.delta = Vec3 {
+ x: old_delta_movement.x,
+ y: jump_power,
+ z: old_delta_movement.z,
};
- }
+ if **sprinting {
+ // sprint jumping gives some extra velocity
+ let y_rot = physics.y_rot * 0.017453292;
+ physics.delta += Vec3 {
+ x: (-f32::sin(y_rot) * 0.2) as f64,
+ y: 0.,
+ z: (f32::cos(y_rot) * 0.2) as f64,
+ };
+ }
- self.has_impulse = true;
+ physics.has_impulse = true;
+ }
}
}
-fn get_block_pos_below_that_affects_movement(entity: &EntityData) -> BlockPos {
+fn get_block_pos_below_that_affects_movement(position: &Position) -> BlockPos {
BlockPos::new(
- entity.pos().x.floor() as i32,
+ position.x.floor() as i32,
// TODO: this uses bounding_box.min_y instead of position.y
- (entity.pos().y - 0.5f64).floor() as i32,
- entity.pos().z.floor() as i32,
+ (position.y - 0.5f64).floor() as i32,
+ position.z.floor() as i32,
)
}
-fn handle_relative_friction_and_calculate_movement<D: Deref<Target = WeakWorld>>(
- entity: &mut Entity<D>,
- acceleration: &Vec3,
+fn handle_relative_friction_and_calculate_movement(
block_friction: f32,
+ world: &World,
+ physics: &mut Physics,
+ position: &mut Position,
+ attributes: &Attributes,
) -> Vec3 {
- entity.move_relative(
- get_friction_influenced_speed(&*entity, block_friction),
- acceleration,
+ move_relative(
+ physics,
+ get_friction_influenced_speed(physics, attributes, block_friction),
+ &Vec3 {
+ x: physics.xxa as f64,
+ y: physics.yya as f64,
+ z: physics.zza as f64,
+ },
);
// entity.delta = entity.handle_on_climbable(entity.delta);
- entity
- .move_colliding(&MoverType::Own, &entity.delta.clone())
- .expect("Entity should exist.");
+ move_colliding(
+ &MoverType::Own,
+ &physics.delta.clone(),
+ world,
+ position,
+ physics,
+ )
+ .expect("Entity should exist.");
// let delta_movement = entity.delta;
// ladders
// if ((entity.horizontalCollision || entity.jumping) && (entity.onClimbable()
@@ -164,16 +237,16 @@ fn handle_relative_friction_and_calculate_movement<D: Deref<Target = WeakWorld>>
// Vec3(var3.x, 0.2D, var3.z); }
// TODO: powdered snow
- entity.delta
+ physics.delta
}
// private float getFrictionInfluencedSpeed(float friction) {
// return this.onGround ? this.getSpeed() * (0.21600002F / (friction *
// friction * friction)) : this.flyingSpeed; }
-fn get_friction_influenced_speed(entity: &EntityData, friction: f32) -> f32 {
+fn get_friction_influenced_speed(physics: &Physics, attributes: &Attributes, friction: f32) -> f32 {
// TODO: have speed & flying_speed fields in entity
- if entity.on_ground {
- let speed: f32 = entity.attributes.speed.calculate() as f32;
+ if physics.on_ground {
+ let speed: f32 = attributes.speed.calculate() as f32;
speed * (0.216f32 / (friction * friction * friction))
} else {
// entity.flying_speed
@@ -183,11 +256,11 @@ fn get_friction_influenced_speed(entity: &EntityData, friction: f32) -> f32 {
/// Returns the what the entity's jump should be multiplied by based on the
/// block they're standing on.
-fn block_jump_factor<D: Deref<Target = WeakWorld>>(entity: &Entity<D>) -> f32 {
- let block_at_pos = entity.world.get_block_state(&entity.pos().into());
- let block_below = entity
- .world
- .get_block_state(&get_block_pos_below_that_affects_movement(entity));
+fn block_jump_factor(world: &World, position: &Position) -> f32 {
+ let block_at_pos = world.chunks.get_block_state(&position.into());
+ let block_below = world
+ .chunks
+ .get_block_state(&get_block_pos_below_that_affects_movement(position));
let block_at_pos_jump_factor = if let Some(block) = block_at_pos {
Box::<dyn Block>::from(block).behavior().jump_factor
@@ -211,11 +284,11 @@ fn block_jump_factor<D: Deref<Target = WeakWorld>>(entity: &Entity<D>) -> f32 {
// public double getJumpBoostPower() {
// return this.hasEffect(MobEffects.JUMP) ? (double)(0.1F *
// (float)(this.getEffect(MobEffects.JUMP).getAmplifier() + 1)) : 0.0D; }
-fn jump_power<D: Deref<Target = WeakWorld>>(entity: &Entity<D>) -> f32 {
- 0.42 * block_jump_factor(entity)
+fn jump_power(world: &World, position: &Position) -> f32 {
+ 0.42 * block_jump_factor(world, position)
}
-fn jump_boost_power<D: Deref<Target = WeakWorld>>(_entity: &Entity<D>) -> f64 {
+fn jump_boost_power() -> f64 {
// TODO: potion effects
// if let Some(effects) = entity.effects() {
// if let Some(jump_effect) = effects.get(&Effect::Jump) {
@@ -231,131 +304,218 @@ fn jump_boost_power<D: Deref<Target = WeakWorld>>(_entity: &Entity<D>) -> f64 {
#[cfg(test)]
mod tests {
+ use std::time::Duration;
+
use super::*;
- use azalea_core::ChunkPos;
+ use azalea_core::{ChunkPos, ResourceLocation};
+ use azalea_ecs::{app::App, TickPlugin};
use azalea_world::{
- entity::{metadata, EntityMetadata},
- Chunk, PartialWorld,
+ entity::{EntityBundle, MinecraftEntityId},
+ Chunk, EntityPlugin, PartialWorld,
};
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_plugin(TickPlugin {
+ tick_interval: Duration::ZERO,
+ })
+ .add_plugin(PhysicsPlugin)
+ .add_plugin(EntityPlugin)
+ .init_resource::<WorldContainer>();
+ app
+ }
+
#[test]
fn test_gravity() {
- let mut world = PartialWorld::default();
-
- world.add_entity(
- 0,
- EntityData::new(
- Uuid::nil(),
- Vec3 {
- x: 0.,
- y: 70.,
- z: 0.,
- },
- EntityMetadata::Player(metadata::Player::default()),
- ),
- );
- let mut entity = world.entity_mut(0).unwrap();
- // y should start at 70
- assert_eq!(entity.pos().y, 70.);
- entity.ai_step();
- // delta is applied before gravity, so the first tick only sets the delta
- assert_eq!(entity.pos().y, 70.);
- assert!(entity.delta.y < 0.);
- entity.ai_step();
- // the second tick applies the delta to the position, so now it should go down
- assert!(
- entity.pos().y < 70.,
- "Entity y ({}) didn't go down after physics steps",
- entity.pos().y
+ let mut app = make_test_app();
+ let _world_lock = app.world.resource_mut::<WorldContainer>().insert(
+ ResourceLocation::new("minecraft:overworld").unwrap(),
+ 384,
+ -64,
);
+
+ let entity = app
+ .world
+ .spawn((
+ EntityBundle::new(
+ Uuid::nil(),
+ Vec3 {
+ x: 0.,
+ y: 70.,
+ z: 0.,
+ },
+ azalea_registry::EntityKind::Zombie,
+ ResourceLocation::new("minecraft:overworld").unwrap(),
+ ),
+ MinecraftEntityId(0),
+ Local,
+ ))
+ .id();
+ {
+ let entity_pos = *app.world.get::<Position>(entity).unwrap();
+ // y should start at 70
+ assert_eq!(entity_pos.y, 70.);
+ }
+ app.update();
+ {
+ let entity_pos = *app.world.get::<Position>(entity).unwrap();
+ // delta is applied before gravity, so the first tick only sets the delta
+ assert_eq!(entity_pos.y, 70.);
+ let entity_physics = app.world.get::<Physics>(entity).unwrap().clone();
+ assert!(entity_physics.delta.y < 0.);
+ }
+ app.update();
+ {
+ let entity_pos = *app.world.get::<Position>(entity).unwrap();
+ // the second tick applies the delta to the position, so now it should go down
+ assert!(
+ entity_pos.y < 70.,
+ "Entity y ({}) didn't go down after physics steps",
+ entity_pos.y
+ );
+ }
}
#[test]
fn test_collision() {
- let mut world = PartialWorld::default();
- world
- .set_chunk(&ChunkPos { x: 0, z: 0 }, Some(Chunk::default()))
- .unwrap();
- world.add_entity(
- 0,
- EntityData::new(
- Uuid::nil(),
- Vec3 {
- x: 0.5,
- y: 70.,
- z: 0.5,
- },
- EntityMetadata::Player(metadata::Player::default()),
- ),
+ let mut app = make_test_app();
+ let world_lock = app.world.resource_mut::<WorldContainer>().insert(
+ ResourceLocation::new("minecraft:overworld").unwrap(),
+ 384,
+ -64,
+ );
+ let mut partial_world = PartialWorld::default();
+
+ partial_world.chunks.set(
+ &ChunkPos { x: 0, z: 0 },
+ Some(Chunk::default()),
+ &mut world_lock.write().chunks,
+ );
+ let entity = app
+ .world
+ .spawn((
+ EntityBundle::new(
+ Uuid::nil(),
+ Vec3 {
+ x: 0.5,
+ y: 70.,
+ z: 0.5,
+ },
+ azalea_registry::EntityKind::Player,
+ ResourceLocation::new("minecraft:overworld").unwrap(),
+ ),
+ MinecraftEntityId(0),
+ Local,
+ ))
+ .id();
+ let block_state = partial_world.chunks.set_block_state(
+ &BlockPos { x: 0, y: 69, z: 0 },
+ BlockState::Stone,
+ &mut world_lock.write().chunks,
);
- let block_state = world.set_block_state(&BlockPos { x: 0, y: 69, z: 0 }, BlockState::Stone);
assert!(
block_state.is_some(),
"Block state should exist, if this fails that means the chunk wasn't loaded and the block didn't get placed"
);
- let mut entity = world.entity_mut(0).unwrap();
- entity.ai_step();
- // delta will change, but it won't move until next tick
- assert_eq!(entity.pos().y, 70.);
- assert!(entity.delta.y < 0.);
- entity.ai_step();
- // the second tick applies the delta to the position, but it also does collision
- assert_eq!(entity.pos().y, 70.);
+ app.update();
+ {
+ let entity_pos = *app.world.get::<Position>(entity).unwrap();
+ // delta will change, but it won't move until next tick
+ assert_eq!(entity_pos.y, 70.);
+ let entity_physics = app.world.get::<Physics>(entity).unwrap().clone();
+ assert!(entity_physics.delta.y < 0.);
+ }
+ app.update();
+ {
+ let entity_pos = *app.world.get::<Position>(entity).unwrap();
+ // the second tick applies the delta to the position, but it also does collision
+ assert_eq!(entity_pos.y, 70.);
+ }
}
#[test]
fn test_slab_collision() {
- let mut world = PartialWorld::default();
- world
- .set_chunk(&ChunkPos { x: 0, z: 0 }, Some(Chunk::default()))
- .unwrap();
- world.add_entity(
- 0,
- EntityData::new(
- Uuid::nil(),
- Vec3 {
- x: 0.5,
- y: 71.,
- z: 0.5,
- },
- EntityMetadata::Player(metadata::Player::default()),
- ),
+ let mut app = make_test_app();
+ let world_lock = app.world.resource_mut::<WorldContainer>().insert(
+ ResourceLocation::new("minecraft:overworld").unwrap(),
+ 384,
+ -64,
);
- let block_state = world.set_block_state(
+ let mut partial_world = PartialWorld::default();
+
+ partial_world.chunks.set(
+ &ChunkPos { x: 0, z: 0 },
+ Some(Chunk::default()),
+ &mut world_lock.write().chunks,
+ );
+ let entity = app
+ .world
+ .spawn((
+ EntityBundle::new(
+ Uuid::nil(),
+ Vec3 {
+ x: 0.5,
+ y: 71.,
+ z: 0.5,
+ },
+ azalea_registry::EntityKind::Player,
+ ResourceLocation::new("minecraft:overworld").unwrap(),
+ ),
+ MinecraftEntityId(0),
+ Local,
+ ))
+ .id();
+ let block_state = partial_world.chunks.set_block_state(
&BlockPos { x: 0, y: 69, z: 0 },
BlockState::StoneSlab_BottomFalse,
+ &mut world_lock.write().chunks,
);
assert!(
block_state.is_some(),
"Block state should exist, if this fails that means the chunk wasn't loaded and the block didn't get placed"
);
- let mut entity = world.entity_mut(0).unwrap();
// do a few steps so we fall on the slab
for _ in 0..20 {
- entity.ai_step();
+ app.update();
}
- assert_eq!(entity.pos().y, 69.5);
+ let entity_pos = app.world.get::<Position>(entity).unwrap();
+ assert_eq!(entity_pos.y, 69.5);
}
#[test]
fn test_top_slab_collision() {
- let mut world = PartialWorld::default();
- world
- .set_chunk(&ChunkPos { x: 0, z: 0 }, Some(Chunk::default()))
- .unwrap();
- world.add_entity(
- 0,
- EntityData::new(
- Uuid::nil(),
- Vec3 {
- x: 0.5,
- y: 71.,
- z: 0.5,
- },
- EntityMetadata::Player(metadata::Player::default()),
- ),
+ let mut app = make_test_app();
+ let world_lock = app.world.resource_mut::<WorldContainer>().insert(
+ ResourceLocation::new("minecraft:overworld").unwrap(),
+ 384,
+ -64,
+ );
+ let mut partial_world = PartialWorld::default();
+
+ partial_world.chunks.set(
+ &ChunkPos { x: 0, z: 0 },
+ Some(Chunk::default()),
+ &mut world_lock.write().chunks,
);
- let block_state = world.set_block_state(
+ let entity = app
+ .world
+ .spawn((
+ EntityBundle::new(
+ Uuid::nil(),
+ Vec3 {
+ x: 0.5,
+ y: 71.,
+ z: 0.5,
+ },
+ azalea_registry::EntityKind::Player,
+ ResourceLocation::new("minecraft:overworld").unwrap(),
+ ),
+ MinecraftEntityId(0),
+ Local,
+ ))
+ .id();
+ let block_state = world_lock.write().chunks.set_block_state(
&BlockPos { x: 0, y: 69, z: 0 },
BlockState::StoneSlab_TopFalse,
);
@@ -363,33 +523,47 @@ mod tests {
block_state.is_some(),
"Block state should exist, if this fails that means the chunk wasn't loaded and the block didn't get placed"
);
- let mut entity = world.entity_mut(0).unwrap();
// do a few steps so we fall on the slab
for _ in 0..20 {
- entity.ai_step();
+ app.update();
}
- assert_eq!(entity.pos().y, 70.);
+ let entity_pos = app.world.get::<Position>(entity).unwrap();
+ assert_eq!(entity_pos.y, 70.);
}
#[test]
fn test_weird_wall_collision() {
- let mut world = PartialWorld::default();
- world
- .set_chunk(&ChunkPos { x: 0, z: 0 }, Some(Chunk::default()))
- .unwrap();
- world.add_entity(
- 0,
- EntityData::new(
- Uuid::nil(),
- Vec3 {
- x: 0.5,
- y: 73.,
- z: 0.5,
- },
- EntityMetadata::Player(metadata::Player::default()),
- ),
+ let mut app = make_test_app();
+ let world_lock = app.world.resource_mut::<WorldContainer>().insert(
+ ResourceLocation::new("minecraft:overworld").unwrap(),
+ 384,
+ -64,
+ );
+ let mut partial_world = PartialWorld::default();
+
+ partial_world.chunks.set(
+ &ChunkPos { x: 0, z: 0 },
+ Some(Chunk::default()),
+ &mut world_lock.write().chunks,
);
- let block_state = world.set_block_state(
+ let entity = app
+ .world
+ .spawn((
+ EntityBundle::new(
+ Uuid::nil(),
+ Vec3 {
+ x: 0.5,
+ y: 73.,
+ z: 0.5,
+ },
+ azalea_registry::EntityKind::Player,
+ ResourceLocation::new("minecraft:overworld").unwrap(),
+ ),
+ MinecraftEntityId(0),
+ Local,
+ ))
+ .id();
+ let block_state = world_lock.write().chunks.set_block_state(
&BlockPos { x: 0, y: 69, z: 0 },
BlockState::CobblestoneWall_LowLowLowFalseFalseLow,
);
@@ -397,11 +571,12 @@ mod tests {
block_state.is_some(),
"Block state should exist, if this fails that means the chunk wasn't loaded and the block didn't get placed"
);
- let mut entity = world.entity_mut(0).unwrap();
// do a few steps so we fall on the slab
for _ in 0..20 {
- entity.ai_step();
+ app.update();
}
- assert_eq!(entity.pos().y, 70.5);
+
+ let entity_pos = app.world.get::<Position>(entity).unwrap();
+ assert_eq!(entity_pos.y, 70.5);
}
}
diff --git a/azalea-protocol/Cargo.toml b/azalea-protocol/Cargo.toml
index 79a8913f..735c0397 100644
--- a/azalea-protocol/Cargo.toml
+++ b/azalea-protocol/Cargo.toml
@@ -22,6 +22,7 @@ azalea-nbt = {path = "../azalea-nbt", version = "^0.5.0" }
azalea-protocol-macros = {path = "./azalea-protocol-macros", version = "^0.5.0" }
azalea-registry = {path = "../azalea-registry", version = "^0.5.0" }
azalea-world = {path = "../azalea-world", version = "^0.5.0" }
+bevy_ecs = { version = "0.9.1", default-features = false }
byteorder = "^1.4.3"
bytes = "^1.1.0"
flate2 = "1.0.23"
diff --git a/azalea-protocol/azalea-protocol-macros/src/lib.rs b/azalea-protocol/azalea-protocol-macros/src/lib.rs
index 849a49e7..e04a2dbc 100755
--- a/azalea-protocol/azalea-protocol-macros/src/lib.rs
+++ b/azalea-protocol/azalea-protocol-macros/src/lib.rs
@@ -3,19 +3,17 @@ use quote::quote;
use syn::{
self, braced,
parse::{Parse, ParseStream, Result},
- parse_macro_input, DeriveInput, FieldsNamed, Ident, LitInt, Token,
+ parse_macro_input, DeriveInput, Ident, LitInt, Token,
};
fn as_packet_derive(input: TokenStream, state: proc_macro2::TokenStream) -> TokenStream {
let DeriveInput { ident, data, .. } = parse_macro_input!(input);
- let fields = match &data {
- syn::Data::Struct(syn::DataStruct { fields, .. }) => fields,
- _ => panic!("#[derive(*Packet)] can only be used on structs"),
+ let syn::Data::Struct(syn::DataStruct { fields, .. }) = &data else {
+ panic!("#[derive(*Packet)] can only be used on structs")
};
- let FieldsNamed { named: _, .. } = match fields {
- syn::Fields::Named(f) => f,
- _ => panic!("#[derive(*Packet)] can only be used on structs with named fields"),
+ let syn::Fields::Named(_) = fields else {
+ panic!("#[derive(*Packet)] can only be used on structs with named fields")
};
let variant_name = variant_name_from(&ident);
diff --git a/azalea-protocol/src/packets/game/clientbound_add_entity_packet.rs b/azalea-protocol/src/packets/game/clientbound_add_entity_packet.rs
index fcbd8fa5..75f3f4dc 100755
--- a/azalea-protocol/src/packets/game/clientbound_add_entity_packet.rs
+++ b/azalea-protocol/src/packets/game/clientbound_add_entity_packet.rs
@@ -1,7 +1,7 @@
use azalea_buf::McBuf;
-use azalea_core::Vec3;
+use azalea_core::{ResourceLocation, Vec3};
use azalea_protocol_macros::ClientboundGamePacket;
-use azalea_world::entity::{EntityData, EntityMetadata};
+use azalea_world::entity::{metadata::apply_default_metadata, EntityBundle};
use uuid::Uuid;
#[derive(Clone, Debug, McBuf, ClientboundGamePacket)]
@@ -10,10 +10,8 @@ pub struct ClientboundAddEntityPacket {
#[var]
pub id: u32,
pub uuid: Uuid,
- pub entity_type: azalea_registry::EntityType,
- pub x: f64,
- pub y: f64,
- pub z: f64,
+ pub entity_type: azalea_registry::EntityKind,
+ pub position: Vec3,
pub x_rot: i8,
pub y_rot: i8,
pub y_head_rot: i8,
@@ -24,17 +22,31 @@ pub struct ClientboundAddEntityPacket {
pub z_vel: i16,
}
-impl From<&ClientboundAddEntityPacket> for EntityData {
- fn from(p: &ClientboundAddEntityPacket) -> Self {
- Self::new(
- p.uuid,
- Vec3 {
- x: p.x,
- y: p.y,
- z: p.z,
- },
- // default metadata for the entity type
- EntityMetadata::from(p.entity_type),
- )
+// impl From<&ClientboundAddEntityPacket> for EntityData {
+// fn from(p: &ClientboundAddEntityPacket) -> Self {
+// Self::new(
+// p.uuid,
+// Vec3 {
+// x: p.x,
+// y: p.y,
+// z: p.z,
+// },
+// // default metadata for the entity type
+// EntityMetadata::from(p.entity_type),
+// )
+// }
+// }
+
+impl ClientboundAddEntityPacket {
+ /// Make the entity into a bundle that can be inserted into the ECS. You
+ /// must apply the metadata after inserting the bundle with
+ /// [`Self::apply_metadata`].
+ pub fn as_entity_bundle(&self, world_name: ResourceLocation) -> EntityBundle {
+ EntityBundle::new(self.uuid, self.position, self.entity_type, world_name)
+ }
+
+ /// Apply the default metadata for the given entity.
+ pub fn apply_metadata(&self, entity: &mut bevy_ecs::system::EntityCommands) {
+ apply_default_metadata(entity, self.entity_type);
}
}
diff --git a/azalea-protocol/src/packets/game/clientbound_add_player_packet.rs b/azalea-protocol/src/packets/game/clientbound_add_player_packet.rs
index 58c7b0a3..4cbeb1b9 100755
--- a/azalea-protocol/src/packets/game/clientbound_add_player_packet.rs
+++ b/azalea-protocol/src/packets/game/clientbound_add_player_packet.rs
@@ -1,7 +1,8 @@
use azalea_buf::McBuf;
-use azalea_core::Vec3;
+use azalea_core::{ResourceLocation, Vec3};
use azalea_protocol_macros::ClientboundGamePacket;
-use azalea_world::entity::{metadata, EntityData, EntityMetadata};
+use azalea_registry::EntityKind;
+use azalea_world::entity::{metadata::PlayerMetadataBundle, EntityBundle, PlayerBundle};
use uuid::Uuid;
/// This packet is sent by the server when a player comes into visible range,
@@ -11,23 +12,16 @@ pub struct ClientboundAddPlayerPacket {
#[var]
pub id: u32,
pub uuid: Uuid,
- pub x: f64,
- pub y: f64,
- pub z: f64,
+ pub position: Vec3,
pub x_rot: i8,
pub y_rot: i8,
}
-impl From<&ClientboundAddPlayerPacket> for EntityData {
- fn from(p: &ClientboundAddPlayerPacket) -> Self {
- Self::new(
- p.uuid,
- Vec3 {
- x: p.x,
- y: p.y,
- z: p.z,
- },
- EntityMetadata::Player(metadata::Player::default()),
- )
+impl ClientboundAddPlayerPacket {
+ pub fn as_player_bundle(&self, world_name: ResourceLocation) -> PlayerBundle {
+ PlayerBundle {
+ entity: EntityBundle::new(self.uuid, self.position, EntityKind::Player, world_name),
+ metadata: PlayerMetadataBundle::default(),
+ }
}
}
diff --git a/azalea-protocol/src/packets/game/clientbound_award_stats_packet.rs b/azalea-protocol/src/packets/game/clientbound_award_stats_packet.rs
index 2812987d..aaec1849 100755
--- a/azalea-protocol/src/packets/game/clientbound_award_stats_packet.rs
+++ b/azalea-protocol/src/packets/game/clientbound_award_stats_packet.rs
@@ -16,7 +16,7 @@ pub enum Stat {
Broken(azalea_registry::Item),
PickedUp(azalea_registry::Item),
Dropped(azalea_registry::Item),
- Killed(azalea_registry::EntityType),
- KilledBy(azalea_registry::EntityType),
+ Killed(azalea_registry::EntityKind),
+ KilledBy(azalea_registry::EntityKind),
Custom(azalea_registry::CustomStat),
}
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 faa3b1a9..e3c95034 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
@@ -5,6 +5,6 @@ use azalea_protocol_macros::ClientboundGamePacket;
#[derive(Clone, Debug, McBuf, ClientboundGamePacket)]
pub struct ClientboundBlockEntityDataPacket {
pub pos: BlockPos,
- pub block_entity_type: azalea_registry::BlockEntityType,
+ pub block_entity_type: azalea_registry::BlockEntityKind,
pub tag: azalea_nbt::Tag,
}
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 3ffbaea1..e73b55ba 100755
--- a/azalea-protocol/src/packets/game/clientbound_boss_event_packet.rs
+++ b/azalea-protocol/src/packets/game/clientbound_boss_event_packet.rs
@@ -1,7 +1,7 @@
use azalea_buf::{
BufReadError, McBuf, McBufReadable, McBufVarReadable, McBufVarWritable, McBufWritable,
};
-use azalea_chat::Component;
+use azalea_chat::FormattedText;
use azalea_core::FixedBitSet;
use azalea_protocol_macros::ClientboundGamePacket;
use std::io::Cursor;
@@ -19,7 +19,7 @@ pub enum Operation {
Add(AddOperation),
Remove,
UpdateProgress(f32),
- UpdateName(Component),
+ UpdateName(FormattedText),
UpdateStyle(Style),
UpdateProperties(Properties),
}
@@ -31,7 +31,7 @@ impl McBufReadable for Operation {
0 => Operation::Add(AddOperation::read_from(buf)?),
1 => Operation::Remove,
2 => Operation::UpdateProgress(f32::read_from(buf)?),
- 3 => Operation::UpdateName(Component::read_from(buf)?),
+ 3 => Operation::UpdateName(FormattedText::read_from(buf)?),
4 => Operation::UpdateStyle(Style::read_from(buf)?),
5 => Operation::UpdateProperties(Properties::read_from(buf)?),
_ => {
@@ -76,7 +76,7 @@ impl McBufWritable for Operation {
#[derive(Clone, Debug, McBuf)]
pub struct AddOperation {
- name: Component,
+ name: FormattedText,
progress: f32,
style: Style,
properties: Properties,
diff --git a/azalea-protocol/src/packets/game/clientbound_chat_preview_packet.rs b/azalea-protocol/src/packets/game/clientbound_chat_preview_packet.rs
index 5a72ae33..40f28259 100755
--- a/azalea-protocol/src/packets/game/clientbound_chat_preview_packet.rs
+++ b/azalea-protocol/src/packets/game/clientbound_chat_preview_packet.rs
@@ -1,9 +1,9 @@
use azalea_buf::McBuf;
-use azalea_chat::Component;
+use azalea_chat::FormattedText;
use azalea_protocol_macros::ClientboundGamePacket;
#[derive(Clone, Debug, McBuf, ClientboundGamePacket)]
pub struct ClientboundChatPreviewPacket {
pub query_id: i32,
- pub preview: Option<Component>,
+ pub preview: Option<FormattedText>,
}
diff --git a/azalea-protocol/src/packets/game/clientbound_command_suggestions_packet.rs b/azalea-protocol/src/packets/game/clientbound_command_suggestions_packet.rs
index 652ce78a..88c6f29e 100755
--- a/azalea-protocol/src/packets/game/clientbound_command_suggestions_packet.rs
+++ b/azalea-protocol/src/packets/game/clientbound_command_suggestions_packet.rs
@@ -1,13 +1,13 @@
use azalea_brigadier::suggestion::Suggestions;
use azalea_buf::McBuf;
-use azalea_chat::Component;
+use azalea_chat::FormattedText;
use azalea_protocol_macros::ClientboundGamePacket;
#[derive(Clone, Debug, McBuf, ClientboundGamePacket)]
pub struct ClientboundCommandSuggestionsPacket {
#[var]
pub id: u32,
- pub suggestions: Suggestions<Component>,
+ pub suggestions: Suggestions<FormattedText>,
}
#[cfg(test)]
@@ -24,7 +24,7 @@ mod tests {
suggestions: vec![Suggestion {
text: "foo".to_string(),
range: StringRange::new(1, 4),
- tooltip: Some(Component::from("bar".to_string())),
+ tooltip: Some(FormattedText::from("bar".to_string())),
}],
};
let mut buf = Vec::new();
diff --git a/azalea-protocol/src/packets/game/clientbound_commands_packet.rs b/azalea-protocol/src/packets/game/clientbound_commands_packet.rs
index fa11a355..73dcbce7 100755
--- a/azalea-protocol/src/packets/game/clientbound_commands_packet.rs
+++ b/azalea-protocol/src/packets/game/clientbound_commands_packet.rs
@@ -114,7 +114,7 @@ pub enum BrigadierParser {
ItemStack,
ItemPredicate,
Color,
- Component,
+ FormattedText,
Message,
NbtCompoundTag,
NbtTag,
diff --git a/azalea-protocol/src/packets/game/clientbound_disconnect_packet.rs b/azalea-protocol/src/packets/game/clientbound_disconnect_packet.rs
index ecff0278..f8771c37 100755
--- a/azalea-protocol/src/packets/game/clientbound_disconnect_packet.rs
+++ b/azalea-protocol/src/packets/game/clientbound_disconnect_packet.rs
@@ -1,8 +1,8 @@
use azalea_buf::McBuf;
-use azalea_chat::Component;
+use azalea_chat::FormattedText;
use azalea_protocol_macros::ClientboundGamePacket;
#[derive(Clone, Debug, McBuf, ClientboundGamePacket)]
pub struct ClientboundDisconnectPacket {
- pub reason: Component,
+ pub reason: FormattedText,
}
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 701284a0..9aa9fd6f 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,10 @@
use super::clientbound_player_chat_packet::ChatTypeBound;
use azalea_buf::McBuf;
-use azalea_chat::Component;
+use azalea_chat::FormattedText;
use azalea_protocol_macros::ClientboundGamePacket;
#[derive(Clone, Debug, McBuf, ClientboundGamePacket)]
pub struct ClientboundDisguisedChatPacket {
- pub message: Component,
+ pub message: FormattedText,
pub chat_type: ChatTypeBound,
}
diff --git a/azalea-protocol/src/packets/game/clientbound_map_item_data_packet.rs b/azalea-protocol/src/packets/game/clientbound_map_item_data_packet.rs
index 38d92135..0f858181 100755
--- a/azalea-protocol/src/packets/game/clientbound_map_item_data_packet.rs
+++ b/azalea-protocol/src/packets/game/clientbound_map_item_data_packet.rs
@@ -1,5 +1,5 @@
use azalea_buf::{McBuf, McBufReadable, McBufWritable};
-use azalea_chat::Component;
+use azalea_chat::FormattedText;
use azalea_protocol_macros::ClientboundGamePacket;
#[derive(Clone, Debug, ClientboundGamePacket, McBuf)]
@@ -20,7 +20,7 @@ pub struct MapDecoration {
/// Minecraft does & 15 on this value, azalea-protocol doesn't. I don't
/// think it matters.
pub rot: i8,
- pub name: Option<Component>,
+ pub name: Option<FormattedText>,
}
#[derive(Debug, Clone)]
diff --git a/azalea-protocol/src/packets/game/clientbound_open_screen_packet.rs b/azalea-protocol/src/packets/game/clientbound_open_screen_packet.rs
index f127f587..9b8b02a1 100755
--- a/azalea-protocol/src/packets/game/clientbound_open_screen_packet.rs
+++ b/azalea-protocol/src/packets/game/clientbound_open_screen_packet.rs
@@ -1,5 +1,5 @@
use azalea_buf::McBuf;
-use azalea_chat::Component;
+use azalea_chat::FormattedText;
use azalea_protocol_macros::ClientboundGamePacket;
#[derive(Clone, Debug, McBuf, ClientboundGamePacket)]
@@ -7,5 +7,5 @@ pub struct ClientboundOpenScreenPacket {
#[var]
pub container_id: u32,
pub menu_type: azalea_registry::Menu,
- pub title: Component,
+ pub title: FormattedText,
}
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 098ffa03..fb805dea 100644
--- a/azalea-protocol/src/packets/game/clientbound_player_chat_packet.rs
+++ b/azalea-protocol/src/packets/game/clientbound_player_chat_packet.rs
@@ -3,7 +3,7 @@ use azalea_buf::{
};
use azalea_chat::{
translatable_component::{StringOrComponent, TranslatableComponent},
- Component,
+ FormattedText,
};
use azalea_core::BitSet;
use azalea_crypto::MessageSignature;
@@ -18,7 +18,7 @@ pub struct ClientboundPlayerChatPacket {
pub index: u32,
pub signature: Option<MessageSignature>,
pub body: PackedSignedMessageBody,
- pub unsigned_content: Option<Component>,
+ pub unsigned_content: Option<FormattedText>,
pub filter_mask: FilterMask,
pub chat_type: ChatTypeBound,
}
@@ -66,8 +66,8 @@ pub enum ChatType {
#[derive(Clone, Debug, McBuf, PartialEq)]
pub struct ChatTypeBound {
pub chat_type: ChatType,
- pub name: Component,
- pub target_name: Option<Component>,
+ pub name: FormattedText,
+ pub target_name: Option<FormattedText>,
}
// must be in Client
@@ -87,19 +87,19 @@ pub struct MessageSignatureCache {
// {} }
impl ClientboundPlayerChatPacket {
- /// Returns the content of the message. If you want to get the Component
+ /// Returns the content of the message. If you want to get the FormattedText
/// for the whole message including the sender part, use
/// [`ClientboundPlayerChatPacket::message`].
#[must_use]
- pub fn content(&self) -> Component {
+ pub fn content(&self) -> FormattedText {
self.unsigned_content
.clone()
- .unwrap_or_else(|| Component::from(self.body.content.clone()))
+ .unwrap_or_else(|| FormattedText::from(self.body.content.clone()))
}
/// Get the full message, including the sender part.
#[must_use]
- pub fn message(&self) -> Component {
+ pub fn message(&self) -> FormattedText {
let sender = self.chat_type.name.clone();
let content = self.content();
let target = self.chat_type.target_name.clone();
@@ -107,16 +107,16 @@ impl ClientboundPlayerChatPacket {
let translation_key = self.chat_type.chat_type.chat_translation_key();
let mut args = vec![
- StringOrComponent::Component(sender),
- StringOrComponent::Component(content),
+ StringOrComponent::FormattedText(sender),
+ StringOrComponent::FormattedText(content),
];
if let Some(target) = target {
- args.push(StringOrComponent::Component(target));
+ args.push(StringOrComponent::FormattedText(target));
}
let component = TranslatableComponent::new(translation_key.to_string(), args);
- Component::Translatable(component)
+ FormattedText::Translatable(component)
}
}
diff --git a/azalea-protocol/src/packets/game/clientbound_player_combat_kill_packet.rs b/azalea-protocol/src/packets/game/clientbound_player_combat_kill_packet.rs
index fa547af1..eae96f65 100755
--- a/azalea-protocol/src/packets/game/clientbound_player_combat_kill_packet.rs
+++ b/azalea-protocol/src/packets/game/clientbound_player_combat_kill_packet.rs
@@ -1,5 +1,5 @@
use azalea_buf::McBuf;
-use azalea_chat::Component;
+use azalea_chat::FormattedText;
use azalea_protocol_macros::ClientboundGamePacket;
/// Used to send a respawn screen.
@@ -8,5 +8,5 @@ pub struct ClientboundPlayerCombatKillPacket {
#[var]
pub player_id: u32,
pub killer_id: u32,
- pub message: Component,
+ pub message: FormattedText,
}
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 4fa16209..dc518c9c 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
@@ -2,7 +2,7 @@ use azalea_auth::game_profile::{GameProfile, ProfilePropertyValue};
use azalea_buf::{
BufReadError, McBuf, McBufReadable, McBufVarReadable, McBufVarWritable, McBufWritable,
};
-use azalea_chat::Component;
+use azalea_chat::FormattedText;
use azalea_core::{FixedBitSet, GameType};
use azalea_protocol_macros::ClientboundGamePacket;
use std::{
@@ -25,7 +25,7 @@ pub struct PlayerInfoEntry {
pub listed: bool,
pub latency: i32,
pub game_mode: GameType,
- pub display_name: Option<Component>,
+ pub display_name: Option<FormattedText>,
pub chat_session: Option<RemoteChatSessionData>,
}
@@ -53,7 +53,7 @@ pub struct UpdateLatencyAction {
}
#[derive(Clone, Debug, McBuf)]
pub struct UpdateDisplayNameAction {
- pub display_name: Option<Component>,
+ pub display_name: Option<FormattedText>,
}
impl McBufReadable for ClientboundPlayerInfoUpdatePacket {
diff --git a/azalea-protocol/src/packets/game/clientbound_resource_pack_packet.rs b/azalea-protocol/src/packets/game/clientbound_resource_pack_packet.rs
index 73ade728..a545ff31 100755
--- a/azalea-protocol/src/packets/game/clientbound_resource_pack_packet.rs
+++ b/azalea-protocol/src/packets/game/clientbound_resource_pack_packet.rs
@@ -1,5 +1,5 @@
use azalea_buf::McBuf;
-use azalea_chat::Component;
+use azalea_chat::FormattedText;
use azalea_protocol_macros::ClientboundGamePacket;
#[derive(Clone, Debug, McBuf, ClientboundGamePacket)]
@@ -7,5 +7,5 @@ pub struct ClientboundResourcePackPacket {
pub url: String,
pub hash: String,
pub required: bool,
- pub prompt: Option<Component>,
+ pub prompt: Option<FormattedText>,
}
diff --git a/azalea-protocol/src/packets/game/clientbound_server_data_packet.rs b/azalea-protocol/src/packets/game/clientbound_server_data_packet.rs
index 16841e3a..4b2bf055 100755
--- a/azalea-protocol/src/packets/game/clientbound_server_data_packet.rs
+++ b/azalea-protocol/src/packets/game/clientbound_server_data_packet.rs
@@ -1,10 +1,10 @@
use azalea_buf::McBuf;
-use azalea_chat::Component;
+use azalea_chat::FormattedText;
use azalea_protocol_macros::ClientboundGamePacket;
#[derive(Clone, Debug, McBuf, ClientboundGamePacket)]
pub struct ClientboundServerDataPacket {
- pub motd: Option<Component>,
+ pub motd: Option<FormattedText>,
pub icon_base64: Option<String>,
pub enforces_secure_chat: bool,
}
diff --git a/azalea-protocol/src/packets/game/clientbound_set_action_bar_text_packet.rs b/azalea-protocol/src/packets/game/clientbound_set_action_bar_text_packet.rs
index e279e33c..60b80fe9 100755
--- a/azalea-protocol/src/packets/game/clientbound_set_action_bar_text_packet.rs
+++ b/azalea-protocol/src/packets/game/clientbound_set_action_bar_text_packet.rs
@@ -1,8 +1,8 @@
use azalea_buf::McBuf;
-use azalea_chat::Component;
+use azalea_chat::FormattedText;
use azalea_protocol_macros::ClientboundGamePacket;
#[derive(Clone, Debug, McBuf, ClientboundGamePacket)]
pub struct ClientboundSetActionBarTextPacket {
- pub text: Component,
+ pub text: FormattedText,
}
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 9e2da325..3809b5b6 100755
--- a/azalea-protocol/src/packets/game/clientbound_set_objective_packet.rs
+++ b/azalea-protocol/src/packets/game/clientbound_set_objective_packet.rs
@@ -1,5 +1,5 @@
use azalea_buf::{BufReadError, McBuf, McBufReadable, McBufWritable};
-use azalea_chat::Component;
+use azalea_chat::FormattedText;
use azalea_protocol_macros::ClientboundGamePacket;
use std::io::{Cursor, Write};
@@ -48,7 +48,7 @@ impl McBufWritable for Method {
#[derive(McBuf, Clone, Debug)]
pub struct DisplayInfo {
- pub display_name: Component,
+ pub display_name: FormattedText,
pub render_type: RenderType,
}
diff --git a/azalea-protocol/src/packets/game/clientbound_set_player_team_packet.rs b/azalea-protocol/src/packets/game/clientbound_set_player_team_packet.rs
index 0b3c1e24..1a09aeb5 100755
--- a/azalea-protocol/src/packets/game/clientbound_set_player_team_packet.rs
+++ b/azalea-protocol/src/packets/game/clientbound_set_player_team_packet.rs
@@ -1,5 +1,5 @@
use azalea_buf::{BufReadError, McBuf, McBufReadable, McBufWritable};
-use azalea_chat::{style::ChatFormatting, Component};
+use azalea_chat::{style::ChatFormatting, FormattedText};
use azalea_protocol_macros::ClientboundGamePacket;
use std::io::{Cursor, Write};
@@ -61,13 +61,13 @@ impl McBufWritable for Method {
#[derive(McBuf, Clone, Debug)]
pub struct Parameters {
- pub display_name: Component,
+ pub display_name: FormattedText,
pub options: u8,
pub nametag_visibility: String,
pub collision_rule: String,
pub color: ChatFormatting,
- pub player_prefix: Component,
- pub player_suffix: Component,
+ pub player_prefix: FormattedText,
+ pub player_suffix: FormattedText,
}
type PlayerList = Vec<String>;
diff --git a/azalea-protocol/src/packets/game/clientbound_set_subtitle_text_packet.rs b/azalea-protocol/src/packets/game/clientbound_set_subtitle_text_packet.rs
index b7d1e7a4..9b25ac05 100755
--- a/azalea-protocol/src/packets/game/clientbound_set_subtitle_text_packet.rs
+++ b/azalea-protocol/src/packets/game/clientbound_set_subtitle_text_packet.rs
@@ -1,8 +1,8 @@
use azalea_buf::McBuf;
-use azalea_chat::Component;
+use azalea_chat::FormattedText;
use azalea_protocol_macros::ClientboundGamePacket;
#[derive(Clone, Debug, McBuf, ClientboundGamePacket)]
pub struct ClientboundSetSubtitleTextPacket {
- pub text: Component,
+ pub text: FormattedText,
}
diff --git a/azalea-protocol/src/packets/game/clientbound_set_title_text_packet.rs b/azalea-protocol/src/packets/game/clientbound_set_title_text_packet.rs
index a9185e1a..fb00a4e5 100755
--- a/azalea-protocol/src/packets/game/clientbound_set_title_text_packet.rs
+++ b/azalea-protocol/src/packets/game/clientbound_set_title_text_packet.rs
@@ -1,8 +1,8 @@
use azalea_buf::McBuf;
-use azalea_chat::Component;
+use azalea_chat::FormattedText;
use azalea_protocol_macros::ClientboundGamePacket;
#[derive(Clone, Debug, McBuf, ClientboundGamePacket)]
pub struct ClientboundSetTitleTextPacket {
- pub text: Component,
+ pub text: FormattedText,
}
diff --git a/azalea-protocol/src/packets/game/clientbound_system_chat_packet.rs b/azalea-protocol/src/packets/game/clientbound_system_chat_packet.rs
index 9fe03fb2..8a5823a0 100755
--- a/azalea-protocol/src/packets/game/clientbound_system_chat_packet.rs
+++ b/azalea-protocol/src/packets/game/clientbound_system_chat_packet.rs
@@ -1,9 +1,9 @@
use azalea_buf::McBuf;
-use azalea_chat::Component;
+use azalea_chat::FormattedText;
use azalea_protocol_macros::ClientboundGamePacket;
#[derive(Clone, Debug, McBuf, ClientboundGamePacket, PartialEq)]
pub struct ClientboundSystemChatPacket {
- pub content: Component,
+ pub content: FormattedText,
pub overlay: bool,
}
diff --git a/azalea-protocol/src/packets/game/clientbound_tab_list_packet.rs b/azalea-protocol/src/packets/game/clientbound_tab_list_packet.rs
index 94f23241..47dd1ab2 100755
--- a/azalea-protocol/src/packets/game/clientbound_tab_list_packet.rs
+++ b/azalea-protocol/src/packets/game/clientbound_tab_list_packet.rs
@@ -1,9 +1,9 @@
use azalea_buf::McBuf;
-use azalea_chat::Component;
+use azalea_chat::FormattedText;
use azalea_protocol_macros::ClientboundGamePacket;
#[derive(Clone, Debug, McBuf, ClientboundGamePacket)]
pub struct ClientboundTabListPacket {
- pub header: Component,
- pub footer: Component,
+ pub header: FormattedText,
+ pub footer: FormattedText,
}
diff --git a/azalea-protocol/src/packets/game/clientbound_teleport_entity_packet.rs b/azalea-protocol/src/packets/game/clientbound_teleport_entity_packet.rs
index 05a912ba..eceaa3aa 100755
--- a/azalea-protocol/src/packets/game/clientbound_teleport_entity_packet.rs
+++ b/azalea-protocol/src/packets/game/clientbound_teleport_entity_packet.rs
@@ -1,13 +1,12 @@
use azalea_buf::McBuf;
+use azalea_core::Vec3;
use azalea_protocol_macros::ClientboundGamePacket;
#[derive(Clone, Debug, McBuf, ClientboundGamePacket)]
pub struct ClientboundTeleportEntityPacket {
#[var]
pub id: u32,
- pub x: f64,
- pub y: f64,
- pub z: f64,
+ pub position: Vec3,
pub y_rot: i8,
pub x_rot: i8,
pub on_ground: bool,
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 e2a46d38..038cdcf2 100755
--- a/azalea-protocol/src/packets/game/clientbound_update_advancements_packet.rs
+++ b/azalea-protocol/src/packets/game/clientbound_update_advancements_packet.rs
@@ -1,5 +1,5 @@
use azalea_buf::McBuf;
-use azalea_chat::Component;
+use azalea_chat::FormattedText;
use azalea_core::{ResourceLocation, Slot};
use azalea_protocol_macros::ClientboundGamePacket;
use std::collections::HashMap;
@@ -23,8 +23,8 @@ pub struct Advancement {
#[derive(Clone, Debug)]
pub struct DisplayInfo {
- pub title: Component,
- pub description: Component,
+ pub title: FormattedText,
+ pub description: FormattedText,
pub icon: Slot,
pub frame: FrameType,
pub show_toast: bool,
@@ -128,8 +128,8 @@ mod tests {
Advancement {
parent_id: None,
display: Some(DisplayInfo {
- title: Component::from("title".to_string()),
- description: Component::from("description".to_string()),
+ title: FormattedText::from("title".to_string()),
+ description: FormattedText::from("description".to_string()),
icon: Slot::Empty,
frame: FrameType::Task,
show_toast: true,
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 989009f4..dbc08b16 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
@@ -15,8 +15,7 @@ pub struct ServerboundSetJigsawBlockPacket {
pub target: ResourceLocation,
pub pool: ResourceLocation,
pub final_state: String,
- pub joint: String, /* TODO: Does JigsawBlockEntity$JointType::getSerializedName, may not be
- * implemented */
+ pub joint: String,
}
pub enum JointType {
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 983dc31b..31cd370d 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,8 @@
use azalea_buf::McBuf;
-use azalea_chat::Component;
+use azalea_chat::FormattedText;
use azalea_protocol_macros::ClientboundLoginPacket;
#[derive(Clone, Debug, McBuf, ClientboundLoginPacket)]
pub struct ClientboundLoginDisconnectPacket {
- pub reason: Component,
+ pub reason: FormattedText,
}
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 efa6080c..40452c87 100755
--- a/azalea-protocol/src/packets/status/clientbound_status_response_packet.rs
+++ b/azalea-protocol/src/packets/status/clientbound_status_response_packet.rs
@@ -1,5 +1,5 @@
use azalea_buf::{BufReadError, McBufReadable, McBufWritable};
-use azalea_chat::Component;
+use azalea_chat::FormattedText;
use azalea_protocol_macros::ClientboundStatusPacket;
use serde::{Deserialize, Serialize};
use serde_json::{value::Serializer, Value};
@@ -28,7 +28,7 @@ pub struct Players {
// the entire packet is just json, which is why it has deserialize
#[derive(Clone, Debug, Serialize, Deserialize, ClientboundStatusPacket)]
pub struct ClientboundStatusResponsePacket {
- pub description: Component,
+ pub description: FormattedText,
#[serde(default)]
#[serde(skip_serializing_if = "Option::is_none")]
pub favicon: Option<String>,
diff --git a/azalea-registry/Cargo.toml b/azalea-registry/Cargo.toml
index dcab681b..5f1d7e3a 100755
--- a/azalea-registry/Cargo.toml
+++ b/azalea-registry/Cargo.toml
@@ -3,11 +3,12 @@ description = "Use Minecraft's registries."
edition = "2021"
license = "MIT"
name = "azalea-registry"
-version = "0.5.0"
repository = "https://github.com/mat-1/azalea/tree/main/azalea-registry"
+version = "0.5.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.5.0" }
-azalea-registry-macros = {path = "./azalea-registry-macros", version = "^0.5.0" }
+azalea-buf = {path = "../azalea-buf", version = "^0.5.0"}
+azalea-registry-macros = {path = "./azalea-registry-macros", version = "^0.5.0"}
+enum-as-inner = "0.5.1"
diff --git a/azalea-registry/azalea-registry-macros/src/lib.rs b/azalea-registry/azalea-registry-macros/src/lib.rs
index 68f2330e..d72f24cf 100755
--- a/azalea-registry/azalea-registry-macros/src/lib.rs
+++ b/azalea-registry/azalea-registry-macros/src/lib.rs
@@ -128,7 +128,7 @@ pub fn registry(input: TokenStream) -> TokenStream {
// Display that uses registry ids
let mut display_items = quote! {};
- for item in input.items.iter() {
+ for item in &input.items {
let name = &item.name;
let id = &item.id;
display_items.extend(quote! {
diff --git a/azalea-registry/src/lib.rs b/azalea-registry/src/lib.rs
index 7a0b9234..96c9ea26 100755
--- a/azalea-registry/src/lib.rs
+++ b/azalea-registry/src/lib.rs
@@ -1103,7 +1103,7 @@ registry!(Block, {
ReinforcedDeepslate => "minecraft:reinforced_deepslate",
});
-registry!(BlockEntityType, {
+registry!(BlockEntityKind, {
Furnace => "minecraft:furnace",
Chest => "minecraft:chest",
TrappedChest => "minecraft:trapped_chest",
@@ -1144,7 +1144,7 @@ registry!(BlockEntityType, {
ChiseledBookshelf => "minecraft:chiseled_bookshelf",
});
-registry!(BlockPredicateType, {
+registry!(BlockPredicateKind, {
MatchingBlocks => "minecraft:matching_blocks",
MatchingBlockTag => "minecraft:matching_block_tag",
MatchingFluids => "minecraft:matching_fluids",
@@ -1189,7 +1189,7 @@ registry!(ChunkStatus, {
Full => "minecraft:full",
});
-registry!(CommandArgumentType, {
+registry!(CommandArgumentKind, {
Bool => "brigadier:bool",
Float => "brigadier:float",
Double => "brigadier:double",
@@ -1360,7 +1360,7 @@ registry!(Enchantment, {
VanishingCurse => "minecraft:vanishing_curse",
});
-registry!(EntityType, {
+registry!(EntityKind, {
Allay => "minecraft:allay",
AreaEffectCloud => "minecraft:area_effect_cloud",
ArmorStand => "minecraft:armor_stand",
@@ -1482,7 +1482,7 @@ registry!(EntityType, {
FishingBobber => "minecraft:fishing_bobber",
});
-registry!(FloatProviderType, {
+registry!(FloatProviderKind, {
Constant => "minecraft:constant",
Uniform => "minecraft:uniform",
ClampedNormal => "minecraft:clamped_normal",
@@ -1552,7 +1552,7 @@ registry!(GameEvent, {
Teleport => "minecraft:teleport",
});
-registry!(HeightProviderType, {
+registry!(HeightProviderKind, {
Constant => "minecraft:constant",
Uniform => "minecraft:uniform",
BiasedToBottom => "minecraft:biased_to_bottom",
@@ -1572,7 +1572,7 @@ registry!(Instrument, {
DreamGoatHorn => "minecraft:dream_goat_horn",
});
-registry!(IntProviderType, {
+registry!(IntProviderKind, {
Constant => "minecraft:constant",
Uniform => "minecraft:uniform",
BiasedToBottom => "minecraft:biased_to_bottom",
@@ -2770,7 +2770,7 @@ registry!(Item, {
EchoShard => "minecraft:echo_shard",
});
-registry!(LootConditionType, {
+registry!(LootConditionKind, {
Inverted => "minecraft:inverted",
Alternative => "minecraft:alternative",
RandomChance => "minecraft:random_chance",
@@ -2790,7 +2790,7 @@ registry!(LootConditionType, {
ValueCheck => "minecraft:value_check",
});
-registry!(LootFunctionType, {
+registry!(LootFunctionKind, {
SetCount => "minecraft:set_count",
EnchantWithLevels => "minecraft:enchant_with_levels",
EnchantRandomly => "minecraft:enchant_randomly",
@@ -2818,19 +2818,19 @@ registry!(LootFunctionType, {
SetInstrument => "minecraft:set_instrument",
});
-registry!(LootNbtProviderType, {
+registry!(LootNbtProviderKind, {
Storage => "minecraft:storage",
Context => "minecraft:context",
});
-registry!(LootNumberProviderType, {
+registry!(LootNumberProviderKind, {
Constant => "minecraft:constant",
Uniform => "minecraft:uniform",
Binomial => "minecraft:binomial",
Score => "minecraft:score",
});
-registry!(LootPoolEntryType, {
+registry!(LootPoolEntryKind, {
Empty => "minecraft:empty",
Item => "minecraft:item",
LootTable => "minecraft:loot_table",
@@ -2841,12 +2841,12 @@ registry!(LootPoolEntryType, {
Group => "minecraft:group",
});
-registry!(LootScoreProviderType, {
+registry!(LootScoreProviderKind, {
Fixed => "minecraft:fixed",
Context => "minecraft:context",
});
-registry!(MemoryModuleType, {
+registry!(MemoryModuleKind, {
Dummy => "minecraft:dummy",
Home => "minecraft:home",
JobSite => "minecraft:job_site",
@@ -3038,7 +3038,7 @@ registry!(PaintingVariant, {
DonkeyKong => "minecraft:donkey_kong",
});
-registry!(ParticleType, {
+registry!(ParticleKind, {
AmbientEntityEffect => "minecraft:ambient_entity_effect",
AngryVillager => "minecraft:angry_villager",
Block => "minecraft:block",
@@ -3134,7 +3134,7 @@ registry!(ParticleType, {
Shriek => "minecraft:shriek",
});
-registry!(PointOfInterestType, {
+registry!(PointOfInterestKind, {
Armorer => "minecraft:armorer",
Butcher => "minecraft:butcher",
Cartographer => "minecraft:cartographer",
@@ -3163,7 +3163,7 @@ registry!(PosRuleTest, {
AxisAlignedLinearPos => "minecraft:axis_aligned_linear_pos",
});
-registry!(PositionSourceType, {
+registry!(PositionSourceKind, {
Block => "minecraft:block",
Entity => "minecraft:entity",
});
@@ -3238,7 +3238,7 @@ registry!(RecipeSerializer, {
Smithing => "minecraft:smithing",
});
-registry!(RecipeType, {
+registry!(RecipeKind, {
Crafting => "minecraft:crafting",
Smelting => "minecraft:smelting",
Blasting => "minecraft:blasting",
@@ -3264,7 +3264,7 @@ registry!(Schedule, {
VillagerDefault => "minecraft:villager_default",
});
-registry!(SensorType, {
+registry!(SensorKind, {
Dummy => "minecraft:dummy",
NearestItems => "minecraft:nearest_items",
NearestLivingEntities => "minecraft:nearest_living_entities",
@@ -4684,7 +4684,7 @@ registry!(SoundEvent, {
EntityZombieVillagerStep => "minecraft:entity.zombie_villager.step",
});
-registry!(StatType, {
+registry!(StatKind, {
Mined => "minecraft:mined",
Crafted => "minecraft:crafted",
Used => "minecraft:used",
@@ -4714,7 +4714,7 @@ registry!(VillagerProfession, {
Weaponsmith => "minecraft:weaponsmith",
});
-registry!(VillagerType, {
+registry!(VillagerKind, {
Desert => "minecraft:desert",
Jungle => "minecraft:jungle",
Plains => "minecraft:plains",
@@ -4731,7 +4731,7 @@ registry!(WorldgenBiomeSource, {
TheEnd => "minecraft:the_end",
});
-registry!(WorldgenBlockStateProviderType, {
+registry!(WorldgenBlockStateProviderKind, {
SimpleStateProvider => "minecraft:simple_state_provider",
WeightedStateProvider => "minecraft:weighted_state_provider",
NoiseThresholdProvider => "minecraft:noise_threshold_provider",
@@ -4753,7 +4753,7 @@ registry!(WorldgenChunkGenerator, {
Debug => "minecraft:debug",
});
-registry!(WorldgenDensityFunctionType, {
+registry!(WorldgenDensityFunctionKind, {
BlendAlpha => "minecraft:blend_alpha",
BlendOffset => "minecraft:blend_offset",
Beardifier => "minecraft:beardifier",
@@ -4852,12 +4852,12 @@ registry!(WorldgenFeature, {
SculkPatch => "minecraft:sculk_patch",
});
-registry!(WorldgenFeatureSizeType, {
+registry!(WorldgenFeatureSizeKind, {
TwoLayersFeatureSize => "minecraft:two_layers_feature_size",
ThreeLayersFeatureSize => "minecraft:three_layers_feature_size",
});
-registry!(WorldgenFoliagePlacerType, {
+registry!(WorldgenFoliagePlacerKind, {
BlobFoliagePlacer => "minecraft:blob_foliage_placer",
SpruceFoliagePlacer => "minecraft:spruce_foliage_placer",
PineFoliagePlacer => "minecraft:pine_foliage_placer",
@@ -4891,7 +4891,7 @@ registry!(WorldgenMaterialRule, {
Condition => "minecraft:condition",
});
-registry!(WorldgenPlacementModifierType, {
+registry!(WorldgenPlacementModifierKind, {
BlockPredicateFilter => "minecraft:block_predicate_filter",
RarityFilter => "minecraft:rarity_filter",
SurfaceRelativeThresholdFilter => "minecraft:surface_relative_threshold_filter",
@@ -4909,7 +4909,7 @@ registry!(WorldgenPlacementModifierType, {
CarvingMask => "minecraft:carving_mask",
});
-registry!(WorldgenRootPlacerType, {
+registry!(WorldgenRootPlacerKind, {
MangroveRootPlacer => "minecraft:mangrove_root_placer",
});
@@ -4998,7 +4998,7 @@ registry!(WorldgenStructureProcessor, {
ProtectedBlocks => "minecraft:protected_blocks",
});
-registry!(WorldgenStructureType, {
+registry!(WorldgenStructureKind, {
BuriedTreasure => "minecraft:buried_treasure",
DesertPyramid => "minecraft:desert_pyramid",
EndCity => "minecraft:end_city",
@@ -5017,7 +5017,7 @@ registry!(WorldgenStructureType, {
WoodlandMansion => "minecraft:woodland_mansion",
});
-registry!(WorldgenTreeDecoratorType, {
+registry!(WorldgenTreeDecoratorKind, {
TrunkVine => "minecraft:trunk_vine",
LeaveVine => "minecraft:leave_vine",
Cocoa => "minecraft:cocoa",
@@ -5026,7 +5026,7 @@ registry!(WorldgenTreeDecoratorType, {
AttachedToLeaves => "minecraft:attached_to_leaves",
});
-registry!(WorldgenTrunkPlacerType, {
+registry!(WorldgenTrunkPlacerKind, {
StraightTrunkPlacer => "minecraft:straight_trunk_placer",
ForkingTrunkPlacer => "minecraft:forking_trunk_placer",
GiantTrunkPlacer => "minecraft:giant_trunk_placer",
diff --git a/azalea-world/Cargo.toml b/azalea-world/Cargo.toml
index 00d81ee1..4ddbd54f 100644
--- a/azalea-world/Cargo.toml
+++ b/azalea-world/Cargo.toml
@@ -9,15 +9,19 @@ version = "0.5.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.5.0" }
-azalea-buf = {path = "../azalea-buf", version = "^0.5.0" }
-azalea-chat = {path = "../azalea-chat", version = "^0.5.0" }
-azalea-core = {path = "../azalea-core", version = "^0.5.0" }
-azalea-nbt = {path = "../azalea-nbt", version = "^0.5.0" }
-azalea-registry = {path = "../azalea-registry", version = "^0.5.0" }
+azalea-block = {path = "../azalea-block", default-features = false, version = "^0.5.0"}
+azalea-buf = {path = "../azalea-buf", version = "^0.5.0"}
+azalea-chat = {path = "../azalea-chat", version = "^0.5.0"}
+azalea-core = {path = "../azalea-core", version = "^0.5.0", features = ["bevy_ecs"]}
+azalea-ecs = { version = "0.5.0", path = "../azalea-ecs" }
+azalea-nbt = {path = "../azalea-nbt", version = "^0.5.0"}
+azalea-registry = {path = "../azalea-registry", version = "^0.5.0"}
+derive_more = {version = "0.99.17", features = ["deref", "deref_mut"]}
enum-as-inner = "0.5.1"
+iyes_loopless = "0.9.1"
log = "0.4.17"
nohash-hasher = "0.2.0"
+once_cell = "1.16.0"
parking_lot = "^0.12.1"
thiserror = "1.0.34"
uuid = "1.1.2"
diff --git a/azalea-world/src/chunk_storage.rs b/azalea-world/src/chunk_storage.rs
index c0c5f43a..734dfe29 100755
--- a/azalea-world/src/chunk_storage.rs
+++ b/azalea-world/src/chunk_storage.rs
@@ -15,13 +15,10 @@ use std::{
const SECTION_HEIGHT: u32 = 16;
/// An efficient storage of chunks for a client that has a limited render
-/// distance. This has support for using a shared [`WeakChunkStorage`]. If you
-/// have an infinite render distance (like a server), you should use
-/// [`ChunkStorage`] instead.
+/// distance. This has support for using a shared [`ChunkStorage`].
pub struct PartialChunkStorage {
- /// Chunk storage that can be shared by clients.
- pub shared: Arc<RwLock<WeakChunkStorage>>,
-
+ /// The center of the view, i.e. the chunk the player is currently in. You
+ /// can safely modify this.
pub view_center: ChunkPos,
chunk_radius: u32,
view_range: u32,
@@ -33,23 +30,16 @@ pub struct PartialChunkStorage {
/// actively being used somewhere else they'll be forgotten. This is used for
/// shared worlds.
#[derive(Debug)]
-pub struct WeakChunkStorage {
- pub height: u32,
- pub min_y: i32,
- pub chunks: HashMap<ChunkPos, Weak<RwLock<Chunk>>>,
-}
-
-/// A storage of potentially infinite chunks in a world. Chunks are stored as
-/// an `Arc<Mutex>` so they can be shared across threads.
pub struct ChunkStorage {
pub height: u32,
pub min_y: i32,
- pub chunks: HashMap<ChunkPos, Arc<RwLock<Chunk>>>,
+ pub chunks: HashMap<ChunkPos, Weak<RwLock<Chunk>>>,
}
/// A single chunk in a world (16*?*16 blocks). This only contains the blocks
/// and biomes. You can derive the height of the chunk from the number of
-/// sections, but you need a [`ChunkStorage`] to get the minimum Y coordinate.
+/// sections, but you need a [`ChunkStorage`] to get the minimum Y
+/// coordinate.
#[derive(Debug)]
pub struct Chunk {
pub sections: Vec<Section>,
@@ -82,10 +72,9 @@ impl Default for Chunk {
}
impl PartialChunkStorage {
- pub fn new(chunk_radius: u32, shared: Arc<RwLock<WeakChunkStorage>>) -> Self {
+ pub fn new(chunk_radius: u32) -> Self {
let view_range = chunk_radius * 2 + 1;
PartialChunkStorage {
- shared,
view_center: ChunkPos::new(0, 0),
chunk_radius,
view_range,
@@ -93,13 +82,6 @@ impl PartialChunkStorage {
}
}
- pub fn min_y(&self) -> i32 {
- self.shared.read().min_y
- }
- pub fn height(&self) -> u32 {
- self.shared.read().height
- }
-
fn get_index(&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
@@ -110,20 +92,28 @@ impl PartialChunkStorage {
&& (chunk_pos.z - self.view_center.z).unsigned_abs() <= self.chunk_radius
}
- pub fn set_block_state(&self, pos: &BlockPos, state: BlockState) -> Option<BlockState> {
- if pos.y < self.min_y() || pos.y >= (self.min_y() + self.height() as i32) {
+ pub fn set_block_state(
+ &self,
+ pos: &BlockPos,
+ state: BlockState,
+ chunk_storage: &mut ChunkStorage,
+ ) -> Option<BlockState> {
+ if pos.y < chunk_storage.min_y
+ || pos.y >= (chunk_storage.min_y + chunk_storage.height as i32)
+ {
return None;
}
let chunk_pos = ChunkPos::from(pos);
- let chunk = self.get(&chunk_pos)?;
- let mut chunk = chunk.write();
- Some(chunk.get_and_set(&ChunkBlockPos::from(pos), state, self.min_y()))
+ let chunk_lock = chunk_storage.get(&chunk_pos)?;
+ let mut chunk = chunk_lock.write();
+ Some(chunk.get_and_set(&ChunkBlockPos::from(pos), state, chunk_storage.min_y))
}
pub fn replace_with_packet_data(
&mut self,
pos: &ChunkPos,
data: &mut Cursor<&[u8]>,
+ chunk_storage: &mut ChunkStorage,
) -> Result<(), BufReadError> {
debug!("Replacing chunk at {:?}", pos);
if !self.in_range(pos) {
@@ -135,19 +125,16 @@ impl PartialChunkStorage {
return Ok(());
}
- let chunk = Arc::new(RwLock::new(Chunk::read_with_dimension_height(
- data,
- self.height(),
- )?));
+ let chunk = Chunk::read_with_dimension_height(data, chunk_storage.height)?;
trace!("Loaded chunk {:?}", pos);
- self.set(pos, Some(chunk));
+ self.set(pos, Some(chunk), chunk_storage);
Ok(())
}
/// Get a [`Chunk`] within render distance, or `None` if it's not loaded.
- /// Use [`PartialChunkStorage::get`] to get a chunk from the shared storage.
+ /// Use [`ChunkStorage::get`] to get a chunk from the shared storage.
pub fn limited_get(&self, pos: &ChunkPos) -> Option<&Arc<RwLock<Chunk>>> {
if !self.in_range(pos) {
warn!(
@@ -161,7 +148,7 @@ impl PartialChunkStorage {
self.chunks[index].as_ref()
}
/// Get a mutable reference to a [`Chunk`] within render distance, or
- /// `None` if it's not loaded. Use [`PartialChunkStorage::get`] to get
+ /// `None` if it's not loaded. Use [`ChunkStorage::get`] to get
/// a chunk from the shared storage.
pub fn limited_get_mut(&mut self, pos: &ChunkPos) -> Option<&mut Option<Arc<RwLock<Chunk>>>> {
if !self.in_range(pos) {
@@ -172,26 +159,30 @@ impl PartialChunkStorage {
Some(&mut self.chunks[index])
}
- /// Get a chunk,
- pub fn get(&self, pos: &ChunkPos) -> Option<Arc<RwLock<Chunk>>> {
- self.shared
- .read()
- .chunks
- .get(pos)
- .and_then(|chunk| chunk.upgrade())
+ /// 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<RwLock<Chunk>>`.
+ ///
+ /// # Panics
+ /// If the chunk is not in the render distance.
+ pub fn set(&mut self, pos: &ChunkPos, chunk: Option<Chunk>, chunk_storage: &mut ChunkStorage) {
+ self.set_with_shared_reference(pos, chunk.map(|c| Arc::new(RwLock::new(c))), chunk_storage);
}
/// Set a chunk in the shared storage and reference it from the limited
- /// storage.
+ /// storage. Use [`Self::set`] if you don't already have an
+ /// `Arc<RwLock<Chunk>>` (it'll make it for you).
///
/// # Panics
/// If the chunk is not in the render distance.
- pub fn set(&mut self, pos: &ChunkPos, chunk: Option<Arc<RwLock<Chunk>>>) {
+ pub fn set_with_shared_reference(
+ &mut self,
+ pos: &ChunkPos,
+ chunk: Option<Arc<RwLock<Chunk>>>,
+ chunk_storage: &mut ChunkStorage,
+ ) {
if let Some(chunk) = &chunk {
- self.shared
- .write()
- .chunks
- .insert(*pos, Arc::downgrade(chunk));
+ chunk_storage.chunks.insert(*pos, Arc::downgrade(chunk));
} else {
// don't remove it from the shared storage, since it'll be removed
// automatically if this was the last reference
@@ -201,9 +192,9 @@ impl PartialChunkStorage {
}
}
}
-impl WeakChunkStorage {
+impl ChunkStorage {
pub fn new(height: u32, min_y: i32) -> Self {
- WeakChunkStorage {
+ ChunkStorage {
height,
min_y,
chunks: HashMap::new(),
@@ -280,7 +271,7 @@ impl Chunk {
// TODO: make sure the section exists
let section = &mut self.sections[section_index as usize];
let chunk_section_pos = ChunkSectionBlockPos::from(pos);
- section.set(chunk_section_pos, state)
+ section.set(chunk_section_pos, state);
}
}
@@ -295,12 +286,10 @@ impl McBufWritable for Chunk {
impl Debug for PartialChunkStorage {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- f.debug_struct("ChunkStorage")
+ f.debug_struct("PartialChunkStorage")
.field("view_center", &self.view_center)
.field("chunk_radius", &self.chunk_radius)
.field("view_range", &self.view_range)
- .field("height", &self.height())
- .field("min_y", &self.min_y())
// .field("chunks", &self.chunks)
.field("chunks", &format_args!("{} items", self.chunks.len()))
.finish()
@@ -373,10 +362,10 @@ impl Section {
impl Default for PartialChunkStorage {
fn default() -> Self {
- Self::new(8, Arc::new(RwLock::new(WeakChunkStorage::default())))
+ Self::new(8)
}
}
-impl Default for WeakChunkStorage {
+impl Default for ChunkStorage {
fn default() -> Self {
Self::new(384, -64)
}
@@ -408,34 +397,26 @@ mod tests {
#[test]
fn test_out_of_bounds_y() {
- let mut chunk_storage = PartialChunkStorage::default();
- chunk_storage.set(
+ let mut chunk_storage = ChunkStorage::default();
+ let mut partial_chunk_storage = PartialChunkStorage::default();
+ partial_chunk_storage.set(
&ChunkPos { x: 0, z: 0 },
- Some(Arc::new(RwLock::new(Chunk::default()))),
+ Some(Chunk::default()),
+ &mut chunk_storage,
);
assert!(chunk_storage
- .shared
- .read()
.get_block_state(&BlockPos { x: 0, y: 319, z: 0 })
.is_some());
assert!(chunk_storage
- .shared
- .read()
.get_block_state(&BlockPos { x: 0, y: 320, z: 0 })
.is_none());
assert!(chunk_storage
- .shared
- .read()
.get_block_state(&BlockPos { x: 0, y: 338, z: 0 })
.is_none());
assert!(chunk_storage
- .shared
- .read()
.get_block_state(&BlockPos { x: 0, y: -64, z: 0 })
.is_some());
assert!(chunk_storage
- .shared
- .read()
.get_block_state(&BlockPos { x: 0, y: -65, z: 0 })
.is_none());
}
diff --git a/azalea-world/src/container.rs b/azalea-world/src/container.rs
index acdc9b05..74d70659 100644
--- a/azalea-world/src/container.rs
+++ b/azalea-world/src/container.rs
@@ -1,52 +1,76 @@
-use crate::WeakWorld;
use azalea_core::ResourceLocation;
+use azalea_ecs::system::Resource;
use log::error;
+use nohash_hasher::IntMap;
+use parking_lot::RwLock;
use std::{
collections::HashMap,
sync::{Arc, Weak},
};
-/// A container of [`WeakWorld`]s. Worlds are stored as a Weak pointer here, so
+use crate::{ChunkStorage, World};
+
+/// A container of [`World`]s. Worlds are stored as a Weak pointer here, so
/// if no clients are using a world it will be forgotten.
-#[derive(Default)]
-pub struct WeakWorldContainer {
- pub worlds: HashMap<ResourceLocation, Weak<WeakWorld>>,
+#[derive(Default, Resource)]
+pub struct WorldContainer {
+ // We just refer to the chunks here and don't include entities because there's not that many
+ // cases where we'd want to get every entity in the world (just getting the entities in chunks
+ // should work fine).
+
+ // Entities are garbage collected (by manual reference counting in EntityInfos) so we don't
+ // need to worry about them here.
+
+ // If it looks like we're relying on the server giving us unique world names, that's because we
+ // are. An evil server could give us two worlds with the same name and then we'd have no way of
+ // telling them apart. We hope most servers are nice and don't do that though. It's only an
+ // issue when there's multiple clients with the same WorldContainer in different worlds
+ // anyways.
+ pub worlds: HashMap<ResourceLocation, Weak<RwLock<World>>>,
}
-impl WeakWorldContainer {
+impl WorldContainer {
pub fn new() -> Self {
- WeakWorldContainer {
+ WorldContainer {
worlds: HashMap::new(),
}
}
/// Get a world from the container.
- pub fn get(&self, name: &ResourceLocation) -> Option<Arc<WeakWorld>> {
+ pub fn get(&self, name: &ResourceLocation) -> Option<Arc<RwLock<World>>> {
self.worlds.get(name).and_then(|world| world.upgrade())
}
/// Add an empty world to the container (or not if it already exists) and
/// returns a strong reference to the world.
#[must_use = "the world will be immediately forgotten if unused"]
- pub fn insert(&mut self, name: ResourceLocation, height: u32, min_y: i32) -> Arc<WeakWorld> {
- if let Some(existing) = self.worlds.get(&name).and_then(|world| world.upgrade()) {
- if existing.height() != height {
+ pub fn insert(
+ &mut self,
+ name: ResourceLocation,
+ height: u32,
+ min_y: i32,
+ ) -> Arc<RwLock<World>> {
+ if let Some(existing_lock) = self.worlds.get(&name).and_then(|world| world.upgrade()) {
+ let existing = existing_lock.read();
+ if existing.chunks.height != height {
error!(
"Shared dimension height mismatch: {} != {}",
- existing.height(),
- height,
+ existing.chunks.height, height,
);
}
- if existing.min_y() != min_y {
+ if existing.chunks.min_y != min_y {
error!(
"Shared world min_y mismatch: {} != {}",
- existing.min_y(),
- min_y,
+ existing.chunks.min_y, min_y,
);
}
- existing
+ existing_lock.clone()
} else {
- let world = Arc::new(WeakWorld::new(height, min_y));
+ let world = Arc::new(RwLock::new(World {
+ chunks: ChunkStorage::new(height, min_y),
+ entities_by_chunk: HashMap::new(),
+ entity_by_id: IntMap::default(),
+ }));
self.worlds.insert(name, Arc::downgrade(&world));
world
}
diff --git a/azalea-world/src/entity/attributes.rs b/azalea-world/src/entity/attributes.rs
index fca6b88f..fd78a328 100644
--- a/azalea-world/src/entity/attributes.rs
+++ b/azalea-world/src/entity/attributes.rs
@@ -6,11 +6,12 @@ use std::{
};
use azalea_buf::{BufReadError, McBuf, McBufReadable, McBufWritable};
+use azalea_ecs::component::Component;
use thiserror::Error;
use uuid::{uuid, Uuid};
-#[derive(Clone, Debug)]
-pub struct AttributeModifiers {
+#[derive(Clone, Debug, Component)]
+pub struct Attributes {
pub speed: AttributeInstance,
}
@@ -41,7 +42,7 @@ impl AttributeInstance {
_ => {}
}
if let AttributeModifierOperation::MultiplyTotal = modifier.operation {
- total *= 1.0 + modifier.amount
+ total *= 1.0 + modifier.amount;
}
}
total
diff --git a/azalea-world/src/entity/data.rs b/azalea-world/src/entity/data.rs
index baebd210..14d257e3 100755
--- a/azalea-world/src/entity/data.rs
+++ b/azalea-world/src/entity/data.rs
@@ -1,15 +1,20 @@
+//! Define some types needed for entity metadata.
+
use azalea_block::BlockState;
-use azalea_buf::{BufReadError, McBufVarReadable, McBufVarWritable};
-use azalea_buf::{McBuf, McBufReadable, McBufWritable};
-use azalea_chat::Component;
+use azalea_buf::{
+ BufReadError, McBuf, McBufReadable, McBufVarReadable, McBufVarWritable, McBufWritable,
+};
+use azalea_chat::FormattedText;
use azalea_core::{BlockPos, Direction, GlobalPos, Particle, Slot};
+use azalea_ecs::component::Component;
+use derive_more::Deref;
use enum_as_inner::EnumAsInner;
use nohash_hasher::IntSet;
use std::io::{Cursor, Write};
use uuid::Uuid;
-#[derive(Clone, Debug)]
-pub struct EntityMetadataItems(pub Vec<EntityDataItem>);
+#[derive(Clone, Debug, Deref)]
+pub struct EntityMetadataItems(Vec<EntityDataItem>);
#[derive(Clone, Debug)]
pub struct EntityDataItem {
@@ -52,8 +57,8 @@ pub enum EntityDataValue {
Long(i64),
Float(f32),
String(String),
- Component(Component),
- OptionalComponent(Option<Component>),
+ FormattedText(FormattedText),
+ OptionalFormattedText(Option<FormattedText>),
ItemStack(Slot),
Boolean(bool),
Rotations(Rotations),
@@ -105,7 +110,7 @@ pub struct Rotations {
pub z: f32,
}
-#[derive(Clone, Debug, Copy, McBuf, Default)]
+#[derive(Clone, Debug, Copy, McBuf, Default, Component)]
pub enum Pose {
#[default]
Standing = 0,
@@ -120,7 +125,7 @@ pub enum Pose {
#[derive(Debug, Clone, McBuf)]
pub struct VillagerData {
- pub kind: azalea_registry::VillagerType,
+ pub kind: azalea_registry::VillagerKind,
pub profession: azalea_registry::VillagerProfession,
#[var]
pub level: u32,
diff --git a/azalea-world/src/entity/dimensions.rs b/azalea-world/src/entity/dimensions.rs
index 1d013d10..daf85432 100755
--- a/azalea-world/src/entity/dimensions.rs
+++ b/azalea-world/src/entity/dimensions.rs
@@ -1,4 +1,7 @@
use azalea_core::{Vec3, AABB};
+use azalea_ecs::{query::Changed, system::Query};
+
+use super::{Physics, Position};
#[derive(Debug, Default)]
pub struct EntityDimensions {
@@ -21,3 +24,15 @@ impl EntityDimensions {
}
}
}
+
+/// Sets the position of the entity. This doesn't update the cache in
+/// azalea-world, and should only be used within azalea-world!
+///
+/// # Safety
+/// Cached position in the world must be updated.
+pub fn update_bounding_box(mut query: Query<(&Position, &mut Physics), Changed<Position>>) {
+ for (position, mut physics) in query.iter_mut() {
+ let bounding_box = physics.dimensions.make_bounding_box(position);
+ physics.bounding_box = bounding_box;
+ }
+}
diff --git a/azalea-world/src/entity/metadata.rs b/azalea-world/src/entity/metadata.rs
index 44ec6dae..c95d8c3a 100644
--- a/azalea-world/src/entity/metadata.rs
+++ b/azalea-world/src/entity/metadata.rs
@@ -1,8758 +1,10682 @@
+#![allow(clippy::single_match)]
+
// This file is generated from codegen/lib/code/entity.py.
// Don't change it manually!
-#![allow(clippy::clone_on_copy, clippy::derivable_impls)]
-use super::{EntityDataValue, OptionalUnsignedInt, Pose, Rotations, VillagerData};
+use super::{EntityDataItem, EntityDataValue, OptionalUnsignedInt, Pose, Rotations, VillagerData};
use azalea_block::BlockState;
-use azalea_chat::Component;
+use azalea_chat::FormattedText;
use azalea_core::{BlockPos, Direction, Particle, Slot};
-use enum_as_inner::EnumAsInner;
-use std::{
- collections::VecDeque,
- ops::{Deref, DerefMut},
-};
+use azalea_ecs::{bundle::Bundle, component::Component};
+use derive_more::{Deref, DerefMut};
+use thiserror::Error;
use uuid::Uuid;
-#[derive(Debug, Clone)]
-pub struct Allay {
- pub abstract_creature: AbstractCreature,
- pub dancing: bool,
- pub can_duplicate: bool,
-}
-
-impl Allay {
- pub fn read(metadata: &mut VecDeque<EntityDataValue>) -> Option<Self> {
- let abstract_creature = AbstractCreature::read(metadata)?;
- let dancing = metadata.pop_front()?.into_boolean().ok()?;
- let can_duplicate = metadata.pop_front()?.into_boolean().ok()?;
- Some(Self {
- abstract_creature,
- dancing,
- can_duplicate,
- })
- }
-
- pub fn write(&self) -> Vec<EntityDataValue> {
- let mut metadata = Vec::new();
- metadata.extend(self.abstract_creature.write());
- metadata.push(EntityDataValue::Boolean(self.dancing.clone()));
- metadata.push(EntityDataValue::Boolean(self.can_duplicate.clone()));
- metadata
- }
-}
-
-impl Default for Allay {
- fn default() -> Self {
- Self {
- abstract_creature: Default::default(),
- dancing: false,
- can_duplicate: true,
- }
- }
-}
-
+#[derive(Error, Debug)]
+pub enum UpdateMetadataError {
+ #[error("Wrong type ({0:?})")]
+ WrongType(EntityDataValue),
+}
+impl From<EntityDataValue> for UpdateMetadataError {
+ fn from(value: EntityDataValue) -> Self {
+ Self::WrongType(value)
+ }
+}
+
+#[derive(Component, Deref, DerefMut)]
+pub struct OnFire(pub bool);
+#[derive(Component, Deref, DerefMut)]
+pub struct ShiftKeyDown(pub bool);
+#[derive(Component, Deref, DerefMut)]
+pub struct Sprinting(pub bool);
+#[derive(Component, Deref, DerefMut)]
+pub struct Swimming(pub bool);
+#[derive(Component, Deref, DerefMut)]
+pub struct CurrentlyGlowing(pub bool);
+#[derive(Component, Deref, DerefMut)]
+pub struct Invisible(pub bool);
+#[derive(Component, Deref, DerefMut)]
+pub struct FallFlying(pub bool);
+#[derive(Component, Deref, DerefMut)]
+pub struct AirSupply(pub i32);
+#[derive(Component, Deref, DerefMut)]
+pub struct CustomName(pub Option<FormattedText>);
+#[derive(Component, Deref, DerefMut)]
+pub struct CustomNameVisible(pub bool);
+#[derive(Component, Deref, DerefMut)]
+pub struct Silent(pub bool);
+#[derive(Component, Deref, DerefMut)]
+pub struct NoGravity(pub bool);
+#[derive(Component, Deref, DerefMut)]
+pub struct TicksFrozen(pub i32);
+#[derive(Component, Deref, DerefMut)]
+pub struct AutoSpinAttack(pub bool);
+#[derive(Component, Deref, DerefMut)]
+pub struct AbstractLivingUsingItem(pub bool);
+#[derive(Component, Deref, DerefMut)]
+pub struct Health(pub f32);
+#[derive(Component, Deref, DerefMut)]
+pub struct AbstractLivingEffectColor(pub i32);
+#[derive(Component, Deref, DerefMut)]
+pub struct EffectAmbience(pub bool);
+#[derive(Component, Deref, DerefMut)]
+pub struct ArrowCount(pub i32);
+#[derive(Component, Deref, DerefMut)]
+pub struct StingerCount(pub i32);
+#[derive(Component, Deref, DerefMut)]
+pub struct SleepingPos(pub Option<BlockPos>);
+#[derive(Component, Deref, DerefMut)]
+pub struct NoAi(pub bool);
+#[derive(Component, Deref, DerefMut)]
+pub struct LeftHanded(pub bool);
+#[derive(Component, Deref, DerefMut)]
+pub struct Aggressive(pub bool);
+#[derive(Component, Deref, DerefMut)]
+pub struct Dancing(pub bool);
+#[derive(Component, Deref, DerefMut)]
+pub struct CanDuplicate(pub bool);
+#[derive(Component)]
+pub struct Allay;
impl Allay {
- pub fn set_index(&mut self, index: u8, value: EntityDataValue) -> Option<()> {
- match index {
- 0..=15 => self.abstract_creature.set_index(index, value)?,
- 16 => self.dancing = value.into_boolean().ok()?,
- 17 => self.can_duplicate = value.into_boolean().ok()?,
+ pub fn apply_metadata(
+ entity: &mut azalea_ecs::system::EntityCommands,
+ d: EntityDataItem,
+ ) -> Result<(), UpdateMetadataError> {
+ match d.index {
+ 0..=15 => AbstractCreature::apply_metadata(entity, d)?,
+ 16 => {
+ entity.insert(Dancing(d.value.into_boolean()?));
+ }
+ 17 => {
+ entity.insert(CanDuplicate(d.value.into_boolean()?));
+ }
_ => {}
}
- Some(())
- }
-}
-impl Deref for Allay {
- type Target = AbstractCreature;
- fn deref(&self) -> &Self::Target {
- &self.abstract_creature
- }
-}
-impl DerefMut for Allay {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.abstract_creature
- }
-}
-
-#[derive(Debug, Clone)]
-pub struct AreaEffectCloud {
- pub abstract_entity: AbstractEntity,
- pub radius: f32,
- pub color: i32,
- pub waiting: bool,
- pub particle: Particle,
-}
-
-impl AreaEffectCloud {
- pub fn read(metadata: &mut VecDeque<EntityDataValue>) -> Option<Self> {
- let abstract_entity = AbstractEntity::read(metadata)?;
- let radius = metadata.pop_front()?.into_float().ok()?;
- let color = metadata.pop_front()?.into_int().ok()?;
- let waiting = metadata.pop_front()?.into_boolean().ok()?;
- let particle = metadata.pop_front()?.into_particle().ok()?;
- Some(Self {
- abstract_entity,
- radius,
- color,
- waiting,
- particle,
- })
- }
-
- pub fn write(&self) -> Vec<EntityDataValue> {
- let mut metadata = Vec::new();
- metadata.extend(self.abstract_entity.write());
- metadata.push(EntityDataValue::Float(self.radius.clone()));
- metadata.push(EntityDataValue::Int(self.color.clone()));
- metadata.push(EntityDataValue::Boolean(self.waiting.clone()));
- metadata.push(EntityDataValue::Particle(self.particle.clone()));
- metadata
- }
-}
-
-impl Default for AreaEffectCloud {
- fn default() -> Self {
- Self {
- abstract_entity: Default::default(),
- radius: 3.0,
- color: 0,
- waiting: false,
- particle: Default::default(),
+ Ok(())
+ }
+}
+
+#[derive(Bundle)]
+pub struct AllayMetadataBundle {
+ _marker: Allay,
+ parent: AbstractCreatureMetadataBundle,
+ dancing: Dancing,
+ can_duplicate: CanDuplicate,
+}
+impl Default for AllayMetadataBundle {
+ fn default() -> Self {
+ Self {
+ _marker: Allay,
+ 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),
+ },
+ },
+ dancing: Dancing(false),
+ can_duplicate: CanDuplicate(true),
}
}
}
+#[derive(Component, Deref, DerefMut)]
+pub struct Radius(pub f32);
+#[derive(Component, Deref, DerefMut)]
+pub struct AreaEffectCloudColor(pub i32);
+#[derive(Component, Deref, DerefMut)]
+pub struct Waiting(pub bool);
+#[derive(Component)]
+pub struct AreaEffectCloud;
impl AreaEffectCloud {
- pub fn set_index(&mut self, index: u8, value: EntityDataValue) -> Option<()> {
- match index {
- 0..=7 => self.abstract_entity.set_index(index, value)?,
- 8 => self.radius = value.into_float().ok()?,
- 9 => self.color = value.into_int().ok()?,
- 10 => self.waiting = value.into_boolean().ok()?,
- 11 => self.particle = value.into_particle().ok()?,
+ pub fn apply_metadata(
+ entity: &mut azalea_ecs::system::EntityCommands,
+ d: EntityDataItem,
+ ) -> Result<(), UpdateMetadataError> {
+ match d.index {
+ 0..=7 => AbstractEntity::apply_metadata(entity, d)?,
+ 8 => {
+ entity.insert(Radius(d.value.into_float()?));
+ }
+ 9 => {
+ entity.insert(AreaEffectCloudColor(d.value.into_int()?));
+ }
+ 10 => {
+ entity.insert(Waiting(d.value.into_boolean()?));
+ }
+ 11 => {
+ entity.insert(d.value.into_particle()?);
+ }
_ => {}
}
- Some(())
- }
-}
-impl Deref for AreaEffectCloud {
- type Target = AbstractEntity;
- fn deref(&self) -> &Self::Target {
- &self.abstract_entity
- }
-}
-impl DerefMut for AreaEffectCloud {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.abstract_entity
- }
-}
-
-#[derive(Debug, Clone)]
-pub struct ArmorStand {
- pub abstract_living: AbstractLiving,
- pub small: bool,
- pub show_arms: bool,
- pub no_base_plate: bool,
- pub marker: bool,
- pub head_pose: Rotations,
- pub body_pose: Rotations,
- pub left_arm_pose: Rotations,
- pub right_arm_pose: Rotations,
- pub left_leg_pose: Rotations,
- pub right_leg_pose: Rotations,
-}
-
-impl ArmorStand {
- pub fn read(metadata: &mut VecDeque<EntityDataValue>) -> Option<Self> {
- let abstract_living = AbstractLiving::read(metadata)?;
- let bitfield = metadata.pop_front()?.into_byte().ok()?;
- let small = bitfield & 0x1 != 0;
- let show_arms = bitfield & 0x4 != 0;
- let no_base_plate = bitfield & 0x8 != 0;
- let marker = bitfield & 0x10 != 0;
- let head_pose = metadata.pop_front()?.into_rotations().ok()?;
- let body_pose = metadata.pop_front()?.into_rotations().ok()?;
- let left_arm_pose = metadata.pop_front()?.into_rotations().ok()?;
- let right_arm_pose = metadata.pop_front()?.into_rotations().ok()?;
- let left_leg_pose = metadata.pop_front()?.into_rotations().ok()?;
- let right_leg_pose = metadata.pop_front()?.into_rotations().ok()?;
- Some(Self {
- abstract_living,
- small,
- show_arms,
- no_base_plate,
- marker,
- head_pose,
- body_pose,
- left_arm_pose,
- right_arm_pose,
- left_leg_pose,
- right_leg_pose,
- })
- }
-
- pub fn write(&self) -> Vec<EntityDataValue> {
- let mut metadata = Vec::new();
- metadata.extend(self.abstract_living.write());
- let mut bitfield = 0u8;
- if self.small {
- bitfield &= 0x1;
- }
- if self.show_arms {
- bitfield &= 0x4;
- }
- if self.no_base_plate {
- bitfield &= 0x8;
- }
- if self.marker {
- bitfield &= 0x10;
- }
- metadata.push(EntityDataValue::Byte(bitfield));
- metadata.push(EntityDataValue::Rotations(self.head_pose.clone()));
- metadata.push(EntityDataValue::Rotations(self.body_pose.clone()));
- metadata.push(EntityDataValue::Rotations(self.left_arm_pose.clone()));
- metadata.push(EntityDataValue::Rotations(self.right_arm_pose.clone()));
- metadata.push(EntityDataValue::Rotations(self.left_leg_pose.clone()));
- metadata.push(EntityDataValue::Rotations(self.right_leg_pose.clone()));
- metadata
- }
-}
-
-impl Default for ArmorStand {
- fn default() -> Self {
- Self {
- abstract_living: Default::default(),
- small: false,
- show_arms: false,
- no_base_plate: false,
- marker: false,
- head_pose: Default::default(),
- body_pose: Default::default(),
- left_arm_pose: Default::default(),
- right_arm_pose: Default::default(),
- left_leg_pose: Default::default(),
- right_leg_pose: Default::default(),
- }
- }
-}
-
+ Ok(())
+ }
+}
+
+#[derive(Bundle)]
+pub struct AreaEffectCloudMetadataBundle {
+ _marker: AreaEffectCloud,
+ parent: AbstractEntityMetadataBundle,
+ radius: Radius,
+ area_effect_cloud_color: AreaEffectCloudColor,
+ waiting: Waiting,
+ particle: Particle,
+}
+impl Default for AreaEffectCloudMetadataBundle {
+ fn default() -> Self {
+ Self {
+ _marker: AreaEffectCloud,
+ 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),
+ },
+ radius: Radius(3.0),
+ area_effect_cloud_color: AreaEffectCloudColor(0),
+ waiting: Waiting(false),
+ particle: Particle::default(),
+ }
+ }
+}
+
+#[derive(Component, Deref, DerefMut)]
+pub struct Small(pub bool);
+#[derive(Component, Deref, DerefMut)]
+pub struct ShowArms(pub bool);
+#[derive(Component, Deref, DerefMut)]
+pub struct NoBasePlate(pub bool);
+#[derive(Component, Deref, DerefMut)]
+pub struct ArmorStandMarker(pub bool);
+#[derive(Component, Deref, DerefMut)]
+pub struct HeadPose(pub Rotations);
+#[derive(Component, Deref, DerefMut)]
+pub struct BodyPose(pub Rotations);
+#[derive(Component, Deref, DerefMut)]
+pub struct LeftArmPose(pub Rotations);
+#[derive(Component, Deref, DerefMut)]
+pub struct RightArmPose(pub Rotations);
+#[derive(Component, Deref, DerefMut)]
+pub struct LeftLegPose(pub Rotations);
+#[derive(Component, Deref, DerefMut)]
+pub struct RightLegPose(pub Rotations);
+#[derive(Component)]
+pub struct ArmorStand;
impl ArmorStand {
- pub fn set_index(&mut self, index: u8, value: EntityDataValue) -> Option<()> {
- match index {
- 0..=14 => self.abstract_living.set_index(index, value)?,
+ pub fn apply_metadata(
+ entity: &mut azalea_ecs::system::EntityCommands,
+ d: EntityDataItem,
+ ) -> Result<(), UpdateMetadataError> {
+ match d.index {
+ 0..=14 => AbstractLiving::apply_metadata(entity, d)?,
15 => {
- let bitfield = value.into_byte().ok()?;
- self.small = bitfield & 0x1 != 0;
- self.show_arms = bitfield & 0x4 != 0;
- self.no_base_plate = bitfield & 0x8 != 0;
- self.marker = bitfield & 0x10 != 0;
+ let bitfield = d.value.into_byte()?;
+ entity.insert(Small(bitfield & 0x1 != 0));
+ entity.insert(ShowArms(bitfield & 0x4 != 0));
+ entity.insert(NoBasePlate(bitfield & 0x8 != 0));
+ entity.insert(ArmorStandMarker(bitfield & 0x10 != 0));
+ }
+ 16 => {
+ entity.insert(HeadPose(d.value.into_rotations()?));
+ }
+ 17 => {
+ entity.insert(BodyPose(d.value.into_rotations()?));
+ }
+ 18 => {
+ entity.insert(LeftArmPose(d.value.into_rotations()?));
+ }
+ 19 => {
+ entity.insert(RightArmPose(d.value.into_rotations()?));
+ }
+ 20 => {
+ entity.insert(LeftLegPose(d.value.into_rotations()?));
+ }
+ 21 => {
+ entity.insert(RightLegPose(d.value.into_rotations()?));
}
- 16 => self.head_pose = value.into_rotations().ok()?,
- 17 => self.body_pose = value.into_rotations().ok()?,
- 18 => self.left_arm_pose = value.into_rotations().ok()?,
- 19 => self.right_arm_pose = value.into_rotations().ok()?,
- 20 => self.left_leg_pose = value.into_rotations().ok()?,
- 21 => self.right_leg_pose = value.into_rotations().ok()?,
_ => {}
}
- Some(())
- }
-}
-impl Deref for ArmorStand {
- type Target = AbstractLiving;
- fn deref(&self) -> &Self::Target {
- &self.abstract_living
- }
-}
-impl DerefMut for ArmorStand {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.abstract_living
- }
-}
-
-#[derive(Debug, Clone)]
-pub struct Arrow {
- pub abstract_entity: AbstractEntity,
- pub crit_arrow: bool,
- pub shot_from_crossbow: bool,
- pub no_physics: bool,
- pub pierce_level: u8,
- pub effect_color: i32,
-}
-
-impl Arrow {
- pub fn read(metadata: &mut VecDeque<EntityDataValue>) -> Option<Self> {
- let abstract_entity = AbstractEntity::read(metadata)?;
- let bitfield = metadata.pop_front()?.into_byte().ok()?;
- let crit_arrow = bitfield & 0x1 != 0;
- let shot_from_crossbow = bitfield & 0x4 != 0;
- let no_physics = bitfield & 0x2 != 0;
- let pierce_level = metadata.pop_front()?.into_byte().ok()?;
- let effect_color = metadata.pop_front()?.into_int().ok()?;
- Some(Self {
- abstract_entity,
- crit_arrow,
- shot_from_crossbow,
- no_physics,
- pierce_level,
- effect_color,
- })
- }
-
- pub fn write(&self) -> Vec<EntityDataValue> {
- let mut metadata = Vec::new();
- metadata.extend(self.abstract_entity.write());
- let mut bitfield = 0u8;
- if self.crit_arrow {
- bitfield &= 0x1;
- }
- if self.shot_from_crossbow {
- bitfield &= 0x4;
- }
- if self.no_physics {
- bitfield &= 0x2;
- }
- metadata.push(EntityDataValue::Byte(bitfield));
- metadata.push(EntityDataValue::Byte(self.pierce_level.clone()));
- metadata.push(EntityDataValue::Int(self.effect_color.clone()));
- metadata
- }
-}
-
-impl Default for Arrow {
- fn default() -> Self {
- Self {
- abstract_entity: Default::default(),
- crit_arrow: false,
- shot_from_crossbow: false,
- no_physics: false,
- pierce_level: 0,
- effect_color: -1,
- }
- }
-}
-
+ Ok(())
+ }
+}
+
+#[derive(Bundle)]
+pub struct ArmorStandMetadataBundle {
+ _marker: ArmorStand,
+ parent: AbstractLivingMetadataBundle,
+ small: Small,
+ show_arms: ShowArms,
+ no_base_plate: NoBasePlate,
+ armor_stand_marker: ArmorStandMarker,
+ head_pose: HeadPose,
+ body_pose: BodyPose,
+ left_arm_pose: LeftArmPose,
+ right_arm_pose: RightArmPose,
+ left_leg_pose: LeftLegPose,
+ right_leg_pose: RightLegPose,
+}
+impl Default for ArmorStandMetadataBundle {
+ fn default() -> Self {
+ Self {
+ _marker: ArmorStand,
+ 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),
+ },
+ small: Small(false),
+ show_arms: ShowArms(false),
+ no_base_plate: NoBasePlate(false),
+ armor_stand_marker: ArmorStandMarker(false),
+ head_pose: HeadPose(Default::default()),
+ body_pose: BodyPose(Default::default()),
+ left_arm_pose: LeftArmPose(Default::default()),
+ right_arm_pose: RightArmPose(Default::default()),
+ left_leg_pose: LeftLegPose(Default::default()),
+ right_leg_pose: RightLegPose(Default::default()),
+ }
+ }
+}
+
+#[derive(Component, Deref, DerefMut)]
+pub struct ArrowCritArrow(pub bool);
+#[derive(Component, Deref, DerefMut)]
+pub struct ArrowShotFromCrossbow(pub bool);
+#[derive(Component, Deref, DerefMut)]
+pub struct ArrowNoPhysics(pub bool);
+#[derive(Component, Deref, DerefMut)]
+pub struct ArrowPierceLevel(pub u8);
+#[derive(Component, Deref, DerefMut)]
+pub struct ArrowEffectColor(pub i32);
+#[derive(Component)]
+pub struct Arrow;
impl Arrow {
- pub fn set_index(&mut self, index: u8, value: EntityDataValue) -> Option<()> {
- match index {
- 0..=7 => self.abstract_entity.set_index(index, value)?,
+ pub fn apply_metadata(
+ entity: &mut azalea_ecs::system::EntityCommands,
+ d: EntityDataItem,
+ ) -> Result<(), UpdateMetadataError> {
+ match d.index {
+ 0..=7 => AbstractEntity::apply_metadata(entity, d)?,
8 => {
- let bitfield = value.into_byte().ok()?;
- self.crit_arrow = bitfield & 0x1 != 0;
- self.shot_from_crossbow = bitfield & 0x4 != 0;
- self.no_physics = bitfield & 0x2 != 0;
+ let bitfield = d.value.into_byte()?;
+ entity.insert(ArrowCritArrow(bitfield & 0x1 != 0));
+ entity.insert(ArrowShotFromCrossbow(bitfield & 0x4 != 0));
+ entity.insert(ArrowNoPhysics(bitfield & 0x2 != 0));
+ }
+ 9 => {
+ entity.insert(ArrowPierceLevel(d.value.into_byte()?));
+ }
+ 10 => {
+ entity.insert(ArrowEffectColor(d.value.into_int()?));
}
- 9 => self.pierce_level = value.into_byte().ok()?,
- 10 => self.effect_color = value.into_int().ok()?,
_ => {}
}
- Some(())
- }
-}
-impl Deref for Arrow {
- type Target = AbstractEntity;
- fn deref(&self) -> &Self::Target {
- &self.abstract_entity
- }
-}
-impl DerefMut for Arrow {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.abstract_entity
- }
-}
-
-#[derive(Debug, Clone)]
-pub struct Axolotl {
- pub abstract_animal: AbstractAnimal,
- pub variant: i32,
- pub playing_dead: bool,
- pub from_bucket: bool,
-}
-
-impl Axolotl {
- pub fn read(metadata: &mut VecDeque<EntityDataValue>) -> Option<Self> {
- let abstract_animal = AbstractAnimal::read(metadata)?;
- let variant = metadata.pop_front()?.into_int().ok()?;
- let playing_dead = metadata.pop_front()?.into_boolean().ok()?;
- let from_bucket = metadata.pop_front()?.into_boolean().ok()?;
- Some(Self {
- abstract_animal,
- variant,
- playing_dead,
- from_bucket,
- })
- }
-
- pub fn write(&self) -> Vec<EntityDataValue> {
- let mut metadata = Vec::new();
- metadata.extend(self.abstract_animal.write());
- metadata.push(EntityDataValue::Int(self.variant.clone()));
- metadata.push(EntityDataValue::Boolean(self.playing_dead.clone()));
- metadata.push(EntityDataValue::Boolean(self.from_bucket.clone()));
- metadata
- }
-}
-
-impl Default for Axolotl {
- fn default() -> Self {
- Self {
- abstract_animal: Default::default(),
- variant: 0,
- playing_dead: false,
- from_bucket: false,
+ Ok(())
+ }
+}
+
+#[derive(Bundle)]
+pub struct ArrowMetadataBundle {
+ _marker: Arrow,
+ parent: AbstractEntityMetadataBundle,
+ arrow_crit_arrow: ArrowCritArrow,
+ arrow_shot_from_crossbow: ArrowShotFromCrossbow,
+ arrow_no_physics: ArrowNoPhysics,
+ arrow_pierce_level: ArrowPierceLevel,
+ arrow_effect_color: ArrowEffectColor,
+}
+impl Default for ArrowMetadataBundle {
+ fn default() -> Self {
+ Self {
+ _marker: Arrow,
+ 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),
+ },
+ arrow_crit_arrow: ArrowCritArrow(false),
+ arrow_shot_from_crossbow: ArrowShotFromCrossbow(false),
+ arrow_no_physics: ArrowNoPhysics(false),
+ arrow_pierce_level: ArrowPierceLevel(0),
+ arrow_effect_color: ArrowEffectColor(-1),
}
}
}
+#[derive(Component, Deref, DerefMut)]
+pub struct AbstractAgeableBaby(pub bool);
+#[derive(Component, Deref, DerefMut)]
+pub struct AxolotlVariant(pub i32);
+#[derive(Component, Deref, DerefMut)]
+pub struct PlayingDead(pub bool);
+#[derive(Component, Deref, DerefMut)]
+pub struct AxolotlFromBucket(pub bool);
+#[derive(Component)]
+pub struct Axolotl;
impl Axolotl {
- pub fn set_index(&mut self, index: u8, value: EntityDataValue) -> Option<()> {
- match index {
- 0..=16 => self.abstract_animal.set_index(index, value)?,
- 17 => self.variant = value.into_int().ok()?,
- 18 => self.playing_dead = value.into_boolean().ok()?,
- 19 => self.from_bucket = value.into_boolean().ok()?,
+ pub fn apply_metadata(
+ entity: &mut azalea_ecs::system::EntityCommands,
+ d: EntityDataItem,
+ ) -> Result<(), UpdateMetadataError> {
+ match d.index {
+ 0..=16 => AbstractAnimal::apply_metadata(entity, d)?,
+ 17 => {
+ entity.insert(AxolotlVariant(d.value.into_int()?));
+ }
+ 18 => {
+ entity.insert(PlayingDead(d.value.into_boolean()?));
+ }
+ 19 => {
+ entity.insert(AxolotlFromBucket(d.value.into_boolean()?));
+ }
_ => {}
}
- Some(())
- }
-}
-impl Deref for Axolotl {
- type Target = AbstractAnimal;
- fn deref(&self) -> &Self::Target {
- &self.abstract_animal
- }
-}
-impl DerefMut for Axolotl {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.abstract_animal
- }
-}
-
-#[derive(Debug, Clone)]
-pub struct Bat {
- pub abstract_insentient: AbstractInsentient,
- pub resting: bool,
-}
-
-impl Bat {
- pub fn read(metadata: &mut VecDeque<EntityDataValue>) -> Option<Self> {
- let abstract_insentient = AbstractInsentient::read(metadata)?;
- let bitfield = metadata.pop_front()?.into_byte().ok()?;
- let resting = bitfield & 0x1 != 0;
- Some(Self {
- abstract_insentient,
- resting,
- })
- }
-
- pub fn write(&self) -> Vec<EntityDataValue> {
- let mut metadata = Vec::new();
- metadata.extend(self.abstract_insentient.write());
- let mut bitfield = 0u8;
- if self.resting {
- bitfield &= 0x1;
- }
- metadata.push(EntityDataValue::Byte(bitfield));
- metadata
- }
-}
-
-impl Default for Bat {
- fn default() -> Self {
- Self {
- abstract_insentient: Default::default(),
- resting: false,
+ Ok(())
+ }
+}
+
+#[derive(Bundle)]
+pub struct AxolotlMetadataBundle {
+ _marker: Axolotl,
+ parent: AbstractAnimalMetadataBundle,
+ axolotl_variant: AxolotlVariant,
+ playing_dead: PlayingDead,
+ axolotl_from_bucket: AxolotlFromBucket,
+}
+impl Default for AxolotlMetadataBundle {
+ fn default() -> Self {
+ Self {
+ _marker: Axolotl,
+ parent: AbstractAnimalMetadataBundle {
+ _marker: AbstractAnimal,
+ parent: AbstractAgeableMetadataBundle {
+ _marker: AbstractAgeable,
+ 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),
+ },
+ },
+ abstract_ageable_baby: AbstractAgeableBaby(false),
+ },
+ },
+ axolotl_variant: AxolotlVariant(0),
+ playing_dead: PlayingDead(false),
+ axolotl_from_bucket: AxolotlFromBucket(false),
}
}
}
+#[derive(Component, Deref, DerefMut)]
+pub struct Resting(pub bool);
+#[derive(Component)]
+pub struct Bat;
impl Bat {
- pub fn set_index(&mut self, index: u8, value: EntityDataValue) -> Option<()> {
- match index {
- 0..=15 => self.abstract_insentient.set_index(index, value)?,
+ pub fn apply_metadata(
+ entity: &mut azalea_ecs::system::EntityCommands,
+ d: EntityDataItem,
+ ) -> Result<(), UpdateMetadataError> {
+ match d.index {
+ 0..=15 => AbstractInsentient::apply_metadata(entity, d)?,
16 => {
- let bitfield = value.into_byte().ok()?;
- self.resting = bitfield & 0x1 != 0;
+ let bitfield = d.value.into_byte()?;
+ entity.insert(Resting(bitfield & 0x1 != 0));
}
_ => {}
}
- Some(())
- }
-}
-impl Deref for Bat {
- type Target = AbstractInsentient;
- fn deref(&self) -> &Self::Target {
- &self.abstract_insentient
- }
-}
-impl DerefMut for Bat {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.abstract_insentient
- }
-}
-
-#[derive(Debug, Clone)]
-pub struct Bee {
- pub abstract_animal: AbstractAnimal,
- pub has_nectar: bool,
- pub has_stung: bool,
- pub rolling: bool,
- pub remaining_anger_time: i32,
-}
-
-impl Bee {
- pub fn read(metadata: &mut VecDeque<EntityDataValue>) -> Option<Self> {
- let abstract_animal = AbstractAnimal::read(metadata)?;
- let bitfield = metadata.pop_front()?.into_byte().ok()?;
- let has_nectar = bitfield & 0x8 != 0;
- let has_stung = bitfield & 0x4 != 0;
- let rolling = bitfield & 0x2 != 0;
- let remaining_anger_time = metadata.pop_front()?.into_int().ok()?;
- Some(Self {
- abstract_animal,
- has_nectar,
- has_stung,
- rolling,
- remaining_anger_time,
- })
- }
-
- pub fn write(&self) -> Vec<EntityDataValue> {
- let mut metadata = Vec::new();
- metadata.extend(self.abstract_animal.write());
- let mut bitfield = 0u8;
- if self.has_nectar {
- bitfield &= 0x8;
- }
- if self.has_stung {
- bitfield &= 0x4;
- }
- if self.rolling {
- bitfield &= 0x2;
- }
- metadata.push(EntityDataValue::Byte(bitfield));
- metadata.push(EntityDataValue::Int(self.remaining_anger_time.clone()));
- metadata
- }
-}
-
-impl Default for Bee {
- fn default() -> Self {
- Self {
- abstract_animal: Default::default(),
- has_nectar: false,
- has_stung: false,
- rolling: false,
- remaining_anger_time: 0,
+ Ok(())
+ }
+}
+
+#[derive(Bundle)]
+pub struct BatMetadataBundle {
+ _marker: Bat,
+ parent: AbstractInsentientMetadataBundle,
+ resting: Resting,
+}
+impl Default for BatMetadataBundle {
+ fn default() -> Self {
+ Self {
+ _marker: Bat,
+ 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),
+ },
+ resting: Resting(false),
}
}
}
+#[derive(Component, Deref, DerefMut)]
+pub struct HasNectar(pub bool);
+#[derive(Component, Deref, DerefMut)]
+pub struct HasStung(pub bool);
+#[derive(Component, Deref, DerefMut)]
+pub struct BeeRolling(pub bool);
+#[derive(Component, Deref, DerefMut)]
+pub struct BeeRemainingAngerTime(pub i32);
+#[derive(Component)]
+pub struct Bee;
impl Bee {
- pub fn set_index(&mut self, index: u8, value: EntityDataValue) -> Option<()> {
- match index {
- 0..=16 => self.abstract_animal.set_index(index, value)?,
+ pub fn apply_metadata(
+ entity: &mut azalea_ecs::system::EntityCommands,
+ d: EntityDataItem,
+ ) -> Result<(), UpdateMetadataError> {
+ match d.index {
+ 0..=16 => AbstractAnimal::apply_metadata(entity, d)?,
17 => {
- let bitfield = value.into_byte().ok()?;
- self.has_nectar = bitfield & 0x8 != 0;
- self.has_stung = bitfield & 0x4 != 0;
- self.rolling = bitfield & 0x2 != 0;
+ let bitfield = d.value.into_byte()?;
+ entity.insert(HasNectar(bitfield & 0x8 != 0));
+ entity.insert(HasStung(bitfield & 0x4 != 0));
+ entity.insert(BeeRolling(bitfield & 0x2 != 0));
+ }
+ 18 => {
+ entity.insert(BeeRemainingAngerTime(d.value.into_int()?));
}
- 18 => self.remaining_anger_time = value.into_int().ok()?,
_ => {}
}
- Some(())
- }
-}
-impl Deref for Bee {
- type Target = AbstractAnimal;
- fn deref(&self) -> &Self::Target {
- &self.abstract_animal
- }
-}
-impl DerefMut for Bee {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.abstract_animal
- }
-}
-
-#[derive(Debug, Clone)]
-pub struct Blaze {
- pub abstract_monster: AbstractMonster,
- pub charged: bool,
-}
-
-impl Blaze {
- pub fn read(metadata: &mut VecDeque<EntityDataValue>) -> Option<Self> {
- let abstract_monster = AbstractMonster::read(metadata)?;
- let bitfield = metadata.pop_front()?.into_byte().ok()?;
- let charged = bitfield & 0x1 != 0;
- Some(Self {
- abstract_monster,
- charged,
- })
- }
-
- pub fn write(&self) -> Vec<EntityDataValue> {
- let mut metadata = Vec::new();
- metadata.extend(self.abstract_monster.write());
- let mut bitfield = 0u8;
- if self.charged {
- bitfield &= 0x1;
- }
- metadata.push(EntityDataValue::Byte(bitfield));
- metadata
- }
-}
-
-impl Default for Blaze {
- fn default() -> Self {
- Self {
- abstract_monster: Default::default(),
- charged: false,
+ Ok(())
+ }
+}
+
+#[derive(Bundle)]
+pub struct BeeMetadataBundle {
+ _marker: Bee,
+ parent: AbstractAnimalMetadataBundle,
+ has_nectar: HasNectar,
+ has_stung: HasStung,
+ bee_rolling: BeeRolling,
+ bee_remaining_anger_time: BeeRemainingAngerTime,
+}
+impl Default for BeeMetadataBundle {
+ fn default() -> Self {
+ Self {
+ _marker: Bee,
+ parent: AbstractAnimalMetadataBundle {
+ _marker: AbstractAnimal,
+ parent: AbstractAgeableMetadataBundle {
+ _marker: AbstractAgeable,
+ 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),
+ },
+ },
+ abstract_ageable_baby: AbstractAgeableBaby(false),
+ },
+ },
+ has_nectar: HasNectar(false),
+ has_stung: HasStung(false),
+ bee_rolling: BeeRolling(false),
+ bee_remaining_anger_time: BeeRemainingAngerTime(0),
}
}
}
+#[derive(Component, Deref, DerefMut)]
+pub struct Charged(pub bool);
+#[derive(Component)]
+pub struct Blaze;
impl Blaze {
- pub fn set_index(&mut self, index: u8, value: EntityDataValue) -> Option<()> {
- match index {
- 0..=15 => self.abstract_monster.set_index(index, value)?,
+ pub fn apply_metadata(
+ entity: &mut azalea_ecs::system::EntityCommands,
+ d: EntityDataItem,
+ ) -> Result<(), UpdateMetadataError> {
+ match d.index {
+ 0..=15 => AbstractMonster::apply_metadata(entity, d)?,
16 => {
- let bitfield = value.into_byte().ok()?;
- self.charged = bitfield & 0x1 != 0;
+ let bitfield = d.value.into_byte()?;
+ entity.insert(Charged(bitfield & 0x1 != 0));
}
_ => {}
}
- Some(())
- }
-}
-impl Deref for Blaze {
- type Target = AbstractMonster;
- fn deref(&self) -> &Self::Target {
- &self.abstract_monster
- }
-}
-impl DerefMut for Blaze {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.abstract_monster
- }
-}
-
-#[derive(Debug, Clone)]
-pub struct Boat {
- pub abstract_entity: AbstractEntity,
- pub hurt: i32,
- pub hurtdir: i32,
- pub damage: f32,
- pub kind: i32,
- pub paddle_left: bool,
- pub paddle_right: bool,
- pub bubble_time: i32,
-}
-
-impl Boat {
- pub fn read(metadata: &mut VecDeque<EntityDataValue>) -> Option<Self> {
- let abstract_entity = AbstractEntity::read(metadata)?;
- let hurt = metadata.pop_front()?.into_int().ok()?;
- let hurtdir = metadata.pop_front()?.into_int().ok()?;
- let damage = metadata.pop_front()?.into_float().ok()?;
- let kind = metadata.pop_front()?.into_int().ok()?;
- let paddle_left = metadata.pop_front()?.into_boolean().ok()?;
- let paddle_right = metadata.pop_front()?.into_boolean().ok()?;
- let bubble_time = metadata.pop_front()?.into_int().ok()?;
- Some(Self {
- abstract_entity,
- hurt,
- hurtdir,
- damage,
- kind,
- paddle_left,
- paddle_right,
- bubble_time,
- })
- }
-
- pub fn write(&self) -> Vec<EntityDataValue> {
- let mut metadata = Vec::new();
- metadata.extend(self.abstract_entity.write());
- metadata.push(EntityDataValue::Int(self.hurt.clone()));
- metadata.push(EntityDataValue::Int(self.hurtdir.clone()));
- metadata.push(EntityDataValue::Float(self.damage.clone()));
- metadata.push(EntityDataValue::Int(self.kind.clone()));
- metadata.push(EntityDataValue::Boolean(self.paddle_left.clone()));
- metadata.push(EntityDataValue::Boolean(self.paddle_right.clone()));
- metadata.push(EntityDataValue::Int(self.bubble_time.clone()));
- metadata
- }
-}
-
-impl Default for Boat {
- fn default() -> Self {
- Self {
- abstract_entity: Default::default(),
- hurt: 0,
- hurtdir: 1,
- damage: 0.0,
- kind: Default::default(),
- paddle_left: false,
- paddle_right: false,
- bubble_time: 0,
- }
- }
-}
-
+ Ok(())
+ }
+}
+
+#[derive(Bundle)]
+pub struct BlazeMetadataBundle {
+ _marker: Blaze,
+ parent: AbstractMonsterMetadataBundle,
+ charged: Charged,
+}
+impl Default for BlazeMetadataBundle {
+ fn default() -> Self {
+ Self {
+ _marker: Blaze,
+ 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),
+ },
+ },
+ },
+ charged: Charged(false),
+ }
+ }
+}
+
+#[derive(Component, Deref, DerefMut)]
+pub struct BoatHurt(pub i32);
+#[derive(Component, Deref, DerefMut)]
+pub struct BoatHurtdir(pub i32);
+#[derive(Component, Deref, DerefMut)]
+pub struct BoatDamage(pub f32);
+#[derive(Component, Deref, DerefMut)]
+pub struct BoatKind(pub i32);
+#[derive(Component, Deref, DerefMut)]
+pub struct PaddleLeft(pub bool);
+#[derive(Component, Deref, DerefMut)]
+pub struct PaddleRight(pub bool);
+#[derive(Component, Deref, DerefMut)]
+pub struct BubbleTime(pub i32);
+#[derive(Component)]
+pub struct Boat;
impl Boat {
- pub fn set_index(&mut self, index: u8, value: EntityDataValue) -> Option<()> {
- match index {
- 0..=7 => self.abstract_entity.set_index(index, value)?,
- 8 => self.hurt = value.into_int().ok()?,
- 9 => self.hurtdir = value.into_int().ok()?,
- 10 => self.damage = value.into_float().ok()?,
- 11 => self.kind = value.into_int().ok()?,
- 12 => self.paddle_left = value.into_boolean().ok()?,
- 13 => self.paddle_right = value.into_boolean().ok()?,
- 14 => self.bubble_time = value.into_int().ok()?,
+ pub fn apply_metadata(
+ entity: &mut azalea_ecs::system::EntityCommands,
+ d: EntityDataItem,
+ ) -> Result<(), UpdateMetadataError> {
+ match d.index {
+ 0..=7 => AbstractEntity::apply_metadata(entity, d)?,
+ 8 => {
+ entity.insert(BoatHurt(d.value.into_int()?));
+ }
+ 9 => {
+ entity.insert(BoatHurtdir(d.value.into_int()?));
+ }
+ 10 => {
+ entity.insert(BoatDamage(d.value.into_float()?));
+ }
+ 11 => {
+ entity.insert(BoatKind(d.value.into_int()?));
+ }
+ 12 => {
+ entity.insert(PaddleLeft(d.value.into_boolean()?));
+ }
+ 13 => {
+ entity.insert(PaddleRight(d.value.into_boolean()?));
+ }
+ 14 => {
+ entity.insert(BubbleTime(d.value.into_int()?));
+ }
_ => {}
}
- Some(())
- }
-}
-impl Deref for Boat {
- type Target = AbstractEntity;
- fn deref(&self) -> &Self::Target {
- &self.abstract_entity
- }
-}
-impl DerefMut for Boat {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.abstract_entity
- }
-}
-
-#[derive(Debug, Clone)]
-pub struct Camel {
- pub abstract_animal: AbstractAnimal,
- pub tamed: bool,
- pub eating: bool,
- pub standing: bool,
- pub bred: bool,
- pub saddled: bool,
- pub owner_uuid: Option<Uuid>,
- pub dash: bool,
- pub last_pose_change_tick: i64,
-}
-
-impl Camel {
- pub fn read(metadata: &mut VecDeque<EntityDataValue>) -> Option<Self> {
- let abstract_animal = AbstractAnimal::read(metadata)?;
- let bitfield = metadata.pop_front()?.into_byte().ok()?;
- let tamed = bitfield & 0x2 != 0;
- let eating = bitfield & 0x10 != 0;
- let standing = bitfield & 0x20 != 0;
- let bred = bitfield & 0x8 != 0;
- let saddled = bitfield & 0x4 != 0;
- let owner_uuid = metadata.pop_front()?.into_optional_uuid().ok()?;
- let dash = metadata.pop_front()?.into_boolean().ok()?;
- let last_pose_change_tick = metadata.pop_front()?.into_long().ok()?;
- Some(Self {
- abstract_animal,
- tamed,
- eating,
- standing,
- bred,
- saddled,
- owner_uuid,
- dash,
- last_pose_change_tick,
- })
- }
-
- pub fn write(&self) -> Vec<EntityDataValue> {
- let mut metadata = Vec::new();
- metadata.extend(self.abstract_animal.write());
- let mut bitfield = 0u8;
- if self.tamed {
- bitfield &= 0x2;
- }
- if self.eating {
- bitfield &= 0x10;
- }
- if self.standing {
- bitfield &= 0x20;
- }
- if self.bred {
- bitfield &= 0x8;
- }
- if self.saddled {
- bitfield &= 0x4;
- }
- metadata.push(EntityDataValue::Byte(bitfield));
- metadata.push(EntityDataValue::OptionalUuid(self.owner_uuid.clone()));
- metadata.push(EntityDataValue::Boolean(self.dash.clone()));
- metadata.push(EntityDataValue::Long(self.last_pose_change_tick.clone()));
- metadata
- }
-}
-
-impl Default for Camel {
- fn default() -> Self {
- Self {
- abstract_animal: Default::default(),
- tamed: false,
- eating: false,
- standing: false,
- bred: false,
- saddled: false,
- owner_uuid: None,
- dash: false,
- last_pose_change_tick: -52,
- }
- }
-}
-
+ Ok(())
+ }
+}
+
+#[derive(Bundle)]
+pub struct BoatMetadataBundle {
+ _marker: Boat,
+ parent: AbstractEntityMetadataBundle,
+ boat_hurt: BoatHurt,
+ boat_hurtdir: BoatHurtdir,
+ boat_damage: BoatDamage,
+ boat_kind: BoatKind,
+ paddle_left: PaddleLeft,
+ paddle_right: PaddleRight,
+ bubble_time: BubbleTime,
+}
+impl Default for BoatMetadataBundle {
+ fn default() -> Self {
+ Self {
+ _marker: Boat,
+ 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),
+ },
+ boat_hurt: BoatHurt(0),
+ boat_hurtdir: BoatHurtdir(1),
+ boat_damage: BoatDamage(0.0),
+ boat_kind: BoatKind(Default::default()),
+ paddle_left: PaddleLeft(false),
+ paddle_right: PaddleRight(false),
+ bubble_time: BubbleTime(0),
+ }
+ }
+}
+
+#[derive(Component, Deref, DerefMut)]
+pub struct CamelTamed(pub bool);
+#[derive(Component, Deref, DerefMut)]
+pub struct CamelEating(pub bool);
+#[derive(Component, Deref, DerefMut)]
+pub struct CamelStanding(pub bool);
+#[derive(Component, Deref, DerefMut)]
+pub struct CamelBred(pub bool);
+#[derive(Component, Deref, DerefMut)]
+pub struct CamelSaddled(pub bool);
+#[derive(Component, Deref, DerefMut)]
+pub struct CamelOwnerUuid(pub Option<Uuid>);
+#[derive(Component, Deref, DerefMut)]
+pub struct Dash(pub bool);
+#[derive(Component, Deref, DerefMut)]
+pub struct LastPoseChangeTick(pub i64);
+#[derive(Component)]
+pub struct Camel;
impl Camel {
- pub fn set_index(&mut self, index: u8, value: EntityDataValue) -> Option<()> {
- match index {
- 0..=16 => self.abstract_animal.set_index(index, value)?,
+ pub fn apply_metadata(
+ entity: &mut azalea_ecs::system::EntityCommands,
+ d: EntityDataItem,
+ ) -> Result<(), UpdateMetadataError> {
+ match d.index {
+ 0..=16 => AbstractAnimal::apply_metadata(entity, d)?,
17 => {
- let bitfield = value.into_byte().ok()?;
- self.tamed = bitfield & 0x2 != 0;
- self.eating = bitfield & 0x10 != 0;
- self.standing = bitfield & 0x20 != 0;
- self.bred = bitfield & 0x8 != 0;
- self.saddled = bitfield & 0x4 != 0;
+ let bitfield = d.value.into_byte()?;
+ entity.insert(CamelTamed(bitfield & 0x2 != 0));
+ entity.insert(CamelEating(bitfield & 0x10 != 0));
+ entity.insert(CamelStanding(bitfield & 0x20 != 0));
+ entity.insert(CamelBred(bitfield & 0x8 != 0));
+ entity.insert(CamelSaddled(bitfield & 0x4 != 0));
+ }
+ 18 => {
+ entity.insert(CamelOwnerUuid(d.value.into_optional_uuid()?));
+ }
+ 19 => {
+ entity.insert(Dash(d.value.into_boolean()?));
+ }
+ 20 => {
+ entity.insert(LastPoseChangeTick(d.value.into_long()?));
}
- 18 => self.owner_uuid = value.into_optional_uuid().ok()?,
- 19 => self.dash = value.into_boolean().ok()?,
- 20 => self.last_pose_change_tick = value.into_long().ok()?,
_ => {}
}
- Some(())
- }
-}
-impl Deref for Camel {
- type Target = AbstractAnimal;
- fn deref(&self) -> &Self::Target {
- &self.abstract_animal
- }
-}
-impl DerefMut for Camel {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.abstract_animal
- }
-}
-
-#[derive(Debug, Clone)]
-pub struct Cat {
- pub abstract_tameable: AbstractTameable,
- pub variant: azalea_registry::CatVariant,
- pub is_lying: bool,
- pub relax_state_one: bool,
- pub collar_color: i32,
-}
-
-impl Cat {
- pub fn read(metadata: &mut VecDeque<EntityDataValue>) -> Option<Self> {
- let abstract_tameable = AbstractTameable::read(metadata)?;
- let variant = metadata.pop_front()?.into_cat_variant().ok()?;
- let is_lying = metadata.pop_front()?.into_boolean().ok()?;
- let relax_state_one = metadata.pop_front()?.into_boolean().ok()?;
- let collar_color = metadata.pop_front()?.into_int().ok()?;
- Some(Self {
- abstract_tameable,
- variant,
- is_lying,
- relax_state_one,
- collar_color,
- })
- }
-
- pub fn write(&self) -> Vec<EntityDataValue> {
- let mut metadata = Vec::new();
- metadata.extend(self.abstract_tameable.write());
- metadata.push(EntityDataValue::CatVariant(self.variant.clone()));
- metadata.push(EntityDataValue::Boolean(self.is_lying.clone()));
- metadata.push(EntityDataValue::Boolean(self.relax_state_one.clone()));
- metadata.push(EntityDataValue::Int(self.collar_color.clone()));
- metadata
- }
-}
-
-impl Default for Cat {
- fn default() -> Self {
- Self {
- abstract_tameable: Default::default(),
- variant: azalea_registry::CatVariant::Tabby,
- is_lying: false,
- relax_state_one: false,
- collar_color: Default::default(),
- }
- }
-}
-
+ Ok(())
+ }
+}
+
+#[derive(Bundle)]
+pub struct CamelMetadataBundle {
+ _marker: Camel,
+ parent: AbstractAnimalMetadataBundle,
+ camel_tamed: CamelTamed,
+ camel_eating: CamelEating,
+ camel_standing: CamelStanding,
+ camel_bred: CamelBred,
+ camel_saddled: CamelSaddled,
+ camel_owner_uuid: CamelOwnerUuid,
+ dash: Dash,
+ last_pose_change_tick: LastPoseChangeTick,
+}
+impl Default for CamelMetadataBundle {
+ fn default() -> Self {
+ Self {
+ _marker: Camel,
+ parent: AbstractAnimalMetadataBundle {
+ _marker: AbstractAnimal,
+ parent: AbstractAgeableMetadataBundle {
+ _marker: AbstractAgeable,
+ 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),
+ },
+ },
+ abstract_ageable_baby: AbstractAgeableBaby(false),
+ },
+ },
+ camel_tamed: CamelTamed(false),
+ camel_eating: CamelEating(false),
+ camel_standing: CamelStanding(false),
+ camel_bred: CamelBred(false),
+ camel_saddled: CamelSaddled(false),
+ camel_owner_uuid: CamelOwnerUuid(None),
+ dash: Dash(false),
+ last_pose_change_tick: LastPoseChangeTick(-52),
+ }
+ }
+}
+
+#[derive(Component, Deref, DerefMut)]
+pub struct Tame(pub bool);
+#[derive(Component, Deref, DerefMut)]
+pub struct InSittingPose(pub bool);
+#[derive(Component, Deref, DerefMut)]
+pub struct Owneruuid(pub Option<Uuid>);
+#[derive(Component, Deref, DerefMut)]
+pub struct CatVariant(pub azalea_registry::CatVariant);
+#[derive(Component, Deref, DerefMut)]
+pub struct IsLying(pub bool);
+#[derive(Component, Deref, DerefMut)]
+pub struct RelaxStateOne(pub bool);
+#[derive(Component, Deref, DerefMut)]
+pub struct CatCollarColor(pub i32);
+#[derive(Component)]
+pub struct Cat;
impl Cat {
- pub fn set_index(&mut self, index: u8, value: EntityDataValue) -> Option<()> {
- match index {
- 0..=18 => self.abstract_tameable.set_index(index, value)?,
- 19 => self.variant = value.into_cat_variant().ok()?,
- 20 => self.is_lying = value.into_boolean().ok()?,
- 21 => self.relax_state_one = value.into_boolean().ok()?,
- 22 => self.collar_color = value.into_int().ok()?,
+ pub fn apply_metadata(
+ entity: &mut azalea_ecs::system::EntityCommands,
+ d: EntityDataItem,
+ ) -> Result<(), UpdateMetadataError> {
+ match d.index {
+ 0..=18 => AbstractTameable::apply_metadata(entity, d)?,
+ 19 => {
+ entity.insert(CatVariant(d.value.into_cat_variant()?));
+ }
+ 20 => {
+ entity.insert(IsLying(d.value.into_boolean()?));
+ }
+ 21 => {
+ entity.insert(RelaxStateOne(d.value.into_boolean()?));
+ }
+ 22 => {
+ entity.insert(CatCollarColor(d.value.into_int()?));
+ }
_ => {}
}
- Some(())
- }
-}
-impl Deref for Cat {
- type Target = AbstractTameable;
- fn deref(&self) -> &Self::Target {
- &self.abstract_tameable
- }
-}
-impl DerefMut for Cat {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.abstract_tameable
- }
-}
-
-#[derive(Debug, Clone)]
-pub struct CaveSpider {
- pub spider: Spider,
-}
-
-impl CaveSpider {
- pub fn read(metadata: &mut VecDeque<EntityDataValue>) -> Option<Self> {
- let spider = Spider::read(metadata)?;
- Some(Self { spider })
- }
-
- pub fn write(&self) -> Vec<EntityDataValue> {
- let mut metadata = Vec::new();
- metadata.extend(self.spider.write());
- metadata
- }
-}
-
-impl Default for CaveSpider {
- fn default() -> Self {
- Self {
- spider: Default::default(),
+ Ok(())
+ }
+}
+
+#[derive(Bundle)]
+pub struct CatMetadataBundle {
+ _marker: Cat,
+ parent: AbstractTameableMetadataBundle,
+ cat_variant: CatVariant,
+ is_lying: IsLying,
+ relax_state_one: RelaxStateOne,
+ cat_collar_color: CatCollarColor,
+}
+impl Default for CatMetadataBundle {
+ fn default() -> Self {
+ Self {
+ _marker: Cat,
+ parent: AbstractTameableMetadataBundle {
+ _marker: AbstractTameable,
+ parent: AbstractAnimalMetadataBundle {
+ _marker: AbstractAnimal,
+ parent: AbstractAgeableMetadataBundle {
+ _marker: AbstractAgeable,
+ 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),
+ },
+ },
+ abstract_ageable_baby: AbstractAgeableBaby(false),
+ },
+ },
+ tame: Tame(false),
+ in_sitting_pose: InSittingPose(false),
+ owneruuid: Owneruuid(None),
+ },
+ cat_variant: CatVariant(azalea_registry::CatVariant::Tabby),
+ is_lying: IsLying(false),
+ relax_state_one: RelaxStateOne(false),
+ cat_collar_color: CatCollarColor(Default::default()),
}
}
}
+#[derive(Component, Deref, DerefMut)]
+pub struct Climbing(pub bool);
+#[derive(Component)]
+pub struct CaveSpider;
impl CaveSpider {
- pub fn set_index(&mut self, index: u8, value: EntityDataValue) -> Option<()> {
- self.spider.set_index(index, value)
- }
-}
-impl Deref for CaveSpider {
- type Target = Spider;
- fn deref(&self) -> &Self::Target {
- &self.spider
- }
-}
-impl DerefMut for CaveSpider {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.spider
- }
-}
-
-#[derive(Debug, Clone)]
-pub struct ChestBoat {
- pub boat: Boat,
-}
-
-impl ChestBoat {
- pub fn read(metadata: &mut VecDeque<EntityDataValue>) -> Option<Self> {
- let boat = Boat::read(metadata)?;
- Some(Self { boat })
- }
-
- pub fn write(&self) -> Vec<EntityDataValue> {
- let mut metadata = Vec::new();
- metadata.extend(self.boat.write());
- metadata
- }
-}
-
-impl Default for ChestBoat {
- fn default() -> Self {
- Self {
- boat: Default::default(),
+ pub fn apply_metadata(
+ entity: &mut azalea_ecs::system::EntityCommands,
+ d: EntityDataItem,
+ ) -> Result<(), UpdateMetadataError> {
+ match d.index {
+ 0..=16 => Spider::apply_metadata(entity, d)?,
+ _ => {}
+ }
+ Ok(())
+ }
+}
+
+#[derive(Bundle)]
+pub struct CaveSpiderMetadataBundle {
+ _marker: CaveSpider,
+ parent: SpiderMetadataBundle,
+}
+impl Default for CaveSpiderMetadataBundle {
+ fn default() -> Self {
+ Self {
+ _marker: CaveSpider,
+ parent: SpiderMetadataBundle {
+ _marker: Spider,
+ 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),
+ },
+ },
+ },
+ climbing: Climbing(false),
+ },
}
}
}
+#[derive(Component)]
+pub struct ChestBoat;
impl ChestBoat {
- pub fn set_index(&mut self, index: u8, value: EntityDataValue) -> Option<()> {
- self.boat.set_index(index, value)
- }
-}
-impl Deref for ChestBoat {
- type Target = Boat;
- fn deref(&self) -> &Self::Target {
- &self.boat
- }
-}
-impl DerefMut for ChestBoat {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.boat
- }
-}
-
-#[derive(Debug, Clone)]
-pub struct ChestMinecart {
- pub abstract_minecart: AbstractMinecart,
-}
-
-impl ChestMinecart {
- pub fn read(metadata: &mut VecDeque<EntityDataValue>) -> Option<Self> {
- let abstract_minecart = AbstractMinecart::read(metadata)?;
- Some(Self { abstract_minecart })
- }
-
- pub fn write(&self) -> Vec<EntityDataValue> {
- let mut metadata = Vec::new();
- metadata.extend(self.abstract_minecart.write());
- metadata
- }
-}
-
-impl Default for ChestMinecart {
- fn default() -> Self {
- Self {
- abstract_minecart: Default::default(),
+ pub fn apply_metadata(
+ entity: &mut azalea_ecs::system::EntityCommands,
+ d: EntityDataItem,
+ ) -> Result<(), UpdateMetadataError> {
+ match d.index {
+ 0..=14 => Boat::apply_metadata(entity, d)?,
+ _ => {}
+ }
+ Ok(())
+ }
+}
+
+#[derive(Bundle)]
+pub struct ChestBoatMetadataBundle {
+ _marker: ChestBoat,
+ parent: BoatMetadataBundle,
+}
+impl Default for ChestBoatMetadataBundle {
+ fn default() -> Self {
+ Self {
+ _marker: ChestBoat,
+ parent: BoatMetadataBundle {
+ _marker: Boat,
+ 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),
+ },
+ boat_hurt: BoatHurt(0),
+ boat_hurtdir: BoatHurtdir(1),
+ boat_damage: BoatDamage(0.0),
+ boat_kind: BoatKind(Default::default()),
+ paddle_left: PaddleLeft(false),
+ paddle_right: PaddleRight(false),
+ bubble_time: BubbleTime(0),
+ },
}
}
}
+#[derive(Component, Deref, DerefMut)]
+pub struct AbstractMinecartHurt(pub i32);
+#[derive(Component, Deref, DerefMut)]
+pub struct AbstractMinecartHurtdir(pub i32);
+#[derive(Component, Deref, DerefMut)]
+pub struct AbstractMinecartDamage(pub f32);
+#[derive(Component, Deref, DerefMut)]
+pub struct DisplayBlock(pub i32);
+#[derive(Component, Deref, DerefMut)]
+pub struct DisplayOffset(pub i32);
+#[derive(Component, Deref, DerefMut)]
+pub struct CustomDisplay(pub bool);
+#[derive(Component)]
+pub struct ChestMinecart;
impl ChestMinecart {
- pub fn set_index(&mut self, index: u8, value: EntityDataValue) -> Option<()> {
- self.abstract_minecart.set_index(index, value)
- }
-}
-impl Deref for ChestMinecart {
- type Target = AbstractMinecart;
- fn deref(&self) -> &Self::Target {
- &self.abstract_minecart
- }
-}
-impl DerefMut for ChestMinecart {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.abstract_minecart
- }
-}
-
-#[derive(Debug, Clone)]
-pub struct Chicken {
- pub abstract_animal: AbstractAnimal,
-}
-
-impl Chicken {
- pub fn read(metadata: &mut VecDeque<EntityDataValue>) -> Option<Self> {
- let abstract_animal = AbstractAnimal::read(metadata)?;
- Some(Self { abstract_animal })
- }
-
- pub fn write(&self) -> Vec<EntityDataValue> {
- let mut metadata = Vec::new();
- metadata.extend(self.abstract_animal.write());
- metadata
- }
-}
-
-impl Default for Chicken {
- fn default() -> Self {
- Self {
- abstract_animal: Default::default(),
+ pub fn apply_metadata(
+ entity: &mut azalea_ecs::system::EntityCommands,
+ d: EntityDataItem,
+ ) -> Result<(), UpdateMetadataError> {
+ match d.index {
+ 0..=13 => AbstractMinecart::apply_metadata(entity, d)?,
+ _ => {}
+ }
+ Ok(())
+ }
+}
+
+#[derive(Bundle)]
+pub struct ChestMinecartMetadataBundle {
+ _marker: ChestMinecart,
+ parent: AbstractMinecartMetadataBundle,
+}
+impl Default for ChestMinecartMetadataBundle {
+ fn default() -> Self {
+ Self {
+ _marker: ChestMinecart,
+ parent: AbstractMinecartMetadataBundle {
+ _marker: AbstractMinecart,
+ 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),
+ },
+ abstract_minecart_hurt: AbstractMinecartHurt(0),
+ abstract_minecart_hurtdir: AbstractMinecartHurtdir(1),
+ abstract_minecart_damage: AbstractMinecartDamage(0.0),
+ display_block: DisplayBlock(Default::default()),
+ display_offset: DisplayOffset(6),
+ custom_display: CustomDisplay(false),
+ },
}
}
}
+#[derive(Component)]
+pub struct Chicken;
impl Chicken {
- pub fn set_index(&mut self, index: u8, value: EntityDataValue) -> Option<()> {
- self.abstract_animal.set_index(index, value)
- }
-}
-impl Deref for Chicken {
- type Target = AbstractAnimal;
- fn deref(&self) -> &Self::Target {
- &self.abstract_animal
- }
-}
-impl DerefMut for Chicken {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.abstract_animal
- }
-}
-
-#[derive(Debug, Clone)]
-pub struct Cod {
- pub abstract_creature: AbstractCreature,
- pub from_bucket: bool,
-}
-
-impl Cod {
- pub fn read(metadata: &mut VecDeque<EntityDataValue>) -> Option<Self> {
- let abstract_creature = AbstractCreature::read(metadata)?;
- let from_bucket = metadata.pop_front()?.into_boolean().ok()?;
- Some(Self {
- abstract_creature,
- from_bucket,
- })
- }
-
- pub fn write(&self) -> Vec<EntityDataValue> {
- let mut metadata = Vec::new();
- metadata.extend(self.abstract_creature.write());
- metadata.push(EntityDataValue::Boolean(self.from_bucket.clone()));
- metadata
- }
-}
-
-impl Default for Cod {
- fn default() -> Self {
- Self {
- abstract_creature: Default::default(),
- from_bucket: false,
+ pub fn apply_metadata(
+ entity: &mut azalea_ecs::system::EntityCommands,
+ d: EntityDataItem,
+ ) -> Result<(), UpdateMetadataError> {
+ match d.index {
+ 0..=16 => AbstractAnimal::apply_metadata(entity, d)?,
+ _ => {}
+ }
+ Ok(())
+ }
+}
+
+#[derive(Bundle)]
+pub struct ChickenMetadataBundle {
+ _marker: Chicken,
+ parent: AbstractAnimalMetadataBundle,
+}
+impl Default for ChickenMetadataBundle {
+ fn default() -> Self {
+ Self {
+ _marker: Chicken,
+ parent: AbstractAnimalMetadataBundle {
+ _marker: AbstractAnimal,
+ parent: AbstractAgeableMetadataBundle {
+ _marker: AbstractAgeable,
+ 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),
+ },
+ },
+ abstract_ageable_baby: AbstractAgeableBaby(false),
+ },
+ },
}
}
}
+#[derive(Component, Deref, DerefMut)]
+pub struct CodFromBucket(pub bool);
+#[derive(Component)]
+pub struct Cod;
impl Cod {
- pub fn set_index(&mut self, index: u8, value: EntityDataValue) -> Option<()> {
- match index {
- 0..=15 => self.abstract_creature.set_index(index, value)?,
- 16 => self.from_bucket = value.into_boolean().ok()?,
+ pub fn apply_metadata(
+ entity: &mut azalea_ecs::system::EntityCommands,
+ d: EntityDataItem,
+ ) -> Result<(), UpdateMetadataError> {
+ match d.index {
+ 0..=15 => AbstractCreature::apply_metadata(entity, d)?,
+ 16 => {
+ entity.insert(CodFromBucket(d.value.into_boolean()?));
+ }
_ => {}
}
- Some(())
- }
-}
-impl Deref for Cod {
- type Target = AbstractCreature;
- fn deref(&self) -> &Self::Target {
- &self.abstract_creature
- }
-}
-impl DerefMut for Cod {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.abstract_creature
- }
-}
-
-#[derive(Debug, Clone)]
-pub struct CommandBlockMinecart {
- pub abstract_minecart: AbstractMinecart,
- pub command_name: String,
- pub last_output: Component,
-}
-
-impl CommandBlockMinecart {
- pub fn read(metadata: &mut VecDeque<EntityDataValue>) -> Option<Self> {
- let abstract_minecart = AbstractMinecart::read(metadata)?;
- let command_name = metadata.pop_front()?.into_string().ok()?;
- let last_output = metadata.pop_front()?.into_component().ok()?;
- Some(Self {
- abstract_minecart,
- command_name,
- last_output,
- })
- }
-
- pub fn write(&self) -> Vec<EntityDataValue> {
- let mut metadata = Vec::new();
- metadata.extend(self.abstract_minecart.write());
- metadata.push(EntityDataValue::String(self.command_name.clone()));
- metadata.push(EntityDataValue::Component(self.last_output.clone()));
- metadata
- }
-}
-
-impl Default for CommandBlockMinecart {
- fn default() -> Self {
- Self {
- abstract_minecart: Default::default(),
- command_name: "".to_string(),
- last_output: Default::default(),
+ Ok(())
+ }
+}
+
+#[derive(Bundle)]
+pub struct CodMetadataBundle {
+ _marker: Cod,
+ parent: AbstractCreatureMetadataBundle,
+ cod_from_bucket: CodFromBucket,
+}
+impl Default for CodMetadataBundle {
+ fn default() -> Self {
+ Self {
+ _marker: Cod,
+ 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),
+ },
+ },
+ cod_from_bucket: CodFromBucket(false),
}
}
}
+#[derive(Component, Deref, DerefMut)]
+pub struct CommandName(pub String);
+#[derive(Component, Deref, DerefMut)]
+pub struct LastOutput(pub FormattedText);
+#[derive(Component)]
+pub struct CommandBlockMinecart;
impl CommandBlockMinecart {
- pub fn set_index(&mut self, index: u8, value: EntityDataValue) -> Option<()> {
- match index {
- 0..=13 => self.abstract_minecart.set_index(index, value)?,
- 14 => self.command_name = value.into_string().ok()?,
- 15 => self.last_output = value.into_component().ok()?,
+ pub fn apply_metadata(
+ entity: &mut azalea_ecs::system::EntityCommands,
+ d: EntityDataItem,
+ ) -> Result<(), UpdateMetadataError> {
+ match d.index {
+ 0..=13 => AbstractMinecart::apply_metadata(entity, d)?,
+ 14 => {
+ entity.insert(CommandName(d.value.into_string()?));
+ }
+ 15 => {
+ entity.insert(LastOutput(d.value.into_formatted_text()?));
+ }
_ => {}
}
- Some(())
- }
-}
-impl Deref for CommandBlockMinecart {
- type Target = AbstractMinecart;
- fn deref(&self) -> &Self::Target {
- &self.abstract_minecart
- }
-}
-impl DerefMut for CommandBlockMinecart {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.abstract_minecart
- }
-}
-
-#[derive(Debug, Clone)]
-pub struct Cow {
- pub abstract_animal: AbstractAnimal,
-}
-
-impl Cow {
- pub fn read(metadata: &mut VecDeque<EntityDataValue>) -> Option<Self> {
- let abstract_animal = AbstractAnimal::read(metadata)?;
- Some(Self { abstract_animal })
- }
-
- pub fn write(&self) -> Vec<EntityDataValue> {
- let mut metadata = Vec::new();
- metadata.extend(self.abstract_animal.write());
- metadata
- }
-}
-
-impl Default for Cow {
- fn default() -> Self {
- Self {
- abstract_animal: Default::default(),
+ Ok(())
+ }
+}
+
+#[derive(Bundle)]
+pub struct CommandBlockMinecartMetadataBundle {
+ _marker: CommandBlockMinecart,
+ parent: AbstractMinecartMetadataBundle,
+ command_name: CommandName,
+ last_output: LastOutput,
+}
+impl Default for CommandBlockMinecartMetadataBundle {
+ fn default() -> Self {
+ Self {
+ _marker: CommandBlockMinecart,
+ parent: AbstractMinecartMetadataBundle {
+ _marker: AbstractMinecart,
+ 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),
+ },
+ abstract_minecart_hurt: AbstractMinecartHurt(0),
+ abstract_minecart_hurtdir: AbstractMinecartHurtdir(1),
+ abstract_minecart_damage: AbstractMinecartDamage(0.0),
+ display_block: DisplayBlock(Default::default()),
+ display_offset: DisplayOffset(6),
+ custom_display: CustomDisplay(false),
+ },
+ command_name: CommandName("".to_string()),
+ last_output: LastOutput(Default::default()),
}
}
}
+#[derive(Component)]
+pub struct Cow;
impl Cow {
- pub fn set_index(&mut self, index: u8, value: EntityDataValue) -> Option<()> {
- self.abstract_animal.set_index(index, value)
- }
-}
-impl Deref for Cow {
- type Target = AbstractAnimal;
- fn deref(&self) -> &Self::Target {
- &self.abstract_animal
- }
-}
-impl DerefMut for Cow {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.abstract_animal
- }
-}
-
-#[derive(Debug, Clone)]
-pub struct Creeper {
- pub abstract_monster: AbstractMonster,
- pub swell_dir: i32,
- pub is_powered: bool,
- pub is_ignited: bool,
-}
-
-impl Creeper {
- pub fn read(metadata: &mut VecDeque<EntityDataValue>) -> Option<Self> {
- let abstract_monster = AbstractMonster::read(metadata)?;
- let swell_dir = metadata.pop_front()?.into_int().ok()?;
- let is_powered = metadata.pop_front()?.into_boolean().ok()?;
- let is_ignited = metadata.pop_front()?.into_boolean().ok()?;
- Some(Self {
- abstract_monster,
- swell_dir,
- is_powered,
- is_ignited,
- })
- }
-
- pub fn write(&self) -> Vec<EntityDataValue> {
- let mut metadata = Vec::new();
- metadata.extend(self.abstract_monster.write());
- metadata.push(EntityDataValue::Int(self.swell_dir.clone()));
- metadata.push(EntityDataValue::Boolean(self.is_powered.clone()));
- metadata.push(EntityDataValue::Boolean(self.is_ignited.clone()));
- metadata
- }
-}
-
-impl Default for Creeper {
- fn default() -> Self {
- Self {
- abstract_monster: Default::default(),
- swell_dir: -1,
- is_powered: false,
- is_ignited: false,
+ pub fn apply_metadata(
+ entity: &mut azalea_ecs::system::EntityCommands,
+ d: EntityDataItem,
+ ) -> Result<(), UpdateMetadataError> {
+ match d.index {
+ 0..=16 => AbstractAnimal::apply_metadata(entity, d)?,
+ _ => {}
+ }
+ Ok(())
+ }
+}
+
+#[derive(Bundle)]
+pub struct CowMetadataBundle {
+ _marker: Cow,
+ parent: AbstractAnimalMetadataBundle,
+}
+impl Default for CowMetadataBundle {
+ fn default() -> Self {
+ Self {
+ _marker: Cow,
+ parent: AbstractAnimalMetadataBundle {
+ _marker: AbstractAnimal,
+ parent: AbstractAgeableMetadataBundle {
+ _marker: AbstractAgeable,
+ 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),
+ },
+ },
+ abstract_ageable_baby: AbstractAgeableBaby(false),
+ },
+ },
}
}
}
+#[derive(Component, Deref, DerefMut)]
+pub struct SwellDir(pub i32);
+#[derive(Component, Deref, DerefMut)]
+pub struct IsPowered(pub bool);
+#[derive(Component, Deref, DerefMut)]
+pub struct IsIgnited(pub bool);
+#[derive(Component)]
+pub struct Creeper;
impl Creeper {
- pub fn set_index(&mut self, index: u8, value: EntityDataValue) -> Option<()> {
- match index {
- 0..=15 => self.abstract_monster.set_index(index, value)?,
- 16 => self.swell_dir = value.into_int().ok()?,
- 17 => self.is_powered = value.into_boolean().ok()?,
- 18 => self.is_ignited = value.into_boolean().ok()?,
+ pub fn apply_metadata(
+ entity: &mut azalea_ecs::system::EntityCommands,
+ d: EntityDataItem,
+ ) -> Result<(), UpdateMetadataError> {
+ match d.index {
+ 0..=15 => AbstractMonster::apply_metadata(entity, d)?,
+ 16 => {
+ entity.insert(SwellDir(d.value.into_int()?));
+ }
+ 17 => {
+ entity.insert(IsPowered(d.value.into_boolean()?));
+ }
+ 18 => {
+ entity.insert(IsIgnited(d.value.into_boolean()?));
+ }
_ => {}
}
- Some(())
- }
-}
-impl Deref for Creeper {
- type Target = AbstractMonster;
- fn deref(&self) -> &Self::Target {
- &self.abstract_monster
- }
-}
-impl DerefMut for Creeper {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.abstract_monster
- }
-}
-
-#[derive(Debug, Clone)]
-pub struct Dolphin {
- pub abstract_creature: AbstractCreature,
- pub treasure_pos: BlockPos,
- pub got_fish: bool,
- pub moistness_level: i32,
-}
-
-impl Dolphin {
- pub fn read(metadata: &mut VecDeque<EntityDataValue>) -> Option<Self> {
- let abstract_creature = AbstractCreature::read(metadata)?;
- let treasure_pos = metadata.pop_front()?.into_block_pos().ok()?;
- let got_fish = metadata.pop_front()?.into_boolean().ok()?;
- let moistness_level = metadata.pop_front()?.into_int().ok()?;
- Some(Self {
- abstract_creature,
- treasure_pos,
- got_fish,
- moistness_level,
- })
- }
-
- pub fn write(&self) -> Vec<EntityDataValue> {
- let mut metadata = Vec::new();
- metadata.extend(self.abstract_creature.write());
- metadata.push(EntityDataValue::BlockPos(self.treasure_pos.clone()));
- metadata.push(EntityDataValue::Boolean(self.got_fish.clone()));
- metadata.push(EntityDataValue::Int(self.moistness_level.clone()));
- metadata
- }
-}
-
-impl Default for Dolphin {
- fn default() -> Self {
- Self {
- abstract_creature: Default::default(),
- treasure_pos: BlockPos::new(0, 0, 0),
- got_fish: false,
- moistness_level: 2400,
+ Ok(())
+ }
+}
+
+#[derive(Bundle)]
+pub struct CreeperMetadataBundle {
+ _marker: Creeper,
+ parent: AbstractMonsterMetadataBundle,
+ swell_dir: SwellDir,
+ is_powered: IsPowered,
+ is_ignited: IsIgnited,
+}
+impl Default for CreeperMetadataBundle {
+ fn default() -> Self {
+ Self {
+ _marker: Creeper,
+ 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),
+ },
+ },
+ },
+ swell_dir: SwellDir(-1),
+ is_powered: IsPowered(false),
+ is_ignited: IsIgnited(false),
}
}
}
+#[derive(Component, Deref, DerefMut)]
+pub struct TreasurePos(pub BlockPos);
+#[derive(Component, Deref, DerefMut)]
+pub struct GotFish(pub bool);
+#[derive(Component, Deref, DerefMut)]
+pub struct MoistnessLevel(pub i32);
+#[derive(Component)]
+pub struct Dolphin;
impl Dolphin {
- pub fn set_index(&mut self, index: u8, value: EntityDataValue) -> Option<()> {
- match index {
- 0..=15 => self.abstract_creature.set_index(index, value)?,
- 16 => self.treasure_pos = value.into_block_pos().ok()?,
- 17 => self.got_fish = value.into_boolean().ok()?,
- 18 => self.moistness_level = value.into_int().ok()?,
+ pub fn apply_metadata(
+ entity: &mut azalea_ecs::system::EntityCommands,
+ d: EntityDataItem,
+ ) -> Result<(), UpdateMetadataError> {
+ match d.index {
+ 0..=15 => AbstractCreature::apply_metadata(entity, d)?,
+ 16 => {
+ entity.insert(TreasurePos(d.value.into_block_pos()?));
+ }
+ 17 => {
+ entity.insert(GotFish(d.value.into_boolean()?));
+ }
+ 18 => {
+ entity.insert(MoistnessLevel(d.value.into_int()?));
+ }
_ => {}
}
- Some(())
- }
-}
-impl Deref for Dolphin {
- type Target = AbstractCreature;
- fn deref(&self) -> &Self::Target {
- &self.abstract_creature
- }
-}
-impl DerefMut for Dolphin {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.abstract_creature
- }
-}
-
-#[derive(Debug, Clone)]
-pub struct Donkey {
- pub abstract_animal: AbstractAnimal,
- pub tamed: bool,
- pub eating: bool,
- pub standing: bool,
- pub bred: bool,
- pub saddled: bool,
- pub owner_uuid: Option<Uuid>,
- pub chest: bool,
-}
-
-impl Donkey {
- pub fn read(metadata: &mut VecDeque<EntityDataValue>) -> Option<Self> {
- let abstract_animal = AbstractAnimal::read(metadata)?;
- let bitfield = metadata.pop_front()?.into_byte().ok()?;
- let tamed = bitfield & 0x2 != 0;
- let eating = bitfield & 0x10 != 0;
- let standing = bitfield & 0x20 != 0;
- let bred = bitfield & 0x8 != 0;
- let saddled = bitfield & 0x4 != 0;
- let owner_uuid = metadata.pop_front()?.into_optional_uuid().ok()?;
- let chest = metadata.pop_front()?.into_boolean().ok()?;
- Some(Self {
- abstract_animal,
- tamed,
- eating,
- standing,
- bred,
- saddled,
- owner_uuid,
- chest,
- })
- }
-
- pub fn write(&self) -> Vec<EntityDataValue> {
- let mut metadata = Vec::new();
- metadata.extend(self.abstract_animal.write());
- let mut bitfield = 0u8;
- if self.tamed {
- bitfield &= 0x2;
- }
- if self.eating {
- bitfield &= 0x10;
- }
- if self.standing {
- bitfield &= 0x20;
- }
- if self.bred {
- bitfield &= 0x8;
- }
- if self.saddled {
- bitfield &= 0x4;
- }
- metadata.push(EntityDataValue::Byte(bitfield));
- metadata.push(EntityDataValue::OptionalUuid(self.owner_uuid.clone()));
- metadata.push(EntityDataValue::Boolean(self.chest.clone()));
- metadata
- }
-}
-
-impl Default for Donkey {
- fn default() -> Self {
- Self {
- abstract_animal: Default::default(),
- tamed: false,
- eating: false,
- standing: false,
- bred: false,
- saddled: false,
- owner_uuid: None,
- chest: false,
- }
- }
-}
-
+ Ok(())
+ }
+}
+
+#[derive(Bundle)]
+pub struct DolphinMetadataBundle {
+ _marker: Dolphin,
+ parent: AbstractCreatureMetadataBundle,
+ treasure_pos: TreasurePos,
+ got_fish: GotFish,
+ moistness_level: MoistnessLevel,
+}
+impl Default for DolphinMetadataBundle {
+ fn default() -> Self {
+ Self {
+ _marker: Dolphin,
+ 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),
+ },
+ },
+ treasure_pos: TreasurePos(BlockPos::new(0, 0, 0)),
+ got_fish: GotFish(false),
+ moistness_level: MoistnessLevel(2400),
+ }
+ }
+}
+
+#[derive(Component, Deref, DerefMut)]
+pub struct DonkeyTamed(pub bool);
+#[derive(Component, Deref, DerefMut)]
+pub struct DonkeyEating(pub bool);
+#[derive(Component, Deref, DerefMut)]
+pub struct DonkeyStanding(pub bool);
+#[derive(Component, Deref, DerefMut)]
+pub struct DonkeyBred(pub bool);
+#[derive(Component, Deref, DerefMut)]
+pub struct DonkeySaddled(pub bool);
+#[derive(Component, Deref, DerefMut)]
+pub struct DonkeyOwnerUuid(pub Option<Uuid>);
+#[derive(Component, Deref, DerefMut)]
+pub struct DonkeyChest(pub bool);
+#[derive(Component)]
+pub struct Donkey;
impl Donkey {
- pub fn set_index(&mut self, index: u8, value: EntityDataValue) -> Option<()> {
- match index {
- 0..=16 => self.abstract_animal.set_index(index, value)?,
+ pub fn apply_metadata(
+ entity: &mut azalea_ecs::system::EntityCommands,
+ d: EntityDataItem,
+ ) -> Result<(), UpdateMetadataError> {
+ match d.index {
+ 0..=16 => AbstractAnimal::apply_metadata(entity, d)?,
17 => {
- let bitfield = value.into_byte().ok()?;
- self.tamed = bitfield & 0x2 != 0;
- self.eating = bitfield & 0x10 != 0;
- self.standing = bitfield & 0x20 != 0;
- self.bred = bitfield & 0x8 != 0;
- self.saddled = bitfield & 0x4 != 0;
+ let bitfield = d.value.into_byte()?;
+ entity.insert(DonkeyTamed(bitfield & 0x2 != 0));
+ entity.insert(DonkeyEating(bitfield & 0x10 != 0));
+ entity.insert(DonkeyStanding(bitfield & 0x20 != 0));
+ entity.insert(DonkeyBred(bitfield & 0x8 != 0));
+ entity.insert(DonkeySaddled(bitfield & 0x4 != 0));
+ }
+ 18 => {
+ entity.insert(DonkeyOwnerUuid(d.value.into_optional_uuid()?));
+ }
+ 19 => {
+ entity.insert(DonkeyChest(d.value.into_boolean()?));
}
- 18 => self.owner_uuid = value.into_optional_uuid().ok()?,
- 19 => self.chest = value.into_boolean().ok()?,
_ => {}
}
- Some(())
- }
-}
-impl Deref for Donkey {
- type Target = AbstractAnimal;
- fn deref(&self) -> &Self::Target {
- &self.abstract_animal
- }
-}
-impl DerefMut for Donkey {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.abstract_animal
- }
-}
-
-#[derive(Debug, Clone)]
-pub struct DragonFireball {
- pub abstract_entity: AbstractEntity,
-}
-
-impl DragonFireball {
- pub fn read(metadata: &mut VecDeque<EntityDataValue>) -> Option<Self> {
- let abstract_entity = AbstractEntity::read(metadata)?;
- Some(Self { abstract_entity })
- }
-
- pub fn write(&self) -> Vec<EntityDataValue> {
- let mut metadata = Vec::new();
- metadata.extend(self.abstract_entity.write());
- metadata
- }
-}
-
-impl Default for DragonFireball {
- fn default() -> Self {
- Self {
- abstract_entity: Default::default(),
+ Ok(())
+ }
+}
+
+#[derive(Bundle)]
+pub struct DonkeyMetadataBundle {
+ _marker: Donkey,
+ parent: AbstractAnimalMetadataBundle,
+ donkey_tamed: DonkeyTamed,
+ donkey_eating: DonkeyEating,
+ donkey_standing: DonkeyStanding,
+ donkey_bred: DonkeyBred,
+ donkey_saddled: DonkeySaddled,
+ donkey_owner_uuid: DonkeyOwnerUuid,
+ donkey_chest: DonkeyChest,
+}
+impl Default for DonkeyMetadataBundle {
+ fn default() -> Self {
+ Self {
+ _marker: Donkey,
+ parent: AbstractAnimalMetadataBundle {
+ _marker: AbstractAnimal,
+ parent: AbstractAgeableMetadataBundle {
+ _marker: AbstractAgeable,
+ 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),
+ },
+ },
+ abstract_ageable_baby: AbstractAgeableBaby(false),
+ },
+ },
+ donkey_tamed: DonkeyTamed(false),
+ donkey_eating: DonkeyEating(false),
+ donkey_standing: DonkeyStanding(false),
+ donkey_bred: DonkeyBred(false),
+ donkey_saddled: DonkeySaddled(false),
+ donkey_owner_uuid: DonkeyOwnerUuid(None),
+ donkey_chest: DonkeyChest(false),
}
}
}
+#[derive(Component)]
+pub struct DragonFireball;
impl DragonFireball {
- pub fn set_index(&mut self, index: u8, value: EntityDataValue) -> Option<()> {
- self.abstract_entity.set_index(index, value)
- }
-}
-impl Deref for DragonFireball {
- type Target = AbstractEntity;
- fn deref(&self) -> &Self::Target {
- &self.abstract_entity
- }
-}
-impl DerefMut for DragonFireball {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.abstract_entity
+ pub fn apply_metadata(
+ entity: &mut azalea_ecs::system::EntityCommands,
+ d: EntityDataItem,
+ ) -> Result<(), UpdateMetadataError> {
+ match d.index {
+ 0..=7 => AbstractEntity::apply_metadata(entity, d)?,
+ _ => {}
+ }
+ Ok(())
}
}
-#[derive(Debug, Clone)]
-pub struct Drowned {
- pub zombie: Zombie,
-}
-
-impl Drowned {
- pub fn read(metadata: &mut VecDeque<EntityDataValue>) -> Option<Self> {
- let zombie = Zombie::read(metadata)?;
- Some(Self { zombie })
- }
-
- pub fn write(&self) -> Vec<EntityDataValue> {
- let mut metadata = Vec::new();
- metadata.extend(self.zombie.write());
- metadata
- }
+#[derive(Bundle)]
+pub struct DragonFireballMetadataBundle {
+ _marker: DragonFireball,
+ parent: AbstractEntityMetadataBundle,
}
-
-impl Default for Drowned {
+impl Default for DragonFireballMetadataBundle {
fn default() -> Self {
Self {
- zombie: Default::default(),
+ _marker: DragonFireball,
+ 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)]
+pub struct ZombieBaby(pub bool);
+#[derive(Component, Deref, DerefMut)]
+pub struct SpecialType(pub i32);
+#[derive(Component, Deref, DerefMut)]
+pub struct DrownedConversion(pub bool);
+#[derive(Component)]
+pub struct Drowned;
impl Drowned {
- pub fn set_index(&mut self, index: u8, value: EntityDataValue) -> Option<()> {
- self.zombie.set_index(index, value)
- }
-}
-impl Deref for Drowned {
- type Target = Zombie;
- fn deref(&self) -> &Self::Target {
- &self.zombie
- }
-}
-impl DerefMut for Drowned {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.zombie
- }
-}
-
-#[derive(Debug, Clone)]
-pub struct Egg {
- pub abstract_entity: AbstractEntity,
- pub item_stack: Slot,
-}
-
-impl Egg {
- pub fn read(metadata: &mut VecDeque<EntityDataValue>) -> Option<Self> {
- let abstract_entity = AbstractEntity::read(metadata)?;
- let item_stack = metadata.pop_front()?.into_item_stack().ok()?;
- Some(Self {
- abstract_entity,
- item_stack,
- })
- }
-
- pub fn write(&self) -> Vec<EntityDataValue> {
- let mut metadata = Vec::new();
- metadata.extend(self.abstract_entity.write());
- metadata.push(EntityDataValue::ItemStack(self.item_stack.clone()));
- metadata
- }
-}
-
-impl Default for Egg {
- fn default() -> Self {
- Self {
- abstract_entity: Default::default(),
- item_stack: Slot::Empty,
+ pub fn apply_metadata(
+ entity: &mut azalea_ecs::system::EntityCommands,
+ d: EntityDataItem,
+ ) -> Result<(), UpdateMetadataError> {
+ match d.index {
+ 0..=18 => Zombie::apply_metadata(entity, d)?,
+ _ => {}
+ }
+ Ok(())
+ }
+}
+
+#[derive(Bundle)]
+pub struct DrownedMetadataBundle {
+ _marker: Drowned,
+ parent: ZombieMetadataBundle,
+}
+impl Default for DrownedMetadataBundle {
+ fn default() -> Self {
+ Self {
+ _marker: Drowned,
+ parent: ZombieMetadataBundle {
+ _marker: Zombie,
+ 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),
+ },
+ },
+ },
+ zombie_baby: ZombieBaby(false),
+ special_type: SpecialType(0),
+ drowned_conversion: DrownedConversion(false),
+ },
}
}
}
+#[derive(Component, Deref, DerefMut)]
+pub struct EggItemStack(pub Slot);
+#[derive(Component)]
+pub struct Egg;
impl Egg {
- pub fn set_index(&mut self, index: u8, value: EntityDataValue) -> Option<()> {
- match index {
- 0..=7 => self.abstract_entity.set_index(index, value)?,
- 8 => self.item_stack = value.into_item_stack().ok()?,
+ pub fn apply_metadata(
+ entity: &mut azalea_ecs::system::EntityCommands,
+ d: EntityDataItem,
+ ) -> Result<(), UpdateMetadataError> {
+ match d.index {
+ 0..=7 => AbstractEntity::apply_metadata(entity, d)?,
+ 8 => {
+ entity.insert(EggItemStack(d.value.into_item_stack()?));
+ }
_ => {}
}
- Some(())
- }
-}
-impl Deref for Egg {
- type Target = AbstractEntity;
- fn deref(&self) -> &Self::Target {
- &self.abstract_entity
- }
-}
-impl DerefMut for Egg {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.abstract_entity
+ Ok(())
}
}
-#[derive(Debug, Clone)]
-pub struct ElderGuardian {
- pub guardian: Guardian,
-}
-
-impl ElderGuardian {
- pub fn read(metadata: &mut VecDeque<EntityDataValue>) -> Option<Self> {
- let guardian = Guardian::read(metadata)?;
- Some(Self { guardian })
- }
-
- pub fn write(&self) -> Vec<EntityDataValue> {
- let mut metadata = Vec::new();
- metadata.extend(self.guardian.write());
- metadata
- }
+#[derive(Bundle)]
+pub struct EggMetadataBundle {
+ _marker: Egg,
+ parent: AbstractEntityMetadataBundle,
+ egg_item_stack: EggItemStack,
}
-
-impl Default for ElderGuardian {
+impl Default for EggMetadataBundle {
fn default() -> Self {
Self {
- guardian: Default::default(),
+ _marker: Egg,
+ 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),
+ },
+ egg_item_stack: EggItemStack(Slot::Empty),
}
}
}
+#[derive(Component, Deref, DerefMut)]
+pub struct Moving(pub bool);
+#[derive(Component, Deref, DerefMut)]
+pub struct AttackTarget(pub i32);
+#[derive(Component)]
+pub struct ElderGuardian;
impl ElderGuardian {
- pub fn set_index(&mut self, index: u8, value: EntityDataValue) -> Option<()> {
- self.guardian.set_index(index, value)
- }
-}
-impl Deref for ElderGuardian {
- type Target = Guardian;
- fn deref(&self) -> &Self::Target {
- &self.guardian
- }
-}
-impl DerefMut for ElderGuardian {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.guardian
- }
-}
-
-#[derive(Debug, Clone)]
-pub struct EndCrystal {
- pub abstract_entity: AbstractEntity,
- pub beam_target: Option<BlockPos>,
- pub show_bottom: bool,
-}
-
-impl EndCrystal {
- pub fn read(metadata: &mut VecDeque<EntityDataValue>) -> Option<Self> {
- let abstract_entity = AbstractEntity::read(metadata)?;
- let beam_target = metadata.pop_front()?.into_optional_block_pos().ok()?;
- let show_bottom = metadata.pop_front()?.into_boolean().ok()?;
- Some(Self {
- abstract_entity,
- beam_target,
- show_bottom,
- })
- }
-
- pub fn write(&self) -> Vec<EntityDataValue> {
- let mut metadata = Vec::new();
- metadata.extend(self.abstract_entity.write());
- metadata.push(EntityDataValue::OptionalBlockPos(self.beam_target.clone()));
- metadata.push(EntityDataValue::Boolean(self.show_bottom.clone()));
- metadata
- }
-}
-
-impl Default for EndCrystal {
- fn default() -> Self {
- Self {
- abstract_entity: Default::default(),
- beam_target: None,
- show_bottom: true,
+ pub fn apply_metadata(
+ entity: &mut azalea_ecs::system::EntityCommands,
+ d: EntityDataItem,
+ ) -> Result<(), UpdateMetadataError> {
+ match d.index {
+ 0..=17 => Guardian::apply_metadata(entity, d)?,
+ _ => {}
+ }
+ Ok(())
+ }
+}
+
+#[derive(Bundle)]
+pub struct ElderGuardianMetadataBundle {
+ _marker: ElderGuardian,
+ parent: GuardianMetadataBundle,
+}
+impl Default for ElderGuardianMetadataBundle {
+ fn default() -> Self {
+ Self {
+ _marker: ElderGuardian,
+ parent: GuardianMetadataBundle {
+ _marker: Guardian,
+ 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),
+ },
+ },
+ },
+ moving: Moving(false),
+ attack_target: AttackTarget(0),
+ },
}
}
}
+#[derive(Component, Deref, DerefMut)]
+pub struct BeamTarget(pub Option<BlockPos>);
+#[derive(Component, Deref, DerefMut)]
+pub struct ShowBottom(pub bool);
+#[derive(Component)]
+pub struct EndCrystal;
impl EndCrystal {
- pub fn set_index(&mut self, index: u8, value: EntityDataValue) -> Option<()> {
- match index {
- 0..=7 => self.abstract_entity.set_index(index, value)?,
- 8 => self.beam_target = value.into_optional_block_pos().ok()?,
- 9 => self.show_bottom = value.into_boolean().ok()?,
+ pub fn apply_metadata(
+ entity: &mut azalea_ecs::system::EntityCommands,
+ d: EntityDataItem,
+ ) -> Result<(), UpdateMetadataError> {
+ match d.index {
+ 0..=7 => AbstractEntity::apply_metadata(entity, d)?,
+ 8 => {
+ entity.insert(BeamTarget(d.value.into_optional_block_pos()?));
+ }
+ 9 => {
+ entity.insert(ShowBottom(d.value.into_boolean()?));
+ }
_ => {}
}
- Some(())
- }
-}
-impl Deref for EndCrystal {
- type Target = AbstractEntity;
- fn deref(&self) -> &Self::Target {
- &self.abstract_entity
- }
-}
-impl DerefMut for EndCrystal {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.abstract_entity
- }
-}
-
-#[derive(Debug, Clone)]
-pub struct EnderDragon {
- pub abstract_insentient: AbstractInsentient,
- pub phase: i32,
-}
-
-impl EnderDragon {
- pub fn read(metadata: &mut VecDeque<EntityDataValue>) -> Option<Self> {
- let abstract_insentient = AbstractInsentient::read(metadata)?;
- let phase = metadata.pop_front()?.into_int().ok()?;
- Some(Self {
- abstract_insentient,
- phase,
- })
- }
-
- pub fn write(&self) -> Vec<EntityDataValue> {
- let mut metadata = Vec::new();
- metadata.extend(self.abstract_insentient.write());
- metadata.push(EntityDataValue::Int(self.phase.clone()));
- metadata
- }
-}
-
-impl Default for EnderDragon {
- fn default() -> Self {
- Self {
- abstract_insentient: Default::default(),
- phase: Default::default(),
+ Ok(())
+ }
+}
+
+#[derive(Bundle)]
+pub struct EndCrystalMetadataBundle {
+ _marker: EndCrystal,
+ parent: AbstractEntityMetadataBundle,
+ beam_target: BeamTarget,
+ show_bottom: ShowBottom,
+}
+impl Default for EndCrystalMetadataBundle {
+ fn default() -> Self {
+ Self {
+ _marker: EndCrystal,
+ 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),
+ },
+ beam_target: BeamTarget(None),
+ show_bottom: ShowBottom(true),
}
}
}
+#[derive(Component, Deref, DerefMut)]
+pub struct Phase(pub i32);
+#[derive(Component)]
+pub struct EnderDragon;
impl EnderDragon {
- pub fn set_index(&mut self, index: u8, value: EntityDataValue) -> Option<()> {
- match index {
- 0..=15 => self.abstract_insentient.set_index(index, value)?,
- 16 => self.phase = value.into_int().ok()?,
+ pub fn apply_metadata(
+ entity: &mut azalea_ecs::system::EntityCommands,
+ d: EntityDataItem,
+ ) -> Result<(), UpdateMetadataError> {
+ match d.index {
+ 0..=15 => AbstractInsentient::apply_metadata(entity, d)?,
+ 16 => {
+ entity.insert(Phase(d.value.into_int()?));
+ }
_ => {}
}
- Some(())
- }
-}
-impl Deref for EnderDragon {
- type Target = AbstractInsentient;
- fn deref(&self) -> &Self::Target {
- &self.abstract_insentient
- }
-}
-impl DerefMut for EnderDragon {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.abstract_insentient
- }
-}
-
-#[derive(Debug, Clone)]
-pub struct EnderPearl {
- pub abstract_entity: AbstractEntity,
- pub item_stack: Slot,
-}
-
-impl EnderPearl {
- pub fn read(metadata: &mut VecDeque<EntityDataValue>) -> Option<Self> {
- let abstract_entity = AbstractEntity::read(metadata)?;
- let item_stack = metadata.pop_front()?.into_item_stack().ok()?;
- Some(Self {
- abstract_entity,
- item_stack,
- })
- }
-
- pub fn write(&self) -> Vec<EntityDataValue> {
- let mut metadata = Vec::new();
- metadata.extend(self.abstract_entity.write());
- metadata.push(EntityDataValue::ItemStack(self.item_stack.clone()));
- metadata
- }
-}
-
-impl Default for EnderPearl {
- fn default() -> Self {
- Self {
- abstract_entity: Default::default(),
- item_stack: Slot::Empty,
+ Ok(())
+ }
+}
+
+#[derive(Bundle)]
+pub struct EnderDragonMetadataBundle {
+ _marker: EnderDragon,
+ parent: AbstractInsentientMetadataBundle,
+ phase: Phase,
+}
+impl Default for EnderDragonMetadataBundle {
+ fn default() -> Self {
+ Self {
+ _marker: EnderDragon,
+ 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),
+ },
+ phase: Phase(Default::default()),
}
}
}
+#[derive(Component, Deref, DerefMut)]
+pub struct EnderPearlItemStack(pub Slot);
+#[derive(Component)]
+pub struct EnderPearl;
impl EnderPearl {
- pub fn set_index(&mut self, index: u8, value: EntityDataValue) -> Option<()> {
- match index {
- 0..=7 => self.abstract_entity.set_index(index, value)?,
- 8 => self.item_stack = value.into_item_stack().ok()?,
+ pub fn apply_metadata(
+ entity: &mut azalea_ecs::system::EntityCommands,
+ d: EntityDataItem,
+ ) -> Result<(), UpdateMetadataError> {
+ match d.index {
+ 0..=7 => AbstractEntity::apply_metadata(entity, d)?,
+ 8 => {
+ entity.insert(EnderPearlItemStack(d.value.into_item_stack()?));
+ }
_ => {}
}
- Some(())
- }
-}
-impl Deref for EnderPearl {
- type Target = AbstractEntity;
- fn deref(&self) -> &Self::Target {
- &self.abstract_entity
- }
-}
-impl DerefMut for EnderPearl {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.abstract_entity
+ Ok(())
}
}
-#[derive(Debug, Clone)]
-pub struct Enderman {
- pub abstract_monster: AbstractMonster,
- pub carry_state: BlockState,
- pub creepy: bool,
- pub stared_at: bool,
+#[derive(Bundle)]
+pub struct EnderPearlMetadataBundle {
+ _marker: EnderPearl,
+ parent: AbstractEntityMetadataBundle,
+ ender_pearl_item_stack: EnderPearlItemStack,
}
-
-impl Enderman {
- pub fn read(metadata: &mut VecDeque<EntityDataValue>) -> Option<Self> {
- let abstract_monster = AbstractMonster::read(metadata)?;
- let carry_state = metadata.pop_front()?.into_block_state().ok()?;
- let creepy = metadata.pop_front()?.into_boolean().ok()?;
- let stared_at = metadata.pop_front()?.into_boolean().ok()?;
- Some(Self {
- abstract_monster,
- carry_state,
- creepy,
- stared_at,
- })
- }
-
- pub fn write(&self) -> Vec<EntityDataValue> {
- let mut metadata = Vec::new();
- metadata.extend(self.abstract_monster.write());
- metadata.push(EntityDataValue::BlockState(self.carry_state.clone()));
- metadata.push(EntityDataValue::Boolean(self.creepy.clone()));
- metadata.push(EntityDataValue::Boolean(self.stared_at.clone()));
- metadata
- }
-}
-
-impl Default for Enderman {
+impl Default for EnderPearlMetadataBundle {
fn default() -> Self {
Self {
- abstract_monster: Default::default(),
- carry_state: BlockState::Air,
- creepy: false,
- stared_at: false,
+ _marker: EnderPearl,
+ 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),
+ },
+ ender_pearl_item_stack: EnderPearlItemStack(Slot::Empty),
}
}
}
+#[derive(Component, Deref, DerefMut)]
+pub struct CarryState(pub BlockState);
+#[derive(Component, Deref, DerefMut)]
+pub struct Creepy(pub bool);
+#[derive(Component, Deref, DerefMut)]
+pub struct StaredAt(pub bool);
+#[derive(Component)]
+pub struct Enderman;
impl Enderman {
- pub fn set_index(&mut self, index: u8, value: EntityDataValue) -> Option<()> {
- match index {
- 0..=15 => self.abstract_monster.set_index(index, value)?,
- 16 => self.carry_state = value.into_block_state().ok()?,
- 17 => self.creepy = value.into_boolean().ok()?,
- 18 => self.stared_at = value.into_boolean().ok()?,
+ pub fn apply_metadata(
+ entity: &mut azalea_ecs::system::EntityCommands,
+ d: EntityDataItem,
+ ) -> Result<(), UpdateMetadataError> {
+ match d.index {
+ 0..=15 => AbstractMonster::apply_metadata(entity, d)?,
+ 16 => {
+ entity.insert(CarryState(d.value.into_block_state()?));
+ }
+ 17 => {
+ entity.insert(Creepy(d.value.into_boolean()?));
+ }
+ 18 => {
+ entity.insert(StaredAt(d.value.into_boolean()?));
+ }
_ => {}
}
- Some(())
- }
-}
-impl Deref for Enderman {
- type Target = AbstractMonster;
- fn deref(&self) -> &Self::Target {
- &self.abstract_monster
- }
-}
-impl DerefMut for Enderman {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.abstract_monster
- }
-}
-
-#[derive(Debug, Clone)]
-pub struct Endermite {
- pub abstract_monster: AbstractMonster,
-}
-
-impl Endermite {
- pub fn read(metadata: &mut VecDeque<EntityDataValue>) -> Option<Self> {
- let abstract_monster = AbstractMonster::read(metadata)?;
- Some(Self { abstract_monster })
- }
-
- pub fn write(&self) -> Vec<EntityDataValue> {
- let mut metadata = Vec::new();
- metadata.extend(self.abstract_monster.write());
- metadata
- }
-}
-
-impl Default for Endermite {
- fn default() -> Self {
- Self {
- abstract_monster: Default::default(),
+ Ok(())
+ }
+}
+
+#[derive(Bundle)]
+pub struct EndermanMetadataBundle {
+ _marker: Enderman,
+ parent: AbstractMonsterMetadataBundle,
+ carry_state: CarryState,
+ creepy: Creepy,
+ stared_at: StaredAt,
+}
+impl Default for EndermanMetadataBundle {
+ fn default() -> Self {
+ Self {
+ _marker: Enderman,
+ 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),
+ },
+ },
+ },
+ carry_state: CarryState(BlockState::Air),
+ creepy: Creepy(false),
+ stared_at: StaredAt(false),
}
}
}
+#[derive(Component)]
+pub struct Endermite;
impl Endermite {
- pub fn set_index(&mut self, index: u8, value: EntityDataValue) -> Option<()> {
- self.abstract_monster.set_index(index, value)
- }
-}
-impl Deref for Endermite {
- type Target = AbstractMonster;
- fn deref(&self) -> &Self::Target {
- &self.abstract_monster
- }
-}
-impl DerefMut for Endermite {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.abstract_monster
- }
-}
-
-#[derive(Debug, Clone)]
-pub struct Evoker {
- pub abstract_monster: AbstractMonster,
- pub is_celebrating: bool,
- pub spell_casting: u8,
-}
-
-impl Evoker {
- pub fn read(metadata: &mut VecDeque<EntityDataValue>) -> Option<Self> {
- let abstract_monster = AbstractMonster::read(metadata)?;
- let is_celebrating = metadata.pop_front()?.into_boolean().ok()?;
- let spell_casting = metadata.pop_front()?.into_byte().ok()?;
- Some(Self {
- abstract_monster,
- is_celebrating,
- spell_casting,
- })
- }
-
- pub fn write(&self) -> Vec<EntityDataValue> {
- let mut metadata = Vec::new();
- metadata.extend(self.abstract_monster.write());
- metadata.push(EntityDataValue::Boolean(self.is_celebrating.clone()));
- metadata.push(EntityDataValue::Byte(self.spell_casting.clone()));
- metadata
- }
-}
-
-impl Default for Evoker {
- fn default() -> Self {
- Self {
- abstract_monster: Default::default(),
- is_celebrating: false,
- spell_casting: 0,
+ pub fn apply_metadata(
+ entity: &mut azalea_ecs::system::EntityCommands,
+ d: EntityDataItem,
+ ) -> Result<(), UpdateMetadataError> {
+ match d.index {
+ 0..=15 => AbstractMonster::apply_metadata(entity, d)?,
+ _ => {}
+ }
+ Ok(())
+ }
+}
+
+#[derive(Bundle)]
+pub struct EndermiteMetadataBundle {
+ _marker: Endermite,
+ parent: AbstractMonsterMetadataBundle,
+}
+impl Default for EndermiteMetadataBundle {
+ fn default() -> Self {
+ Self {
+ _marker: Endermite,
+ 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)]
+pub struct EvokerIsCelebrating(pub bool);
+#[derive(Component, Deref, DerefMut)]
+pub struct EvokerSpellCasting(pub u8);
+#[derive(Component)]
+pub struct Evoker;
impl Evoker {
- pub fn set_index(&mut self, index: u8, value: EntityDataValue) -> Option<()> {
- match index {
- 0..=15 => self.abstract_monster.set_index(index, value)?,
- 16 => self.is_celebrating = value.into_boolean().ok()?,
- 17 => self.spell_casting = value.into_byte().ok()?,
+ pub fn apply_metadata(
+ entity: &mut azalea_ecs::system::EntityCommands,
+ d: EntityDataItem,
+ ) -> Result<(), UpdateMetadataError> {
+ match d.index {
+ 0..=15 => AbstractMonster::apply_metadata(entity, d)?,
+ 16 => {
+ entity.insert(EvokerIsCelebrating(d.value.into_boolean()?));
+ }
+ 17 => {
+ entity.insert(EvokerSpellCasting(d.value.into_byte()?));
+ }
_ => {}
}
- Some(())
- }
-}
-impl Deref for Evoker {
- type Target = AbstractMonster;
- fn deref(&self) -> &Self::Target {
- &self.abstract_monster
- }
-}
-impl DerefMut for Evoker {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.abstract_monster
- }
-}
-
-#[derive(Debug, Clone)]
-pub struct EvokerFangs {
- pub abstract_entity: AbstractEntity,
-}
-
-impl EvokerFangs {
- pub fn read(metadata: &mut VecDeque<EntityDataValue>) -> Option<Self> {
- let abstract_entity = AbstractEntity::read(metadata)?;
- Some(Self { abstract_entity })
- }
-
- pub fn write(&self) -> Vec<EntityDataValue> {
- let mut metadata = Vec::new();
- metadata.extend(self.abstract_entity.write());
- metadata
- }
-}
-
-impl Default for EvokerFangs {
- fn default() -> Self {
- Self {
- abstract_entity: Default::default(),
+ Ok(())
+ }
+}
+
+#[derive(Bundle)]
+pub struct EvokerMetadataBundle {
+ _marker: Evoker,
+ parent: AbstractMonsterMetadataBundle,
+ evoker_is_celebrating: EvokerIsCelebrating,
+ evoker_spell_casting: EvokerSpellCasting,
+}
+impl Default for EvokerMetadataBundle {
+ fn default() -> Self {
+ Self {
+ _marker: Evoker,
+ 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),
+ },
+ },
+ },
+ evoker_is_celebrating: EvokerIsCelebrating(false),
+ evoker_spell_casting: EvokerSpellCasting(0),
}
}
}
+#[derive(Component)]
+pub struct EvokerFangs;
impl EvokerFangs {
- pub fn set_index(&mut self, index: u8, value: EntityDataValue) -> Option<()> {
- self.abstract_entity.set_index(index, value)
- }
-}
-impl Deref for EvokerFangs {
- type Target = AbstractEntity;
- fn deref(&self) -> &Self::Target {
- &self.abstract_entity
- }
-}
-impl DerefMut for EvokerFangs {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.abstract_entity
+ pub fn apply_metadata(
+ entity: &mut azalea_ecs::system::EntityCommands,
+ d: EntityDataItem,
+ ) -> Result<(), UpdateMetadataError> {
+ match d.index {
+ 0..=7 => AbstractEntity::apply_metadata(entity, d)?,
+ _ => {}
+ }
+ Ok(())
}
}
-#[derive(Debug, Clone)]
-pub struct ExperienceBottle {
- pub abstract_entity: AbstractEntity,
- pub item_stack: Slot,
+#[derive(Bundle)]
+pub struct EvokerFangsMetadataBundle {
+ _marker: EvokerFangs,
+ parent: AbstractEntityMetadataBundle,
}
-
-impl ExperienceBottle {
- pub fn read(metadata: &mut VecDeque<EntityDataValue>) -> Option<Self> {
- let abstract_entity = AbstractEntity::read(metadata)?;
- let item_stack = metadata.pop_front()?.into_item_stack().ok()?;
- Some(Self {
- abstract_entity,
- item_stack,
- })
- }
-
- pub fn write(&self) -> Vec<EntityDataValue> {
- let mut metadata = Vec::new();
- metadata.extend(self.abstract_entity.write());
- metadata.push(EntityDataValue::ItemStack(self.item_stack.clone()));
- metadata
- }
-}
-
-impl Default for ExperienceBottle {
+impl Default for EvokerFangsMetadataBundle {
fn default() -> Self {
Self {
- abstract_entity: Default::default(),
- item_stack: Slot::Empty,
+ _marker: EvokerFangs,
+ 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)]
+pub struct ExperienceBottleItemStack(pub Slot);
+#[derive(Component)]
+pub struct ExperienceBottle;
impl ExperienceBottle {
- pub fn set_index(&mut self, index: u8, value: EntityDataValue) -> Option<()> {
- match index {
- 0..=7 => self.abstract_entity.set_index(index, value)?,
- 8 => self.item_stack = value.into_item_stack().ok()?,
+ pub fn apply_metadata(
+ entity: &mut azalea_ecs::system::EntityCommands,
+ d: EntityDataItem,
+ ) -> Result<(), UpdateMetadataError> {
+ match d.index {
+ 0..=7 => AbstractEntity::apply_metadata(entity, d)?,
+ 8 => {
+ entity.insert(ExperienceBottleItemStack(d.value.into_item_stack()?));
+ }
_ => {}
}
- Some(())
- }
-}
-impl Deref for ExperienceBottle {
- type Target = AbstractEntity;
- fn deref(&self) -> &Self::Target {
- &self.abstract_entity
- }
-}
-impl DerefMut for ExperienceBottle {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.abstract_entity
+ Ok(())
}
}
-#[derive(Debug, Clone)]
-pub struct ExperienceOrb {
- pub abstract_entity: AbstractEntity,
+#[derive(Bundle)]
+pub struct ExperienceBottleMetadataBundle {
+ _marker: ExperienceBottle,
+ parent: AbstractEntityMetadataBundle,
+ experience_bottle_item_stack: ExperienceBottleItemStack,
}
-
-impl ExperienceOrb {
- pub fn read(metadata: &mut VecDeque<EntityDataValue>) -> Option<Self> {
- let abstract_entity = AbstractEntity::read(metadata)?;
- Some(Self { abstract_entity })
- }
-
- pub fn write(&self) -> Vec<EntityDataValue> {
- let mut metadata = Vec::new();
- metadata.extend(self.abstract_entity.write());
- metadata
- }
-}
-
-impl Default for ExperienceOrb {
+impl Default for ExperienceBottleMetadataBundle {
fn default() -> Self {
Self {
- abstract_entity: Default::default(),
+ _marker: ExperienceBottle,
+ 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),
+ },
+ experience_bottle_item_stack: ExperienceBottleItemStack(Slot::Empty),
}
}
}
+#[derive(Component)]
+pub struct ExperienceOrb;
impl ExperienceOrb {
- pub fn set_index(&mut self, index: u8, value: EntityDataValue) -> Option<()> {
- self.abstract_entity.set_index(index, value)
- }
-}
-impl Deref for ExperienceOrb {
- type Target = AbstractEntity;
- fn deref(&self) -> &Self::Target {
- &self.abstract_entity
- }
-}
-impl DerefMut for ExperienceOrb {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.abstract_entity
+ pub fn apply_metadata(
+ entity: &mut azalea_ecs::system::EntityCommands,
+ d: EntityDataItem,
+ ) -> Result<(), UpdateMetadataError> {
+ match d.index {
+ 0..=7 => AbstractEntity::apply_metadata(entity, d)?,
+ _ => {}
+ }
+ Ok(())
}
}
-#[derive(Debug, Clone)]
-pub struct EyeOfEnder {
- pub abstract_entity: AbstractEntity,
- pub item_stack: Slot,
-}
-
-impl EyeOfEnder {
- pub fn read(metadata: &mut VecDeque<EntityDataValue>) -> Option<Self> {
- let abstract_entity = AbstractEntity::read(metadata)?;
- let item_stack = metadata.pop_front()?.into_item_stack().ok()?;
- Some(Self {
- abstract_entity,
- item_stack,
- })
- }
-
- pub fn write(&self) -> Vec<EntityDataValue> {
- let mut metadata = Vec::new();
- metadata.extend(self.abstract_entity.write());
- metadata.push(EntityDataValue::ItemStack(self.item_stack.clone()));
- metadata
- }
+#[derive(Bundle)]
+pub struct ExperienceOrbMetadataBundle {
+ _marker: ExperienceOrb,
+ parent: AbstractEntityMetadataBundle,
}
-
-impl Default for EyeOfEnder {
+impl Default for ExperienceOrbMetadataBundle {
fn default() -> Self {
Self {
- abstract_entity: Default::default(),
- item_stack: Slot::Empty,
+ _marker: ExperienceOrb,
+ 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)]
+pub struct EyeOfEnderItemStack(pub Slot);
+#[derive(Component)]
+pub struct EyeOfEnder;
impl EyeOfEnder {
- pub fn set_index(&mut self, index: u8, value: EntityDataValue) -> Option<()> {
- match index {
- 0..=7 => self.abstract_entity.set_index(index, value)?,
- 8 => self.item_stack = value.into_item_stack().ok()?,
+ pub fn apply_metadata(
+ entity: &mut azalea_ecs::system::EntityCommands,
+ d: EntityDataItem,
+ ) -> Result<(), UpdateMetadataError> {
+ match d.index {
+ 0..=7 => AbstractEntity::apply_metadata(entity, d)?,
+ 8 => {
+ entity.insert(EyeOfEnderItemStack(d.value.into_item_stack()?));
+ }
_ => {}
}
- Some(())
- }
-}
-impl Deref for EyeOfEnder {
- type Target = AbstractEntity;
- fn deref(&self) -> &Self::Target {
- &self.abstract_entity
- }
-}
-impl DerefMut for EyeOfEnder {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.abstract_entity
+ Ok(())
}
}
-#[derive(Debug, Clone)]
-pub struct FallingBlock {
- pub abstract_entity: AbstractEntity,
- pub start_pos: BlockPos,
+#[derive(Bundle)]
+pub struct EyeOfEnderMetadataBundle {
+ _marker: EyeOfEnder,
+ parent: AbstractEntityMetadataBundle,
+ eye_of_ender_item_stack: EyeOfEnderItemStack,
}
-
-impl FallingBlock {
- pub fn read(metadata: &mut VecDeque<EntityDataValue>) -> Option<Self> {
- let abstract_entity = AbstractEntity::read(metadata)?;
- let start_pos = metadata.pop_front()?.into_block_pos().ok()?;
- Some(Self {
- abstract_entity,
- start_pos,
- })
- }
-
- pub fn write(&self) -> Vec<EntityDataValue> {
- let mut metadata = Vec::new();
- metadata.extend(self.abstract_entity.write());
- metadata.push(EntityDataValue::BlockPos(self.start_pos.clone()));
- metadata
- }
-}
-
-impl Default for FallingBlock {
+impl Default for EyeOfEnderMetadataBundle {
fn default() -> Self {
Self {
- abstract_entity: Default::default(),
- start_pos: BlockPos::new(0, 0, 0),
+ _marker: EyeOfEnder,
+ 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),
+ },
+ eye_of_ender_item_stack: EyeOfEnderItemStack(Slot::Empty),
}
}
}
+#[derive(Component, Deref, DerefMut)]
+pub struct StartPos(pub BlockPos);
+#[derive(Component)]
+pub struct FallingBlock;
impl FallingBlock {
- pub fn set_index(&mut self, index: u8, value: EntityDataValue) -> Option<()> {
- match index {
- 0..=7 => self.abstract_entity.set_index(index, value)?,
- 8 => self.start_pos = value.into_block_pos().ok()?,
+ pub fn apply_metadata(
+ entity: &mut azalea_ecs::system::EntityCommands,
+ d: EntityDataItem,
+ ) -> Result<(), UpdateMetadataError> {
+ match d.index {
+ 0..=7 => AbstractEntity::apply_metadata(entity, d)?,
+ 8 => {
+ entity.insert(StartPos(d.value.into_block_pos()?));
+ }
_ => {}
}
- Some(())
+ Ok(())
}
}
-impl Deref for FallingBlock {
- type Target = AbstractEntity;
- fn deref(&self) -> &Self::Target {
- &self.abstract_entity
- }
-}
-impl DerefMut for FallingBlock {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.abstract_entity
- }
-}
-
-#[derive(Debug, Clone)]
-pub struct Fireball {
- pub abstract_entity: AbstractEntity,
- pub item_stack: Slot,
-}
-impl Fireball {
- pub fn read(metadata: &mut VecDeque<EntityDataValue>) -> Option<Self> {
- let abstract_entity = AbstractEntity::read(metadata)?;
- let item_stack = metadata.pop_front()?.into_item_stack().ok()?;
- Some(Self {
- abstract_entity,
- item_stack,
- })
- }
-
- pub fn write(&self) -> Vec<EntityDataValue> {
- let mut metadata = Vec::new();
- metadata.extend(self.abstract_entity.write());
- metadata.push(EntityDataValue::ItemStack(self.item_stack.clone()));
- metadata
- }
+#[derive(Bundle)]
+pub struct FallingBlockMetadataBundle {
+ _marker: FallingBlock,
+ parent: AbstractEntityMetadataBundle,
+ start_pos: StartPos,
}
-
-impl Default for Fireball {
+impl Default for FallingBlockMetadataBundle {
fn default() -> Self {
Self {
- abstract_entity: Default::default(),
- item_stack: Slot::Empty,
+ _marker: FallingBlock,
+ 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),
+ },
+ start_pos: StartPos(BlockPos::new(0, 0, 0)),
}
}
}
+#[derive(Component, Deref, DerefMut)]
+pub struct FireballItemStack(pub Slot);
+#[derive(Component)]
+pub struct Fireball;
impl Fireball {
- pub fn set_index(&mut self, index: u8, value: EntityDataValue) -> Option<()> {
- match index {
- 0..=7 => self.abstract_entity.set_index(index, value)?,
- 8 => self.item_stack = value.into_item_stack().ok()?,
+ pub fn apply_metadata(
+ entity: &mut azalea_ecs::system::EntityCommands,
+ d: EntityDataItem,
+ ) -> Result<(), UpdateMetadataError> {
+ match d.index {
+ 0..=7 => AbstractEntity::apply_metadata(entity, d)?,
+ 8 => {
+ entity.insert(FireballItemStack(d.value.into_item_stack()?));
+ }
_ => {}
}
- Some(())
- }
-}
-impl Deref for Fireball {
- type Target = AbstractEntity;
- fn deref(&self) -> &Self::Target {
- &self.abstract_entity
- }
-}
-impl DerefMut for Fireball {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.abstract_entity
+ Ok(())
}
}
-#[derive(Debug, Clone)]
-pub struct FireworkRocket {
- pub abstract_entity: AbstractEntity,
- pub fireworks_item: Slot,
- pub attached_to_target: OptionalUnsignedInt,
- pub shot_at_angle: bool,
+#[derive(Bundle)]
+pub struct FireballMetadataBundle {
+ _marker: Fireball,
+ parent: AbstractEntityMetadataBundle,
+ fireball_item_stack: FireballItemStack,
}
-
-impl FireworkRocket {
- pub fn read(metadata: &mut VecDeque<EntityDataValue>) -> Option<Self> {
- let abstract_entity = AbstractEntity::read(metadata)?;
- let fireworks_item = metadata.pop_front()?.into_item_stack().ok()?;
- let attached_to_target = metadata.pop_front()?.into_optional_unsigned_int().ok()?;
- let shot_at_angle = metadata.pop_front()?.into_boolean().ok()?;
- Some(Self {
- abstract_entity,
- fireworks_item,
- attached_to_target,
- shot_at_angle,
- })
- }
-
- pub fn write(&self) -> Vec<EntityDataValue> {
- let mut metadata = Vec::new();
- metadata.extend(self.abstract_entity.write());
- metadata.push(EntityDataValue::ItemStack(self.fireworks_item.clone()));
- metadata.push(EntityDataValue::OptionalUnsignedInt(
- self.attached_to_target.clone(),
- ));
- metadata.push(EntityDataValue::Boolean(self.shot_at_angle.clone()));
- metadata
- }
-}
-
-impl Default for FireworkRocket {
+impl Default for FireballMetadataBundle {
fn default() -> Self {
Self {
- abstract_entity: Default::default(),
- fireworks_item: Slot::Empty,
- attached_to_target: OptionalUnsignedInt(None),
- shot_at_angle: false,
+ _marker: Fireball,
+ 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),
+ },
+ fireball_item_stack: FireballItemStack(Slot::Empty),
}
}
}
+#[derive(Component, Deref, DerefMut)]
+pub struct FireworksItem(pub Slot);
+#[derive(Component, Deref, DerefMut)]
+pub struct AttachedToTarget(pub OptionalUnsignedInt);
+#[derive(Component, Deref, DerefMut)]
+pub struct ShotAtAngle(pub bool);
+#[derive(Component)]
+pub struct FireworkRocket;
impl FireworkRocket {
- pub fn set_index(&mut self, index: u8, value: EntityDataValue) -> Option<()> {
- match index {
- 0..=7 => self.abstract_entity.set_index(index, value)?,
- 8 => self.fireworks_item = value.into_item_stack().ok()?,
- 9 => self.attached_to_target = value.into_optional_unsigned_int().ok()?,
- 10 => self.shot_at_angle = value.into_boolean().ok()?,
+ pub fn apply_metadata(
+ entity: &mut azalea_ecs::system::EntityCommands,
+ d: EntityDataItem,
+ ) -> Result<(), UpdateMetadataError> {
+ match d.index {
+ 0..=7 => AbstractEntity::apply_metadata(entity, d)?,
+ 8 => {
+ entity.insert(FireworksItem(d.value.into_item_stack()?));
+ }
+ 9 => {
+ entity.insert(AttachedToTarget(d.value.into_optional_unsigned_int()?));
+ }
+ 10 => {
+ entity.insert(ShotAtAngle(d.value.into_boolean()?));
+ }
_ => {}
}
- Some(())
- }
-}
-impl Deref for FireworkRocket {
- type Target = AbstractEntity;
- fn deref(&self) -> &Self::Target {
- &self.abstract_entity
- }
-}
-impl DerefMut for FireworkRocket {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.abstract_entity
- }
-}
-
-#[derive(Debug, Clone)]
-pub struct FishingBobber {
- pub abstract_entity: AbstractEntity,
- pub hooked_entity: i32,
- pub biting: bool,
-}
-
-impl FishingBobber {
- pub fn read(metadata: &mut VecDeque<EntityDataValue>) -> Option<Self> {
- let abstract_entity = AbstractEntity::read(metadata)?;
- let hooked_entity = metadata.pop_front()?.into_int().ok()?;
- let biting = metadata.pop_front()?.into_boolean().ok()?;
- Some(Self {
- abstract_entity,
- hooked_entity,
- biting,
- })
- }
-
- pub fn write(&self) -> Vec<EntityDataValue> {
- let mut metadata = Vec::new();
- metadata.extend(self.abstract_entity.write());
- metadata.push(EntityDataValue::Int(self.hooked_entity.clone()));
- metadata.push(EntityDataValue::Boolean(self.biting.clone()));
- metadata
- }
-}
-
-impl Default for FishingBobber {
- fn default() -> Self {
- Self {
- abstract_entity: Default::default(),
- hooked_entity: 0,
- biting: false,
+ Ok(())
+ }
+}
+
+#[derive(Bundle)]
+pub struct FireworkRocketMetadataBundle {
+ _marker: FireworkRocket,
+ parent: AbstractEntityMetadataBundle,
+ fireworks_item: FireworksItem,
+ attached_to_target: AttachedToTarget,
+ shot_at_angle: ShotAtAngle,
+}
+impl Default for FireworkRocketMetadataBundle {
+ fn default() -> Self {
+ Self {
+ _marker: FireworkRocket,
+ 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),
+ },
+ fireworks_item: FireworksItem(Slot::Empty),
+ attached_to_target: AttachedToTarget(OptionalUnsignedInt(None)),
+ shot_at_angle: ShotAtAngle(false),
}
}
}
+#[derive(Component, Deref, DerefMut)]
+pub struct HookedEntity(pub i32);
+#[derive(Component, Deref, DerefMut)]
+pub struct Biting(pub bool);
+#[derive(Component)]
+pub struct FishingBobber;
impl FishingBobber {
- pub fn set_index(&mut self, index: u8, value: EntityDataValue) -> Option<()> {
- match index {
- 0..=7 => self.abstract_entity.set_index(index, value)?,
- 8 => self.hooked_entity = value.into_int().ok()?,
- 9 => self.biting = value.into_boolean().ok()?,
+ pub fn apply_metadata(
+ entity: &mut azalea_ecs::system::EntityCommands,
+ d: EntityDataItem,
+ ) -> Result<(), UpdateMetadataError> {
+ match d.index {
+ 0..=7 => AbstractEntity::apply_metadata(entity, d)?,
+ 8 => {
+ entity.insert(HookedEntity(d.value.into_int()?));
+ }
+ 9 => {
+ entity.insert(Biting(d.value.into_boolean()?));
+ }
_ => {}
}
- Some(())
- }
-}
-impl Deref for FishingBobber {
- type Target = AbstractEntity;
- fn deref(&self) -> &Self::Target {
- &self.abstract_entity
- }
-}
-impl DerefMut for FishingBobber {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.abstract_entity
- }
-}
-
-#[derive(Debug, Clone)]
-pub struct Fox {
- pub abstract_animal: AbstractAnimal,
- pub kind: i32,
- pub sitting: bool,
- pub faceplanted: bool,
- pub sleeping: bool,
- pub pouncing: bool,
- pub crouching: bool,
- pub interested: bool,
- pub trusted_id_0: Option<Uuid>,
- pub trusted_id_1: Option<Uuid>,
-}
-
-impl Fox {
- pub fn read(metadata: &mut VecDeque<EntityDataValue>) -> Option<Self> {
- let abstract_animal = AbstractAnimal::read(metadata)?;
- let kind = metadata.pop_front()?.into_int().ok()?;
- let bitfield = metadata.pop_front()?.into_byte().ok()?;
- let sitting = bitfield & 0x1 != 0;
- let faceplanted = bitfield & 0x40 != 0;
- let sleeping = bitfield & 0x20 != 0;
- let pouncing = bitfield & 0x10 != 0;
- let crouching = bitfield & 0x4 != 0;
- let interested = bitfield & 0x8 != 0;
- let trusted_id_0 = metadata.pop_front()?.into_optional_uuid().ok()?;
- let trusted_id_1 = metadata.pop_front()?.into_optional_uuid().ok()?;
- Some(Self {
- abstract_animal,
- kind,
- sitting,
- faceplanted,
- sleeping,
- pouncing,
- crouching,
- interested,
- trusted_id_0,
- trusted_id_1,
- })
- }
-
- pub fn write(&self) -> Vec<EntityDataValue> {
- let mut metadata = Vec::new();
- metadata.extend(self.abstract_animal.write());
- metadata.push(EntityDataValue::Int(self.kind.clone()));
- let mut bitfield = 0u8;
- if self.sitting {
- bitfield &= 0x1;
- }
- if self.faceplanted {
- bitfield &= 0x40;
- }
- if self.sleeping {
- bitfield &= 0x20;
- }
- if self.pouncing {
- bitfield &= 0x10;
- }
- if self.crouching {
- bitfield &= 0x4;
- }
- if self.interested {
- bitfield &= 0x8;
- }
- metadata.push(EntityDataValue::Byte(bitfield));
- metadata.push(EntityDataValue::OptionalUuid(self.trusted_id_0.clone()));
- metadata.push(EntityDataValue::OptionalUuid(self.trusted_id_1.clone()));
- metadata
- }
-}
-
-impl Default for Fox {
- fn default() -> Self {
- Self {
- abstract_animal: Default::default(),
- kind: 0,
- sitting: false,
- faceplanted: false,
- sleeping: false,
- pouncing: false,
- crouching: false,
- interested: false,
- trusted_id_0: None,
- trusted_id_1: None,
- }
- }
-}
-
+ Ok(())
+ }
+}
+
+#[derive(Bundle)]
+pub struct FishingBobberMetadataBundle {
+ _marker: FishingBobber,
+ parent: AbstractEntityMetadataBundle,
+ hooked_entity: HookedEntity,
+ biting: Biting,
+}
+impl Default for FishingBobberMetadataBundle {
+ fn default() -> Self {
+ Self {
+ _marker: FishingBobber,
+ 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),
+ },
+ hooked_entity: HookedEntity(0),
+ biting: Biting(false),
+ }
+ }
+}
+
+#[derive(Component, Deref, DerefMut)]
+pub struct FoxKind(pub i32);
+#[derive(Component, Deref, DerefMut)]
+pub struct FoxSitting(pub bool);
+#[derive(Component, Deref, DerefMut)]
+pub struct Faceplanted(pub bool);
+#[derive(Component, Deref, DerefMut)]
+pub struct Sleeping(pub bool);
+#[derive(Component, Deref, DerefMut)]
+pub struct Pouncing(pub bool);
+#[derive(Component, Deref, DerefMut)]
+pub struct Crouching(pub bool);
+#[derive(Component, Deref, DerefMut)]
+pub struct FoxInterested(pub bool);
+#[derive(Component, Deref, DerefMut)]
+pub struct TrustedId0(pub Option<Uuid>);
+#[derive(Component, Deref, DerefMut)]
+pub struct TrustedId1(pub Option<Uuid>);
+#[derive(Component)]
+pub struct Fox;
impl Fox {
- pub fn set_index(&mut self, index: u8, value: EntityDataValue) -> Option<()> {
- match index {
- 0..=16 => self.abstract_animal.set_index(index, value)?,
- 17 => self.kind = value.into_int().ok()?,
+ pub fn apply_metadata(
+ entity: &mut azalea_ecs::system::EntityCommands,
+ d: EntityDataItem,
+ ) -> Result<(), UpdateMetadataError> {
+ match d.index {
+ 0..=16 => AbstractAnimal::apply_metadata(entity, d)?,
+ 17 => {
+ entity.insert(FoxKind(d.value.into_int()?));
+ }
18 => {
- let bitfield = value.into_byte().ok()?;
- self.sitting = bitfield & 0x1 != 0;
- self.faceplanted = bitfield & 0x40 != 0;
- self.sleeping = bitfield & 0x20 != 0;
- self.pouncing = bitfield & 0x10 != 0;
- self.crouching = bitfield & 0x4 != 0;
- self.interested = bitfield & 0x8 != 0;
+ let bitfield = d.value.into_byte()?;
+ entity.insert(FoxSitting(bitfield & 0x1 != 0));
+ entity.insert(Faceplanted(bitfield & 0x40 != 0));
+ entity.insert(Sleeping(bitfield & 0x20 != 0));
+ entity.insert(Pouncing(bitfield & 0x10 != 0));
+ entity.insert(Crouching(bitfield & 0x4 != 0));
+ entity.insert(FoxInterested(bitfield & 0x8 != 0));
+ }
+ 19 => {
+ entity.insert(TrustedId0(d.value.into_optional_uuid()?));
+ }
+ 20 => {
+ entity.insert(TrustedId1(d.value.into_optional_uuid()?));
}
- 19 => self.trusted_id_0 = value.into_optional_uuid().ok()?,
- 20 => self.trusted_id_1 = value.into_optional_uuid().ok()?,
_ => {}
}
- Some(())
- }
-}
-impl Deref for Fox {
- type Target = AbstractAnimal;
- fn deref(&self) -> &Self::Target {
- &self.abstract_animal
- }
-}
-impl DerefMut for Fox {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.abstract_animal
- }
-}
-
-#[derive(Debug, Clone)]
-pub struct Frog {
- pub abstract_animal: AbstractAnimal,
- pub variant: azalea_registry::FrogVariant,
- pub tongue_target: OptionalUnsignedInt,
-}
-
-impl Frog {
- pub fn read(metadata: &mut VecDeque<EntityDataValue>) -> Option<Self> {
- let abstract_animal = AbstractAnimal::read(metadata)?;
- let variant = metadata.pop_front()?.into_frog_variant().ok()?;
- let tongue_target = metadata.pop_front()?.into_optional_unsigned_int().ok()?;
- Some(Self {
- abstract_animal,
- variant,
- tongue_target,
- })
- }
-
- pub fn write(&self) -> Vec<EntityDataValue> {
- let mut metadata = Vec::new();
- metadata.extend(self.abstract_animal.write());
- metadata.push(EntityDataValue::FrogVariant(self.variant.clone()));
- metadata.push(EntityDataValue::OptionalUnsignedInt(
- self.tongue_target.clone(),
- ));
- metadata
- }
-}
-
-impl Default for Frog {
- fn default() -> Self {
- Self {
- abstract_animal: Default::default(),
- variant: azalea_registry::FrogVariant::Temperate,
- tongue_target: OptionalUnsignedInt(None),
+ Ok(())
+ }
+}
+
+#[derive(Bundle)]
+pub struct FoxMetadataBundle {
+ _marker: Fox,
+ parent: AbstractAnimalMetadataBundle,
+ fox_kind: FoxKind,
+ fox_sitting: FoxSitting,
+ faceplanted: Faceplanted,
+ sleeping: Sleeping,
+ pouncing: Pouncing,
+ crouching: Crouching,
+ fox_interested: FoxInterested,
+ trusted_id_0: TrustedId0,
+ trusted_id_1: TrustedId1,
+}
+impl Default for FoxMetadataBundle {
+ fn default() -> Self {
+ Self {
+ _marker: Fox,
+ parent: AbstractAnimalMetadataBundle {
+ _marker: AbstractAnimal,
+ parent: AbstractAgeableMetadataBundle {
+ _marker: AbstractAgeable,
+ 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),
+ },
+ },
+ abstract_ageable_baby: AbstractAgeableBaby(false),
+ },
+ },
+ fox_kind: FoxKind(0),
+ fox_sitting: FoxSitting(false),
+ faceplanted: Faceplanted(false),
+ sleeping: Sleeping(false),
+ pouncing: Pouncing(false),
+ crouching: Crouching(false),
+ fox_interested: FoxInterested(false),
+ trusted_id_0: TrustedId0(None),
+ trusted_id_1: TrustedId1(None),
}
}
}
+#[derive(Component, Deref, DerefMut)]
+pub struct FrogVariant(pub azalea_registry::FrogVariant);
+#[derive(Component, Deref, DerefMut)]
+pub struct TongueTarget(pub OptionalUnsignedInt);
+#[derive(Component)]
+pub struct Frog;
impl Frog {
- pub fn set_index(&mut self, index: u8, value: EntityDataValue) -> Option<()> {
- match index {
- 0..=16 => self.abstract_animal.set_index(index, value)?,
- 17 => self.variant = value.into_frog_variant().ok()?,
- 18 => self.tongue_target = value.into_optional_unsigned_int().ok()?,
+ pub fn apply_metadata(
+ entity: &mut azalea_ecs::system::EntityCommands,
+ d: EntityDataItem,
+ ) -> Result<(), UpdateMetadataError> {
+ match d.index {
+ 0..=16 => AbstractAnimal::apply_metadata(entity, d)?,
+ 17 => {
+ entity.insert(FrogVariant(d.value.into_frog_variant()?));
+ }
+ 18 => {
+ entity.insert(TongueTarget(d.value.into_optional_unsigned_int()?));
+ }
_ => {}
}
- Some(())
- }
-}
-impl Deref for Frog {
- type Target = AbstractAnimal;
- fn deref(&self) -> &Self::Target {
- &self.abstract_animal
- }
-}
-impl DerefMut for Frog {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.abstract_animal
- }
-}
-
-#[derive(Debug, Clone)]
-pub struct FurnaceMinecart {
- pub abstract_minecart: AbstractMinecart,
- pub fuel: bool,
-}
-
-impl FurnaceMinecart {
- pub fn read(metadata: &mut VecDeque<EntityDataValue>) -> Option<Self> {
- let abstract_minecart = AbstractMinecart::read(metadata)?;
- let fuel = metadata.pop_front()?.into_boolean().ok()?;
- Some(Self {
- abstract_minecart,
- fuel,
- })
- }
-
- pub fn write(&self) -> Vec<EntityDataValue> {
- let mut metadata = Vec::new();
- metadata.extend(self.abstract_minecart.write());
- metadata.push(EntityDataValue::Boolean(self.fuel.clone()));
- metadata
- }
-}
-
-impl Default for FurnaceMinecart {
- fn default() -> Self {
- Self {
- abstract_minecart: Default::default(),
- fuel: false,
+ Ok(())
+ }
+}
+
+#[derive(Bundle)]
+pub struct FrogMetadataBundle {
+ _marker: Frog,
+ parent: AbstractAnimalMetadataBundle,
+ frog_variant: FrogVariant,
+ tongue_target: TongueTarget,
+}
+impl Default for FrogMetadataBundle {
+ fn default() -> Self {
+ Self {
+ _marker: Frog,
+ parent: AbstractAnimalMetadataBundle {
+ _marker: AbstractAnimal,
+ parent: AbstractAgeableMetadataBundle {
+ _marker: AbstractAgeable,
+ 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),
+ },
+ },
+ abstract_ageable_baby: AbstractAgeableBaby(false),
+ },
+ },
+ frog_variant: FrogVariant(azalea_registry::FrogVariant::Temperate),
+ tongue_target: TongueTarget(OptionalUnsignedInt(None)),
}
}
}
+#[derive(Component, Deref, DerefMut)]
+pub struct Fuel(pub bool);
+#[derive(Component)]
+pub struct FurnaceMinecart;
impl FurnaceMinecart {
- pub fn set_index(&mut self, index: u8, value: EntityDataValue) -> Option<()> {
- match index {
- 0..=13 => self.abstract_minecart.set_index(index, value)?,
- 14 => self.fuel = value.into_boolean().ok()?,
+ pub fn apply_metadata(
+ entity: &mut azalea_ecs::system::EntityCommands,
+ d: EntityDataItem,
+ ) -> Result<(), UpdateMetadataError> {
+ match d.index {
+ 0..=13 => AbstractMinecart::apply_metadata(entity, d)?,
+ 14 => {
+ entity.insert(Fuel(d.value.into_boolean()?));
+ }
_ => {}
}
- Some(())
- }
-}
-impl Deref for FurnaceMinecart {
- type Target = AbstractMinecart;
- fn deref(&self) -> &Self::Target {
- &self.abstract_minecart
- }
-}
-impl DerefMut for FurnaceMinecart {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.abstract_minecart
- }
-}
-
-#[derive(Debug, Clone)]
-pub struct Ghast {
- pub abstract_insentient: AbstractInsentient,
- pub is_charging: bool,
-}
-
-impl Ghast {
- pub fn read(metadata: &mut VecDeque<EntityDataValue>) -> Option<Self> {
- let abstract_insentient = AbstractInsentient::read(metadata)?;
- let is_charging = metadata.pop_front()?.into_boolean().ok()?;
- Some(Self {
- abstract_insentient,
- is_charging,
- })
- }
-
- pub fn write(&self) -> Vec<EntityDataValue> {
- let mut metadata = Vec::new();
- metadata.extend(self.abstract_insentient.write());
- metadata.push(EntityDataValue::Boolean(self.is_charging.clone()));
- metadata
- }
-}
-
-impl Default for Ghast {
- fn default() -> Self {
- Self {
- abstract_insentient: Default::default(),
- is_charging: false,
+ Ok(())
+ }
+}
+
+#[derive(Bundle)]
+pub struct FurnaceMinecartMetadataBundle {
+ _marker: FurnaceMinecart,
+ parent: AbstractMinecartMetadataBundle,
+ fuel: Fuel,
+}
+impl Default for FurnaceMinecartMetadataBundle {
+ fn default() -> Self {
+ Self {
+ _marker: FurnaceMinecart,
+ parent: AbstractMinecartMetadataBundle {
+ _marker: AbstractMinecart,
+ 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),
+ },
+ abstract_minecart_hurt: AbstractMinecartHurt(0),
+ abstract_minecart_hurtdir: AbstractMinecartHurtdir(1),
+ abstract_minecart_damage: AbstractMinecartDamage(0.0),
+ display_block: DisplayBlock(Default::default()),
+ display_offset: DisplayOffset(6),
+ custom_display: CustomDisplay(false),
+ },
+ fuel: Fuel(false),
}
}
}
+#[derive(Component, Deref, DerefMut)]
+pub struct IsCharging(pub bool);
+#[derive(Component)]
+pub struct Ghast;
impl Ghast {
- pub fn set_index(&mut self, index: u8, value: EntityDataValue) -> Option<()> {
- match index {
- 0..=15 => self.abstract_insentient.set_index(index, value)?,
- 16 => self.is_charging = value.into_boolean().ok()?,
+ pub fn apply_metadata(
+ entity: &mut azalea_ecs::system::EntityCommands,
+ d: EntityDataItem,
+ ) -> Result<(), UpdateMetadataError> {
+ match d.index {
+ 0..=15 => AbstractInsentient::apply_metadata(entity, d)?,
+ 16 => {
+ entity.insert(IsCharging(d.value.into_boolean()?));
+ }
_ => {}
}
- Some(())
- }
-}
-impl Deref for Ghast {
- type Target = AbstractInsentient;
- fn deref(&self) -> &Self::Target {
- &self.abstract_insentient
- }
-}
-impl DerefMut for Ghast {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.abstract_insentient
- }
-}
-
-#[derive(Debug, Clone)]
-pub struct Giant {
- pub abstract_monster: AbstractMonster,
-}
-
-impl Giant {
- pub fn read(metadata: &mut VecDeque<EntityDataValue>) -> Option<Self> {
- let abstract_monster = AbstractMonster::read(metadata)?;
- Some(Self { abstract_monster })
- }
-
- pub fn write(&self) -> Vec<EntityDataValue> {
- let mut metadata = Vec::new();
- metadata.extend(self.abstract_monster.write());
- metadata
- }
-}
-
-impl Default for Giant {
- fn default() -> Self {
- Self {
- abstract_monster: Default::default(),
+ Ok(())
+ }
+}
+
+#[derive(Bundle)]
+pub struct GhastMetadataBundle {
+ _marker: Ghast,
+ parent: AbstractInsentientMetadataBundle,
+ is_charging: IsCharging,
+}
+impl Default for GhastMetadataBundle {
+ fn default() -> Self {
+ Self {
+ _marker: Ghast,
+ 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),
+ },
+ is_charging: IsCharging(false),
}
}
}
+#[derive(Component)]
+pub struct Giant;
impl Giant {
- pub fn set_index(&mut self, index: u8, value: EntityDataValue) -> Option<()> {
- self.abstract_monster.set_index(index, value)
- }
-}
-impl Deref for Giant {
- type Target = AbstractMonster;
- fn deref(&self) -> &Self::Target {
- &self.abstract_monster
- }
-}
-impl DerefMut for Giant {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.abstract_monster
- }
-}
-
-#[derive(Debug, Clone)]
-pub struct GlowItemFrame {
- pub item_frame: ItemFrame,
-}
-
-impl GlowItemFrame {
- pub fn read(metadata: &mut VecDeque<EntityDataValue>) -> Option<Self> {
- let item_frame = ItemFrame::read(metadata)?;
- Some(Self { item_frame })
- }
-
- pub fn write(&self) -> Vec<EntityDataValue> {
- let mut metadata = Vec::new();
- metadata.extend(self.item_frame.write());
- metadata
- }
-}
-
-impl Default for GlowItemFrame {
- fn default() -> Self {
- Self {
- item_frame: Default::default(),
+ pub fn apply_metadata(
+ entity: &mut azalea_ecs::system::EntityCommands,
+ d: EntityDataItem,
+ ) -> Result<(), UpdateMetadataError> {
+ match d.index {
+ 0..=15 => AbstractMonster::apply_metadata(entity, d)?,
+ _ => {}
+ }
+ Ok(())
+ }
+}
+
+#[derive(Bundle)]
+pub struct GiantMetadataBundle {
+ _marker: Giant,
+ parent: AbstractMonsterMetadataBundle,
+}
+impl Default for GiantMetadataBundle {
+ fn default() -> Self {
+ Self {
+ _marker: Giant,
+ 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)]
+pub struct ItemFrameItem(pub Slot);
+#[derive(Component, Deref, DerefMut)]
+pub struct Rotation(pub i32);
+#[derive(Component)]
+pub struct GlowItemFrame;
impl GlowItemFrame {
- pub fn set_index(&mut self, index: u8, value: EntityDataValue) -> Option<()> {
- self.item_frame.set_index(index, value)
- }
-}
-impl Deref for GlowItemFrame {
- type Target = ItemFrame;
- fn deref(&self) -> &Self::Target {
- &self.item_frame
- }
-}
-impl DerefMut for GlowItemFrame {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.item_frame
- }
-}
-
-#[derive(Debug, Clone)]
-pub struct GlowSquid {
- pub squid: Squid,
- pub dark_ticks_remaining: i32,
-}
-
-impl GlowSquid {
- pub fn read(metadata: &mut VecDeque<EntityDataValue>) -> Option<Self> {
- let squid = Squid::read(metadata)?;
- let dark_ticks_remaining = metadata.pop_front()?.into_int().ok()?;
- Some(Self {
- squid,
- dark_ticks_remaining,
- })
- }
-
- pub fn write(&self) -> Vec<EntityDataValue> {
- let mut metadata = Vec::new();
- metadata.extend(self.squid.write());
- metadata.push(EntityDataValue::Int(self.dark_ticks_remaining.clone()));
- metadata
- }
-}
-
-impl Default for GlowSquid {
- fn default() -> Self {
- Self {
- squid: Default::default(),
- dark_ticks_remaining: 0,
+ pub fn apply_metadata(
+ entity: &mut azalea_ecs::system::EntityCommands,
+ d: EntityDataItem,
+ ) -> Result<(), UpdateMetadataError> {
+ match d.index {
+ 0..=9 => ItemFrame::apply_metadata(entity, d)?,
+ _ => {}
+ }
+ Ok(())
+ }
+}
+
+#[derive(Bundle)]
+pub struct GlowItemFrameMetadataBundle {
+ _marker: GlowItemFrame,
+ parent: ItemFrameMetadataBundle,
+}
+impl Default for GlowItemFrameMetadataBundle {
+ fn default() -> Self {
+ Self {
+ _marker: GlowItemFrame,
+ parent: ItemFrameMetadataBundle {
+ _marker: ItemFrame,
+ 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),
+ },
+ item_frame_item: ItemFrameItem(Slot::Empty),
+ rotation: Rotation(0),
+ },
}
}
}
+#[derive(Component, Deref, DerefMut)]
+pub struct DarkTicksRemaining(pub i32);
+#[derive(Component)]
+pub struct GlowSquid;
impl GlowSquid {
- pub fn set_index(&mut self, index: u8, value: EntityDataValue) -> Option<()> {
- match index {
- 0..=15 => self.squid.set_index(index, value)?,
- 16 => self.dark_ticks_remaining = value.into_int().ok()?,
+ pub fn apply_metadata(
+ entity: &mut azalea_ecs::system::EntityCommands,
+ d: EntityDataItem,
+ ) -> Result<(), UpdateMetadataError> {
+ match d.index {
+ 0..=15 => Squid::apply_metadata(entity, d)?,
+ 16 => {
+ entity.insert(DarkTicksRemaining(d.value.into_int()?));
+ }
_ => {}
}
- Some(())
- }
-}
-impl Deref for GlowSquid {
- type Target = Squid;
- fn deref(&self) -> &Self::Target {
- &self.squid
- }
-}
-impl DerefMut for GlowSquid {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.squid
- }
-}
-
-#[derive(Debug, Clone)]
-pub struct Goat {
- pub abstract_animal: AbstractAnimal,
- pub is_screaming_goat: bool,
- pub has_left_horn: bool,
- pub has_right_horn: bool,
-}
-
-impl Goat {
- pub fn read(metadata: &mut VecDeque<EntityDataValue>) -> Option<Self> {
- let abstract_animal = AbstractAnimal::read(metadata)?;
- let is_screaming_goat = metadata.pop_front()?.into_boolean().ok()?;
- let has_left_horn = metadata.pop_front()?.into_boolean().ok()?;
- let has_right_horn = metadata.pop_front()?.into_boolean().ok()?;
- Some(Self {
- abstract_animal,
- is_screaming_goat,
- has_left_horn,
- has_right_horn,
- })
- }
-
- pub fn write(&self) -> Vec<EntityDataValue> {
- let mut metadata = Vec::new();
- metadata.extend(self.abstract_animal.write());
- metadata.push(EntityDataValue::Boolean(self.is_screaming_goat.clone()));
- metadata.push(EntityDataValue::Boolean(self.has_left_horn.clone()));
- metadata.push(EntityDataValue::Boolean(self.has_right_horn.clone()));
- metadata
- }
-}
-
-impl Default for Goat {
- fn default() -> Self {
- Self {
- abstract_animal: Default::default(),
- is_screaming_goat: false,
- has_left_horn: true,
- has_right_horn: true,
+ Ok(())
+ }
+}
+
+#[derive(Bundle)]
+pub struct GlowSquidMetadataBundle {
+ _marker: GlowSquid,
+ parent: SquidMetadataBundle,
+ dark_ticks_remaining: DarkTicksRemaining,
+}
+impl Default for GlowSquidMetadataBundle {
+ fn default() -> Self {
+ Self {
+ _marker: GlowSquid,
+ parent: SquidMetadataBundle {
+ _marker: Squid,
+ 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),
+ },
+ },
+ },
+ dark_ticks_remaining: DarkTicksRemaining(0),
}
}
}
+#[derive(Component, Deref, DerefMut)]
+pub struct IsScreamingGoat(pub bool);
+#[derive(Component, Deref, DerefMut)]
+pub struct HasLeftHorn(pub bool);
+#[derive(Component, Deref, DerefMut)]
+pub struct HasRightHorn(pub bool);
+#[derive(Component)]
+pub struct Goat;
impl Goat {
- pub fn set_index(&mut self, index: u8, value: EntityDataValue) -> Option<()> {
- match index {
- 0..=16 => self.abstract_animal.set_index(index, value)?,
- 17 => self.is_screaming_goat = value.into_boolean().ok()?,
- 18 => self.has_left_horn = value.into_boolean().ok()?,
- 19 => self.has_right_horn = value.into_boolean().ok()?,
+ pub fn apply_metadata(
+ entity: &mut azalea_ecs::system::EntityCommands,
+ d: EntityDataItem,
+ ) -> Result<(), UpdateMetadataError> {
+ match d.index {
+ 0..=16 => AbstractAnimal::apply_metadata(entity, d)?,
+ 17 => {
+ entity.insert(IsScreamingGoat(d.value.into_boolean()?));
+ }
+ 18 => {
+ entity.insert(HasLeftHorn(d.value.into_boolean()?));
+ }
+ 19 => {
+ entity.insert(HasRightHorn(d.value.into_boolean()?));
+ }
_ => {}
}
- Some(())
- }
-}
-impl Deref for Goat {
- type Target = AbstractAnimal;
- fn deref(&self) -> &Self::Target {
- &self.abstract_animal
- }
-}
-impl DerefMut for Goat {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.abstract_animal
- }
-}
-
-#[derive(Debug, Clone)]
-pub struct Guardian {
- pub abstract_monster: AbstractMonster,
- pub moving: bool,
- pub attack_target: i32,
-}
-
-impl Guardian {
- pub fn read(metadata: &mut VecDeque<EntityDataValue>) -> Option<Self> {
- let abstract_monster = AbstractMonster::read(metadata)?;
- let moving = metadata.pop_front()?.into_boolean().ok()?;
- let attack_target = metadata.pop_front()?.into_int().ok()?;
- Some(Self {
- abstract_monster,
- moving,
- attack_target,
- })
- }
-
- pub fn write(&self) -> Vec<EntityDataValue> {
- let mut metadata = Vec::new();
- metadata.extend(self.abstract_monster.write());
- metadata.push(EntityDataValue::Boolean(self.moving.clone()));
- metadata.push(EntityDataValue::Int(self.attack_target.clone()));
- metadata
- }
-}
-
-impl Default for Guardian {
- fn default() -> Self {
- Self {
- abstract_monster: Default::default(),
- moving: false,
- attack_target: 0,
+ Ok(())
+ }
+}
+
+#[derive(Bundle)]
+pub struct GoatMetadataBundle {
+ _marker: Goat,
+ parent: AbstractAnimalMetadataBundle,
+ is_screaming_goat: IsScreamingGoat,
+ has_left_horn: HasLeftHorn,
+ has_right_horn: HasRightHorn,
+}
+impl Default for GoatMetadataBundle {
+ fn default() -> Self {
+ Self {
+ _marker: Goat,
+ parent: AbstractAnimalMetadataBundle {
+ _marker: AbstractAnimal,
+ parent: AbstractAgeableMetadataBundle {
+ _marker: AbstractAgeable,
+ 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),
+ },
+ },
+ abstract_ageable_baby: AbstractAgeableBaby(false),
+ },
+ },
+ is_screaming_goat: IsScreamingGoat(false),
+ has_left_horn: HasLeftHorn(true),
+ has_right_horn: HasRightHorn(true),
}
}
}
+#[derive(Component)]
+pub struct Guardian;
impl Guardian {
- pub fn set_index(&mut self, index: u8, value: EntityDataValue) -> Option<()> {
- match index {
- 0..=15 => self.abstract_monster.set_index(index, value)?,
- 16 => self.moving = value.into_boolean().ok()?,
- 17 => self.attack_target = value.into_int().ok()?,
+ pub fn apply_metadata(
+ entity: &mut azalea_ecs::system::EntityCommands,
+ d: EntityDataItem,
+ ) -> Result<(), UpdateMetadataError> {
+ match d.index {
+ 0..=15 => AbstractMonster::apply_metadata(entity, d)?,
+ 16 => {
+ entity.insert(Moving(d.value.into_boolean()?));
+ }
+ 17 => {
+ entity.insert(AttackTarget(d.value.into_int()?));
+ }
_ => {}
}
- Some(())
- }
-}
-impl Deref for Guardian {
- type Target = AbstractMonster;
- fn deref(&self) -> &Self::Target {
- &self.abstract_monster
- }
-}
-impl DerefMut for Guardian {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.abstract_monster
- }
-}
-
-#[derive(Debug, Clone)]
-pub struct Hoglin {
- pub abstract_animal: AbstractAnimal,
- pub immune_to_zombification: bool,
-}
-
-impl Hoglin {
- pub fn read(metadata: &mut VecDeque<EntityDataValue>) -> Option<Self> {
- let abstract_animal = AbstractAnimal::read(metadata)?;
- let immune_to_zombification = metadata.pop_front()?.into_boolean().ok()?;
- Some(Self {
- abstract_animal,
- immune_to_zombification,
- })
- }
-
- pub fn write(&self) -> Vec<EntityDataValue> {
- let mut metadata = Vec::new();
- metadata.extend(self.abstract_animal.write());
- metadata.push(EntityDataValue::Boolean(
- self.immune_to_zombification.clone(),
- ));
- metadata
- }
-}
-
-impl Default for Hoglin {
- fn default() -> Self {
- Self {
- abstract_animal: Default::default(),
- immune_to_zombification: false,
+ Ok(())
+ }
+}
+
+#[derive(Bundle)]
+pub struct GuardianMetadataBundle {
+ _marker: Guardian,
+ parent: AbstractMonsterMetadataBundle,
+ moving: Moving,
+ attack_target: AttackTarget,
+}
+impl Default for GuardianMetadataBundle {
+ fn default() -> Self {
+ Self {
+ _marker: Guardian,
+ 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),
+ },
+ },
+ },
+ moving: Moving(false),
+ attack_target: AttackTarget(0),
}
}
}
+#[derive(Component, Deref, DerefMut)]
+pub struct HoglinImmuneToZombification(pub bool);
+#[derive(Component)]
+pub struct Hoglin;
impl Hoglin {
- pub fn set_index(&mut self, index: u8, value: EntityDataValue) -> Option<()> {
- match index {
- 0..=16 => self.abstract_animal.set_index(index, value)?,
- 17 => self.immune_to_zombification = value.into_boolean().ok()?,
+ pub fn apply_metadata(
+ entity: &mut azalea_ecs::system::EntityCommands,
+ d: EntityDataItem,
+ ) -> Result<(), UpdateMetadataError> {
+ match d.index {
+ 0..=16 => AbstractAnimal::apply_metadata(entity, d)?,
+ 17 => {
+ entity.insert(HoglinImmuneToZombification(d.value.into_boolean()?));
+ }
_ => {}
}
- Some(())
- }
-}
-impl Deref for Hoglin {
- type Target = AbstractAnimal;
- fn deref(&self) -> &Self::Target {
- &self.abstract_animal
- }
-}
-impl DerefMut for Hoglin {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.abstract_animal
- }
-}
-
-#[derive(Debug, Clone)]
-pub struct HopperMinecart {
- pub abstract_minecart: AbstractMinecart,
-}
-
-impl HopperMinecart {
- pub fn read(metadata: &mut VecDeque<EntityDataValue>) -> Option<Self> {
- let abstract_minecart = AbstractMinecart::read(metadata)?;
- Some(Self { abstract_minecart })
- }
-
- pub fn write(&self) -> Vec<EntityDataValue> {
- let mut metadata = Vec::new();
- metadata.extend(self.abstract_minecart.write());
- metadata
- }
-}
-
-impl Default for HopperMinecart {
- fn default() -> Self {
- Self {
- abstract_minecart: Default::default(),
+ Ok(())
+ }
+}
+
+#[derive(Bundle)]
+pub struct HoglinMetadataBundle {
+ _marker: Hoglin,
+ parent: AbstractAnimalMetadataBundle,
+ hoglin_immune_to_zombification: HoglinImmuneToZombification,
+}
+impl Default for HoglinMetadataBundle {
+ fn default() -> Self {
+ Self {
+ _marker: Hoglin,
+ parent: AbstractAnimalMetadataBundle {
+ _marker: AbstractAnimal,
+ parent: AbstractAgeableMetadataBundle {
+ _marker: AbstractAgeable,
+ 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),
+ },
+ },
+ abstract_ageable_baby: AbstractAgeableBaby(false),
+ },
+ },
+ hoglin_immune_to_zombification: HoglinImmuneToZombification(false),
}
}
}
+#[derive(Component)]
+pub struct HopperMinecart;
impl HopperMinecart {
- pub fn set_index(&mut self, index: u8, value: EntityDataValue) -> Option<()> {
- self.abstract_minecart.set_index(index, value)
- }
-}
-impl Deref for HopperMinecart {
- type Target = AbstractMinecart;
- fn deref(&self) -> &Self::Target {
- &self.abstract_minecart
- }
-}
-impl DerefMut for HopperMinecart {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.abstract_minecart
- }
-}
-
-#[derive(Debug, Clone)]
-pub struct Horse {
- pub abstract_animal: AbstractAnimal,
- pub tamed: bool,
- pub eating: bool,
- pub standing: bool,
- pub bred: bool,
- pub saddled: bool,
- pub owner_uuid: Option<Uuid>,
- pub type_variant: i32,
-}
-
-impl Horse {
- pub fn read(metadata: &mut VecDeque<EntityDataValue>) -> Option<Self> {
- let abstract_animal = AbstractAnimal::read(metadata)?;
- let bitfield = metadata.pop_front()?.into_byte().ok()?;
- let tamed = bitfield & 0x2 != 0;
- let eating = bitfield & 0x10 != 0;
- let standing = bitfield & 0x20 != 0;
- let bred = bitfield & 0x8 != 0;
- let saddled = bitfield & 0x4 != 0;
- let owner_uuid = metadata.pop_front()?.into_optional_uuid().ok()?;
- let type_variant = metadata.pop_front()?.into_int().ok()?;
- Some(Self {
- abstract_animal,
- tamed,
- eating,
- standing,
- bred,
- saddled,
- owner_uuid,
- type_variant,
- })
- }
-
- pub fn write(&self) -> Vec<EntityDataValue> {
- let mut metadata = Vec::new();
- metadata.extend(self.abstract_animal.write());
- let mut bitfield = 0u8;
- if self.tamed {
- bitfield &= 0x2;
- }
- if self.eating {
- bitfield &= 0x10;
- }
- if self.standing {
- bitfield &= 0x20;
- }
- if self.bred {
- bitfield &= 0x8;
- }
- if self.saddled {
- bitfield &= 0x4;
+ pub fn apply_metadata(
+ entity: &mut azalea_ecs::system::EntityCommands,
+ d: EntityDataItem,
+ ) -> Result<(), UpdateMetadataError> {
+ match d.index {
+ 0..=13 => AbstractMinecart::apply_metadata(entity, d)?,
+ _ => {}
}
- metadata.push(EntityDataValue::Byte(bitfield));
- metadata.push(EntityDataValue::OptionalUuid(self.owner_uuid.clone()));
- metadata.push(EntityDataValue::Int(self.type_variant.clone()));
- metadata
- }
-}
-
-impl Default for Horse {
- fn default() -> Self {
- Self {
- abstract_animal: Default::default(),
- tamed: false,
- eating: false,
- standing: false,
- bred: false,
- saddled: false,
- owner_uuid: None,
- type_variant: 0,
+ Ok(())
+ }
+}
+
+#[derive(Bundle)]
+pub struct HopperMinecartMetadataBundle {
+ _marker: HopperMinecart,
+ parent: AbstractMinecartMetadataBundle,
+}
+impl Default for HopperMinecartMetadataBundle {
+ fn default() -> Self {
+ Self {
+ _marker: HopperMinecart,
+ parent: AbstractMinecartMetadataBundle {
+ _marker: AbstractMinecart,
+ 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),
+ },
+ abstract_minecart_hurt: AbstractMinecartHurt(0),
+ abstract_minecart_hurtdir: AbstractMinecartHurtdir(1),
+ abstract_minecart_damage: AbstractMinecartDamage(0.0),
+ display_block: DisplayBlock(Default::default()),
+ display_offset: DisplayOffset(6),
+ custom_display: CustomDisplay(false),
+ },
}
}
}
+#[derive(Component, Deref, DerefMut)]
+pub struct HorseTamed(pub bool);
+#[derive(Component, Deref, DerefMut)]
+pub struct HorseEating(pub bool);
+#[derive(Component, Deref, DerefMut)]
+pub struct HorseStanding(pub bool);
+#[derive(Component, Deref, DerefMut)]
+pub struct HorseBred(pub bool);
+#[derive(Component, Deref, DerefMut)]
+pub struct HorseSaddled(pub bool);
+#[derive(Component, Deref, DerefMut)]
+pub struct HorseOwnerUuid(pub Option<Uuid>);
+#[derive(Component, Deref, DerefMut)]
+pub struct HorseTypeVariant(pub i32);
+#[derive(Component)]
+pub struct Horse;
impl Horse {
- pub fn set_index(&mut self, index: u8, value: EntityDataValue) -> Option<()> {
- match index {
- 0..=16 => self.abstract_animal.set_index(index, value)?,
+ pub fn apply_metadata(
+ entity: &mut azalea_ecs::system::EntityCommands,
+ d: EntityDataItem,
+ ) -> Result<(), UpdateMetadataError> {
+ match d.index {
+ 0..=16 => AbstractAnimal::apply_metadata(entity, d)?,
17 => {
- let bitfield = value.into_byte().ok()?;
- self.tamed = bitfield & 0x2 != 0;
- self.eating = bitfield & 0x10 != 0;
- self.standing = bitfield & 0x20 != 0;
- self.bred = bitfield & 0x8 != 0;
- self.saddled = bitfield & 0x4 != 0;
+ let bitfield = d.value.into_byte()?;
+ entity.insert(HorseTamed(bitfield & 0x2 != 0));
+ entity.insert(HorseEating(bitfield & 0x10 != 0));
+ entity.insert(HorseStanding(bitfield & 0x20 != 0));
+ entity.insert(HorseBred(bitfield & 0x8 != 0));
+ entity.insert(HorseSaddled(bitfield & 0x4 != 0));
+ }
+ 18 => {
+ entity.insert(HorseOwnerUuid(d.value.into_optional_uuid()?));
+ }
+ 19 => {
+ entity.insert(HorseTypeVariant(d.value.into_int()?));
}
- 18 => self.owner_uuid = value.into_optional_uuid().ok()?,
- 19 => self.type_variant = value.into_int().ok()?,
_ => {}
}
- Some(())
- }
-}
-impl Deref for Horse {
- type Target = AbstractAnimal;
- fn deref(&self) -> &Self::Target {
- &self.abstract_animal
- }
-}
-impl DerefMut for Horse {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.abstract_animal
- }
-}
-
-#[derive(Debug, Clone)]
-pub struct Husk {
- pub zombie: Zombie,
-}
-
-impl Husk {
- pub fn read(metadata: &mut VecDeque<EntityDataValue>) -> Option<Self> {
- let zombie = Zombie::read(metadata)?;
- Some(Self { zombie })
- }
-
- pub fn write(&self) -> Vec<EntityDataValue> {
- let mut metadata = Vec::new();
- metadata.extend(self.zombie.write());
- metadata
- }
-}
-
-impl Default for Husk {
- fn default() -> Self {
- Self {
- zombie: Default::default(),
+ Ok(())
+ }
+}
+
+#[derive(Bundle)]
+pub struct HorseMetadataBundle {
+ _marker: Horse,
+ parent: AbstractAnimalMetadataBundle,
+ horse_tamed: HorseTamed,
+ horse_eating: HorseEating,
+ horse_standing: HorseStanding,
+ horse_bred: HorseBred,
+ horse_saddled: HorseSaddled,
+ horse_owner_uuid: HorseOwnerUuid,
+ horse_type_variant: HorseTypeVariant,
+}
+impl Default for HorseMetadataBundle {
+ fn default() -> Self {
+ Self {
+ _marker: Horse,
+ parent: AbstractAnimalMetadataBundle {
+ _marker: AbstractAnimal,
+ parent: AbstractAgeableMetadataBundle {
+ _marker: AbstractAgeable,
+ 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),
+ },
+ },
+ abstract_ageable_baby: AbstractAgeableBaby(false),
+ },
+ },
+ horse_tamed: HorseTamed(false),
+ horse_eating: HorseEating(false),
+ horse_standing: HorseStanding(false),
+ horse_bred: HorseBred(false),
+ horse_saddled: HorseSaddled(false),
+ horse_owner_uuid: HorseOwnerUuid(None),
+ horse_type_variant: HorseTypeVariant(0),
}
}
}
+#[derive(Component)]
+pub struct Husk;
impl Husk {
- pub fn set_index(&mut self, index: u8, value: EntityDataValue) -> Option<()> {
- self.zombie.set_index(index, value)
- }
-}
-impl Deref for Husk {
- type Target = Zombie;
- fn deref(&self) -> &Self::Target {
- &self.zombie
- }
-}
-impl DerefMut for Husk {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.zombie
- }
-}
-
-#[derive(Debug, Clone)]
-pub struct Illusioner {
- pub abstract_monster: AbstractMonster,
- pub is_celebrating: bool,
- pub spell_casting: u8,
-}
-
-impl Illusioner {
- pub fn read(metadata: &mut VecDeque<EntityDataValue>) -> Option<Self> {
- let abstract_monster = AbstractMonster::read(metadata)?;
- let is_celebrating = metadata.pop_front()?.into_boolean().ok()?;
- let spell_casting = metadata.pop_front()?.into_byte().ok()?;
- Some(Self {
- abstract_monster,
- is_celebrating,
- spell_casting,
- })
- }
-
- pub fn write(&self) -> Vec<EntityDataValue> {
- let mut metadata = Vec::new();
- metadata.extend(self.abstract_monster.write());
- metadata.push(EntityDataValue::Boolean(self.is_celebrating.clone()));
- metadata.push(EntityDataValue::Byte(self.spell_casting.clone()));
- metadata
- }
-}
-
-impl Default for Illusioner {
- fn default() -> Self {
- Self {
- abstract_monster: Default::default(),
- is_celebrating: false,
- spell_casting: 0,
+ pub fn apply_metadata(
+ entity: &mut azalea_ecs::system::EntityCommands,
+ d: EntityDataItem,
+ ) -> Result<(), UpdateMetadataError> {
+ match d.index {
+ 0..=18 => Zombie::apply_metadata(entity, d)?,
+ _ => {}
+ }
+ Ok(())
+ }
+}
+
+#[derive(Bundle)]
+pub struct HuskMetadataBundle {
+ _marker: Husk,
+ parent: ZombieMetadataBundle,
+}
+impl Default for HuskMetadataBundle {
+ fn default() -> Self {
+ Self {
+ _marker: Husk,
+ parent: ZombieMetadataBundle {
+ _marker: Zombie,
+ 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),
+ },
+ },
+ },
+ zombie_baby: ZombieBaby(false),
+ special_type: SpecialType(0),
+ drowned_conversion: DrownedConversion(false),
+ },
}
}
}
+#[derive(Component, Deref, DerefMut)]
+pub struct IllusionerIsCelebrating(pub bool);
+#[derive(Component, Deref, DerefMut)]
+pub struct IllusionerSpellCasting(pub u8);
+#[derive(Component)]
+pub struct Illusioner;
impl Illusioner {
- pub fn set_index(&mut self, index: u8, value: EntityDataValue) -> Option<()> {
- match index {
- 0..=15 => self.abstract_monster.set_index(index, value)?,
- 16 => self.is_celebrating = value.into_boolean().ok()?,
- 17 => self.spell_casting = value.into_byte().ok()?,
+ pub fn apply_metadata(
+ entity: &mut azalea_ecs::system::EntityCommands,
+ d: EntityDataItem,
+ ) -> Result<(), UpdateMetadataError> {
+ match d.index {
+ 0..=15 => AbstractMonster::apply_metadata(entity, d)?,
+ 16 => {
+ entity.insert(IllusionerIsCelebrating(d.value.into_boolean()?));
+ }
+ 17 => {
+ entity.insert(IllusionerSpellCasting(d.value.into_byte()?));
+ }
_ => {}
}
- Some(())
- }
-}
-impl Deref for Illusioner {
- type Target = AbstractMonster;
- fn deref(&self) -> &Self::Target {
- &self.abstract_monster
- }
-}
-impl DerefMut for Illusioner {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.abstract_monster
- }
-}
-
-#[derive(Debug, Clone)]
-pub struct IronGolem {
- pub abstract_creature: AbstractCreature,
- pub player_created: bool,
-}
-
-impl IronGolem {
- pub fn read(metadata: &mut VecDeque<EntityDataValue>) -> Option<Self> {
- let abstract_creature = AbstractCreature::read(metadata)?;
- let bitfield = metadata.pop_front()?.into_byte().ok()?;
- let player_created = bitfield & 0x1 != 0;
- Some(Self {
- abstract_creature,
- player_created,
- })
- }
-
- pub fn write(&self) -> Vec<EntityDataValue> {
- let mut metadata = Vec::new();
- metadata.extend(self.abstract_creature.write());
- let mut bitfield = 0u8;
- if self.player_created {
- bitfield &= 0x1;
- }
- metadata.push(EntityDataValue::Byte(bitfield));
- metadata
- }
-}
-
-impl Default for IronGolem {
- fn default() -> Self {
- Self {
- abstract_creature: Default::default(),
- player_created: false,
+ Ok(())
+ }
+}
+
+#[derive(Bundle)]
+pub struct IllusionerMetadataBundle {
+ _marker: Illusioner,
+ parent: AbstractMonsterMetadataBundle,
+ illusioner_is_celebrating: IllusionerIsCelebrating,
+ illusioner_spell_casting: IllusionerSpellCasting,
+}
+impl Default for IllusionerMetadataBundle {
+ fn default() -> Self {
+ Self {
+ _marker: Illusioner,
+ 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),
+ },
+ },
+ },
+ illusioner_is_celebrating: IllusionerIsCelebrating(false),
+ illusioner_spell_casting: IllusionerSpellCasting(0),
}
}
}
+#[derive(Component, Deref, DerefMut)]
+pub struct PlayerCreated(pub bool);
+#[derive(Component)]
+pub struct IronGolem;
impl IronGolem {
- pub fn set_index(&mut self, index: u8, value: EntityDataValue) -> Option<()> {
- match index {
- 0..=15 => self.abstract_creature.set_index(index, value)?,
+ pub fn apply_metadata(
+ entity: &mut azalea_ecs::system::EntityCommands,
+ d: EntityDataItem,
+ ) -> Result<(), UpdateMetadataError> {
+ match d.index {
+ 0..=15 => AbstractCreature::apply_metadata(entity, d)?,
16 => {
- let bitfield = value.into_byte().ok()?;
- self.player_created = bitfield & 0x1 != 0;
+ let bitfield = d.value.into_byte()?;
+ entity.insert(PlayerCreated(bitfield & 0x1 != 0));
}
_ => {}
}
- Some(())
- }
-}
-impl Deref for IronGolem {
- type Target = AbstractCreature;
- fn deref(&self) -> &Self::Target {
- &self.abstract_creature
- }
-}
-impl DerefMut for IronGolem {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.abstract_creature
- }
-}
-
-#[derive(Debug, Clone)]
-pub struct Item {
- pub abstract_entity: AbstractEntity,
- pub item: Slot,
-}
-
-impl Item {
- pub fn read(metadata: &mut VecDeque<EntityDataValue>) -> Option<Self> {
- let abstract_entity = AbstractEntity::read(metadata)?;
- let item = metadata.pop_front()?.into_item_stack().ok()?;
- Some(Self {
- abstract_entity,
- item,
- })
- }
-
- pub fn write(&self) -> Vec<EntityDataValue> {
- let mut metadata = Vec::new();
- metadata.extend(self.abstract_entity.write());
- metadata.push(EntityDataValue::ItemStack(self.item.clone()));
- metadata
- }
-}
-
-impl Default for Item {
- fn default() -> Self {
- Self {
- abstract_entity: Default::default(),
- item: Slot::Empty,
+ Ok(())
+ }
+}
+
+#[derive(Bundle)]
+pub struct IronGolemMetadataBundle {
+ _marker: IronGolem,
+ parent: AbstractCreatureMetadataBundle,
+ player_created: PlayerCreated,
+}
+impl Default for IronGolemMetadataBundle {
+ fn default() -> Self {
+ Self {
+ _marker: IronGolem,
+ 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),
+ },
+ },
+ player_created: PlayerCreated(false),
}
}
}
+#[derive(Component, Deref, DerefMut)]
+pub struct ItemItem(pub Slot);
+#[derive(Component)]
+pub struct Item;
impl Item {
- pub fn set_index(&mut self, index: u8, value: EntityDataValue) -> Option<()> {
- match index {
- 0..=7 => self.abstract_entity.set_index(index, value)?,
- 8 => self.item = value.into_item_stack().ok()?,
+ pub fn apply_metadata(
+ entity: &mut azalea_ecs::system::EntityCommands,
+ d: EntityDataItem,
+ ) -> Result<(), UpdateMetadataError> {
+ match d.index {
+ 0..=7 => AbstractEntity::apply_metadata(entity, d)?,
+ 8 => {
+ entity.insert(ItemItem(d.value.into_item_stack()?));
+ }
_ => {}
}
- Some(())
- }
-}
-impl Deref for Item {
- type Target = AbstractEntity;
- fn deref(&self) -> &Self::Target {
- &self.abstract_entity
- }
-}
-impl DerefMut for Item {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.abstract_entity
+ Ok(())
}
}
-#[derive(Debug, Clone)]
-pub struct ItemFrame {
- pub abstract_entity: AbstractEntity,
- pub item: Slot,
- pub rotation: i32,
+#[derive(Bundle)]
+pub struct ItemMetadataBundle {
+ _marker: Item,
+ parent: AbstractEntityMetadataBundle,
+ item_item: ItemItem,
}
-
-impl ItemFrame {
- pub fn read(metadata: &mut VecDeque<EntityDataValue>) -> Option<Self> {
- let abstract_entity = AbstractEntity::read(metadata)?;
- let item = metadata.pop_front()?.into_item_stack().ok()?;
- let rotation = metadata.pop_front()?.into_int().ok()?;
- Some(Self {
- abstract_entity,
- item,
- rotation,
- })
- }
-
- pub fn write(&self) -> Vec<EntityDataValue> {
- let mut metadata = Vec::new();
- metadata.extend(self.abstract_entity.write());
- metadata.push(EntityDataValue::ItemStack(self.item.clone()));
- metadata.push(EntityDataValue::Int(self.rotation.clone()));
- metadata
- }
-}
-
-impl Default for ItemFrame {
+impl Default for ItemMetadataBundle {
fn default() -> Self {
Self {
- abstract_entity: Default::default(),
- item: Slot::Empty,
- rotation: 0,
+ _marker: Item,
+ 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),
+ },
+ item_item: ItemItem(Slot::Empty),
}
}
}
+#[derive(Component)]
+pub struct ItemFrame;
impl ItemFrame {
- pub fn set_index(&mut self, index: u8, value: EntityDataValue) -> Option<()> {
- match index {
- 0..=7 => self.abstract_entity.set_index(index, value)?,
- 8 => self.item = value.into_item_stack().ok()?,
- 9 => self.rotation = value.into_int().ok()?,
+ pub fn apply_metadata(
+ entity: &mut azalea_ecs::system::EntityCommands,
+ d: EntityDataItem,
+ ) -> Result<(), UpdateMetadataError> {
+ match d.index {
+ 0..=7 => AbstractEntity::apply_metadata(entity, d)?,
+ 8 => {
+ entity.insert(ItemFrameItem(d.value.into_item_stack()?));
+ }
+ 9 => {
+ entity.insert(Rotation(d.value.into_int()?));
+ }
_ => {}
}
- Some(())
- }
-}
-impl Deref for ItemFrame {
- type Target = AbstractEntity;
- fn deref(&self) -> &Self::Target {
- &self.abstract_entity
- }
-}
-impl DerefMut for ItemFrame {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.abstract_entity
- }
-}
-
-#[derive(Debug, Clone)]
-pub struct LeashKnot {
- pub abstract_entity: AbstractEntity,
-}
-
-impl LeashKnot {
- pub fn read(metadata: &mut VecDeque<EntityDataValue>) -> Option<Self> {
- let abstract_entity = AbstractEntity::read(metadata)?;
- Some(Self { abstract_entity })
- }
-
- pub fn write(&self) -> Vec<EntityDataValue> {
- let mut metadata = Vec::new();
- metadata.extend(self.abstract_entity.write());
- metadata
- }
-}
-
-impl Default for LeashKnot {
- fn default() -> Self {
- Self {
- abstract_entity: Default::default(),
+ Ok(())
+ }
+}
+
+#[derive(Bundle)]
+pub struct ItemFrameMetadataBundle {
+ _marker: ItemFrame,
+ parent: AbstractEntityMetadataBundle,
+ item_frame_item: ItemFrameItem,
+ rotation: Rotation,
+}
+impl Default for ItemFrameMetadataBundle {
+ fn default() -> Self {
+ Self {
+ _marker: ItemFrame,
+ 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),
+ },
+ item_frame_item: ItemFrameItem(Slot::Empty),
+ rotation: Rotation(0),
}
}
}
+#[derive(Component)]
+pub struct LeashKnot;
impl LeashKnot {
- pub fn set_index(&mut self, index: u8, value: EntityDataValue) -> Option<()> {
- self.abstract_entity.set_index(index, value)
- }
-}
-impl Deref for LeashKnot {
- type Target = AbstractEntity;
- fn deref(&self) -> &Self::Target {
- &self.abstract_entity
- }
-}
-impl DerefMut for LeashKnot {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.abstract_entity
+ pub fn apply_metadata(
+ entity: &mut azalea_ecs::system::EntityCommands,
+ d: EntityDataItem,
+ ) -> Result<(), UpdateMetadataError> {
+ match d.index {
+ 0..=7 => AbstractEntity::apply_metadata(entity, d)?,
+ _ => {}
+ }
+ Ok(())
}
}
-#[derive(Debug, Clone)]
-pub struct LightningBolt {
- pub abstract_entity: AbstractEntity,
+#[derive(Bundle)]
+pub struct LeashKnotMetadataBundle {
+ _marker: LeashKnot,
+ parent: AbstractEntityMetadataBundle,
}
-
-impl LightningBolt {
- pub fn read(metadata: &mut VecDeque<EntityDataValue>) -> Option<Self> {
- let abstract_entity = AbstractEntity::read(metadata)?;
- Some(Self { abstract_entity })
- }
-
- pub fn write(&self) -> Vec<EntityDataValue> {
- let mut metadata = Vec::new();
- metadata.extend(self.abstract_entity.write());
- metadata
- }
-}
-
-impl Default for LightningBolt {
+impl Default for LeashKnotMetadataBundle {
fn default() -> Self {
Self {
- abstract_entity: Default::default(),
+ _marker: LeashKnot,
+ 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)]
+pub struct LightningBolt;
impl LightningBolt {
- pub fn set_index(&mut self, index: u8, value: EntityDataValue) -> Option<()> {
- self.abstract_entity.set_index(index, value)
- }
-}
-impl Deref for LightningBolt {
- type Target = AbstractEntity;
- fn deref(&self) -> &Self::Target {
- &self.abstract_entity
- }
-}
-impl DerefMut for LightningBolt {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.abstract_entity
+ pub fn apply_metadata(
+ entity: &mut azalea_ecs::system::EntityCommands,
+ d: EntityDataItem,
+ ) -> Result<(), UpdateMetadataError> {
+ match d.index {
+ 0..=7 => AbstractEntity::apply_metadata(entity, d)?,
+ _ => {}
+ }
+ Ok(())
}
}
-#[derive(Debug, Clone)]
-pub struct Llama {
- pub abstract_animal: AbstractAnimal,
- pub tamed: bool,
- pub eating: bool,
- pub standing: bool,
- pub bred: bool,
- pub saddled: bool,
- pub owner_uuid: Option<Uuid>,
- pub chest: bool,
- pub strength: i32,
- pub swag: i32,
- pub variant: i32,
+#[derive(Bundle)]
+pub struct LightningBoltMetadataBundle {
+ _marker: LightningBolt,
+ parent: AbstractEntityMetadataBundle,
}
-
-impl Llama {
- pub fn read(metadata: &mut VecDeque<EntityDataValue>) -> Option<Self> {
- let abstract_animal = AbstractAnimal::read(metadata)?;
- let bitfield = metadata.pop_front()?.into_byte().ok()?;
- let tamed = bitfield & 0x2 != 0;
- let eating = bitfield & 0x10 != 0;
- let standing = bitfield & 0x20 != 0;
- let bred = bitfield & 0x8 != 0;
- let saddled = bitfield & 0x4 != 0;
- let owner_uuid = metadata.pop_front()?.into_optional_uuid().ok()?;
- let chest = metadata.pop_front()?.into_boolean().ok()?;
- let strength = metadata.pop_front()?.into_int().ok()?;
- let swag = metadata.pop_front()?.into_int().ok()?;
- let variant = metadata.pop_front()?.into_int().ok()?;
- Some(Self {
- abstract_animal,
- tamed,
- eating,
- standing,
- bred,
- saddled,
- owner_uuid,
- chest,
- strength,
- swag,
- variant,
- })
- }
-
- pub fn write(&self) -> Vec<EntityDataValue> {
- let mut metadata = Vec::new();
- metadata.extend(self.abstract_animal.write());
- let mut bitfield = 0u8;
- if self.tamed {
- bitfield &= 0x2;
- }
- if self.eating {
- bitfield &= 0x10;
- }
- if self.standing {
- bitfield &= 0x20;
- }
- if self.bred {
- bitfield &= 0x8;
- }
- if self.saddled {
- bitfield &= 0x4;
- }
- metadata.push(EntityDataValue::Byte(bitfield));
- metadata.push(EntityDataValue::OptionalUuid(self.owner_uuid.clone()));
- metadata.push(EntityDataValue::Boolean(self.chest.clone()));
- metadata.push(EntityDataValue::Int(self.strength.clone()));
- metadata.push(EntityDataValue::Int(self.swag.clone()));
- metadata.push(EntityDataValue::Int(self.variant.clone()));
- metadata
- }
-}
-
-impl Default for Llama {
- fn default() -> Self {
- Self {
- abstract_animal: Default::default(),
- tamed: false,
- eating: false,
- standing: false,
- bred: false,
- saddled: false,
- owner_uuid: None,
- chest: false,
- strength: 0,
- swag: -1,
- variant: 0,
+impl Default for LightningBoltMetadataBundle {
+ fn default() -> Self {
+ Self {
+ _marker: LightningBolt,
+ 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)]
+pub struct LlamaTamed(pub bool);
+#[derive(Component, Deref, DerefMut)]
+pub struct LlamaEating(pub bool);
+#[derive(Component, Deref, DerefMut)]
+pub struct LlamaStanding(pub bool);
+#[derive(Component, Deref, DerefMut)]
+pub struct LlamaBred(pub bool);
+#[derive(Component, Deref, DerefMut)]
+pub struct LlamaSaddled(pub bool);
+#[derive(Component, Deref, DerefMut)]
+pub struct LlamaOwnerUuid(pub Option<Uuid>);
+#[derive(Component, Deref, DerefMut)]
+pub struct LlamaChest(pub bool);
+#[derive(Component, Deref, DerefMut)]
+pub struct Strength(pub i32);
+#[derive(Component, Deref, DerefMut)]
+pub struct Swag(pub i32);
+#[derive(Component, Deref, DerefMut)]
+pub struct LlamaVariant(pub i32);
+#[derive(Component)]
+pub struct Llama;
impl Llama {
- pub fn set_index(&mut self, index: u8, value: EntityDataValue) -> Option<()> {
- match index {
- 0..=16 => self.abstract_animal.set_index(index, value)?,
+ pub fn apply_metadata(
+ entity: &mut azalea_ecs::system::EntityCommands,
+ d: EntityDataItem,
+ ) -> Result<(), UpdateMetadataError> {
+ match d.index {
+ 0..=16 => AbstractAnimal::apply_metadata(entity, d)?,
17 => {
- let bitfield = value.into_byte().ok()?;
- self.tamed = bitfield & 0x2 != 0;
- self.eating = bitfield & 0x10 != 0;
- self.standing = bitfield & 0x20 != 0;
- self.bred = bitfield & 0x8 != 0;
- self.saddled = bitfield & 0x4 != 0;
+ let bitfield = d.value.into_byte()?;
+ entity.insert(LlamaTamed(bitfield & 0x2 != 0));
+ entity.insert(LlamaEating(bitfield & 0x10 != 0));
+ entity.insert(LlamaStanding(bitfield & 0x20 != 0));
+ entity.insert(LlamaBred(bitfield & 0x8 != 0));
+ entity.insert(LlamaSaddled(bitfield & 0x4 != 0));
+ }
+ 18 => {
+ entity.insert(LlamaOwnerUuid(d.value.into_optional_uuid()?));
+ }
+ 19 => {
+ entity.insert(LlamaChest(d.value.into_boolean()?));
+ }
+ 20 => {
+ entity.insert(Strength(d.value.into_int()?));
+ }
+ 21 => {
+ entity.insert(Swag(d.value.into_int()?));
+ }
+ 22 => {
+ entity.insert(LlamaVariant(d.value.into_int()?));
}
- 18 => self.owner_uuid = value.into_optional_uuid().ok()?,
- 19 => self.chest = value.into_boolean().ok()?,
- 20 => self.strength = value.into_int().ok()?,
- 21 => self.swag = value.into_int().ok()?,
- 22 => self.variant = value.into_int().ok()?,
_ => {}
}
- Some(())
- }
-}
-impl Deref for Llama {
- type Target = AbstractAnimal;
- fn deref(&self) -> &Self::Target {
- &self.abstract_animal
- }
-}
-impl DerefMut for Llama {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.abstract_animal
- }
-}
-
-#[derive(Debug, Clone)]
-pub struct LlamaSpit {
- pub abstract_entity: AbstractEntity,
-}
-
-impl LlamaSpit {
- pub fn read(metadata: &mut VecDeque<EntityDataValue>) -> Option<Self> {
- let abstract_entity = AbstractEntity::read(metadata)?;
- Some(Self { abstract_entity })
- }
-
- pub fn write(&self) -> Vec<EntityDataValue> {
- let mut metadata = Vec::new();
- metadata.extend(self.abstract_entity.write());
- metadata
- }
-}
-
-impl Default for LlamaSpit {
- fn default() -> Self {
- Self {
- abstract_entity: Default::default(),
+ Ok(())
+ }
+}
+
+#[derive(Bundle)]
+pub struct LlamaMetadataBundle {
+ _marker: Llama,
+ parent: AbstractAnimalMetadataBundle,
+ llama_tamed: LlamaTamed,
+ llama_eating: LlamaEating,
+ llama_standing: LlamaStanding,
+ llama_bred: LlamaBred,
+ llama_saddled: LlamaSaddled,
+ llama_owner_uuid: LlamaOwnerUuid,
+ llama_chest: LlamaChest,
+ strength: Strength,
+ swag: Swag,
+ llama_variant: LlamaVariant,
+}
+impl Default for LlamaMetadataBundle {
+ fn default() -> Self {
+ Self {
+ _marker: Llama,
+ parent: AbstractAnimalMetadataBundle {
+ _marker: AbstractAnimal,
+ parent: AbstractAgeableMetadataBundle {
+ _marker: AbstractAgeable,
+ 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),
+ },
+ },
+ abstract_ageable_baby: AbstractAgeableBaby(false),
+ },
+ },
+ llama_tamed: LlamaTamed(false),
+ llama_eating: LlamaEating(false),
+ llama_standing: LlamaStanding(false),
+ llama_bred: LlamaBred(false),
+ llama_saddled: LlamaSaddled(false),
+ llama_owner_uuid: LlamaOwnerUuid(None),
+ llama_chest: LlamaChest(false),
+ strength: Strength(0),
+ swag: Swag(-1),
+ llama_variant: LlamaVariant(0),
}
}
}
+#[derive(Component)]
+pub struct LlamaSpit;
impl LlamaSpit {
- pub fn set_index(&mut self, index: u8, value: EntityDataValue) -> Option<()> {
- self.abstract_entity.set_index(index, value)
- }
-}
-impl Deref for LlamaSpit {
- type Target = AbstractEntity;
- fn deref(&self) -> &Self::Target {
- &self.abstract_entity
- }
-}
-impl DerefMut for LlamaSpit {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.abstract_entity
+ pub fn apply_metadata(
+ entity: &mut azalea_ecs::system::EntityCommands,
+ d: EntityDataItem,
+ ) -> Result<(), UpdateMetadataError> {
+ match d.index {
+ 0..=7 => AbstractEntity::apply_metadata(entity, d)?,
+ _ => {}
+ }
+ Ok(())
}
}
-#[derive(Debug, Clone)]
-pub struct MagmaCube {
- pub slime: Slime,
+#[derive(Bundle)]
+pub struct LlamaSpitMetadataBundle {
+ _marker: LlamaSpit,
+ parent: AbstractEntityMetadataBundle,
}
-
-impl MagmaCube {
- pub fn read(metadata: &mut VecDeque<EntityDataValue>) -> Option<Self> {
- let slime = Slime::read(metadata)?;
- Some(Self { slime })
- }
-
- pub fn write(&self) -> Vec<EntityDataValue> {
- let mut metadata = Vec::new();
- metadata.extend(self.slime.write());
- metadata
- }
-}
-
-impl Default for MagmaCube {
+impl Default for LlamaSpitMetadataBundle {
fn default() -> Self {
Self {
- slime: Default::default(),
+ _marker: LlamaSpit,
+ 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)]
+pub struct SlimeSize(pub i32);
+#[derive(Component)]
+pub struct MagmaCube;
impl MagmaCube {
- pub fn set_index(&mut self, index: u8, value: EntityDataValue) -> Option<()> {
- self.slime.set_index(index, value)
- }
-}
-impl Deref for MagmaCube {
- type Target = Slime;
- fn deref(&self) -> &Self::Target {
- &self.slime
- }
-}
-impl DerefMut for MagmaCube {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.slime
- }
-}
-
-#[derive(Debug, Clone)]
-pub struct Marker {
- pub abstract_entity: AbstractEntity,
-}
-
-impl Marker {
- pub fn read(metadata: &mut VecDeque<EntityDataValue>) -> Option<Self> {
- let abstract_entity = AbstractEntity::read(metadata)?;
- Some(Self { abstract_entity })
- }
-
- pub fn write(&self) -> Vec<EntityDataValue> {
- let mut metadata = Vec::new();
- metadata.extend(self.abstract_entity.write());
- metadata
- }
-}
-
-impl Default for Marker {
- fn default() -> Self {
- Self {
- abstract_entity: Default::default(),
+ pub fn apply_metadata(
+ entity: &mut azalea_ecs::system::EntityCommands,
+ d: EntityDataItem,
+ ) -> Result<(), UpdateMetadataError> {
+ match d.index {
+ 0..=16 => Slime::apply_metadata(entity, d)?,
+ _ => {}
+ }
+ Ok(())
+ }
+}
+
+#[derive(Bundle)]
+pub struct MagmaCubeMetadataBundle {
+ _marker: MagmaCube,
+ parent: SlimeMetadataBundle,
+}
+impl Default for MagmaCubeMetadataBundle {
+ fn default() -> Self {
+ Self {
+ _marker: MagmaCube,
+ parent: SlimeMetadataBundle {
+ _marker: Slime,
+ 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),
+ },
+ slime_size: SlimeSize(1),
+ },
}
}
}
+#[derive(Component)]
+pub struct Marker;
impl Marker {
- pub fn set_index(&mut self, index: u8, value: EntityDataValue) -> Option<()> {
- self.abstract_entity.set_index(index, value)
- }
-}
-impl Deref for Marker {
- type Target = AbstractEntity;
- fn deref(&self) -> &Self::Target {
- &self.abstract_entity
- }
-}
-impl DerefMut for Marker {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.abstract_entity
- }
-}
-
-#[derive(Debug, Clone)]
-pub struct Minecart {
- pub abstract_minecart: AbstractMinecart,
-}
-
-impl Minecart {
- pub fn read(metadata: &mut VecDeque<EntityDataValue>) -> Option<Self> {
- let abstract_minecart = AbstractMinecart::read(metadata)?;
- Some(Self { abstract_minecart })
- }
-
- pub fn write(&self) -> Vec<EntityDataValue> {
- let mut metadata = Vec::new();
- metadata.extend(self.abstract_minecart.write());
- metadata
- }
-}
-
-impl Default for Minecart {
- fn default() -> Self {
- Self {
- abstract_minecart: Default::default(),
+ pub fn apply_metadata(
+ entity: &mut azalea_ecs::system::EntityCommands,
+ d: EntityDataItem,
+ ) -> Result<(), UpdateMetadataError> {
+ match d.index {
+ 0..=7 => AbstractEntity::apply_metadata(entity, d)?,
+ _ => {}
}
+ Ok(())
}
}
-impl Minecart {
- pub fn set_index(&mut self, index: u8, value: EntityDataValue) -> Option<()> {
- self.abstract_minecart.set_index(index, value)
- }
-}
-impl Deref for Minecart {
- type Target = AbstractMinecart;
- fn deref(&self) -> &Self::Target {
- &self.abstract_minecart
- }
-}
-impl DerefMut for Minecart {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.abstract_minecart
- }
-}
-
-#[derive(Debug, Clone)]
-pub struct Mooshroom {
- pub cow: Cow,
- pub kind: String,
-}
-
-impl Mooshroom {
- pub fn read(metadata: &mut VecDeque<EntityDataValue>) -> Option<Self> {
- let cow = Cow::read(metadata)?;
- let kind = metadata.pop_front()?.into_string().ok()?;
- Some(Self { cow, kind })
- }
-
- pub fn write(&self) -> Vec<EntityDataValue> {
- let mut metadata = Vec::new();
- metadata.extend(self.cow.write());
- metadata.push(EntityDataValue::String(self.kind.clone()));
- metadata
- }
+#[derive(Bundle)]
+pub struct MarkerMetadataBundle {
+ _marker: Marker,
+ parent: AbstractEntityMetadataBundle,
}
-
-impl Default for Mooshroom {
+impl Default for MarkerMetadataBundle {
fn default() -> Self {
Self {
- cow: Default::default(),
- kind: Default::default(),
+ _marker: Marker,
+ 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),
+ },
}
}
}
-impl Mooshroom {
- pub fn set_index(&mut self, index: u8, value: EntityDataValue) -> Option<()> {
- match index {
- 0..=16 => self.cow.set_index(index, value)?,
- 17 => self.kind = value.into_string().ok()?,
+#[derive(Component)]
+pub struct Minecart;
+impl Minecart {
+ pub fn apply_metadata(
+ entity: &mut azalea_ecs::system::EntityCommands,
+ d: EntityDataItem,
+ ) -> Result<(), UpdateMetadataError> {
+ match d.index {
+ 0..=13 => AbstractMinecart::apply_metadata(entity, d)?,
_ => {}
}
- Some(())
- }
-}
-impl Deref for Mooshroom {
- type Target = Cow;
- fn deref(&self) -> &Self::Target {
- &self.cow
- }
-}
-impl DerefMut for Mooshroom {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.cow
- }
-}
-
-#[derive(Debug, Clone)]
-pub struct Mule {
- pub abstract_animal: AbstractAnimal,
- pub tamed: bool,
- pub eating: bool,
- pub standing: bool,
- pub bred: bool,
- pub saddled: bool,
- pub owner_uuid: Option<Uuid>,
- pub chest: bool,
-}
-
-impl Mule {
- pub fn read(metadata: &mut VecDeque<EntityDataValue>) -> Option<Self> {
- let abstract_animal = AbstractAnimal::read(metadata)?;
- let bitfield = metadata.pop_front()?.into_byte().ok()?;
- let tamed = bitfield & 0x2 != 0;
- let eating = bitfield & 0x10 != 0;
- let standing = bitfield & 0x20 != 0;
- let bred = bitfield & 0x8 != 0;
- let saddled = bitfield & 0x4 != 0;
- let owner_uuid = metadata.pop_front()?.into_optional_uuid().ok()?;
- let chest = metadata.pop_front()?.into_boolean().ok()?;
- Some(Self {
- abstract_animal,
- tamed,
- eating,
- standing,
- bred,
- saddled,
- owner_uuid,
- chest,
- })
- }
-
- pub fn write(&self) -> Vec<EntityDataValue> {
- let mut metadata = Vec::new();
- metadata.extend(self.abstract_animal.write());
- let mut bitfield = 0u8;
- if self.tamed {
- bitfield &= 0x2;
- }
- if self.eating {
- bitfield &= 0x10;
- }
- if self.standing {
- bitfield &= 0x20;
- }
- if self.bred {
- bitfield &= 0x8;
- }
- if self.saddled {
- bitfield &= 0x4;
+ Ok(())
+ }
+}
+
+#[derive(Bundle)]
+pub struct MinecartMetadataBundle {
+ _marker: Minecart,
+ parent: AbstractMinecartMetadataBundle,
+}
+impl Default for MinecartMetadataBundle {
+ fn default() -> Self {
+ Self {
+ _marker: Minecart,
+ parent: AbstractMinecartMetadataBundle {
+ _marker: AbstractMinecart,
+ 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),
+ },
+ abstract_minecart_hurt: AbstractMinecartHurt(0),
+ abstract_minecart_hurtdir: AbstractMinecartHurtdir(1),
+ abstract_minecart_damage: AbstractMinecartDamage(0.0),
+ display_block: DisplayBlock(Default::default()),
+ display_offset: DisplayOffset(6),
+ custom_display: CustomDisplay(false),
+ },
}
- metadata.push(EntityDataValue::Byte(bitfield));
- metadata.push(EntityDataValue::OptionalUuid(self.owner_uuid.clone()));
- metadata.push(EntityDataValue::Boolean(self.chest.clone()));
- metadata
}
}
-impl Default for Mule {
- fn default() -> Self {
- Self {
- abstract_animal: Default::default(),
- tamed: false,
- eating: false,
- standing: false,
- bred: false,
- saddled: false,
- owner_uuid: None,
- chest: false,
+#[derive(Component, Deref, DerefMut)]
+pub struct MooshroomKind(pub String);
+#[derive(Component)]
+pub struct Mooshroom;
+impl Mooshroom {
+ pub fn apply_metadata(
+ entity: &mut azalea_ecs::system::EntityCommands,
+ d: EntityDataItem,
+ ) -> Result<(), UpdateMetadataError> {
+ match d.index {
+ 0..=16 => Cow::apply_metadata(entity, d)?,
+ 17 => {
+ entity.insert(MooshroomKind(d.value.into_string()?));
+ }
+ _ => {}
}
- }
-}
-
+ Ok(())
+ }
+}
+
+#[derive(Bundle)]
+pub struct MooshroomMetadataBundle {
+ _marker: Mooshroom,
+ parent: CowMetadataBundle,
+ mooshroom_kind: MooshroomKind,
+}
+impl Default for MooshroomMetadataBundle {
+ fn default() -> Self {
+ Self {
+ _marker: Mooshroom,
+ parent: CowMetadataBundle {
+ _marker: Cow,
+ parent: AbstractAnimalMetadataBundle {
+ _marker: AbstractAnimal,
+ parent: AbstractAgeableMetadataBundle {
+ _marker: AbstractAgeable,
+ 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),
+ },
+ },
+ abstract_ageable_baby: AbstractAgeableBaby(false),
+ },
+ },
+ },
+ mooshroom_kind: MooshroomKind(Default::default()),
+ }
+ }
+}
+
+#[derive(Component, Deref, DerefMut)]
+pub struct MuleTamed(pub bool);
+#[derive(Component, Deref, DerefMut)]
+pub struct MuleEating(pub bool);
+#[derive(Component, Deref, DerefMut)]
+pub struct MuleStanding(pub bool);
+#[derive(Component, Deref, DerefMut)]
+pub struct MuleBred(pub bool);
+#[derive(Component, Deref, DerefMut)]
+pub struct MuleSaddled(pub bool);
+#[derive(Component, Deref, DerefMut)]
+pub struct MuleOwnerUuid(pub Option<Uuid>);
+#[derive(Component, Deref, DerefMut)]
+pub struct MuleChest(pub bool);
+#[derive(Component)]
+pub struct Mule;
impl Mule {
- pub fn set_index(&mut self, index: u8, value: EntityDataValue) -> Option<()> {
- match index {
- 0..=16 => self.abstract_animal.set_index(index, value)?,
+ pub fn apply_metadata(
+ entity: &mut azalea_ecs::system::EntityCommands,
+ d: EntityDataItem,
+ ) -> Result<(), UpdateMetadataError> {
+ match d.index {
+ 0..=16 => AbstractAnimal::apply_metadata(entity, d)?,
17 => {
- let bitfield = value.into_byte().ok()?;
- self.tamed = bitfield & 0x2 != 0;
- self.eating = bitfield & 0x10 != 0;
- self.standing = bitfield & 0x20 != 0;
- self.bred = bitfield & 0x8 != 0;
- self.saddled = bitfield & 0x4 != 0;
+ let bitfield = d.value.into_byte()?;
+ entity.insert(MuleTamed(bitfield & 0x2 != 0));
+ entity.insert(MuleEating(bitfield & 0x10 != 0));
+ entity.insert(MuleStanding(bitfield & 0x20 != 0));
+ entity.insert(MuleBred(bitfield & 0x8 != 0));
+ entity.insert(MuleSaddled(bitfield & 0x4 != 0));
+ }
+ 18 => {
+ entity.insert(MuleOwnerUuid(d.value.into_optional_uuid()?));
+ }
+ 19 => {
+ entity.insert(MuleChest(d.value.into_boolean()?));
}
- 18 => self.owner_uuid = value.into_optional_uuid().ok()?,
- 19 => self.chest = value.into_boolean().ok()?,
_ => {}
}
- Some(())
- }
-}
-impl Deref for Mule {
- type Target = AbstractAnimal;
- fn deref(&self) -> &Self::Target {
- &self.abstract_animal
- }
-}
-impl DerefMut for Mule {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.abstract_animal
- }
-}
-
-#[derive(Debug, Clone)]
-pub struct Ocelot {
- pub abstract_animal: AbstractAnimal,
- pub trusting: bool,
-}
-
-impl Ocelot {
- pub fn read(metadata: &mut VecDeque<EntityDataValue>) -> Option<Self> {
- let abstract_animal = AbstractAnimal::read(metadata)?;
- let trusting = metadata.pop_front()?.into_boolean().ok()?;
- Some(Self {
- abstract_animal,
- trusting,
- })
- }
-
- pub fn write(&self) -> Vec<EntityDataValue> {
- let mut metadata = Vec::new();
- metadata.extend(self.abstract_animal.write());
- metadata.push(EntityDataValue::Boolean(self.trusting.clone()));
- metadata
- }
-}
-
-impl Default for Ocelot {
- fn default() -> Self {
- Self {
- abstract_animal: Default::default(),
- trusting: false,
+ Ok(())
+ }
+}
+
+#[derive(Bundle)]
+pub struct MuleMetadataBundle {
+ _marker: Mule,
+ parent: AbstractAnimalMetadataBundle,
+ mule_tamed: MuleTamed,
+ mule_eating: MuleEating,
+ mule_standing: MuleStanding,
+ mule_bred: MuleBred,
+ mule_saddled: MuleSaddled,
+ mule_owner_uuid: MuleOwnerUuid,
+ mule_chest: MuleChest,
+}
+impl Default for MuleMetadataBundle {
+ fn default() -> Self {
+ Self {
+ _marker: Mule,
+ parent: AbstractAnimalMetadataBundle {
+ _marker: AbstractAnimal,
+ parent: AbstractAgeableMetadataBundle {
+ _marker: AbstractAgeable,
+ 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),
+ },
+ },
+ abstract_ageable_baby: AbstractAgeableBaby(false),
+ },
+ },
+ mule_tamed: MuleTamed(false),
+ mule_eating: MuleEating(false),
+ mule_standing: MuleStanding(false),
+ mule_bred: MuleBred(false),
+ mule_saddled: MuleSaddled(false),
+ mule_owner_uuid: MuleOwnerUuid(None),
+ mule_chest: MuleChest(false),
}
}
}
+#[derive(Component, Deref, DerefMut)]
+pub struct Trusting(pub bool);
+#[derive(Component)]
+pub struct Ocelot;
impl Ocelot {
- pub fn set_index(&mut self, index: u8, value: EntityDataValue) -> Option<()> {
- match index {
- 0..=16 => self.abstract_animal.set_index(index, value)?,
- 17 => self.trusting = value.into_boolean().ok()?,
+ pub fn apply_metadata(
+ entity: &mut azalea_ecs::system::EntityCommands,
+ d: EntityDataItem,
+ ) -> Result<(), UpdateMetadataError> {
+ match d.index {
+ 0..=16 => AbstractAnimal::apply_metadata(entity, d)?,
+ 17 => {
+ entity.insert(Trusting(d.value.into_boolean()?));
+ }
_ => {}
}
- Some(())
- }
-}
-impl Deref for Ocelot {
- type Target = AbstractAnimal;
- fn deref(&self) -> &Self::Target {
- &self.abstract_animal
- }
-}
-impl DerefMut for Ocelot {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.abstract_animal
- }
-}
-
-#[derive(Debug, Clone)]
-pub struct Painting {
- pub abstract_entity: AbstractEntity,
- pub painting_variant: azalea_registry::PaintingVariant,
-}
-
-impl Painting {
- pub fn read(metadata: &mut VecDeque<EntityDataValue>) -> Option<Self> {
- let abstract_entity = AbstractEntity::read(metadata)?;
- let painting_variant = metadata.pop_front()?.into_painting_variant().ok()?;
- Some(Self {
- abstract_entity,
- painting_variant,
- })
- }
-
- pub fn write(&self) -> Vec<EntityDataValue> {
- let mut metadata = Vec::new();
- metadata.extend(self.abstract_entity.write());
- metadata.push(EntityDataValue::PaintingVariant(
- self.painting_variant.clone(),
- ));
- metadata
- }
-}
-
-impl Default for Painting {
- fn default() -> Self {
- Self {
- abstract_entity: Default::default(),
- painting_variant: azalea_registry::PaintingVariant::Kebab,
+ Ok(())
+ }
+}
+
+#[derive(Bundle)]
+pub struct OcelotMetadataBundle {
+ _marker: Ocelot,
+ parent: AbstractAnimalMetadataBundle,
+ trusting: Trusting,
+}
+impl Default for OcelotMetadataBundle {
+ fn default() -> Self {
+ Self {
+ _marker: Ocelot,
+ parent: AbstractAnimalMetadataBundle {
+ _marker: AbstractAnimal,
+ parent: AbstractAgeableMetadataBundle {
+ _marker: AbstractAgeable,
+ 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),
+ },
+ },
+ abstract_ageable_baby: AbstractAgeableBaby(false),
+ },
+ },
+ trusting: Trusting(false),
}
}
}
+#[derive(Component, Deref, DerefMut)]
+pub struct PaintingVariant(pub azalea_registry::PaintingVariant);
+#[derive(Component)]
+pub struct Painting;
impl Painting {
- pub fn set_index(&mut self, index: u8, value: EntityDataValue) -> Option<()> {
- match index {
- 0..=7 => self.abstract_entity.set_index(index, value)?,
- 8 => self.painting_variant = value.into_painting_variant().ok()?,
+ pub fn apply_metadata(
+ entity: &mut azalea_ecs::system::EntityCommands,
+ d: EntityDataItem,
+ ) -> Result<(), UpdateMetadataError> {
+ match d.index {
+ 0..=7 => AbstractEntity::apply_metadata(entity, d)?,
+ 8 => {
+ entity.insert(PaintingVariant(d.value.into_painting_variant()?));
+ }
_ => {}
}
- Some(())
- }
-}
-impl Deref for Painting {
- type Target = AbstractEntity;
- fn deref(&self) -> &Self::Target {
- &self.abstract_entity
- }
-}
-impl DerefMut for Painting {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.abstract_entity
+ Ok(())
}
}
-#[derive(Debug, Clone)]
-pub struct Panda {
- pub abstract_animal: AbstractAnimal,
- pub unhappy_counter: i32,
- pub sneeze_counter: i32,
- pub eat_counter: i32,
- pub sneezing: bool,
- pub sitting: bool,
- pub on_back: bool,
- pub rolling: bool,
- pub hidden_gene: u8,
- pub flags: u8,
+#[derive(Bundle)]
+pub struct PaintingMetadataBundle {
+ _marker: Painting,
+ parent: AbstractEntityMetadataBundle,
+ painting_variant: PaintingVariant,
}
-
-impl Panda {
- pub fn read(metadata: &mut VecDeque<EntityDataValue>) -> Option<Self> {
- let abstract_animal = AbstractAnimal::read(metadata)?;
- let unhappy_counter = metadata.pop_front()?.into_int().ok()?;
- let sneeze_counter = metadata.pop_front()?.into_int().ok()?;
- let eat_counter = metadata.pop_front()?.into_int().ok()?;
- let bitfield = metadata.pop_front()?.into_byte().ok()?;
- let sneezing = bitfield & 0x2 != 0;
- let sitting = bitfield & 0x8 != 0;
- let on_back = bitfield & 0x10 != 0;
- let rolling = bitfield & 0x4 != 0;
- let hidden_gene = metadata.pop_front()?.into_byte().ok()?;
- let flags = metadata.pop_front()?.into_byte().ok()?;
- Some(Self {
- abstract_animal,
- unhappy_counter,
- sneeze_counter,
- eat_counter,
- sneezing,
- sitting,
- on_back,
- rolling,
- hidden_gene,
- flags,
- })
- }
-
- pub fn write(&self) -> Vec<EntityDataValue> {
- let mut metadata = Vec::new();
- metadata.extend(self.abstract_animal.write());
- metadata.push(EntityDataValue::Int(self.unhappy_counter.clone()));
- metadata.push(EntityDataValue::Int(self.sneeze_counter.clone()));
- metadata.push(EntityDataValue::Int(self.eat_counter.clone()));
- let mut bitfield = 0u8;
- if self.sneezing {
- bitfield &= 0x2;
- }
- if self.sitting {
- bitfield &= 0x8;
- }
- if self.on_back {
- bitfield &= 0x10;
- }
- if self.rolling {
- bitfield &= 0x4;
- }
- metadata.push(EntityDataValue::Byte(bitfield));
- metadata.push(EntityDataValue::Byte(self.hidden_gene.clone()));
- metadata.push(EntityDataValue::Byte(self.flags.clone()));
- metadata
- }
-}
-
-impl Default for Panda {
- fn default() -> Self {
- Self {
- abstract_animal: Default::default(),
- unhappy_counter: 0,
- sneeze_counter: 0,
- eat_counter: 0,
- sneezing: false,
- sitting: false,
- on_back: false,
- rolling: false,
- hidden_gene: 0,
- flags: 0,
- }
- }
-}
-
+impl Default for PaintingMetadataBundle {
+ fn default() -> Self {
+ Self {
+ _marker: Painting,
+ 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),
+ },
+ painting_variant: PaintingVariant(azalea_registry::PaintingVariant::Kebab),
+ }
+ }
+}
+
+#[derive(Component, Deref, DerefMut)]
+pub struct PandaUnhappyCounter(pub i32);
+#[derive(Component, Deref, DerefMut)]
+pub struct SneezeCounter(pub i32);
+#[derive(Component, Deref, DerefMut)]
+pub struct EatCounter(pub i32);
+#[derive(Component, Deref, DerefMut)]
+pub struct Sneezing(pub bool);
+#[derive(Component, Deref, DerefMut)]
+pub struct PandaSitting(pub bool);
+#[derive(Component, Deref, DerefMut)]
+pub struct OnBack(pub bool);
+#[derive(Component, Deref, DerefMut)]
+pub struct PandaRolling(pub bool);
+#[derive(Component, Deref, DerefMut)]
+pub struct HiddenGene(pub u8);
+#[derive(Component, Deref, DerefMut)]
+pub struct PandaFlags(pub u8);
+#[derive(Component)]
+pub struct Panda;
impl Panda {
- pub fn set_index(&mut self, index: u8, value: EntityDataValue) -> Option<()> {
- match index {
- 0..=16 => self.abstract_animal.set_index(index, value)?,
- 17 => self.unhappy_counter = value.into_int().ok()?,
- 18 => self.sneeze_counter = value.into_int().ok()?,
- 19 => self.eat_counter = value.into_int().ok()?,
+ pub fn apply_metadata(
+ entity: &mut azalea_ecs::system::EntityCommands,
+ d: EntityDataItem,
+ ) -> Result<(), UpdateMetadataError> {
+ match d.index {
+ 0..=16 => AbstractAnimal::apply_metadata(entity, d)?,
+ 17 => {
+ entity.insert(PandaUnhappyCounter(d.value.into_int()?));
+ }
+ 18 => {
+ entity.insert(SneezeCounter(d.value.into_int()?));
+ }
+ 19 => {
+ entity.insert(EatCounter(d.value.into_int()?));
+ }
20 => {
- let bitfield = value.into_byte().ok()?;
- self.sneezing = bitfield & 0x2 != 0;
- self.sitting = bitfield & 0x8 != 0;
- self.on_back = bitfield & 0x10 != 0;
- self.rolling = bitfield & 0x4 != 0;
+ let bitfield = d.value.into_byte()?;
+ entity.insert(Sneezing(bitfield & 0x2 != 0));
+ entity.insert(PandaSitting(bitfield & 0x8 != 0));
+ entity.insert(OnBack(bitfield & 0x10 != 0));
+ entity.insert(PandaRolling(bitfield & 0x4 != 0));
+ }
+ 21 => {
+ entity.insert(HiddenGene(d.value.into_byte()?));
+ }
+ 22 => {
+ entity.insert(PandaFlags(d.value.into_byte()?));
}
- 21 => self.hidden_gene = value.into_byte().ok()?,
- 22 => self.flags = value.into_byte().ok()?,
_ => {}
}
- Some(())
- }
-}
-impl Deref for Panda {
- type Target = AbstractAnimal;
- fn deref(&self) -> &Self::Target {
- &self.abstract_animal
- }
-}
-impl DerefMut for Panda {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.abstract_animal
- }
-}
-
-#[derive(Debug, Clone)]
-pub struct Parrot {
- pub abstract_tameable: AbstractTameable,
- pub variant: i32,
-}
-
-impl Parrot {
- pub fn read(metadata: &mut VecDeque<EntityDataValue>) -> Option<Self> {
- let abstract_tameable = AbstractTameable::read(metadata)?;
- let variant = metadata.pop_front()?.into_int().ok()?;
- Some(Self {
- abstract_tameable,
- variant,
- })
- }
-
- pub fn write(&self) -> Vec<EntityDataValue> {
- let mut metadata = Vec::new();
- metadata.extend(self.abstract_tameable.write());
- metadata.push(EntityDataValue::Int(self.variant.clone()));
- metadata
- }
-}
-
-impl Default for Parrot {
- fn default() -> Self {
- Self {
- abstract_tameable: Default::default(),
- variant: 0,
+ Ok(())
+ }
+}
+
+#[derive(Bundle)]
+pub struct PandaMetadataBundle {
+ _marker: Panda,
+ parent: AbstractAnimalMetadataBundle,
+ panda_unhappy_counter: PandaUnhappyCounter,
+ sneeze_counter: SneezeCounter,
+ eat_counter: EatCounter,
+ sneezing: Sneezing,
+ panda_sitting: PandaSitting,
+ on_back: OnBack,
+ panda_rolling: PandaRolling,
+ hidden_gene: HiddenGene,
+ panda_flags: PandaFlags,
+}
+impl Default for PandaMetadataBundle {
+ fn default() -> Self {
+ Self {
+ _marker: Panda,
+ parent: AbstractAnimalMetadataBundle {
+ _marker: AbstractAnimal,
+ parent: AbstractAgeableMetadataBundle {
+ _marker: AbstractAgeable,
+ 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),
+ },
+ },
+ abstract_ageable_baby: AbstractAgeableBaby(false),
+ },
+ },
+ panda_unhappy_counter: PandaUnhappyCounter(0),
+ sneeze_counter: SneezeCounter(0),
+ eat_counter: EatCounter(0),
+ sneezing: Sneezing(false),
+ panda_sitting: PandaSitting(false),
+ on_back: OnBack(false),
+ panda_rolling: PandaRolling(false),
+ hidden_gene: HiddenGene(0),
+ panda_flags: PandaFlags(0),
}
}
}
+#[derive(Component, Deref, DerefMut)]
+pub struct ParrotVariant(pub i32);
+#[derive(Component)]
+pub struct Parrot;
impl Parrot {
- pub fn set_index(&mut self, index: u8, value: EntityDataValue) -> Option<()> {
- match index {
- 0..=18 => self.abstract_tameable.set_index(index, value)?,
- 19 => self.variant = value.into_int().ok()?,
+ pub fn apply_metadata(
+ entity: &mut azalea_ecs::system::EntityCommands,
+ d: EntityDataItem,
+ ) -> Result<(), UpdateMetadataError> {
+ match d.index {
+ 0..=18 => AbstractTameable::apply_metadata(entity, d)?,
+ 19 => {
+ entity.insert(ParrotVariant(d.value.into_int()?));
+ }
_ => {}
}
- Some(())
- }
-}
-impl Deref for Parrot {
- type Target = AbstractTameable;
- fn deref(&self) -> &Self::Target {
- &self.abstract_tameable
- }
-}
-impl DerefMut for Parrot {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.abstract_tameable
- }
-}
-
-#[derive(Debug, Clone)]
-pub struct Phantom {
- pub abstract_insentient: AbstractInsentient,
- pub size: i32,
-}
-
-impl Phantom {
- pub fn read(metadata: &mut VecDeque<EntityDataValue>) -> Option<Self> {
- let abstract_insentient = AbstractInsentient::read(metadata)?;
- let size = metadata.pop_front()?.into_int().ok()?;
- Some(Self {
- abstract_insentient,
- size,
- })
- }
-
- pub fn write(&self) -> Vec<EntityDataValue> {
- let mut metadata = Vec::new();
- metadata.extend(self.abstract_insentient.write());
- metadata.push(EntityDataValue::Int(self.size.clone()));
- metadata
- }
-}
-
-impl Default for Phantom {
- fn default() -> Self {
- Self {
- abstract_insentient: Default::default(),
- size: 0,
+ Ok(())
+ }
+}
+
+#[derive(Bundle)]
+pub struct ParrotMetadataBundle {
+ _marker: Parrot,
+ parent: AbstractTameableMetadataBundle,
+ parrot_variant: ParrotVariant,
+}
+impl Default for ParrotMetadataBundle {
+ fn default() -> Self {
+ Self {
+ _marker: Parrot,
+ parent: AbstractTameableMetadataBundle {
+ _marker: AbstractTameable,
+ parent: AbstractAnimalMetadataBundle {
+ _marker: AbstractAnimal,
+ parent: AbstractAgeableMetadataBundle {
+ _marker: AbstractAgeable,
+ 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),
+ },
+ },
+ abstract_ageable_baby: AbstractAgeableBaby(false),
+ },
+ },
+ tame: Tame(false),
+ in_sitting_pose: InSittingPose(false),
+ owneruuid: Owneruuid(None),
+ },
+ parrot_variant: ParrotVariant(0),
}
}
}
+#[derive(Component, Deref, DerefMut)]
+pub struct PhantomSize(pub i32);
+#[derive(Component)]
+pub struct Phantom;
impl Phantom {
- pub fn set_index(&mut self, index: u8, value: EntityDataValue) -> Option<()> {
- match index {
- 0..=15 => self.abstract_insentient.set_index(index, value)?,
- 16 => self.size = value.into_int().ok()?,
+ pub fn apply_metadata(
+ entity: &mut azalea_ecs::system::EntityCommands,
+ d: EntityDataItem,
+ ) -> Result<(), UpdateMetadataError> {
+ match d.index {
+ 0..=15 => AbstractInsentient::apply_metadata(entity, d)?,
+ 16 => {
+ entity.insert(PhantomSize(d.value.into_int()?));
+ }
_ => {}
}
- Some(())
- }
-}
-impl Deref for Phantom {
- type Target = AbstractInsentient;
- fn deref(&self) -> &Self::Target {
- &self.abstract_insentient
- }
-}
-impl DerefMut for Phantom {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.abstract_insentient
- }
-}
-
-#[derive(Debug, Clone)]
-pub struct Pig {
- pub abstract_animal: AbstractAnimal,
- pub saddle: bool,
- pub boost_time: i32,
-}
-
-impl Pig {
- pub fn read(metadata: &mut VecDeque<EntityDataValue>) -> Option<Self> {
- let abstract_animal = AbstractAnimal::read(metadata)?;
- let saddle = metadata.pop_front()?.into_boolean().ok()?;
- let boost_time = metadata.pop_front()?.into_int().ok()?;
- Some(Self {
- abstract_animal,
- saddle,
- boost_time,
- })
- }
-
- pub fn write(&self) -> Vec<EntityDataValue> {
- let mut metadata = Vec::new();
- metadata.extend(self.abstract_animal.write());
- metadata.push(EntityDataValue::Boolean(self.saddle.clone()));
- metadata.push(EntityDataValue::Int(self.boost_time.clone()));
- metadata
- }
-}
-
-impl Default for Pig {
- fn default() -> Self {
- Self {
- abstract_animal: Default::default(),
- saddle: false,
- boost_time: 0,
+ Ok(())
+ }
+}
+
+#[derive(Bundle)]
+pub struct PhantomMetadataBundle {
+ _marker: Phantom,
+ parent: AbstractInsentientMetadataBundle,
+ phantom_size: PhantomSize,
+}
+impl Default for PhantomMetadataBundle {
+ fn default() -> Self {
+ Self {
+ _marker: Phantom,
+ 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),
+ },
+ phantom_size: PhantomSize(0),
}
}
}
+#[derive(Component, Deref, DerefMut)]
+pub struct PigSaddle(pub bool);
+#[derive(Component, Deref, DerefMut)]
+pub struct PigBoostTime(pub i32);
+#[derive(Component)]
+pub struct Pig;
impl Pig {
- pub fn set_index(&mut self, index: u8, value: EntityDataValue) -> Option<()> {
- match index {
- 0..=16 => self.abstract_animal.set_index(index, value)?,
- 17 => self.saddle = value.into_boolean().ok()?,
- 18 => self.boost_time = value.into_int().ok()?,
+ pub fn apply_metadata(
+ entity: &mut azalea_ecs::system::EntityCommands,
+ d: EntityDataItem,
+ ) -> Result<(), UpdateMetadataError> {
+ match d.index {
+ 0..=16 => AbstractAnimal::apply_metadata(entity, d)?,
+ 17 => {
+ entity.insert(PigSaddle(d.value.into_boolean()?));
+ }
+ 18 => {
+ entity.insert(PigBoostTime(d.value.into_int()?));
+ }
_ => {}
}
- Some(())
- }
-}
-impl Deref for Pig {
- type Target = AbstractAnimal;
- fn deref(&self) -> &Self::Target {
- &self.abstract_animal
- }
-}
-impl DerefMut for Pig {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.abstract_animal
- }
-}
-
-#[derive(Debug, Clone)]
-pub struct Piglin {
- pub abstract_monster: AbstractMonster,
- pub immune_to_zombification: bool,
- pub baby: bool,
- pub is_charging_crossbow: bool,
- pub is_dancing: bool,
-}
-
-impl Piglin {
- pub fn read(metadata: &mut VecDeque<EntityDataValue>) -> Option<Self> {
- let abstract_monster = AbstractMonster::read(metadata)?;
- let immune_to_zombification = metadata.pop_front()?.into_boolean().ok()?;
- let baby = metadata.pop_front()?.into_boolean().ok()?;
- let is_charging_crossbow = metadata.pop_front()?.into_boolean().ok()?;
- let is_dancing = metadata.pop_front()?.into_boolean().ok()?;
- Some(Self {
- abstract_monster,
- immune_to_zombification,
- baby,
- is_charging_crossbow,
- is_dancing,
- })
- }
-
- pub fn write(&self) -> Vec<EntityDataValue> {
- let mut metadata = Vec::new();
- metadata.extend(self.abstract_monster.write());
- metadata.push(EntityDataValue::Boolean(
- self.immune_to_zombification.clone(),
- ));
- metadata.push(EntityDataValue::Boolean(self.baby.clone()));
- metadata.push(EntityDataValue::Boolean(self.is_charging_crossbow.clone()));
- metadata.push(EntityDataValue::Boolean(self.is_dancing.clone()));
- metadata
- }
-}
-
-impl Default for Piglin {
- fn default() -> Self {
- Self {
- abstract_monster: Default::default(),
- immune_to_zombification: false,
- baby: false,
- is_charging_crossbow: false,
- is_dancing: false,
+ Ok(())
+ }
+}
+
+#[derive(Bundle)]
+pub struct PigMetadataBundle {
+ _marker: Pig,
+ parent: AbstractAnimalMetadataBundle,
+ pig_saddle: PigSaddle,
+ pig_boost_time: PigBoostTime,
+}
+impl Default for PigMetadataBundle {
+ fn default() -> Self {
+ Self {
+ _marker: Pig,
+ parent: AbstractAnimalMetadataBundle {
+ _marker: AbstractAnimal,
+ parent: AbstractAgeableMetadataBundle {
+ _marker: AbstractAgeable,
+ 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),
+ },
+ },
+ abstract_ageable_baby: AbstractAgeableBaby(false),
+ },
+ },
+ pig_saddle: PigSaddle(false),
+ pig_boost_time: PigBoostTime(0),
}
}
}
+#[derive(Component, Deref, DerefMut)]
+pub struct PiglinImmuneToZombification(pub bool);
+#[derive(Component, Deref, DerefMut)]
+pub struct PiglinBaby(pub bool);
+#[derive(Component, Deref, DerefMut)]
+pub struct PiglinIsChargingCrossbow(pub bool);
+#[derive(Component, Deref, DerefMut)]
+pub struct IsDancing(pub bool);
+#[derive(Component)]
+pub struct Piglin;
impl Piglin {
- pub fn set_index(&mut self, index: u8, value: EntityDataValue) -> Option<()> {
- match index {
- 0..=15 => self.abstract_monster.set_index(index, value)?,
- 16 => self.immune_to_zombification = value.into_boolean().ok()?,
- 17 => self.baby = value.into_boolean().ok()?,
- 18 => self.is_charging_crossbow = value.into_boolean().ok()?,
- 19 => self.is_dancing = value.into_boolean().ok()?,
+ pub fn apply_metadata(
+ entity: &mut azalea_ecs::system::EntityCommands,
+ d: EntityDataItem,
+ ) -> Result<(), UpdateMetadataError> {
+ match d.index {
+ 0..=15 => AbstractMonster::apply_metadata(entity, d)?,
+ 16 => {
+ entity.insert(PiglinImmuneToZombification(d.value.into_boolean()?));
+ }
+ 17 => {
+ entity.insert(PiglinBaby(d.value.into_boolean()?));
+ }
+ 18 => {
+ entity.insert(PiglinIsChargingCrossbow(d.value.into_boolean()?));
+ }
+ 19 => {
+ entity.insert(IsDancing(d.value.into_boolean()?));
+ }
_ => {}
}
- Some(())
- }
-}
-impl Deref for Piglin {
- type Target = AbstractMonster;
- fn deref(&self) -> &Self::Target {
- &self.abstract_monster
- }
-}
-impl DerefMut for Piglin {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.abstract_monster
- }
-}
-
-#[derive(Debug, Clone)]
-pub struct PiglinBrute {
- pub abstract_monster: AbstractMonster,
- pub immune_to_zombification: bool,
-}
-
-impl PiglinBrute {
- pub fn read(metadata: &mut VecDeque<EntityDataValue>) -> Option<Self> {
- let abstract_monster = AbstractMonster::read(metadata)?;
- let immune_to_zombification = metadata.pop_front()?.into_boolean().ok()?;
- Some(Self {
- abstract_monster,
- immune_to_zombification,
- })
- }
-
- pub fn write(&self) -> Vec<EntityDataValue> {
- let mut metadata = Vec::new();
- metadata.extend(self.abstract_monster.write());
- metadata.push(EntityDataValue::Boolean(
- self.immune_to_zombification.clone(),
- ));
- metadata
- }
-}
-
-impl Default for PiglinBrute {
- fn default() -> Self {
- Self {
- abstract_monster: Default::default(),
- immune_to_zombification: false,
+ Ok(())
+ }
+}
+
+#[derive(Bundle)]
+pub struct PiglinMetadataBundle {
+ _marker: Piglin,
+ parent: AbstractMonsterMetadataBundle,
+ piglin_immune_to_zombification: PiglinImmuneToZombification,
+ piglin_baby: PiglinBaby,
+ piglin_is_charging_crossbow: PiglinIsChargingCrossbow,
+ is_dancing: IsDancing,
+}
+impl Default for PiglinMetadataBundle {
+ fn default() -> Self {
+ Self {
+ _marker: Piglin,
+ 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),
+ },
+ },
+ },
+ piglin_immune_to_zombification: PiglinImmuneToZombification(false),
+ piglin_baby: PiglinBaby(false),
+ piglin_is_charging_crossbow: PiglinIsChargingCrossbow(false),
+ is_dancing: IsDancing(false),
}
}
}
+#[derive(Component, Deref, DerefMut)]
+pub struct PiglinBruteImmuneToZombification(pub bool);
+#[derive(Component)]
+pub struct PiglinBrute;
impl PiglinBrute {
- pub fn set_index(&mut self, index: u8, value: EntityDataValue) -> Option<()> {
- match index {
- 0..=15 => self.abstract_monster.set_index(index, value)?,
- 16 => self.immune_to_zombification = value.into_boolean().ok()?,
+ pub fn apply_metadata(
+ entity: &mut azalea_ecs::system::EntityCommands,
+ d: EntityDataItem,
+ ) -> Result<(), UpdateMetadataError> {
+ match d.index {
+ 0..=15 => AbstractMonster::apply_metadata(entity, d)?,
+ 16 => {
+ entity.insert(PiglinBruteImmuneToZombification(d.value.into_boolean()?));
+ }
_ => {}
}
- Some(())
- }
-}
-impl Deref for PiglinBrute {
- type Target = AbstractMonster;
- fn deref(&self) -> &Self::Target {
- &self.abstract_monster
- }
-}
-impl DerefMut for PiglinBrute {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.abstract_monster
- }
-}
-
-#[derive(Debug, Clone)]
-pub struct Pillager {
- pub abstract_monster: AbstractMonster,
- pub is_celebrating: bool,
- pub is_charging_crossbow: bool,
-}
-
-impl Pillager {
- pub fn read(metadata: &mut VecDeque<EntityDataValue>) -> Option<Self> {
- let abstract_monster = AbstractMonster::read(metadata)?;
- let is_celebrating = metadata.pop_front()?.into_boolean().ok()?;
- let is_charging_crossbow = metadata.pop_front()?.into_boolean().ok()?;
- Some(Self {
- abstract_monster,
- is_celebrating,
- is_charging_crossbow,
- })
- }
-
- pub fn write(&self) -> Vec<EntityDataValue> {
- let mut metadata = Vec::new();
- metadata.extend(self.abstract_monster.write());
- metadata.push(EntityDataValue::Boolean(self.is_celebrating.clone()));
- metadata.push(EntityDataValue::Boolean(self.is_charging_crossbow.clone()));
- metadata
- }
-}
-
-impl Default for Pillager {
- fn default() -> Self {
- Self {
- abstract_monster: Default::default(),
- is_celebrating: false,
- is_charging_crossbow: false,
+ Ok(())
+ }
+}
+
+#[derive(Bundle)]
+pub struct PiglinBruteMetadataBundle {
+ _marker: PiglinBrute,
+ parent: AbstractMonsterMetadataBundle,
+ piglin_brute_immune_to_zombification: PiglinBruteImmuneToZombification,
+}
+impl Default for PiglinBruteMetadataBundle {
+ fn default() -> Self {
+ Self {
+ _marker: PiglinBrute,
+ 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),
+ },
+ },
+ },
+ piglin_brute_immune_to_zombification: PiglinBruteImmuneToZombification(false),
}
}
}
+#[derive(Component, Deref, DerefMut)]
+pub struct PillagerIsCelebrating(pub bool);
+#[derive(Component, Deref, DerefMut)]
+pub struct PillagerIsChargingCrossbow(pub bool);
+#[derive(Component)]
+pub struct Pillager;
impl Pillager {
- pub fn set_index(&mut self, index: u8, value: EntityDataValue) -> Option<()> {
- match index {
- 0..=15 => self.abstract_monster.set_index(index, value)?,
- 16 => self.is_celebrating = value.into_boolean().ok()?,
- 17 => self.is_charging_crossbow = value.into_boolean().ok()?,
+ pub fn apply_metadata(
+ entity: &mut azalea_ecs::system::EntityCommands,
+ d: EntityDataItem,
+ ) -> Result<(), UpdateMetadataError> {
+ match d.index {
+ 0..=15 => AbstractMonster::apply_metadata(entity, d)?,
+ 16 => {
+ entity.insert(PillagerIsCelebrating(d.value.into_boolean()?));
+ }
+ 17 => {
+ entity.insert(PillagerIsChargingCrossbow(d.value.into_boolean()?));
+ }
_ => {}
}
- Some(())
- }
-}
-impl Deref for Pillager {
- type Target = AbstractMonster;
- fn deref(&self) -> &Self::Target {
- &self.abstract_monster
- }
-}
-impl DerefMut for Pillager {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.abstract_monster
- }
-}
-
-#[derive(Debug, Clone)]
-pub struct Player {
- pub abstract_living: AbstractLiving,
- pub player_absorption: f32,
- pub score: i32,
- pub player_mode_customisation: u8,
- pub player_main_hand: u8,
- pub shoulder_left: azalea_nbt::Tag,
- pub shoulder_right: azalea_nbt::Tag,
-}
-
-impl Player {
- pub fn read(metadata: &mut VecDeque<EntityDataValue>) -> Option<Self> {
- let abstract_living = AbstractLiving::read(metadata)?;
- let player_absorption = metadata.pop_front()?.into_float().ok()?;
- let score = metadata.pop_front()?.into_int().ok()?;
- let player_mode_customisation = metadata.pop_front()?.into_byte().ok()?;
- let player_main_hand = metadata.pop_front()?.into_byte().ok()?;
- let shoulder_left = metadata.pop_front()?.into_compound_tag().ok()?;
- let shoulder_right = metadata.pop_front()?.into_compound_tag().ok()?;
- Some(Self {
- abstract_living,
- player_absorption,
- score,
- player_mode_customisation,
- player_main_hand,
- shoulder_left,
- shoulder_right,
- })
- }
-
- pub fn write(&self) -> Vec<EntityDataValue> {
- let mut metadata = Vec::new();
- metadata.extend(self.abstract_living.write());
- metadata.push(EntityDataValue::Float(self.player_absorption.clone()));
- metadata.push(EntityDataValue::Int(self.score.clone()));
- metadata.push(EntityDataValue::Byte(
- self.player_mode_customisation.clone(),
- ));
- metadata.push(EntityDataValue::Byte(self.player_main_hand.clone()));
- metadata.push(EntityDataValue::CompoundTag(self.shoulder_left.clone()));
- metadata.push(EntityDataValue::CompoundTag(self.shoulder_right.clone()));
- metadata
- }
-}
-
-impl Default for Player {
- fn default() -> Self {
- Self {
- abstract_living: Default::default(),
- player_absorption: 0.0,
- score: 0,
- player_mode_customisation: 0,
- player_main_hand: 1,
- shoulder_left: azalea_nbt::Tag::Compound(Default::default()),
- shoulder_right: azalea_nbt::Tag::Compound(Default::default()),
- }
- }
-}
-
+ Ok(())
+ }
+}
+
+#[derive(Bundle)]
+pub struct PillagerMetadataBundle {
+ _marker: Pillager,
+ parent: AbstractMonsterMetadataBundle,
+ pillager_is_celebrating: PillagerIsCelebrating,
+ pillager_is_charging_crossbow: PillagerIsChargingCrossbow,
+}
+impl Default for PillagerMetadataBundle {
+ fn default() -> Self {
+ Self {
+ _marker: Pillager,
+ 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),
+ },
+ },
+ },
+ pillager_is_celebrating: PillagerIsCelebrating(false),
+ pillager_is_charging_crossbow: PillagerIsChargingCrossbow(false),
+ }
+ }
+}
+
+#[derive(Component, Deref, DerefMut)]
+pub struct PlayerAbsorption(pub f32);
+#[derive(Component, Deref, DerefMut)]
+pub struct Score(pub i32);
+#[derive(Component, Deref, DerefMut)]
+pub struct PlayerModeCustomisation(pub u8);
+#[derive(Component, Deref, DerefMut)]
+pub struct PlayerMainHand(pub u8);
+#[derive(Component, Deref, DerefMut)]
+pub struct ShoulderLeft(pub azalea_nbt::Tag);
+#[derive(Component, Deref, DerefMut)]
+pub struct ShoulderRight(pub azalea_nbt::Tag);
+#[derive(Component)]
+pub struct Player;
impl Player {
- pub fn set_index(&mut self, index: u8, value: EntityDataValue) -> Option<()> {
- match index {
- 0..=14 => self.abstract_living.set_index(index, value)?,
- 15 => self.player_absorption = value.into_float().ok()?,
- 16 => self.score = value.into_int().ok()?,
- 17 => self.player_mode_customisation = value.into_byte().ok()?,
- 18 => self.player_main_hand = value.into_byte().ok()?,
- 19 => self.shoulder_left = value.into_compound_tag().ok()?,
- 20 => self.shoulder_right = value.into_compound_tag().ok()?,
+ pub fn apply_metadata(
+ entity: &mut azalea_ecs::system::EntityCommands,
+ d: EntityDataItem,
+ ) -> Result<(), UpdateMetadataError> {
+ match d.index {
+ 0..=14 => AbstractLiving::apply_metadata(entity, d)?,
+ 15 => {
+ entity.insert(PlayerAbsorption(d.value.into_float()?));
+ }
+ 16 => {
+ entity.insert(Score(d.value.into_int()?));
+ }
+ 17 => {
+ entity.insert(PlayerModeCustomisation(d.value.into_byte()?));
+ }
+ 18 => {
+ entity.insert(PlayerMainHand(d.value.into_byte()?));
+ }
+ 19 => {
+ entity.insert(ShoulderLeft(d.value.into_compound_tag()?));
+ }
+ 20 => {
+ entity.insert(ShoulderRight(d.value.into_compound_tag()?));
+ }
_ => {}
}
- Some(())
- }
-}
-impl Deref for Player {
- type Target = AbstractLiving;
- fn deref(&self) -> &Self::Target {
- &self.abstract_living
- }
-}
-impl DerefMut for Player {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.abstract_living
- }
-}
-
-#[derive(Debug, Clone)]
-pub struct PolarBear {
- pub abstract_animal: AbstractAnimal,
- pub standing: bool,
-}
-
-impl PolarBear {
- pub fn read(metadata: &mut VecDeque<EntityDataValue>) -> Option<Self> {
- let abstract_animal = AbstractAnimal::read(metadata)?;
- let standing = metadata.pop_front()?.into_boolean().ok()?;
- Some(Self {
- abstract_animal,
- standing,
- })
- }
-
- pub fn write(&self) -> Vec<EntityDataValue> {
- let mut metadata = Vec::new();
- metadata.extend(self.abstract_animal.write());
- metadata.push(EntityDataValue::Boolean(self.standing.clone()));
- metadata
- }
-}
-
-impl Default for PolarBear {
- fn default() -> Self {
- Self {
- abstract_animal: Default::default(),
- standing: false,
+ Ok(())
+ }
+}
+
+#[derive(Bundle)]
+pub struct PlayerMetadataBundle {
+ _marker: Player,
+ parent: AbstractLivingMetadataBundle,
+ player_absorption: PlayerAbsorption,
+ score: Score,
+ player_mode_customisation: PlayerModeCustomisation,
+ player_main_hand: PlayerMainHand,
+ shoulder_left: ShoulderLeft,
+ shoulder_right: ShoulderRight,
+}
+impl Default for PlayerMetadataBundle {
+ fn default() -> Self {
+ Self {
+ _marker: Player,
+ 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),
+ },
+ player_absorption: PlayerAbsorption(0.0),
+ score: Score(0),
+ player_mode_customisation: PlayerModeCustomisation(0),
+ player_main_hand: PlayerMainHand(1),
+ shoulder_left: ShoulderLeft(azalea_nbt::Tag::Compound(Default::default())),
+ shoulder_right: ShoulderRight(azalea_nbt::Tag::Compound(Default::default())),
}
}
}
+#[derive(Component, Deref, DerefMut)]
+pub struct PolarBearStanding(pub bool);
+#[derive(Component)]
+pub struct PolarBear;
impl PolarBear {
- pub fn set_index(&mut self, index: u8, value: EntityDataValue) -> Option<()> {
- match index {
- 0..=16 => self.abstract_animal.set_index(index, value)?,
- 17 => self.standing = value.into_boolean().ok()?,
+ pub fn apply_metadata(
+ entity: &mut azalea_ecs::system::EntityCommands,
+ d: EntityDataItem,
+ ) -> Result<(), UpdateMetadataError> {
+ match d.index {
+ 0..=16 => AbstractAnimal::apply_metadata(entity, d)?,
+ 17 => {
+ entity.insert(PolarBearStanding(d.value.into_boolean()?));
+ }
_ => {}
}
- Some(())
- }
-}
-impl Deref for PolarBear {
- type Target = AbstractAnimal;
- fn deref(&self) -> &Self::Target {
- &self.abstract_animal
- }
-}
-impl DerefMut for PolarBear {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.abstract_animal
- }
-}
-
-#[derive(Debug, Clone)]
-pub struct Potion {
- pub abstract_entity: AbstractEntity,
- pub item_stack: Slot,
-}
-
-impl Potion {
- pub fn read(metadata: &mut VecDeque<EntityDataValue>) -> Option<Self> {
- let abstract_entity = AbstractEntity::read(metadata)?;
- let item_stack = metadata.pop_front()?.into_item_stack().ok()?;
- Some(Self {
- abstract_entity,
- item_stack,
- })
- }
-
- pub fn write(&self) -> Vec<EntityDataValue> {
- let mut metadata = Vec::new();
- metadata.extend(self.abstract_entity.write());
- metadata.push(EntityDataValue::ItemStack(self.item_stack.clone()));
- metadata
- }
-}
-
-impl Default for Potion {
- fn default() -> Self {
- Self {
- abstract_entity: Default::default(),
- item_stack: Slot::Empty,
+ Ok(())
+ }
+}
+
+#[derive(Bundle)]
+pub struct PolarBearMetadataBundle {
+ _marker: PolarBear,
+ parent: AbstractAnimalMetadataBundle,
+ polar_bear_standing: PolarBearStanding,
+}
+impl Default for PolarBearMetadataBundle {
+ fn default() -> Self {
+ Self {
+ _marker: PolarBear,
+ parent: AbstractAnimalMetadataBundle {
+ _marker: AbstractAnimal,
+ parent: AbstractAgeableMetadataBundle {
+ _marker: AbstractAgeable,
+ 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),
+ },
+ },
+ abstract_ageable_baby: AbstractAgeableBaby(false),
+ },
+ },
+ polar_bear_standing: PolarBearStanding(false),
}
}
}
+#[derive(Component, Deref, DerefMut)]
+pub struct PotionItemStack(pub Slot);
+#[derive(Component)]
+pub struct Potion;
impl Potion {
- pub fn set_index(&mut self, index: u8, value: EntityDataValue) -> Option<()> {
- match index {
- 0..=7 => self.abstract_entity.set_index(index, value)?,
- 8 => self.item_stack = value.into_item_stack().ok()?,
+ pub fn apply_metadata(
+ entity: &mut azalea_ecs::system::EntityCommands,
+ d: EntityDataItem,
+ ) -> Result<(), UpdateMetadataError> {
+ match d.index {
+ 0..=7 => AbstractEntity::apply_metadata(entity, d)?,
+ 8 => {
+ entity.insert(PotionItemStack(d.value.into_item_stack()?));
+ }
_ => {}
}
- Some(())
- }
-}
-impl Deref for Potion {
- type Target = AbstractEntity;
- fn deref(&self) -> &Self::Target {
- &self.abstract_entity
- }
-}
-impl DerefMut for Potion {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.abstract_entity
+ Ok(())
}
}
-#[derive(Debug, Clone)]
-pub struct Pufferfish {
- pub abstract_creature: AbstractCreature,
- pub from_bucket: bool,
- pub puff_state: i32,
+#[derive(Bundle)]
+pub struct PotionMetadataBundle {
+ _marker: Potion,
+ parent: AbstractEntityMetadataBundle,
+ potion_item_stack: PotionItemStack,
}
-
-impl Pufferfish {
- pub fn read(metadata: &mut VecDeque<EntityDataValue>) -> Option<Self> {
- let abstract_creature = AbstractCreature::read(metadata)?;
- let from_bucket = metadata.pop_front()?.into_boolean().ok()?;
- let puff_state = metadata.pop_front()?.into_int().ok()?;
- Some(Self {
- abstract_creature,
- from_bucket,
- puff_state,
- })
- }
-
- pub fn write(&self) -> Vec<EntityDataValue> {
- let mut metadata = Vec::new();
- metadata.extend(self.abstract_creature.write());
- metadata.push(EntityDataValue::Boolean(self.from_bucket.clone()));
- metadata.push(EntityDataValue::Int(self.puff_state.clone()));
- metadata
- }
-}
-
-impl Default for Pufferfish {
+impl Default for PotionMetadataBundle {
fn default() -> Self {
Self {
- abstract_creature: Default::default(),
- from_bucket: false,
- puff_state: 0,
+ _marker: Potion,
+ 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),
+ },
+ potion_item_stack: PotionItemStack(Slot::Empty),
}
}
}
+#[derive(Component, Deref, DerefMut)]
+pub struct PufferfishFromBucket(pub bool);
+#[derive(Component, Deref, DerefMut)]
+pub struct PuffState(pub i32);
+#[derive(Component)]
+pub struct Pufferfish;
impl Pufferfish {
- pub fn set_index(&mut self, index: u8, value: EntityDataValue) -> Option<()> {
- match index {
- 0..=15 => self.abstract_creature.set_index(index, value)?,
- 16 => self.from_bucket = value.into_boolean().ok()?,
- 17 => self.puff_state = value.into_int().ok()?,
+ pub fn apply_metadata(
+ entity: &mut azalea_ecs::system::EntityCommands,
+ d: EntityDataItem,
+ ) -> Result<(), UpdateMetadataError> {
+ match d.index {
+ 0..=15 => AbstractCreature::apply_metadata(entity, d)?,
+ 16 => {
+ entity.insert(PufferfishFromBucket(d.value.into_boolean()?));
+ }
+ 17 => {
+ entity.insert(PuffState(d.value.into_int()?));
+ }
_ => {}
}
- Some(())
- }
-}
-impl Deref for Pufferfish {
- type Target = AbstractCreature;
- fn deref(&self) -> &Self::Target {
- &self.abstract_creature
- }
-}
-impl DerefMut for Pufferfish {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.abstract_creature
- }
-}
-
-#[derive(Debug, Clone)]
-pub struct Rabbit {
- pub abstract_animal: AbstractAnimal,
- pub kind: i32,
-}
-
-impl Rabbit {
- pub fn read(metadata: &mut VecDeque<EntityDataValue>) -> Option<Self> {
- let abstract_animal = AbstractAnimal::read(metadata)?;
- let kind = metadata.pop_front()?.into_int().ok()?;
- Some(Self {
- abstract_animal,
- kind,
- })
- }
-
- pub fn write(&self) -> Vec<EntityDataValue> {
- let mut metadata = Vec::new();
- metadata.extend(self.abstract_animal.write());
- metadata.push(EntityDataValue::Int(self.kind.clone()));
- metadata
- }
-}
-
-impl Default for Rabbit {
- fn default() -> Self {
- Self {
- abstract_animal: Default::default(),
- kind: 0,
+ Ok(())
+ }
+}
+
+#[derive(Bundle)]
+pub struct PufferfishMetadataBundle {
+ _marker: Pufferfish,
+ parent: AbstractCreatureMetadataBundle,
+ pufferfish_from_bucket: PufferfishFromBucket,
+ puff_state: PuffState,
+}
+impl Default for PufferfishMetadataBundle {
+ fn default() -> Self {
+ Self {
+ _marker: Pufferfish,
+ 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),
+ },
+ },
+ pufferfish_from_bucket: PufferfishFromBucket(false),
+ puff_state: PuffState(0),
}
}
}
+#[derive(Component, Deref, DerefMut)]
+pub struct RabbitKind(pub i32);
+#[derive(Component)]
+pub struct Rabbit;
impl Rabbit {
- pub fn set_index(&mut self, index: u8, value: EntityDataValue) -> Option<()> {
- match index {
- 0..=16 => self.abstract_animal.set_index(index, value)?,
- 17 => self.kind = value.into_int().ok()?,
+ pub fn apply_metadata(
+ entity: &mut azalea_ecs::system::EntityCommands,
+ d: EntityDataItem,
+ ) -> Result<(), UpdateMetadataError> {
+ match d.index {
+ 0..=16 => AbstractAnimal::apply_metadata(entity, d)?,
+ 17 => {
+ entity.insert(RabbitKind(d.value.into_int()?));
+ }
_ => {}
}
- Some(())
- }
-}
-impl Deref for Rabbit {
- type Target = AbstractAnimal;
- fn deref(&self) -> &Self::Target {
- &self.abstract_animal
- }
-}
-impl DerefMut for Rabbit {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.abstract_animal
- }
-}
-
-#[derive(Debug, Clone)]
-pub struct Ravager {
- pub abstract_monster: AbstractMonster,
- pub is_celebrating: bool,
-}
-
-impl Ravager {
- pub fn read(metadata: &mut VecDeque<EntityDataValue>) -> Option<Self> {
- let abstract_monster = AbstractMonster::read(metadata)?;
- let is_celebrating = metadata.pop_front()?.into_boolean().ok()?;
- Some(Self {
- abstract_monster,
- is_celebrating,
- })
- }
-
- pub fn write(&self) -> Vec<EntityDataValue> {
- let mut metadata = Vec::new();
- metadata.extend(self.abstract_monster.write());
- metadata.push(EntityDataValue::Boolean(self.is_celebrating.clone()));
- metadata
- }
-}
-
-impl Default for Ravager {
- fn default() -> Self {
- Self {
- abstract_monster: Default::default(),
- is_celebrating: false,
+ Ok(())
+ }
+}
+
+#[derive(Bundle)]
+pub struct RabbitMetadataBundle {
+ _marker: Rabbit,
+ parent: AbstractAnimalMetadataBundle,
+ rabbit_kind: RabbitKind,
+}
+impl Default for RabbitMetadataBundle {
+ fn default() -> Self {
+ Self {
+ _marker: Rabbit,
+ parent: AbstractAnimalMetadataBundle {
+ _marker: AbstractAnimal,
+ parent: AbstractAgeableMetadataBundle {
+ _marker: AbstractAgeable,
+ 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),
+ },
+ },
+ abstract_ageable_baby: AbstractAgeableBaby(false),
+ },
+ },
+ rabbit_kind: RabbitKind(Default::default()),
}
}
}
+#[derive(Component, Deref, DerefMut)]
+pub struct RavagerIsCelebrating(pub bool);
+#[derive(Component)]
+pub struct Ravager;
impl Ravager {
- pub fn set_index(&mut self, index: u8, value: EntityDataValue) -> Option<()> {
- match index {
- 0..=15 => self.abstract_monster.set_index(index, value)?,
- 16 => self.is_celebrating = value.into_boolean().ok()?,
+ pub fn apply_metadata(
+ entity: &mut azalea_ecs::system::EntityCommands,
+ d: EntityDataItem,
+ ) -> Result<(), UpdateMetadataError> {
+ match d.index {
+ 0..=15 => AbstractMonster::apply_metadata(entity, d)?,
+ 16 => {
+ entity.insert(RavagerIsCelebrating(d.value.into_boolean()?));
+ }
_ => {}
}
- Some(())
- }
-}
-impl Deref for Ravager {
- type Target = AbstractMonster;
- fn deref(&self) -> &Self::Target {
- &self.abstract_monster
- }
-}
-impl DerefMut for Ravager {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.abstract_monster
- }
-}
-
-#[derive(Debug, Clone)]
-pub struct Salmon {
- pub abstract_creature: AbstractCreature,
- pub from_bucket: bool,
-}
-
-impl Salmon {
- pub fn read(metadata: &mut VecDeque<EntityDataValue>) -> Option<Self> {
- let abstract_creature = AbstractCreature::read(metadata)?;
- let from_bucket = metadata.pop_front()?.into_boolean().ok()?;
- Some(Self {
- abstract_creature,
- from_bucket,
- })
- }
-
- pub fn write(&self) -> Vec<EntityDataValue> {
- let mut metadata = Vec::new();
- metadata.extend(self.abstract_creature.write());
- metadata.push(EntityDataValue::Boolean(self.from_bucket.clone()));
- metadata
- }
-}
-
-impl Default for Salmon {
- fn default() -> Self {
- Self {
- abstract_creature: Default::default(),
- from_bucket: false,
+ Ok(())
+ }
+}
+
+#[derive(Bundle)]
+pub struct RavagerMetadataBundle {
+ _marker: Ravager,
+ parent: AbstractMonsterMetadataBundle,
+ ravager_is_celebrating: RavagerIsCelebrating,
+}
+impl Default for RavagerMetadataBundle {
+ fn default() -> Self {
+ Self {
+ _marker: Ravager,
+ 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),
+ },
+ },
+ },
+ ravager_is_celebrating: RavagerIsCelebrating(false),
}
}
}
+#[derive(Component, Deref, DerefMut)]
+pub struct SalmonFromBucket(pub bool);
+#[derive(Component)]
+pub struct Salmon;
impl Salmon {
- pub fn set_index(&mut self, index: u8, value: EntityDataValue) -> Option<()> {
- match index {
- 0..=15 => self.abstract_creature.set_index(index, value)?,
- 16 => self.from_bucket = value.into_boolean().ok()?,
+ pub fn apply_metadata(
+ entity: &mut azalea_ecs::system::EntityCommands,
+ d: EntityDataItem,
+ ) -> Result<(), UpdateMetadataError> {
+ match d.index {
+ 0..=15 => AbstractCreature::apply_metadata(entity, d)?,
+ 16 => {
+ entity.insert(SalmonFromBucket(d.value.into_boolean()?));
+ }
_ => {}
}
- Some(())
- }
-}
-impl Deref for Salmon {
- type Target = AbstractCreature;
- fn deref(&self) -> &Self::Target {
- &self.abstract_creature
- }
-}
-impl DerefMut for Salmon {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.abstract_creature
- }
-}
-
-#[derive(Debug, Clone)]
-pub struct Sheep {
- pub abstract_animal: AbstractAnimal,
- pub sheared: bool,
-}
-
-impl Sheep {
- pub fn read(metadata: &mut VecDeque<EntityDataValue>) -> Option<Self> {
- let abstract_animal = AbstractAnimal::read(metadata)?;
- let bitfield = metadata.pop_front()?.into_byte().ok()?;
- let sheared = bitfield & 0x10 != 0;
- Some(Self {
- abstract_animal,
- sheared,
- })
- }
-
- pub fn write(&self) -> Vec<EntityDataValue> {
- let mut metadata = Vec::new();
- metadata.extend(self.abstract_animal.write());
- let mut bitfield = 0u8;
- if self.sheared {
- bitfield &= 0x10;
- }
- metadata.push(EntityDataValue::Byte(bitfield));
- metadata
- }
-}
-
-impl Default for Sheep {
- fn default() -> Self {
- Self {
- abstract_animal: Default::default(),
- sheared: false,
+ Ok(())
+ }
+}
+
+#[derive(Bundle)]
+pub struct SalmonMetadataBundle {
+ _marker: Salmon,
+ parent: AbstractCreatureMetadataBundle,
+ salmon_from_bucket: SalmonFromBucket,
+}
+impl Default for SalmonMetadataBundle {
+ fn default() -> Self {
+ Self {
+ _marker: Salmon,
+ 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),
+ },
+ },
+ salmon_from_bucket: SalmonFromBucket(false),
}
}
}
+#[derive(Component, Deref, DerefMut)]
+pub struct Sheared(pub bool);
+#[derive(Component)]
+pub struct Sheep;
impl Sheep {
- pub fn set_index(&mut self, index: u8, value: EntityDataValue) -> Option<()> {
- match index {
- 0..=16 => self.abstract_animal.set_index(index, value)?,
+ pub fn apply_metadata(
+ entity: &mut azalea_ecs::system::EntityCommands,
+ d: EntityDataItem,
+ ) -> Result<(), UpdateMetadataError> {
+ match d.index {
+ 0..=16 => AbstractAnimal::apply_metadata(entity, d)?,
17 => {
- let bitfield = value.into_byte().ok()?;
- self.sheared = bitfield & 0x10 != 0;
+ let bitfield = d.value.into_byte()?;
+ entity.insert(Sheared(bitfield & 0x10 != 0));
}
_ => {}
}
- Some(())
- }
-}
-impl Deref for Sheep {
- type Target = AbstractAnimal;
- fn deref(&self) -> &Self::Target {
- &self.abstract_animal
- }
-}
-impl DerefMut for Sheep {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.abstract_animal
- }
-}
-
-#[derive(Debug, Clone)]
-pub struct Shulker {
- pub abstract_creature: AbstractCreature,
- pub attach_face: Direction,
- pub peek: u8,
- pub color: u8,
-}
-
-impl Shulker {
- pub fn read(metadata: &mut VecDeque<EntityDataValue>) -> Option<Self> {
- let abstract_creature = AbstractCreature::read(metadata)?;
- let attach_face = metadata.pop_front()?.into_direction().ok()?;
- let peek = metadata.pop_front()?.into_byte().ok()?;
- let color = metadata.pop_front()?.into_byte().ok()?;
- Some(Self {
- abstract_creature,
- attach_face,
- peek,
- color,
- })
- }
-
- pub fn write(&self) -> Vec<EntityDataValue> {
- let mut metadata = Vec::new();
- metadata.extend(self.abstract_creature.write());
- metadata.push(EntityDataValue::Direction(self.attach_face.clone()));
- metadata.push(EntityDataValue::Byte(self.peek.clone()));
- metadata.push(EntityDataValue::Byte(self.color.clone()));
- metadata
- }
-}
-
-impl Default for Shulker {
- fn default() -> Self {
- Self {
- abstract_creature: Default::default(),
- attach_face: Default::default(),
- peek: 0,
- color: 16,
+ Ok(())
+ }
+}
+
+#[derive(Bundle)]
+pub struct SheepMetadataBundle {
+ _marker: Sheep,
+ parent: AbstractAnimalMetadataBundle,
+ sheared: Sheared,
+}
+impl Default for SheepMetadataBundle {
+ fn default() -> Self {
+ Self {
+ _marker: Sheep,
+ parent: AbstractAnimalMetadataBundle {
+ _marker: AbstractAnimal,
+ parent: AbstractAgeableMetadataBundle {
+ _marker: AbstractAgeable,
+ 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),
+ },
+ },
+ abstract_ageable_baby: AbstractAgeableBaby(false),
+ },
+ },
+ sheared: Sheared(false),
}
}
}
+#[derive(Component, Deref, DerefMut)]
+pub struct AttachFace(pub Direction);
+#[derive(Component, Deref, DerefMut)]
+pub struct Peek(pub u8);
+#[derive(Component, Deref, DerefMut)]
+pub struct ShulkerColor(pub u8);
+#[derive(Component)]
+pub struct Shulker;
impl Shulker {
- pub fn set_index(&mut self, index: u8, value: EntityDataValue) -> Option<()> {
- match index {
- 0..=15 => self.abstract_creature.set_index(index, value)?,
- 16 => self.attach_face = value.into_direction().ok()?,
- 17 => self.peek = value.into_byte().ok()?,
- 18 => self.color = value.into_byte().ok()?,
+ pub fn apply_metadata(
+ entity: &mut azalea_ecs::system::EntityCommands,
+ d: EntityDataItem,
+ ) -> Result<(), UpdateMetadataError> {
+ match d.index {
+ 0..=15 => AbstractCreature::apply_metadata(entity, d)?,
+ 16 => {
+ entity.insert(AttachFace(d.value.into_direction()?));
+ }
+ 17 => {
+ entity.insert(Peek(d.value.into_byte()?));
+ }
+ 18 => {
+ entity.insert(ShulkerColor(d.value.into_byte()?));
+ }
_ => {}
}
- Some(())
- }
-}
-impl Deref for Shulker {
- type Target = AbstractCreature;
- fn deref(&self) -> &Self::Target {
- &self.abstract_creature
- }
-}
-impl DerefMut for Shulker {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.abstract_creature
- }
-}
-
-#[derive(Debug, Clone)]
-pub struct ShulkerBullet {
- pub abstract_entity: AbstractEntity,
-}
-
-impl ShulkerBullet {
- pub fn read(metadata: &mut VecDeque<EntityDataValue>) -> Option<Self> {
- let abstract_entity = AbstractEntity::read(metadata)?;
- Some(Self { abstract_entity })
- }
-
- pub fn write(&self) -> Vec<EntityDataValue> {
- let mut metadata = Vec::new();
- metadata.extend(self.abstract_entity.write());
- metadata
- }
-}
-
-impl Default for ShulkerBullet {
- fn default() -> Self {
- Self {
- abstract_entity: Default::default(),
+ Ok(())
+ }
+}
+
+#[derive(Bundle)]
+pub struct ShulkerMetadataBundle {
+ _marker: Shulker,
+ parent: AbstractCreatureMetadataBundle,
+ attach_face: AttachFace,
+ peek: Peek,
+ shulker_color: ShulkerColor,
+}
+impl Default for ShulkerMetadataBundle {
+ fn default() -> Self {
+ Self {
+ _marker: Shulker,
+ 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),
+ },
+ },
+ attach_face: AttachFace(Default::default()),
+ peek: Peek(0),
+ shulker_color: ShulkerColor(16),
}
}
}
+#[derive(Component)]
+pub struct ShulkerBullet;
impl ShulkerBullet {
- pub fn set_index(&mut self, index: u8, value: EntityDataValue) -> Option<()> {
- self.abstract_entity.set_index(index, value)
- }
-}
-impl Deref for ShulkerBullet {
- type Target = AbstractEntity;
- fn deref(&self) -> &Self::Target {
- &self.abstract_entity
- }
-}
-impl DerefMut for ShulkerBullet {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.abstract_entity
+ pub fn apply_metadata(
+ entity: &mut azalea_ecs::system::EntityCommands,
+ d: EntityDataItem,
+ ) -> Result<(), UpdateMetadataError> {
+ match d.index {
+ 0..=7 => AbstractEntity::apply_metadata(entity, d)?,
+ _ => {}
+ }
+ Ok(())
}
}
-#[derive(Debug, Clone)]
-pub struct Silverfish {
- pub abstract_monster: AbstractMonster,
+#[derive(Bundle)]
+pub struct ShulkerBulletMetadataBundle {
+ _marker: ShulkerBullet,
+ parent: AbstractEntityMetadataBundle,
}
-
-impl Silverfish {
- pub fn read(metadata: &mut VecDeque<EntityDataValue>) -> Option<Self> {
- let abstract_monster = AbstractMonster::read(metadata)?;
- Some(Self { abstract_monster })
- }
-
- pub fn write(&self) -> Vec<EntityDataValue> {
- let mut metadata = Vec::new();
- metadata.extend(self.abstract_monster.write());
- metadata
- }
-}
-
-impl Default for Silverfish {
+impl Default for ShulkerBulletMetadataBundle {
fn default() -> Self {
Self {
- abstract_monster: Default::default(),
+ _marker: ShulkerBullet,
+ 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)]
+pub struct Silverfish;
impl Silverfish {
- pub fn set_index(&mut self, index: u8, value: EntityDataValue) -> Option<()> {
- self.abstract_monster.set_index(index, value)
- }
-}
-impl Deref for Silverfish {
- type Target = AbstractMonster;
- fn deref(&self) -> &Self::Target {
- &self.abstract_monster
- }
-}
-impl DerefMut for Silverfish {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.abstract_monster
- }
-}
-
-#[derive(Debug, Clone)]
-pub struct Skeleton {
- pub abstract_monster: AbstractMonster,
- pub stray_conversion: bool,
-}
-
-impl Skeleton {
- pub fn read(metadata: &mut VecDeque<EntityDataValue>) -> Option<Self> {
- let abstract_monster = AbstractMonster::read(metadata)?;
- let stray_conversion = metadata.pop_front()?.into_boolean().ok()?;
- Some(Self {
- abstract_monster,
- stray_conversion,
- })
- }
-
- pub fn write(&self) -> Vec<EntityDataValue> {
- let mut metadata = Vec::new();
- metadata.extend(self.abstract_monster.write());
- metadata.push(EntityDataValue::Boolean(self.stray_conversion.clone()));
- metadata
- }
-}
-
-impl Default for Skeleton {
- fn default() -> Self {
- Self {
- abstract_monster: Default::default(),
- stray_conversion: false,
+ pub fn apply_metadata(
+ entity: &mut azalea_ecs::system::EntityCommands,
+ d: EntityDataItem,
+ ) -> Result<(), UpdateMetadataError> {
+ match d.index {
+ 0..=15 => AbstractMonster::apply_metadata(entity, d)?,
+ _ => {}
+ }
+ Ok(())
+ }
+}
+
+#[derive(Bundle)]
+pub struct SilverfishMetadataBundle {
+ _marker: Silverfish,
+ parent: AbstractMonsterMetadataBundle,
+}
+impl Default for SilverfishMetadataBundle {
+ fn default() -> Self {
+ Self {
+ _marker: Silverfish,
+ 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)]
+pub struct StrayConversion(pub bool);
+#[derive(Component)]
+pub struct Skeleton;
impl Skeleton {
- pub fn set_index(&mut self, index: u8, value: EntityDataValue) -> Option<()> {
- match index {
- 0..=15 => self.abstract_monster.set_index(index, value)?,
- 16 => self.stray_conversion = value.into_boolean().ok()?,
+ pub fn apply_metadata(
+ entity: &mut azalea_ecs::system::EntityCommands,
+ d: EntityDataItem,
+ ) -> Result<(), UpdateMetadataError> {
+ match d.index {
+ 0..=15 => AbstractMonster::apply_metadata(entity, d)?,
+ 16 => {
+ entity.insert(StrayConversion(d.value.into_boolean()?));
+ }
_ => {}
}
- Some(())
- }
-}
-impl Deref for Skeleton {
- type Target = AbstractMonster;
- fn deref(&self) -> &Self::Target {
- &self.abstract_monster
- }
-}
-impl DerefMut for Skeleton {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.abstract_monster
- }
-}
-
-#[derive(Debug, Clone)]
-pub struct SkeletonHorse {
- pub abstract_animal: AbstractAnimal,
- pub tamed: bool,
- pub eating: bool,
- pub standing: bool,
- pub bred: bool,
- pub saddled: bool,
- pub owner_uuid: Option<Uuid>,
-}
-
-impl SkeletonHorse {
- pub fn read(metadata: &mut VecDeque<EntityDataValue>) -> Option<Self> {
- let abstract_animal = AbstractAnimal::read(metadata)?;
- let bitfield = metadata.pop_front()?.into_byte().ok()?;
- let tamed = bitfield & 0x2 != 0;
- let eating = bitfield & 0x10 != 0;
- let standing = bitfield & 0x20 != 0;
- let bred = bitfield & 0x8 != 0;
- let saddled = bitfield & 0x4 != 0;
- let owner_uuid = metadata.pop_front()?.into_optional_uuid().ok()?;
- Some(Self {
- abstract_animal,
- tamed,
- eating,
- standing,
- bred,
- saddled,
- owner_uuid,
- })
- }
-
- pub fn write(&self) -> Vec<EntityDataValue> {
- let mut metadata = Vec::new();
- metadata.extend(self.abstract_animal.write());
- let mut bitfield = 0u8;
- if self.tamed {
- bitfield &= 0x2;
- }
- if self.eating {
- bitfield &= 0x10;
- }
- if self.standing {
- bitfield &= 0x20;
- }
- if self.bred {
- bitfield &= 0x8;
- }
- if self.saddled {
- bitfield &= 0x4;
- }
- metadata.push(EntityDataValue::Byte(bitfield));
- metadata.push(EntityDataValue::OptionalUuid(self.owner_uuid.clone()));
- metadata
- }
-}
-
-impl Default for SkeletonHorse {
- fn default() -> Self {
- Self {
- abstract_animal: Default::default(),
- tamed: false,
- eating: false,
- standing: false,
- bred: false,
- saddled: false,
- owner_uuid: None,
+ Ok(())
+ }
+}
+
+#[derive(Bundle)]
+pub struct SkeletonMetadataBundle {
+ _marker: Skeleton,
+ parent: AbstractMonsterMetadataBundle,
+ stray_conversion: StrayConversion,
+}
+impl Default for SkeletonMetadataBundle {
+ fn default() -> Self {
+ Self {
+ _marker: Skeleton,
+ 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),
+ },
+ },
+ },
+ stray_conversion: StrayConversion(false),
}
}
}
+#[derive(Component, Deref, DerefMut)]
+pub struct SkeletonHorseTamed(pub bool);
+#[derive(Component, Deref, DerefMut)]
+pub struct SkeletonHorseEating(pub bool);
+#[derive(Component, Deref, DerefMut)]
+pub struct SkeletonHorseStanding(pub bool);
+#[derive(Component, Deref, DerefMut)]
+pub struct SkeletonHorseBred(pub bool);
+#[derive(Component, Deref, DerefMut)]
+pub struct SkeletonHorseSaddled(pub bool);
+#[derive(Component, Deref, DerefMut)]
+pub struct SkeletonHorseOwnerUuid(pub Option<Uuid>);
+#[derive(Component)]
+pub struct SkeletonHorse;
impl SkeletonHorse {
- pub fn set_index(&mut self, index: u8, value: EntityDataValue) -> Option<()> {
- match index {
- 0..=16 => self.abstract_animal.set_index(index, value)?,
+ pub fn apply_metadata(
+ entity: &mut azalea_ecs::system::EntityCommands,
+ d: EntityDataItem,
+ ) -> Result<(), UpdateMetadataError> {
+ match d.index {
+ 0..=16 => AbstractAnimal::apply_metadata(entity, d)?,
17 => {
- let bitfield = value.into_byte().ok()?;
- self.tamed = bitfield & 0x2 != 0;
- self.eating = bitfield & 0x10 != 0;
- self.standing = bitfield & 0x20 != 0;
- self.bred = bitfield & 0x8 != 0;
- self.saddled = bitfield & 0x4 != 0;
+ let bitfield = d.value.into_byte()?;
+ entity.insert(SkeletonHorseTamed(bitfield & 0x2 != 0));
+ entity.insert(SkeletonHorseEating(bitfield & 0x10 != 0));
+ entity.insert(SkeletonHorseStanding(bitfield & 0x20 != 0));
+ entity.insert(SkeletonHorseBred(bitfield & 0x8 != 0));
+ entity.insert(SkeletonHorseSaddled(bitfield & 0x4 != 0));
+ }
+ 18 => {
+ entity.insert(SkeletonHorseOwnerUuid(d.value.into_optional_uuid()?));
}
- 18 => self.owner_uuid = value.into_optional_uuid().ok()?,
_ => {}
}
- Some(())
- }
-}
-impl Deref for SkeletonHorse {
- type Target = AbstractAnimal;
- fn deref(&self) -> &Self::Target {
- &self.abstract_animal
- }
-}
-impl DerefMut for SkeletonHorse {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.abstract_animal
- }
-}
-
-#[derive(Debug, Clone)]
-pub struct Slime {
- pub abstract_insentient: AbstractInsentient,
- pub size: i32,
-}
-
-impl Slime {
- pub fn read(metadata: &mut VecDeque<EntityDataValue>) -> Option<Self> {
- let abstract_insentient = AbstractInsentient::read(metadata)?;
- let size = metadata.pop_front()?.into_int().ok()?;
- Some(Self {
- abstract_insentient,
- size,
- })
- }
-
- pub fn write(&self) -> Vec<EntityDataValue> {
- let mut metadata = Vec::new();
- metadata.extend(self.abstract_insentient.write());
- metadata.push(EntityDataValue::Int(self.size.clone()));
- metadata
- }
-}
-
-impl Default for Slime {
- fn default() -> Self {
- Self {
- abstract_insentient: Default::default(),
- size: 1,
+ Ok(())
+ }
+}
+
+#[derive(Bundle)]
+pub struct SkeletonHorseMetadataBundle {
+ _marker: SkeletonHorse,
+ parent: AbstractAnimalMetadataBundle,
+ skeleton_horse_tamed: SkeletonHorseTamed,
+ skeleton_horse_eating: SkeletonHorseEating,
+ skeleton_horse_standing: SkeletonHorseStanding,
+ skeleton_horse_bred: SkeletonHorseBred,
+ skeleton_horse_saddled: SkeletonHorseSaddled,
+ skeleton_horse_owner_uuid: SkeletonHorseOwnerUuid,
+}
+impl Default for SkeletonHorseMetadataBundle {
+ fn default() -> Self {
+ Self {
+ _marker: SkeletonHorse,
+ parent: AbstractAnimalMetadataBundle {
+ _marker: AbstractAnimal,
+ parent: AbstractAgeableMetadataBundle {
+ _marker: AbstractAgeable,
+ 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),
+ },
+ },
+ abstract_ageable_baby: AbstractAgeableBaby(false),
+ },
+ },
+ skeleton_horse_tamed: SkeletonHorseTamed(false),
+ skeleton_horse_eating: SkeletonHorseEating(false),
+ skeleton_horse_standing: SkeletonHorseStanding(false),
+ skeleton_horse_bred: SkeletonHorseBred(false),
+ skeleton_horse_saddled: SkeletonHorseSaddled(false),
+ skeleton_horse_owner_uuid: SkeletonHorseOwnerUuid(None),
}
}
}
+#[derive(Component)]
+pub struct Slime;
impl Slime {
- pub fn set_index(&mut self, index: u8, value: EntityDataValue) -> Option<()> {
- match index {
- 0..=15 => self.abstract_insentient.set_index(index, value)?,
- 16 => self.size = value.into_int().ok()?,
+ pub fn apply_metadata(
+ entity: &mut azalea_ecs::system::EntityCommands,
+ d: EntityDataItem,
+ ) -> Result<(), UpdateMetadataError> {
+ match d.index {
+ 0..=15 => AbstractInsentient::apply_metadata(entity, d)?,
+ 16 => {
+ entity.insert(SlimeSize(d.value.into_int()?));
+ }
_ => {}
}
- Some(())
- }
-}
-impl Deref for Slime {
- type Target = AbstractInsentient;
- fn deref(&self) -> &Self::Target {
- &self.abstract_insentient
- }
-}
-impl DerefMut for Slime {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.abstract_insentient
- }
-}
-
-#[derive(Debug, Clone)]
-pub struct SmallFireball {
- pub abstract_entity: AbstractEntity,
- pub item_stack: Slot,
-}
-
-impl SmallFireball {
- pub fn read(metadata: &mut VecDeque<EntityDataValue>) -> Option<Self> {
- let abstract_entity = AbstractEntity::read(metadata)?;
- let item_stack = metadata.pop_front()?.into_item_stack().ok()?;
- Some(Self {
- abstract_entity,
- item_stack,
- })
- }
-
- pub fn write(&self) -> Vec<EntityDataValue> {
- let mut metadata = Vec::new();
- metadata.extend(self.abstract_entity.write());
- metadata.push(EntityDataValue::ItemStack(self.item_stack.clone()));
- metadata
- }
-}
-
-impl Default for SmallFireball {
- fn default() -> Self {
- Self {
- abstract_entity: Default::default(),
- item_stack: Slot::Empty,
+ Ok(())
+ }
+}
+
+#[derive(Bundle)]
+pub struct SlimeMetadataBundle {
+ _marker: Slime,
+ parent: AbstractInsentientMetadataBundle,
+ slime_size: SlimeSize,
+}
+impl Default for SlimeMetadataBundle {
+ fn default() -> Self {
+ Self {
+ _marker: Slime,
+ 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),
+ },
+ slime_size: SlimeSize(1),
}
}
}
+#[derive(Component, Deref, DerefMut)]
+pub struct SmallFireballItemStack(pub Slot);
+#[derive(Component)]
+pub struct SmallFireball;
impl SmallFireball {
- pub fn set_index(&mut self, index: u8, value: EntityDataValue) -> Option<()> {
- match index {
- 0..=7 => self.abstract_entity.set_index(index, value)?,
- 8 => self.item_stack = value.into_item_stack().ok()?,
+ pub fn apply_metadata(
+ entity: &mut azalea_ecs::system::EntityCommands,
+ d: EntityDataItem,
+ ) -> Result<(), UpdateMetadataError> {
+ match d.index {
+ 0..=7 => AbstractEntity::apply_metadata(entity, d)?,
+ 8 => {
+ entity.insert(SmallFireballItemStack(d.value.into_item_stack()?));
+ }
_ => {}
}
- Some(())
- }
-}
-impl Deref for SmallFireball {
- type Target = AbstractEntity;
- fn deref(&self) -> &Self::Target {
- &self.abstract_entity
- }
-}
-impl DerefMut for SmallFireball {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.abstract_entity
+ Ok(())
}
}
-#[derive(Debug, Clone)]
-pub struct SnowGolem {
- pub abstract_creature: AbstractCreature,
- pub has_pumpkin: bool,
+#[derive(Bundle)]
+pub struct SmallFireballMetadataBundle {
+ _marker: SmallFireball,
+ parent: AbstractEntityMetadataBundle,
+ small_fireball_item_stack: SmallFireballItemStack,
}
-
-impl SnowGolem {
- pub fn read(metadata: &mut VecDeque<EntityDataValue>) -> Option<Self> {
- let abstract_creature = AbstractCreature::read(metadata)?;
- let bitfield = metadata.pop_front()?.into_byte().ok()?;
- let has_pumpkin = bitfield & 0x10 != 0;
- Some(Self {
- abstract_creature,
- has_pumpkin,
- })
- }
-
- pub fn write(&self) -> Vec<EntityDataValue> {
- let mut metadata = Vec::new();
- metadata.extend(self.abstract_creature.write());
- let mut bitfield = 0u8;
- if self.has_pumpkin {
- bitfield &= 0x10;
- }
- metadata.push(EntityDataValue::Byte(bitfield));
- metadata
- }
-}
-
-impl Default for SnowGolem {
+impl Default for SmallFireballMetadataBundle {
fn default() -> Self {
Self {
- abstract_creature: Default::default(),
- has_pumpkin: true,
+ _marker: SmallFireball,
+ 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),
+ },
+ small_fireball_item_stack: SmallFireballItemStack(Slot::Empty),
}
}
}
+#[derive(Component, Deref, DerefMut)]
+pub struct HasPumpkin(pub bool);
+#[derive(Component)]
+pub struct SnowGolem;
impl SnowGolem {
- pub fn set_index(&mut self, index: u8, value: EntityDataValue) -> Option<()> {
- match index {
- 0..=15 => self.abstract_creature.set_index(index, value)?,
+ pub fn apply_metadata(
+ entity: &mut azalea_ecs::system::EntityCommands,
+ d: EntityDataItem,
+ ) -> Result<(), UpdateMetadataError> {
+ match d.index {
+ 0..=15 => AbstractCreature::apply_metadata(entity, d)?,
16 => {
- let bitfield = value.into_byte().ok()?;
- self.has_pumpkin = bitfield & 0x10 != 0;
+ let bitfield = d.value.into_byte()?;
+ entity.insert(HasPumpkin(bitfield & 0x10 != 0));
}
_ => {}
}
- Some(())
- }
-}
-impl Deref for SnowGolem {
- type Target = AbstractCreature;
- fn deref(&self) -> &Self::Target {
- &self.abstract_creature
- }
-}
-impl DerefMut for SnowGolem {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.abstract_creature
- }
-}
-
-#[derive(Debug, Clone)]
-pub struct Snowball {
- pub abstract_entity: AbstractEntity,
- pub item_stack: Slot,
-}
-
-impl Snowball {
- pub fn read(metadata: &mut VecDeque<EntityDataValue>) -> Option<Self> {
- let abstract_entity = AbstractEntity::read(metadata)?;
- let item_stack = metadata.pop_front()?.into_item_stack().ok()?;
- Some(Self {
- abstract_entity,
- item_stack,
- })
- }
-
- pub fn write(&self) -> Vec<EntityDataValue> {
- let mut metadata = Vec::new();
- metadata.extend(self.abstract_entity.write());
- metadata.push(EntityDataValue::ItemStack(self.item_stack.clone()));
- metadata
- }
-}
-
-impl Default for Snowball {
- fn default() -> Self {
- Self {
- abstract_entity: Default::default(),
- item_stack: Slot::Empty,
+ Ok(())
+ }
+}
+
+#[derive(Bundle)]
+pub struct SnowGolemMetadataBundle {
+ _marker: SnowGolem,
+ parent: AbstractCreatureMetadataBundle,
+ has_pumpkin: HasPumpkin,
+}
+impl Default for SnowGolemMetadataBundle {
+ fn default() -> Self {
+ Self {
+ _marker: SnowGolem,
+ 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),
+ },
+ },
+ has_pumpkin: HasPumpkin(true),
}
}
}
+#[derive(Component, Deref, DerefMut)]
+pub struct SnowballItemStack(pub Slot);
+#[derive(Component)]
+pub struct Snowball;
impl Snowball {
- pub fn set_index(&mut self, index: u8, value: EntityDataValue) -> Option<()> {
- match index {
- 0..=7 => self.abstract_entity.set_index(index, value)?,
- 8 => self.item_stack = value.into_item_stack().ok()?,
+ pub fn apply_metadata(
+ entity: &mut azalea_ecs::system::EntityCommands,
+ d: EntityDataItem,
+ ) -> Result<(), UpdateMetadataError> {
+ match d.index {
+ 0..=7 => AbstractEntity::apply_metadata(entity, d)?,
+ 8 => {
+ entity.insert(SnowballItemStack(d.value.into_item_stack()?));
+ }
_ => {}
}
- Some(())
- }
-}
-impl Deref for Snowball {
- type Target = AbstractEntity;
- fn deref(&self) -> &Self::Target {
- &self.abstract_entity
- }
-}
-impl DerefMut for Snowball {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.abstract_entity
+ Ok(())
}
}
-#[derive(Debug, Clone)]
-pub struct SpawnerMinecart {
- pub abstract_minecart: AbstractMinecart,
-}
-
-impl SpawnerMinecart {
- pub fn read(metadata: &mut VecDeque<EntityDataValue>) -> Option<Self> {
- let abstract_minecart = AbstractMinecart::read(metadata)?;
- Some(Self { abstract_minecart })
- }
-
- pub fn write(&self) -> Vec<EntityDataValue> {
- let mut metadata = Vec::new();
- metadata.extend(self.abstract_minecart.write());
- metadata
- }
+#[derive(Bundle)]
+pub struct SnowballMetadataBundle {
+ _marker: Snowball,
+ parent: AbstractEntityMetadataBundle,
+ snowball_item_stack: SnowballItemStack,
}
-
-impl Default for SpawnerMinecart {
+impl Default for SnowballMetadataBundle {
fn default() -> Self {
Self {
- abstract_minecart: Default::default(),
+ _marker: Snowball,
+ 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),
+ },
+ snowball_item_stack: SnowballItemStack(Slot::Empty),
}
}
}
+#[derive(Component)]
+pub struct SpawnerMinecart;
impl SpawnerMinecart {
- pub fn set_index(&mut self, index: u8, value: EntityDataValue) -> Option<()> {
- self.abstract_minecart.set_index(index, value)
- }
-}
-impl Deref for SpawnerMinecart {
- type Target = AbstractMinecart;
- fn deref(&self) -> &Self::Target {
- &self.abstract_minecart
- }
-}
-impl DerefMut for SpawnerMinecart {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.abstract_minecart
- }
-}
-
-#[derive(Debug, Clone)]
-pub struct SpectralArrow {
- pub abstract_entity: AbstractEntity,
- pub crit_arrow: bool,
- pub shot_from_crossbow: bool,
- pub no_physics: bool,
- pub pierce_level: u8,
-}
-
-impl SpectralArrow {
- pub fn read(metadata: &mut VecDeque<EntityDataValue>) -> Option<Self> {
- let abstract_entity = AbstractEntity::read(metadata)?;
- let bitfield = metadata.pop_front()?.into_byte().ok()?;
- let crit_arrow = bitfield & 0x1 != 0;
- let shot_from_crossbow = bitfield & 0x4 != 0;
- let no_physics = bitfield & 0x2 != 0;
- let pierce_level = metadata.pop_front()?.into_byte().ok()?;
- Some(Self {
- abstract_entity,
- crit_arrow,
- shot_from_crossbow,
- no_physics,
- pierce_level,
- })
- }
-
- pub fn write(&self) -> Vec<EntityDataValue> {
- let mut metadata = Vec::new();
- metadata.extend(self.abstract_entity.write());
- let mut bitfield = 0u8;
- if self.crit_arrow {
- bitfield &= 0x1;
- }
- if self.shot_from_crossbow {
- bitfield &= 0x4;
- }
- if self.no_physics {
- bitfield &= 0x2;
+ pub fn apply_metadata(
+ entity: &mut azalea_ecs::system::EntityCommands,
+ d: EntityDataItem,
+ ) -> Result<(), UpdateMetadataError> {
+ match d.index {
+ 0..=13 => AbstractMinecart::apply_metadata(entity, d)?,
+ _ => {}
}
- metadata.push(EntityDataValue::Byte(bitfield));
- metadata.push(EntityDataValue::Byte(self.pierce_level.clone()));
- metadata
- }
-}
-
-impl Default for SpectralArrow {
- fn default() -> Self {
- Self {
- abstract_entity: Default::default(),
- crit_arrow: false,
- shot_from_crossbow: false,
- no_physics: false,
- pierce_level: 0,
+ Ok(())
+ }
+}
+
+#[derive(Bundle)]
+pub struct SpawnerMinecartMetadataBundle {
+ _marker: SpawnerMinecart,
+ parent: AbstractMinecartMetadataBundle,
+}
+impl Default for SpawnerMinecartMetadataBundle {
+ fn default() -> Self {
+ Self {
+ _marker: SpawnerMinecart,
+ parent: AbstractMinecartMetadataBundle {
+ _marker: AbstractMinecart,
+ 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),
+ },
+ abstract_minecart_hurt: AbstractMinecartHurt(0),
+ abstract_minecart_hurtdir: AbstractMinecartHurtdir(1),
+ abstract_minecart_damage: AbstractMinecartDamage(0.0),
+ display_block: DisplayBlock(Default::default()),
+ display_offset: DisplayOffset(6),
+ custom_display: CustomDisplay(false),
+ },
}
}
}
+#[derive(Component, Deref, DerefMut)]
+pub struct SpectralArrowCritArrow(pub bool);
+#[derive(Component, Deref, DerefMut)]
+pub struct SpectralArrowShotFromCrossbow(pub bool);
+#[derive(Component, Deref, DerefMut)]
+pub struct SpectralArrowNoPhysics(pub bool);
+#[derive(Component, Deref, DerefMut)]
+pub struct SpectralArrowPierceLevel(pub u8);
+#[derive(Component)]
+pub struct SpectralArrow;
impl SpectralArrow {
- pub fn set_index(&mut self, index: u8, value: EntityDataValue) -> Option<()> {
- match index {
- 0..=7 => self.abstract_entity.set_index(index, value)?,
+ pub fn apply_metadata(
+ entity: &mut azalea_ecs::system::EntityCommands,
+ d: EntityDataItem,
+ ) -> Result<(), UpdateMetadataError> {
+ match d.index {
+ 0..=7 => AbstractEntity::apply_metadata(entity, d)?,
8 => {
- let bitfield = value.into_byte().ok()?;
- self.crit_arrow = bitfield & 0x1 != 0;
- self.shot_from_crossbow = bitfield & 0x4 != 0;
- self.no_physics = bitfield & 0x2 != 0;
+ let bitfield = d.value.into_byte()?;
+ entity.insert(SpectralArrowCritArrow(bitfield & 0x1 != 0));
+ entity.insert(SpectralArrowShotFromCrossbow(bitfield & 0x4 != 0));
+ entity.insert(SpectralArrowNoPhysics(bitfield & 0x2 != 0));
+ }
+ 9 => {
+ entity.insert(SpectralArrowPierceLevel(d.value.into_byte()?));
}
- 9 => self.pierce_level = value.into_byte().ok()?,
_ => {}
}
- Some(())
- }
-}
-impl Deref for SpectralArrow {
- type Target = AbstractEntity;
- fn deref(&self) -> &Self::Target {
- &self.abstract_entity
- }
-}
-impl DerefMut for SpectralArrow {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.abstract_entity
- }
-}
-
-#[derive(Debug, Clone)]
-pub struct Spider {
- pub abstract_monster: AbstractMonster,
- pub climbing: bool,
-}
-
-impl Spider {
- pub fn read(metadata: &mut VecDeque<EntityDataValue>) -> Option<Self> {
- let abstract_monster = AbstractMonster::read(metadata)?;
- let bitfield = metadata.pop_front()?.into_byte().ok()?;
- let climbing = bitfield & 0x1 != 0;
- Some(Self {
- abstract_monster,
- climbing,
- })
- }
-
- pub fn write(&self) -> Vec<EntityDataValue> {
- let mut metadata = Vec::new();
- metadata.extend(self.abstract_monster.write());
- let mut bitfield = 0u8;
- if self.climbing {
- bitfield &= 0x1;
- }
- metadata.push(EntityDataValue::Byte(bitfield));
- metadata
- }
-}
-
-impl Default for Spider {
- fn default() -> Self {
- Self {
- abstract_monster: Default::default(),
- climbing: false,
+ Ok(())
+ }
+}
+
+#[derive(Bundle)]
+pub struct SpectralArrowMetadataBundle {
+ _marker: SpectralArrow,
+ parent: AbstractEntityMetadataBundle,
+ spectral_arrow_crit_arrow: SpectralArrowCritArrow,
+ spectral_arrow_shot_from_crossbow: SpectralArrowShotFromCrossbow,
+ spectral_arrow_no_physics: SpectralArrowNoPhysics,
+ spectral_arrow_pierce_level: SpectralArrowPierceLevel,
+}
+impl Default for SpectralArrowMetadataBundle {
+ fn default() -> Self {
+ Self {
+ _marker: SpectralArrow,
+ 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),
+ },
+ spectral_arrow_crit_arrow: SpectralArrowCritArrow(false),
+ spectral_arrow_shot_from_crossbow: SpectralArrowShotFromCrossbow(false),
+ spectral_arrow_no_physics: SpectralArrowNoPhysics(false),
+ spectral_arrow_pierce_level: SpectralArrowPierceLevel(0),
}
}
}
+#[derive(Component)]
+pub struct Spider;
impl Spider {
- pub fn set_index(&mut self, index: u8, value: EntityDataValue) -> Option<()> {
- match index {
- 0..=15 => self.abstract_monster.set_index(index, value)?,
+ pub fn apply_metadata(
+ entity: &mut azalea_ecs::system::EntityCommands,
+ d: EntityDataItem,
+ ) -> Result<(), UpdateMetadataError> {
+ match d.index {
+ 0..=15 => AbstractMonster::apply_metadata(entity, d)?,
16 => {
- let bitfield = value.into_byte().ok()?;
- self.climbing = bitfield & 0x1 != 0;
+ let bitfield = d.value.into_byte()?;
+ entity.insert(Climbing(bitfield & 0x1 != 0));
}
_ => {}
}
- Some(())
- }
-}
-impl Deref for Spider {
- type Target = AbstractMonster;
- fn deref(&self) -> &Self::Target {
- &self.abstract_monster
- }
-}
-impl DerefMut for Spider {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.abstract_monster
- }
-}
-
-#[derive(Debug, Clone)]
-pub struct Squid {
- pub abstract_creature: AbstractCreature,
-}
-
-impl Squid {
- pub fn read(metadata: &mut VecDeque<EntityDataValue>) -> Option<Self> {
- let abstract_creature = AbstractCreature::read(metadata)?;
- Some(Self { abstract_creature })
- }
-
- pub fn write(&self) -> Vec<EntityDataValue> {
- let mut metadata = Vec::new();
- metadata.extend(self.abstract_creature.write());
- metadata
- }
-}
-
-impl Default for Squid {
- fn default() -> Self {
- Self {
- abstract_creature: Default::default(),
+ Ok(())
+ }
+}
+
+#[derive(Bundle)]
+pub struct SpiderMetadataBundle {
+ _marker: Spider,
+ parent: AbstractMonsterMetadataBundle,
+ climbing: Climbing,
+}
+impl Default for SpiderMetadataBundle {
+ fn default() -> Self {
+ Self {
+ _marker: Spider,
+ 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),
+ },
+ },
+ },
+ climbing: Climbing(false),
}
}
}
+#[derive(Component)]
+pub struct Squid;
impl Squid {
- pub fn set_index(&mut self, index: u8, value: EntityDataValue) -> Option<()> {
- self.abstract_creature.set_index(index, value)
- }
-}
-impl Deref for Squid {
- type Target = AbstractCreature;
- fn deref(&self) -> &Self::Target {
- &self.abstract_creature
- }
-}
-impl DerefMut for Squid {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.abstract_creature
- }
-}
-
-#[derive(Debug, Clone)]
-pub struct Stray {
- pub abstract_monster: AbstractMonster,
-}
-
-impl Stray {
- pub fn read(metadata: &mut VecDeque<EntityDataValue>) -> Option<Self> {
- let abstract_monster = AbstractMonster::read(metadata)?;
- Some(Self { abstract_monster })
- }
-
- pub fn write(&self) -> Vec<EntityDataValue> {
- let mut metadata = Vec::new();
- metadata.extend(self.abstract_monster.write());
- metadata
- }
-}
-
-impl Default for Stray {
- fn default() -> Self {
- Self {
- abstract_monster: Default::default(),
+ pub fn apply_metadata(
+ entity: &mut azalea_ecs::system::EntityCommands,
+ d: EntityDataItem,
+ ) -> Result<(), UpdateMetadataError> {
+ match d.index {
+ 0..=15 => AbstractCreature::apply_metadata(entity, d)?,
+ _ => {}
+ }
+ Ok(())
+ }
+}
+
+#[derive(Bundle)]
+pub struct SquidMetadataBundle {
+ _marker: Squid,
+ parent: AbstractCreatureMetadataBundle,
+}
+impl Default for SquidMetadataBundle {
+ fn default() -> Self {
+ Self {
+ _marker: Squid,
+ 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)]
+pub struct Stray;
impl Stray {
- pub fn set_index(&mut self, index: u8, value: EntityDataValue) -> Option<()> {
- self.abstract_monster.set_index(index, value)
- }
-}
-impl Deref for Stray {
- type Target = AbstractMonster;
- fn deref(&self) -> &Self::Target {
- &self.abstract_monster
- }
-}
-impl DerefMut for Stray {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.abstract_monster
- }
-}
-
-#[derive(Debug, Clone)]
-pub struct Strider {
- pub abstract_animal: AbstractAnimal,
- pub boost_time: i32,
- pub suffocating: bool,
- pub saddle: bool,
-}
-
-impl Strider {
- pub fn read(metadata: &mut VecDeque<EntityDataValue>) -> Option<Self> {
- let abstract_animal = AbstractAnimal::read(metadata)?;
- let boost_time = metadata.pop_front()?.into_int().ok()?;
- let suffocating = metadata.pop_front()?.into_boolean().ok()?;
- let saddle = metadata.pop_front()?.into_boolean().ok()?;
- Some(Self {
- abstract_animal,
- boost_time,
- suffocating,
- saddle,
- })
- }
-
- pub fn write(&self) -> Vec<EntityDataValue> {
- let mut metadata = Vec::new();
- metadata.extend(self.abstract_animal.write());
- metadata.push(EntityDataValue::Int(self.boost_time.clone()));
- metadata.push(EntityDataValue::Boolean(self.suffocating.clone()));
- metadata.push(EntityDataValue::Boolean(self.saddle.clone()));
- metadata
- }
-}
-
-impl Default for Strider {
- fn default() -> Self {
- Self {
- abstract_animal: Default::default(),
- boost_time: 0,
- suffocating: false,
- saddle: false,
+ pub fn apply_metadata(
+ entity: &mut azalea_ecs::system::EntityCommands,
+ d: EntityDataItem,
+ ) -> Result<(), UpdateMetadataError> {
+ match d.index {
+ 0..=15 => AbstractMonster::apply_metadata(entity, d)?,
+ _ => {}
+ }
+ Ok(())
+ }
+}
+
+#[derive(Bundle)]
+pub struct StrayMetadataBundle {
+ _marker: Stray,
+ parent: AbstractMonsterMetadataBundle,
+}
+impl Default for StrayMetadataBundle {
+ fn default() -> Self {
+ Self {
+ _marker: Stray,
+ 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)]
+pub struct StriderBoostTime(pub i32);
+#[derive(Component, Deref, DerefMut)]
+pub struct Suffocating(pub bool);
+#[derive(Component, Deref, DerefMut)]
+pub struct StriderSaddle(pub bool);
+#[derive(Component)]
+pub struct Strider;
impl Strider {
- pub fn set_index(&mut self, index: u8, value: EntityDataValue) -> Option<()> {
- match index {
- 0..=16 => self.abstract_animal.set_index(index, value)?,
- 17 => self.boost_time = value.into_int().ok()?,
- 18 => self.suffocating = value.into_boolean().ok()?,
- 19 => self.saddle = value.into_boolean().ok()?,
+ pub fn apply_metadata(
+ entity: &mut azalea_ecs::system::EntityCommands,
+ d: EntityDataItem,
+ ) -> Result<(), UpdateMetadataError> {
+ match d.index {
+ 0..=16 => AbstractAnimal::apply_metadata(entity, d)?,
+ 17 => {
+ entity.insert(StriderBoostTime(d.value.into_int()?));
+ }
+ 18 => {
+ entity.insert(Suffocating(d.value.into_boolean()?));
+ }
+ 19 => {
+ entity.insert(StriderSaddle(d.value.into_boolean()?));
+ }
_ => {}
}
- Some(())
- }
-}
-impl Deref for Strider {
- type Target = AbstractAnimal;
- fn deref(&self) -> &Self::Target {
- &self.abstract_animal
- }
-}
-impl DerefMut for Strider {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.abstract_animal
- }
-}
-
-#[derive(Debug, Clone)]
-pub struct Tadpole {
- pub abstract_creature: AbstractCreature,
- pub from_bucket: bool,
-}
-
-impl Tadpole {
- pub fn read(metadata: &mut VecDeque<EntityDataValue>) -> Option<Self> {
- let abstract_creature = AbstractCreature::read(metadata)?;
- let from_bucket = metadata.pop_front()?.into_boolean().ok()?;
- Some(Self {
- abstract_creature,
- from_bucket,
- })
- }
-
- pub fn write(&self) -> Vec<EntityDataValue> {
- let mut metadata = Vec::new();
- metadata.extend(self.abstract_creature.write());
- metadata.push(EntityDataValue::Boolean(self.from_bucket.clone()));
- metadata
- }
-}
-
-impl Default for Tadpole {
- fn default() -> Self {
- Self {
- abstract_creature: Default::default(),
- from_bucket: false,
+ Ok(())
+ }
+}
+
+#[derive(Bundle)]
+pub struct StriderMetadataBundle {
+ _marker: Strider,
+ parent: AbstractAnimalMetadataBundle,
+ strider_boost_time: StriderBoostTime,
+ suffocating: Suffocating,
+ strider_saddle: StriderSaddle,
+}
+impl Default for StriderMetadataBundle {
+ fn default() -> Self {
+ Self {
+ _marker: Strider,
+ parent: AbstractAnimalMetadataBundle {
+ _marker: AbstractAnimal,
+ parent: AbstractAgeableMetadataBundle {
+ _marker: AbstractAgeable,
+ 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),
+ },
+ },
+ abstract_ageable_baby: AbstractAgeableBaby(false),
+ },
+ },
+ strider_boost_time: StriderBoostTime(0),
+ suffocating: Suffocating(false),
+ strider_saddle: StriderSaddle(false),
}
}
}
+#[derive(Component, Deref, DerefMut)]
+pub struct TadpoleFromBucket(pub bool);
+#[derive(Component)]
+pub struct Tadpole;
impl Tadpole {
- pub fn set_index(&mut self, index: u8, value: EntityDataValue) -> Option<()> {
- match index {
- 0..=15 => self.abstract_creature.set_index(index, value)?,
- 16 => self.from_bucket = value.into_boolean().ok()?,
+ pub fn apply_metadata(
+ entity: &mut azalea_ecs::system::EntityCommands,
+ d: EntityDataItem,
+ ) -> Result<(), UpdateMetadataError> {
+ match d.index {
+ 0..=15 => AbstractCreature::apply_metadata(entity, d)?,
+ 16 => {
+ entity.insert(TadpoleFromBucket(d.value.into_boolean()?));
+ }
_ => {}
}
- Some(())
- }
-}
-impl Deref for Tadpole {
- type Target = AbstractCreature;
- fn deref(&self) -> &Self::Target {
- &self.abstract_creature
- }
-}
-impl DerefMut for Tadpole {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.abstract_creature
- }
-}
-
-#[derive(Debug, Clone)]
-pub struct Tnt {
- pub abstract_entity: AbstractEntity,
- pub fuse: i32,
-}
-
-impl Tnt {
- pub fn read(metadata: &mut VecDeque<EntityDataValue>) -> Option<Self> {
- let abstract_entity = AbstractEntity::read(metadata)?;
- let fuse = metadata.pop_front()?.into_int().ok()?;
- Some(Self {
- abstract_entity,
- fuse,
- })
- }
-
- pub fn write(&self) -> Vec<EntityDataValue> {
- let mut metadata = Vec::new();
- metadata.extend(self.abstract_entity.write());
- metadata.push(EntityDataValue::Int(self.fuse.clone()));
- metadata
- }
-}
-
-impl Default for Tnt {
- fn default() -> Self {
- Self {
- abstract_entity: Default::default(),
- fuse: 80,
+ Ok(())
+ }
+}
+
+#[derive(Bundle)]
+pub struct TadpoleMetadataBundle {
+ _marker: Tadpole,
+ parent: AbstractCreatureMetadataBundle,
+ tadpole_from_bucket: TadpoleFromBucket,
+}
+impl Default for TadpoleMetadataBundle {
+ fn default() -> Self {
+ Self {
+ _marker: Tadpole,
+ 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),
+ },
+ },
+ tadpole_from_bucket: TadpoleFromBucket(false),
}
}
}
+#[derive(Component, Deref, DerefMut)]
+pub struct Fuse(pub i32);
+#[derive(Component)]
+pub struct Tnt;
impl Tnt {
- pub fn set_index(&mut self, index: u8, value: EntityDataValue) -> Option<()> {
- match index {
- 0..=7 => self.abstract_entity.set_index(index, value)?,
- 8 => self.fuse = value.into_int().ok()?,
+ pub fn apply_metadata(
+ entity: &mut azalea_ecs::system::EntityCommands,
+ d: EntityDataItem,
+ ) -> Result<(), UpdateMetadataError> {
+ match d.index {
+ 0..=7 => AbstractEntity::apply_metadata(entity, d)?,
+ 8 => {
+ entity.insert(Fuse(d.value.into_int()?));
+ }
_ => {}
}
- Some(())
- }
-}
-impl Deref for Tnt {
- type Target = AbstractEntity;
- fn deref(&self) -> &Self::Target {
- &self.abstract_entity
- }
-}
-impl DerefMut for Tnt {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.abstract_entity
+ Ok(())
}
}
-#[derive(Debug, Clone)]
-pub struct TntMinecart {
- pub abstract_minecart: AbstractMinecart,
+#[derive(Bundle)]
+pub struct TntMetadataBundle {
+ _marker: Tnt,
+ parent: AbstractEntityMetadataBundle,
+ fuse: Fuse,
}
-
-impl TntMinecart {
- pub fn read(metadata: &mut VecDeque<EntityDataValue>) -> Option<Self> {
- let abstract_minecart = AbstractMinecart::read(metadata)?;
- Some(Self { abstract_minecart })
- }
-
- pub fn write(&self) -> Vec<EntityDataValue> {
- let mut metadata = Vec::new();
- metadata.extend(self.abstract_minecart.write());
- metadata
- }
-}
-
-impl Default for TntMinecart {
+impl Default for TntMetadataBundle {
fn default() -> Self {
Self {
- abstract_minecart: Default::default(),
+ _marker: Tnt,
+ 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),
+ },
+ fuse: Fuse(80),
}
}
}
+#[derive(Component)]
+pub struct TntMinecart;
impl TntMinecart {
- pub fn set_index(&mut self, index: u8, value: EntityDataValue) -> Option<()> {
- self.abstract_minecart.set_index(index, value)
- }
-}
-impl Deref for TntMinecart {
- type Target = AbstractMinecart;
- fn deref(&self) -> &Self::Target {
- &self.abstract_minecart
- }
-}
-impl DerefMut for TntMinecart {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.abstract_minecart
- }
-}
-
-#[derive(Debug, Clone)]
-pub struct TraderLlama {
- pub llama: Llama,
-}
-
-impl TraderLlama {
- pub fn read(metadata: &mut VecDeque<EntityDataValue>) -> Option<Self> {
- let llama = Llama::read(metadata)?;
- Some(Self { llama })
- }
-
- pub fn write(&self) -> Vec<EntityDataValue> {
- let mut metadata = Vec::new();
- metadata.extend(self.llama.write());
- metadata
- }
-}
-
-impl Default for TraderLlama {
- fn default() -> Self {
- Self {
- llama: Default::default(),
+ pub fn apply_metadata(
+ entity: &mut azalea_ecs::system::EntityCommands,
+ d: EntityDataItem,
+ ) -> Result<(), UpdateMetadataError> {
+ match d.index {
+ 0..=13 => AbstractMinecart::apply_metadata(entity, d)?,
+ _ => {}
+ }
+ Ok(())
+ }
+}
+
+#[derive(Bundle)]
+pub struct TntMinecartMetadataBundle {
+ _marker: TntMinecart,
+ parent: AbstractMinecartMetadataBundle,
+}
+impl Default for TntMinecartMetadataBundle {
+ fn default() -> Self {
+ Self {
+ _marker: TntMinecart,
+ parent: AbstractMinecartMetadataBundle {
+ _marker: AbstractMinecart,
+ 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),
+ },
+ abstract_minecart_hurt: AbstractMinecartHurt(0),
+ abstract_minecart_hurtdir: AbstractMinecartHurtdir(1),
+ abstract_minecart_damage: AbstractMinecartDamage(0.0),
+ display_block: DisplayBlock(Default::default()),
+ display_offset: DisplayOffset(6),
+ custom_display: CustomDisplay(false),
+ },
}
}
}
+#[derive(Component)]
+pub struct TraderLlama;
impl TraderLlama {
- pub fn set_index(&mut self, index: u8, value: EntityDataValue) -> Option<()> {
- self.llama.set_index(index, value)
- }
-}
-impl Deref for TraderLlama {
- type Target = Llama;
- fn deref(&self) -> &Self::Target {
- &self.llama
- }
-}
-impl DerefMut for TraderLlama {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.llama
- }
-}
-
-#[derive(Debug, Clone)]
-pub struct Trident {
- pub abstract_entity: AbstractEntity,
- pub crit_arrow: bool,
- pub shot_from_crossbow: bool,
- pub no_physics: bool,
- pub pierce_level: u8,
- pub loyalty: u8,
- pub foil: bool,
-}
-
-impl Trident {
- pub fn read(metadata: &mut VecDeque<EntityDataValue>) -> Option<Self> {
- let abstract_entity = AbstractEntity::read(metadata)?;
- let bitfield = metadata.pop_front()?.into_byte().ok()?;
- let crit_arrow = bitfield & 0x1 != 0;
- let shot_from_crossbow = bitfield & 0x4 != 0;
- let no_physics = bitfield & 0x2 != 0;
- let pierce_level = metadata.pop_front()?.into_byte().ok()?;
- let loyalty = metadata.pop_front()?.into_byte().ok()?;
- let foil = metadata.pop_front()?.into_boolean().ok()?;
- Some(Self {
- abstract_entity,
- crit_arrow,
- shot_from_crossbow,
- no_physics,
- pierce_level,
- loyalty,
- foil,
- })
- }
-
- pub fn write(&self) -> Vec<EntityDataValue> {
- let mut metadata = Vec::new();
- metadata.extend(self.abstract_entity.write());
- let mut bitfield = 0u8;
- if self.crit_arrow {
- bitfield &= 0x1;
- }
- if self.shot_from_crossbow {
- bitfield &= 0x4;
- }
- if self.no_physics {
- bitfield &= 0x2;
+ pub fn apply_metadata(
+ entity: &mut azalea_ecs::system::EntityCommands,
+ d: EntityDataItem,
+ ) -> Result<(), UpdateMetadataError> {
+ match d.index {
+ 0..=22 => Llama::apply_metadata(entity, d)?,
+ _ => {}
}
- metadata.push(EntityDataValue::Byte(bitfield));
- metadata.push(EntityDataValue::Byte(self.pierce_level.clone()));
- metadata.push(EntityDataValue::Byte(self.loyalty.clone()));
- metadata.push(EntityDataValue::Boolean(self.foil.clone()));
- metadata
- }
-}
-
-impl Default for Trident {
- fn default() -> Self {
- Self {
- abstract_entity: Default::default(),
- crit_arrow: false,
- shot_from_crossbow: false,
- no_physics: false,
- pierce_level: 0,
- loyalty: 0,
- foil: false,
+ Ok(())
+ }
+}
+
+#[derive(Bundle)]
+pub struct TraderLlamaMetadataBundle {
+ _marker: TraderLlama,
+ parent: LlamaMetadataBundle,
+}
+impl Default for TraderLlamaMetadataBundle {
+ fn default() -> Self {
+ Self {
+ _marker: TraderLlama,
+ parent: LlamaMetadataBundle {
+ _marker: Llama,
+ parent: AbstractAnimalMetadataBundle {
+ _marker: AbstractAnimal,
+ parent: AbstractAgeableMetadataBundle {
+ _marker: AbstractAgeable,
+ 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),
+ },
+ },
+ abstract_ageable_baby: AbstractAgeableBaby(false),
+ },
+ },
+ llama_tamed: LlamaTamed(false),
+ llama_eating: LlamaEating(false),
+ llama_standing: LlamaStanding(false),
+ llama_bred: LlamaBred(false),
+ llama_saddled: LlamaSaddled(false),
+ llama_owner_uuid: LlamaOwnerUuid(None),
+ llama_chest: LlamaChest(false),
+ strength: Strength(0),
+ swag: Swag(-1),
+ llama_variant: LlamaVariant(0),
+ },
}
}
}
+#[derive(Component, Deref, DerefMut)]
+pub struct TridentCritArrow(pub bool);
+#[derive(Component, Deref, DerefMut)]
+pub struct TridentShotFromCrossbow(pub bool);
+#[derive(Component, Deref, DerefMut)]
+pub struct TridentNoPhysics(pub bool);
+#[derive(Component, Deref, DerefMut)]
+pub struct TridentPierceLevel(pub u8);
+#[derive(Component, Deref, DerefMut)]
+pub struct Loyalty(pub u8);
+#[derive(Component, Deref, DerefMut)]
+pub struct Foil(pub bool);
+#[derive(Component)]
+pub struct Trident;
impl Trident {
- pub fn set_index(&mut self, index: u8, value: EntityDataValue) -> Option<()> {
- match index {
- 0..=7 => self.abstract_entity.set_index(index, value)?,
+ pub fn apply_metadata(
+ entity: &mut azalea_ecs::system::EntityCommands,
+ d: EntityDataItem,
+ ) -> Result<(), UpdateMetadataError> {
+ match d.index {
+ 0..=7 => AbstractEntity::apply_metadata(entity, d)?,
8 => {
- let bitfield = value.into_byte().ok()?;
- self.crit_arrow = bitfield & 0x1 != 0;
- self.shot_from_crossbow = bitfield & 0x4 != 0;
- self.no_physics = bitfield & 0x2 != 0;
+ let bitfield = d.value.into_byte()?;
+ entity.insert(TridentCritArrow(bitfield & 0x1 != 0));
+ entity.insert(TridentShotFromCrossbow(bitfield & 0x4 != 0));
+ entity.insert(TridentNoPhysics(bitfield & 0x2 != 0));
+ }
+ 9 => {
+ entity.insert(TridentPierceLevel(d.value.into_byte()?));
+ }
+ 10 => {
+ entity.insert(Loyalty(d.value.into_byte()?));
+ }
+ 11 => {
+ entity.insert(Foil(d.value.into_boolean()?));
}
- 9 => self.pierce_level = value.into_byte().ok()?,
- 10 => self.loyalty = value.into_byte().ok()?,
- 11 => self.foil = value.into_boolean().ok()?,
_ => {}
}
- Some(())
- }
-}
-impl Deref for Trident {
- type Target = AbstractEntity;
- fn deref(&self) -> &Self::Target {
- &self.abstract_entity
- }
-}
-impl DerefMut for Trident {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.abstract_entity
- }
-}
-
-#[derive(Debug, Clone)]
-pub struct TropicalFish {
- pub abstract_creature: AbstractCreature,
- pub from_bucket: bool,
- pub type_variant: i32,
-}
-
-impl TropicalFish {
- pub fn read(metadata: &mut VecDeque<EntityDataValue>) -> Option<Self> {
- let abstract_creature = AbstractCreature::read(metadata)?;
- let from_bucket = metadata.pop_front()?.into_boolean().ok()?;
- let type_variant = metadata.pop_front()?.into_int().ok()?;
- Some(Self {
- abstract_creature,
- from_bucket,
- type_variant,
- })
- }
-
- pub fn write(&self) -> Vec<EntityDataValue> {
- let mut metadata = Vec::new();
- metadata.extend(self.abstract_creature.write());
- metadata.push(EntityDataValue::Boolean(self.from_bucket.clone()));
- metadata.push(EntityDataValue::Int(self.type_variant.clone()));
- metadata
- }
-}
-
-impl Default for TropicalFish {
- fn default() -> Self {
- Self {
- abstract_creature: Default::default(),
- from_bucket: false,
- type_variant: 0,
+ Ok(())
+ }
+}
+
+#[derive(Bundle)]
+pub struct TridentMetadataBundle {
+ _marker: Trident,
+ parent: AbstractEntityMetadataBundle,
+ trident_crit_arrow: TridentCritArrow,
+ trident_shot_from_crossbow: TridentShotFromCrossbow,
+ trident_no_physics: TridentNoPhysics,
+ trident_pierce_level: TridentPierceLevel,
+ loyalty: Loyalty,
+ foil: Foil,
+}
+impl Default for TridentMetadataBundle {
+ fn default() -> Self {
+ Self {
+ _marker: Trident,
+ 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),
+ },
+ trident_crit_arrow: TridentCritArrow(false),
+ trident_shot_from_crossbow: TridentShotFromCrossbow(false),
+ trident_no_physics: TridentNoPhysics(false),
+ trident_pierce_level: TridentPierceLevel(0),
+ loyalty: Loyalty(0),
+ foil: Foil(false),
}
}
}
+#[derive(Component, Deref, DerefMut)]
+pub struct TropicalFishFromBucket(pub bool);
+#[derive(Component, Deref, DerefMut)]
+pub struct TropicalFishTypeVariant(pub i32);
+#[derive(Component)]
+pub struct TropicalFish;
impl TropicalFish {
- pub fn set_index(&mut self, index: u8, value: EntityDataValue) -> Option<()> {
- match index {
- 0..=15 => self.abstract_creature.set_index(index, value)?,
- 16 => self.from_bucket = value.into_boolean().ok()?,
- 17 => self.type_variant = value.into_int().ok()?,
+ pub fn apply_metadata(
+ entity: &mut azalea_ecs::system::EntityCommands,
+ d: EntityDataItem,
+ ) -> Result<(), UpdateMetadataError> {
+ match d.index {
+ 0..=15 => AbstractCreature::apply_metadata(entity, d)?,
+ 16 => {
+ entity.insert(TropicalFishFromBucket(d.value.into_boolean()?));
+ }
+ 17 => {
+ entity.insert(TropicalFishTypeVariant(d.value.into_int()?));
+ }
_ => {}
}
- Some(())
- }
-}
-impl Deref for TropicalFish {
- type Target = AbstractCreature;
- fn deref(&self) -> &Self::Target {
- &self.abstract_creature
- }
-}
-impl DerefMut for TropicalFish {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.abstract_creature
- }
-}
-
-#[derive(Debug, Clone)]
-pub struct Turtle {
- pub abstract_animal: AbstractAnimal,
- pub home_pos: BlockPos,
- pub has_egg: bool,
- pub laying_egg: bool,
- pub travel_pos: BlockPos,
- pub going_home: bool,
- pub travelling: bool,
-}
-
-impl Turtle {
- pub fn read(metadata: &mut VecDeque<EntityDataValue>) -> Option<Self> {
- let abstract_animal = AbstractAnimal::read(metadata)?;
- let home_pos = metadata.pop_front()?.into_block_pos().ok()?;
- let has_egg = metadata.pop_front()?.into_boolean().ok()?;
- let laying_egg = metadata.pop_front()?.into_boolean().ok()?;
- let travel_pos = metadata.pop_front()?.into_block_pos().ok()?;
- let going_home = metadata.pop_front()?.into_boolean().ok()?;
- let travelling = metadata.pop_front()?.into_boolean().ok()?;
- Some(Self {
- abstract_animal,
- home_pos,
- has_egg,
- laying_egg,
- travel_pos,
- going_home,
- travelling,
- })
- }
-
- pub fn write(&self) -> Vec<EntityDataValue> {
- let mut metadata = Vec::new();
- metadata.extend(self.abstract_animal.write());
- metadata.push(EntityDataValue::BlockPos(self.home_pos.clone()));
- metadata.push(EntityDataValue::Boolean(self.has_egg.clone()));
- metadata.push(EntityDataValue::Boolean(self.laying_egg.clone()));
- metadata.push(EntityDataValue::BlockPos(self.travel_pos.clone()));
- metadata.push(EntityDataValue::Boolean(self.going_home.clone()));
- metadata.push(EntityDataValue::Boolean(self.travelling.clone()));
- metadata
- }
-}
-
-impl Default for Turtle {
- fn default() -> Self {
- Self {
- abstract_animal: Default::default(),
- home_pos: BlockPos::new(0, 0, 0),
- has_egg: false,
- laying_egg: false,
- travel_pos: BlockPos::new(0, 0, 0),
- going_home: false,
- travelling: false,
- }
- }
-}
-
+ Ok(())
+ }
+}
+
+#[derive(Bundle)]
+pub struct TropicalFishMetadataBundle {
+ _marker: TropicalFish,
+ parent: AbstractCreatureMetadataBundle,
+ tropical_fish_from_bucket: TropicalFishFromBucket,
+ tropical_fish_type_variant: TropicalFishTypeVariant,
+}
+impl Default for TropicalFishMetadataBundle {
+ fn default() -> Self {
+ Self {
+ _marker: TropicalFish,
+ 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),
+ },
+ },
+ tropical_fish_from_bucket: TropicalFishFromBucket(false),
+ tropical_fish_type_variant: TropicalFishTypeVariant(0),
+ }
+ }
+}
+
+#[derive(Component, Deref, DerefMut)]
+pub struct HomePos(pub BlockPos);
+#[derive(Component, Deref, DerefMut)]
+pub struct HasEgg(pub bool);
+#[derive(Component, Deref, DerefMut)]
+pub struct LayingEgg(pub bool);
+#[derive(Component, Deref, DerefMut)]
+pub struct TravelPos(pub BlockPos);
+#[derive(Component, Deref, DerefMut)]
+pub struct GoingHome(pub bool);
+#[derive(Component, Deref, DerefMut)]
+pub struct Travelling(pub bool);
+#[derive(Component)]
+pub struct Turtle;
impl Turtle {
- pub fn set_index(&mut self, index: u8, value: EntityDataValue) -> Option<()> {
- match index {
- 0..=16 => self.abstract_animal.set_index(index, value)?,
- 17 => self.home_pos = value.into_block_pos().ok()?,
- 18 => self.has_egg = value.into_boolean().ok()?,
- 19 => self.laying_egg = value.into_boolean().ok()?,
- 20 => self.travel_pos = value.into_block_pos().ok()?,
- 21 => self.going_home = value.into_boolean().ok()?,
- 22 => self.travelling = value.into_boolean().ok()?,
+ pub fn apply_metadata(
+ entity: &mut azalea_ecs::system::EntityCommands,
+ d: EntityDataItem,
+ ) -> Result<(), UpdateMetadataError> {
+ match d.index {
+ 0..=16 => AbstractAnimal::apply_metadata(entity, d)?,
+ 17 => {
+ entity.insert(HomePos(d.value.into_block_pos()?));
+ }
+ 18 => {
+ entity.insert(HasEgg(d.value.into_boolean()?));
+ }
+ 19 => {
+ entity.insert(LayingEgg(d.value.into_boolean()?));
+ }
+ 20 => {
+ entity.insert(TravelPos(d.value.into_block_pos()?));
+ }
+ 21 => {
+ entity.insert(GoingHome(d.value.into_boolean()?));
+ }
+ 22 => {
+ entity.insert(Travelling(d.value.into_boolean()?));
+ }
_ => {}
}
- Some(())
- }
-}
-impl Deref for Turtle {
- type Target = AbstractAnimal;
- fn deref(&self) -> &Self::Target {
- &self.abstract_animal
- }
-}
-impl DerefMut for Turtle {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.abstract_animal
- }
-}
-
-#[derive(Debug, Clone)]
-pub struct Vex {
- pub abstract_monster: AbstractMonster,
- pub flags: u8,
-}
-
-impl Vex {
- pub fn read(metadata: &mut VecDeque<EntityDataValue>) -> Option<Self> {
- let abstract_monster = AbstractMonster::read(metadata)?;
- let flags = metadata.pop_front()?.into_byte().ok()?;
- Some(Self {
- abstract_monster,
- flags,
- })
- }
-
- pub fn write(&self) -> Vec<EntityDataValue> {
- let mut metadata = Vec::new();
- metadata.extend(self.abstract_monster.write());
- metadata.push(EntityDataValue::Byte(self.flags.clone()));
- metadata
- }
-}
-
-impl Default for Vex {
- fn default() -> Self {
- Self {
- abstract_monster: Default::default(),
- flags: 0,
+ Ok(())
+ }
+}
+
+#[derive(Bundle)]
+pub struct TurtleMetadataBundle {
+ _marker: Turtle,
+ parent: AbstractAnimalMetadataBundle,
+ home_pos: HomePos,
+ has_egg: HasEgg,
+ laying_egg: LayingEgg,
+ travel_pos: TravelPos,
+ going_home: GoingHome,
+ travelling: Travelling,
+}
+impl Default for TurtleMetadataBundle {
+ fn default() -> Self {
+ Self {
+ _marker: Turtle,
+ parent: AbstractAnimalMetadataBundle {
+ _marker: AbstractAnimal,
+ parent: AbstractAgeableMetadataBundle {
+ _marker: AbstractAgeable,
+ 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),
+ },
+ },
+ abstract_ageable_baby: AbstractAgeableBaby(false),
+ },
+ },
+ home_pos: HomePos(BlockPos::new(0, 0, 0)),
+ has_egg: HasEgg(false),
+ laying_egg: LayingEgg(false),
+ travel_pos: TravelPos(BlockPos::new(0, 0, 0)),
+ going_home: GoingHome(false),
+ travelling: Travelling(false),
}
}
}
+#[derive(Component, Deref, DerefMut)]
+pub struct VexFlags(pub u8);
+#[derive(Component)]
+pub struct Vex;
impl Vex {
- pub fn set_index(&mut self, index: u8, value: EntityDataValue) -> Option<()> {
- match index {
- 0..=15 => self.abstract_monster.set_index(index, value)?,
- 16 => self.flags = value.into_byte().ok()?,
+ pub fn apply_metadata(
+ entity: &mut azalea_ecs::system::EntityCommands,
+ d: EntityDataItem,
+ ) -> Result<(), UpdateMetadataError> {
+ match d.index {
+ 0..=15 => AbstractMonster::apply_metadata(entity, d)?,
+ 16 => {
+ entity.insert(VexFlags(d.value.into_byte()?));
+ }
_ => {}
}
- Some(())
- }
-}
-impl Deref for Vex {
- type Target = AbstractMonster;
- fn deref(&self) -> &Self::Target {
- &self.abstract_monster
- }
-}
-impl DerefMut for Vex {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.abstract_monster
- }
-}
-
-#[derive(Debug, Clone)]
-pub struct Villager {
- pub abstract_ageable: AbstractAgeable,
- pub unhappy_counter: i32,
- pub villager_data: VillagerData,
-}
-
-impl Villager {
- pub fn read(metadata: &mut VecDeque<EntityDataValue>) -> Option<Self> {
- let abstract_ageable = AbstractAgeable::read(metadata)?;
- let unhappy_counter = metadata.pop_front()?.into_int().ok()?;
- let villager_data = metadata.pop_front()?.into_villager_data().ok()?;
- Some(Self {
- abstract_ageable,
- unhappy_counter,
- villager_data,
- })
- }
-
- pub fn write(&self) -> Vec<EntityDataValue> {
- let mut metadata = Vec::new();
- metadata.extend(self.abstract_ageable.write());
- metadata.push(EntityDataValue::Int(self.unhappy_counter.clone()));
- metadata.push(EntityDataValue::VillagerData(self.villager_data.clone()));
- metadata
- }
-}
-
-impl Default for Villager {
- fn default() -> Self {
- Self {
- abstract_ageable: Default::default(),
- unhappy_counter: 0,
- villager_data: VillagerData {
- kind: azalea_registry::VillagerType::Plains,
- profession: azalea_registry::VillagerProfession::None,
- level: 0,
+ Ok(())
+ }
+}
+
+#[derive(Bundle)]
+pub struct VexMetadataBundle {
+ _marker: Vex,
+ parent: AbstractMonsterMetadataBundle,
+ vex_flags: VexFlags,
+}
+impl Default for VexMetadataBundle {
+ fn default() -> Self {
+ Self {
+ _marker: Vex,
+ 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),
+ },
+ },
},
+ vex_flags: VexFlags(0),
}
}
}
+#[derive(Component, Deref, DerefMut)]
+pub struct VillagerUnhappyCounter(pub i32);
+#[derive(Component, Deref, DerefMut)]
+pub struct VillagerVillagerData(pub VillagerData);
+#[derive(Component)]
+pub struct Villager;
impl Villager {
- pub fn set_index(&mut self, index: u8, value: EntityDataValue) -> Option<()> {
- match index {
- 0..=16 => self.abstract_ageable.set_index(index, value)?,
- 17 => self.unhappy_counter = value.into_int().ok()?,
- 18 => self.villager_data = value.into_villager_data().ok()?,
+ pub fn apply_metadata(
+ entity: &mut azalea_ecs::system::EntityCommands,
+ d: EntityDataItem,
+ ) -> Result<(), UpdateMetadataError> {
+ match d.index {
+ 0..=16 => AbstractAgeable::apply_metadata(entity, d)?,
+ 17 => {
+ entity.insert(VillagerUnhappyCounter(d.value.into_int()?));
+ }
+ 18 => {
+ entity.insert(VillagerVillagerData(d.value.into_villager_data()?));
+ }
_ => {}
}
- Some(())
- }
-}
-impl Deref for Villager {
- type Target = AbstractAgeable;
- fn deref(&self) -> &Self::Target {
- &self.abstract_ageable
- }
-}
-impl DerefMut for Villager {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.abstract_ageable
- }
-}
-
-#[derive(Debug, Clone)]
-pub struct Vindicator {
- pub abstract_monster: AbstractMonster,
- pub is_celebrating: bool,
-}
-
-impl Vindicator {
- pub fn read(metadata: &mut VecDeque<EntityDataValue>) -> Option<Self> {
- let abstract_monster = AbstractMonster::read(metadata)?;
- let is_celebrating = metadata.pop_front()?.into_boolean().ok()?;
- Some(Self {
- abstract_monster,
- is_celebrating,
- })
- }
-
- pub fn write(&self) -> Vec<EntityDataValue> {
- let mut metadata = Vec::new();
- metadata.extend(self.abstract_monster.write());
- metadata.push(EntityDataValue::Boolean(self.is_celebrating.clone()));
- metadata
- }
-}
-
-impl Default for Vindicator {
- fn default() -> Self {
- Self {
- abstract_monster: Default::default(),
- is_celebrating: false,
+ Ok(())
+ }
+}
+
+#[derive(Bundle)]
+pub struct VillagerMetadataBundle {
+ _marker: Villager,
+ parent: AbstractAgeableMetadataBundle,
+ villager_unhappy_counter: VillagerUnhappyCounter,
+ villager_villager_data: VillagerVillagerData,
+}
+impl Default for VillagerMetadataBundle {
+ fn default() -> Self {
+ Self {
+ _marker: Villager,
+ parent: AbstractAgeableMetadataBundle {
+ _marker: AbstractAgeable,
+ 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),
+ },
+ },
+ abstract_ageable_baby: AbstractAgeableBaby(false),
+ },
+ villager_unhappy_counter: VillagerUnhappyCounter(0),
+ villager_villager_data: VillagerVillagerData(VillagerData {
+ kind: azalea_registry::VillagerKind::Plains,
+ profession: azalea_registry::VillagerProfession::None,
+ level: 0,
+ }),
}
}
}
+#[derive(Component, Deref, DerefMut)]
+pub struct VindicatorIsCelebrating(pub bool);
+#[derive(Component)]
+pub struct Vindicator;
impl Vindicator {
- pub fn set_index(&mut self, index: u8, value: EntityDataValue) -> Option<()> {
- match index {
- 0..=15 => self.abstract_monster.set_index(index, value)?,
- 16 => self.is_celebrating = value.into_boolean().ok()?,
+ pub fn apply_metadata(
+ entity: &mut azalea_ecs::system::EntityCommands,
+ d: EntityDataItem,
+ ) -> Result<(), UpdateMetadataError> {
+ match d.index {
+ 0..=15 => AbstractMonster::apply_metadata(entity, d)?,
+ 16 => {
+ entity.insert(VindicatorIsCelebrating(d.value.into_boolean()?));
+ }
_ => {}
}
- Some(())
- }
-}
-impl Deref for Vindicator {
- type Target = AbstractMonster;
- fn deref(&self) -> &Self::Target {
- &self.abstract_monster
- }
-}
-impl DerefMut for Vindicator {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.abstract_monster
- }
-}
-
-#[derive(Debug, Clone)]
-pub struct WanderingTrader {
- pub abstract_ageable: AbstractAgeable,
- pub unhappy_counter: i32,
-}
-
-impl WanderingTrader {
- pub fn read(metadata: &mut VecDeque<EntityDataValue>) -> Option<Self> {
- let abstract_ageable = AbstractAgeable::read(metadata)?;
- let unhappy_counter = metadata.pop_front()?.into_int().ok()?;
- Some(Self {
- abstract_ageable,
- unhappy_counter,
- })
- }
-
- pub fn write(&self) -> Vec<EntityDataValue> {
- let mut metadata = Vec::new();
- metadata.extend(self.abstract_ageable.write());
- metadata.push(EntityDataValue::Int(self.unhappy_counter.clone()));
- metadata
- }
-}
-
-impl Default for WanderingTrader {
- fn default() -> Self {
- Self {
- abstract_ageable: Default::default(),
- unhappy_counter: 0,
+ Ok(())
+ }
+}
+
+#[derive(Bundle)]
+pub struct VindicatorMetadataBundle {
+ _marker: Vindicator,
+ parent: AbstractMonsterMetadataBundle,
+ vindicator_is_celebrating: VindicatorIsCelebrating,
+}
+impl Default for VindicatorMetadataBundle {
+ fn default() -> Self {
+ Self {
+ _marker: Vindicator,
+ 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),
+ },
+ },
+ },
+ vindicator_is_celebrating: VindicatorIsCelebrating(false),
}
}
}
+#[derive(Component, Deref, DerefMut)]
+pub struct WanderingTraderUnhappyCounter(pub i32);
+#[derive(Component)]
+pub struct WanderingTrader;
impl WanderingTrader {
- pub fn set_index(&mut self, index: u8, value: EntityDataValue) -> Option<()> {
- match index {
- 0..=16 => self.abstract_ageable.set_index(index, value)?,
- 17 => self.unhappy_counter = value.into_int().ok()?,
+ pub fn apply_metadata(
+ entity: &mut azalea_ecs::system::EntityCommands,
+ d: EntityDataItem,
+ ) -> Result<(), UpdateMetadataError> {
+ match d.index {
+ 0..=16 => AbstractAgeable::apply_metadata(entity, d)?,
+ 17 => {
+ entity.insert(WanderingTraderUnhappyCounter(d.value.into_int()?));
+ }
_ => {}
}
- Some(())
- }
-}
-impl Deref for WanderingTrader {
- type Target = AbstractAgeable;
- fn deref(&self) -> &Self::Target {
- &self.abstract_ageable
- }
-}
-impl DerefMut for WanderingTrader {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.abstract_ageable
- }
-}
-
-#[derive(Debug, Clone)]
-pub struct Warden {
- pub abstract_monster: AbstractMonster,
- pub client_anger_level: i32,
-}
-
-impl Warden {
- pub fn read(metadata: &mut VecDeque<EntityDataValue>) -> Option<Self> {
- let abstract_monster = AbstractMonster::read(metadata)?;
- let client_anger_level = metadata.pop_front()?.into_int().ok()?;
- Some(Self {
- abstract_monster,
- client_anger_level,
- })
- }
-
- pub fn write(&self) -> Vec<EntityDataValue> {
- let mut metadata = Vec::new();
- metadata.extend(self.abstract_monster.write());
- metadata.push(EntityDataValue::Int(self.client_anger_level.clone()));
- metadata
- }
-}
-
-impl Default for Warden {
- fn default() -> Self {
- Self {
- abstract_monster: Default::default(),
- client_anger_level: 0,
+ Ok(())
+ }
+}
+
+#[derive(Bundle)]
+pub struct WanderingTraderMetadataBundle {
+ _marker: WanderingTrader,
+ parent: AbstractAgeableMetadataBundle,
+ wandering_trader_unhappy_counter: WanderingTraderUnhappyCounter,
+}
+impl Default for WanderingTraderMetadataBundle {
+ fn default() -> Self {
+ Self {
+ _marker: WanderingTrader,
+ parent: AbstractAgeableMetadataBundle {
+ _marker: AbstractAgeable,
+ 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),
+ },
+ },
+ abstract_ageable_baby: AbstractAgeableBaby(false),
+ },
+ wandering_trader_unhappy_counter: WanderingTraderUnhappyCounter(0),
}
}
}
+#[derive(Component, Deref, DerefMut)]
+pub struct ClientAngerLevel(pub i32);
+#[derive(Component)]
+pub struct Warden;
impl Warden {
- pub fn set_index(&mut self, index: u8, value: EntityDataValue) -> Option<()> {
- match index {
- 0..=15 => self.abstract_monster.set_index(index, value)?,
- 16 => self.client_anger_level = value.into_int().ok()?,
+ pub fn apply_metadata(
+ entity: &mut azalea_ecs::system::EntityCommands,
+ d: EntityDataItem,
+ ) -> Result<(), UpdateMetadataError> {
+ match d.index {
+ 0..=15 => AbstractMonster::apply_metadata(entity, d)?,
+ 16 => {
+ entity.insert(ClientAngerLevel(d.value.into_int()?));
+ }
_ => {}
}
- Some(())
- }
-}
-impl Deref for Warden {
- type Target = AbstractMonster;
- fn deref(&self) -> &Self::Target {
- &self.abstract_monster
- }
-}
-impl DerefMut for Warden {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.abstract_monster
- }
-}
-
-#[derive(Debug, Clone)]
-pub struct Witch {
- pub abstract_monster: AbstractMonster,
- pub is_celebrating: bool,
- pub using_item: bool,
-}
-
-impl Witch {
- pub fn read(metadata: &mut VecDeque<EntityDataValue>) -> Option<Self> {
- let abstract_monster = AbstractMonster::read(metadata)?;
- let is_celebrating = metadata.pop_front()?.into_boolean().ok()?;
- let using_item = metadata.pop_front()?.into_boolean().ok()?;
- Some(Self {
- abstract_monster,
- is_celebrating,
- using_item,
- })
- }
-
- pub fn write(&self) -> Vec<EntityDataValue> {
- let mut metadata = Vec::new();
- metadata.extend(self.abstract_monster.write());
- metadata.push(EntityDataValue::Boolean(self.is_celebrating.clone()));
- metadata.push(EntityDataValue::Boolean(self.using_item.clone()));
- metadata
- }
-}
-
-impl Default for Witch {
- fn default() -> Self {
- Self {
- abstract_monster: Default::default(),
- is_celebrating: false,
- using_item: false,
+ Ok(())
+ }
+}
+
+#[derive(Bundle)]
+pub struct WardenMetadataBundle {
+ _marker: Warden,
+ parent: AbstractMonsterMetadataBundle,
+ client_anger_level: ClientAngerLevel,
+}
+impl Default for WardenMetadataBundle {
+ fn default() -> Self {
+ Self {
+ _marker: Warden,
+ 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),
+ },
+ },
+ },
+ client_anger_level: ClientAngerLevel(0),
}
}
}
+#[derive(Component, Deref, DerefMut)]
+pub struct WitchIsCelebrating(pub bool);
+#[derive(Component, Deref, DerefMut)]
+pub struct WitchUsingItem(pub bool);
+#[derive(Component)]
+pub struct Witch;
impl Witch {
- pub fn set_index(&mut self, index: u8, value: EntityDataValue) -> Option<()> {
- match index {
- 0..=15 => self.abstract_monster.set_index(index, value)?,
- 16 => self.is_celebrating = value.into_boolean().ok()?,
- 17 => self.using_item = value.into_boolean().ok()?,
+ pub fn apply_metadata(
+ entity: &mut azalea_ecs::system::EntityCommands,
+ d: EntityDataItem,
+ ) -> Result<(), UpdateMetadataError> {
+ match d.index {
+ 0..=15 => AbstractMonster::apply_metadata(entity, d)?,
+ 16 => {
+ entity.insert(WitchIsCelebrating(d.value.into_boolean()?));
+ }
+ 17 => {
+ entity.insert(WitchUsingItem(d.value.into_boolean()?));
+ }
_ => {}
}
- Some(())
- }
-}
-impl Deref for Witch {
- type Target = AbstractMonster;
- fn deref(&self) -> &Self::Target {
- &self.abstract_monster
- }
-}
-impl DerefMut for Witch {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.abstract_monster
- }
-}
-
-#[derive(Debug, Clone)]
-pub struct Wither {
- pub abstract_monster: AbstractMonster,
- pub target_a: i32,
- pub target_b: i32,
- pub target_c: i32,
- pub inv: i32,
-}
-
-impl Wither {
- pub fn read(metadata: &mut VecDeque<EntityDataValue>) -> Option<Self> {
- let abstract_monster = AbstractMonster::read(metadata)?;
- let target_a = metadata.pop_front()?.into_int().ok()?;
- let target_b = metadata.pop_front()?.into_int().ok()?;
- let target_c = metadata.pop_front()?.into_int().ok()?;
- let inv = metadata.pop_front()?.into_int().ok()?;
- Some(Self {
- abstract_monster,
- target_a,
- target_b,
- target_c,
- inv,
- })
- }
-
- pub fn write(&self) -> Vec<EntityDataValue> {
- let mut metadata = Vec::new();
- metadata.extend(self.abstract_monster.write());
- metadata.push(EntityDataValue::Int(self.target_a.clone()));
- metadata.push(EntityDataValue::Int(self.target_b.clone()));
- metadata.push(EntityDataValue::Int(self.target_c.clone()));
- metadata.push(EntityDataValue::Int(self.inv.clone()));
- metadata
- }
-}
-
-impl Default for Wither {
- fn default() -> Self {
- Self {
- abstract_monster: Default::default(),
- target_a: 0,
- target_b: 0,
- target_c: 0,
- inv: 0,
+ Ok(())
+ }
+}
+
+#[derive(Bundle)]
+pub struct WitchMetadataBundle {
+ _marker: Witch,
+ parent: AbstractMonsterMetadataBundle,
+ witch_is_celebrating: WitchIsCelebrating,
+ witch_using_item: WitchUsingItem,
+}
+impl Default for WitchMetadataBundle {
+ fn default() -> Self {
+ Self {
+ _marker: Witch,
+ 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),
+ },
+ },
+ },
+ witch_is_celebrating: WitchIsCelebrating(false),
+ witch_using_item: WitchUsingItem(false),
}
}
}
+#[derive(Component, Deref, DerefMut)]
+pub struct TargetA(pub i32);
+#[derive(Component, Deref, DerefMut)]
+pub struct TargetB(pub i32);
+#[derive(Component, Deref, DerefMut)]
+pub struct TargetC(pub i32);
+#[derive(Component, Deref, DerefMut)]
+pub struct Inv(pub i32);
+#[derive(Component)]
+pub struct Wither;
impl Wither {
- pub fn set_index(&mut self, index: u8, value: EntityDataValue) -> Option<()> {
- match index {
- 0..=15 => self.abstract_monster.set_index(index, value)?,
- 16 => self.target_a = value.into_int().ok()?,
- 17 => self.target_b = value.into_int().ok()?,
- 18 => self.target_c = value.into_int().ok()?,
- 19 => self.inv = value.into_int().ok()?,
+ pub fn apply_metadata(
+ entity: &mut azalea_ecs::system::EntityCommands,
+ d: EntityDataItem,
+ ) -> Result<(), UpdateMetadataError> {
+ match d.index {
+ 0..=15 => AbstractMonster::apply_metadata(entity, d)?,
+ 16 => {
+ entity.insert(TargetA(d.value.into_int()?));
+ }
+ 17 => {
+ entity.insert(TargetB(d.value.into_int()?));
+ }
+ 18 => {
+ entity.insert(TargetC(d.value.into_int()?));
+ }
+ 19 => {
+ entity.insert(Inv(d.value.into_int()?));
+ }
_ => {}
}
- Some(())
- }
-}
-impl Deref for Wither {
- type Target = AbstractMonster;
- fn deref(&self) -> &Self::Target {
- &self.abstract_monster
- }
-}
-impl DerefMut for Wither {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.abstract_monster
- }
-}
-
-#[derive(Debug, Clone)]
-pub struct WitherSkeleton {
- pub abstract_monster: AbstractMonster,
-}
-
-impl WitherSkeleton {
- pub fn read(metadata: &mut VecDeque<EntityDataValue>) -> Option<Self> {
- let abstract_monster = AbstractMonster::read(metadata)?;
- Some(Self { abstract_monster })
- }
-
- pub fn write(&self) -> Vec<EntityDataValue> {
- let mut metadata = Vec::new();
- metadata.extend(self.abstract_monster.write());
- metadata
- }
-}
-
-impl Default for WitherSkeleton {
- fn default() -> Self {
- Self {
- abstract_monster: Default::default(),
+ Ok(())
+ }
+}
+
+#[derive(Bundle)]
+pub struct WitherMetadataBundle {
+ _marker: Wither,
+ parent: AbstractMonsterMetadataBundle,
+ target_a: TargetA,
+ target_b: TargetB,
+ target_c: TargetC,
+ inv: Inv,
+}
+impl Default for WitherMetadataBundle {
+ fn default() -> Self {
+ Self {
+ _marker: Wither,
+ 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),
+ },
+ },
+ },
+ target_a: TargetA(0),
+ target_b: TargetB(0),
+ target_c: TargetC(0),
+ inv: Inv(0),
}
}
}
+#[derive(Component)]
+pub struct WitherSkeleton;
impl WitherSkeleton {
- pub fn set_index(&mut self, index: u8, value: EntityDataValue) -> Option<()> {
- self.abstract_monster.set_index(index, value)
- }
-}
-impl Deref for WitherSkeleton {
- type Target = AbstractMonster;
- fn deref(&self) -> &Self::Target {
- &self.abstract_monster
- }
-}
-impl DerefMut for WitherSkeleton {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.abstract_monster
- }
-}
-
-#[derive(Debug, Clone)]
-pub struct WitherSkull {
- pub abstract_entity: AbstractEntity,
- pub dangerous: bool,
-}
-
-impl WitherSkull {
- pub fn read(metadata: &mut VecDeque<EntityDataValue>) -> Option<Self> {
- let abstract_entity = AbstractEntity::read(metadata)?;
- let dangerous = metadata.pop_front()?.into_boolean().ok()?;
- Some(Self {
- abstract_entity,
- dangerous,
- })
- }
-
- pub fn write(&self) -> Vec<EntityDataValue> {
- let mut metadata = Vec::new();
- metadata.extend(self.abstract_entity.write());
- metadata.push(EntityDataValue::Boolean(self.dangerous.clone()));
- metadata
- }
-}
-
-impl Default for WitherSkull {
- fn default() -> Self {
- Self {
- abstract_entity: Default::default(),
- dangerous: false,
+ pub fn apply_metadata(
+ entity: &mut azalea_ecs::system::EntityCommands,
+ d: EntityDataItem,
+ ) -> Result<(), UpdateMetadataError> {
+ match d.index {
+ 0..=15 => AbstractMonster::apply_metadata(entity, d)?,
+ _ => {}
+ }
+ Ok(())
+ }
+}
+
+#[derive(Bundle)]
+pub struct WitherSkeletonMetadataBundle {
+ _marker: WitherSkeleton,
+ parent: AbstractMonsterMetadataBundle,
+}
+impl Default for WitherSkeletonMetadataBundle {
+ fn default() -> Self {
+ Self {
+ _marker: WitherSkeleton,
+ 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)]
+pub struct Dangerous(pub bool);
+#[derive(Component)]
+pub struct WitherSkull;
impl WitherSkull {
- pub fn set_index(&mut self, index: u8, value: EntityDataValue) -> Option<()> {
- match index {
- 0..=7 => self.abstract_entity.set_index(index, value)?,
- 8 => self.dangerous = value.into_boolean().ok()?,
+ pub fn apply_metadata(
+ entity: &mut azalea_ecs::system::EntityCommands,
+ d: EntityDataItem,
+ ) -> Result<(), UpdateMetadataError> {
+ match d.index {
+ 0..=7 => AbstractEntity::apply_metadata(entity, d)?,
+ 8 => {
+ entity.insert(Dangerous(d.value.into_boolean()?));
+ }
_ => {}
}
- Some(())
- }
-}
-impl Deref for WitherSkull {
- type Target = AbstractEntity;
- fn deref(&self) -> &Self::Target {
- &self.abstract_entity
- }
-}
-impl DerefMut for WitherSkull {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.abstract_entity
+ Ok(())
}
}
-#[derive(Debug, Clone)]
-pub struct Wolf {
- pub abstract_tameable: AbstractTameable,
- pub interested: bool,
- pub collar_color: i32,
- pub remaining_anger_time: i32,
+#[derive(Bundle)]
+pub struct WitherSkullMetadataBundle {
+ _marker: WitherSkull,
+ parent: AbstractEntityMetadataBundle,
+ dangerous: Dangerous,
}
-
-impl Wolf {
- pub fn read(metadata: &mut VecDeque<EntityDataValue>) -> Option<Self> {
- let abstract_tameable = AbstractTameable::read(metadata)?;
- let interested = metadata.pop_front()?.into_boolean().ok()?;
- let collar_color = metadata.pop_front()?.into_int().ok()?;
- let remaining_anger_time = metadata.pop_front()?.into_int().ok()?;
- Some(Self {
- abstract_tameable,
- interested,
- collar_color,
- remaining_anger_time,
- })
- }
-
- pub fn write(&self) -> Vec<EntityDataValue> {
- let mut metadata = Vec::new();
- metadata.extend(self.abstract_tameable.write());
- metadata.push(EntityDataValue::Boolean(self.interested.clone()));
- metadata.push(EntityDataValue::Int(self.collar_color.clone()));
- metadata.push(EntityDataValue::Int(self.remaining_anger_time.clone()));
- metadata
- }
-}
-
-impl Default for Wolf {
+impl Default for WitherSkullMetadataBundle {
fn default() -> Self {
Self {
- abstract_tameable: Default::default(),
- interested: false,
- collar_color: Default::default(),
- remaining_anger_time: 0,
+ _marker: WitherSkull,
+ 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),
+ },
+ dangerous: Dangerous(false),
}
}
}
+#[derive(Component, Deref, DerefMut)]
+pub struct WolfInterested(pub bool);
+#[derive(Component, Deref, DerefMut)]
+pub struct WolfCollarColor(pub i32);
+#[derive(Component, Deref, DerefMut)]
+pub struct WolfRemainingAngerTime(pub i32);
+#[derive(Component)]
+pub struct Wolf;
impl Wolf {
- pub fn set_index(&mut self, index: u8, value: EntityDataValue) -> Option<()> {
- match index {
- 0..=18 => self.abstract_tameable.set_index(index, value)?,
- 19 => self.interested = value.into_boolean().ok()?,
- 20 => self.collar_color = value.into_int().ok()?,
- 21 => self.remaining_anger_time = value.into_int().ok()?,
+ pub fn apply_metadata(
+ entity: &mut azalea_ecs::system::EntityCommands,
+ d: EntityDataItem,
+ ) -> Result<(), UpdateMetadataError> {
+ match d.index {
+ 0..=18 => AbstractTameable::apply_metadata(entity, d)?,
+ 19 => {
+ entity.insert(WolfInterested(d.value.into_boolean()?));
+ }
+ 20 => {
+ entity.insert(WolfCollarColor(d.value.into_int()?));
+ }
+ 21 => {
+ entity.insert(WolfRemainingAngerTime(d.value.into_int()?));
+ }
_ => {}
}
- Some(())
- }
-}
-impl Deref for Wolf {
- type Target = AbstractTameable;
- fn deref(&self) -> &Self::Target {
- &self.abstract_tameable
- }
-}
-impl DerefMut for Wolf {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.abstract_tameable
- }
-}
-
-#[derive(Debug, Clone)]
-pub struct Zoglin {
- pub abstract_monster: AbstractMonster,
- pub baby: bool,
-}
-
-impl Zoglin {
- pub fn read(metadata: &mut VecDeque<EntityDataValue>) -> Option<Self> {
- let abstract_monster = AbstractMonster::read(metadata)?;
- let baby = metadata.pop_front()?.into_boolean().ok()?;
- Some(Self {
- abstract_monster,
- baby,
- })
- }
-
- pub fn write(&self) -> Vec<EntityDataValue> {
- let mut metadata = Vec::new();
- metadata.extend(self.abstract_monster.write());
- metadata.push(EntityDataValue::Boolean(self.baby.clone()));
- metadata
- }
-}
-
-impl Default for Zoglin {
- fn default() -> Self {
- Self {
- abstract_monster: Default::default(),
- baby: false,
+ Ok(())
+ }
+}
+
+#[derive(Bundle)]
+pub struct WolfMetadataBundle {
+ _marker: Wolf,
+ parent: AbstractTameableMetadataBundle,
+ wolf_interested: WolfInterested,
+ wolf_collar_color: WolfCollarColor,
+ wolf_remaining_anger_time: WolfRemainingAngerTime,
+}
+impl Default for WolfMetadataBundle {
+ fn default() -> Self {
+ Self {
+ _marker: Wolf,
+ parent: AbstractTameableMetadataBundle {
+ _marker: AbstractTameable,
+ parent: AbstractAnimalMetadataBundle {
+ _marker: AbstractAnimal,
+ parent: AbstractAgeableMetadataBundle {
+ _marker: AbstractAgeable,
+ 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),
+ },
+ },
+ abstract_ageable_baby: AbstractAgeableBaby(false),
+ },
+ },
+ tame: Tame(false),
+ in_sitting_pose: InSittingPose(false),
+ owneruuid: Owneruuid(None),
+ },
+ wolf_interested: WolfInterested(false),
+ wolf_collar_color: WolfCollarColor(Default::default()),
+ wolf_remaining_anger_time: WolfRemainingAngerTime(0),
}
}
}
+#[derive(Component, Deref, DerefMut)]
+pub struct ZoglinBaby(pub bool);
+#[derive(Component)]
+pub struct Zoglin;
impl Zoglin {
- pub fn set_index(&mut self, index: u8, value: EntityDataValue) -> Option<()> {
- match index {
- 0..=15 => self.abstract_monster.set_index(index, value)?,
- 16 => self.baby = value.into_boolean().ok()?,
+ pub fn apply_metadata(
+ entity: &mut azalea_ecs::system::EntityCommands,
+ d: EntityDataItem,
+ ) -> Result<(), UpdateMetadataError> {
+ match d.index {
+ 0..=15 => AbstractMonster::apply_metadata(entity, d)?,
+ 16 => {
+ entity.insert(ZoglinBaby(d.value.into_boolean()?));
+ }
_ => {}
}
- Some(())
- }
-}
-impl Deref for Zoglin {
- type Target = AbstractMonster;
- fn deref(&self) -> &Self::Target {
- &self.abstract_monster
- }
-}
-impl DerefMut for Zoglin {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.abstract_monster
- }
-}
-
-#[derive(Debug, Clone)]
-pub struct Zombie {
- pub abstract_monster: AbstractMonster,
- pub baby: bool,
- pub special_type: i32,
- pub drowned_conversion: bool,
-}
-
-impl Zombie {
- pub fn read(metadata: &mut VecDeque<EntityDataValue>) -> Option<Self> {
- let abstract_monster = AbstractMonster::read(metadata)?;
- let baby = metadata.pop_front()?.into_boolean().ok()?;
- let special_type = metadata.pop_front()?.into_int().ok()?;
- let drowned_conversion = metadata.pop_front()?.into_boolean().ok()?;
- Some(Self {
- abstract_monster,
- baby,
- special_type,
- drowned_conversion,
- })
- }
-
- pub fn write(&self) -> Vec<EntityDataValue> {
- let mut metadata = Vec::new();
- metadata.extend(self.abstract_monster.write());
- metadata.push(EntityDataValue::Boolean(self.baby.clone()));
- metadata.push(EntityDataValue::Int(self.special_type.clone()));
- metadata.push(EntityDataValue::Boolean(self.drowned_conversion.clone()));
- metadata
- }
-}
-
-impl Default for Zombie {
- fn default() -> Self {
- Self {
- abstract_monster: Default::default(),
- baby: false,
- special_type: 0,
- drowned_conversion: false,
+ Ok(())
+ }
+}
+
+#[derive(Bundle)]
+pub struct ZoglinMetadataBundle {
+ _marker: Zoglin,
+ parent: AbstractMonsterMetadataBundle,
+ zoglin_baby: ZoglinBaby,
+}
+impl Default for ZoglinMetadataBundle {
+ fn default() -> Self {
+ Self {
+ _marker: Zoglin,
+ 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),
+ },
+ },
+ },
+ zoglin_baby: ZoglinBaby(false),
}
}
}
+#[derive(Component)]
+pub struct Zombie;
impl Zombie {
- pub fn set_index(&mut self, index: u8, value: EntityDataValue) -> Option<()> {
- match index {
- 0..=15 => self.abstract_monster.set_index(index, value)?,
- 16 => self.baby = value.into_boolean().ok()?,
- 17 => self.special_type = value.into_int().ok()?,
- 18 => self.drowned_conversion = value.into_boolean().ok()?,
+ pub fn apply_metadata(
+ entity: &mut azalea_ecs::system::EntityCommands,
+ d: EntityDataItem,
+ ) -> Result<(), UpdateMetadataError> {
+ match d.index {
+ 0..=15 => AbstractMonster::apply_metadata(entity, d)?,
+ 16 => {
+ entity.insert(ZombieBaby(d.value.into_boolean()?));
+ }
+ 17 => {
+ entity.insert(SpecialType(d.value.into_int()?));
+ }
+ 18 => {
+ entity.insert(DrownedConversion(d.value.into_boolean()?));
+ }
_ => {}
}
- Some(())
- }
-}
-impl Deref for Zombie {
- type Target = AbstractMonster;
- fn deref(&self) -> &Self::Target {
- &self.abstract_monster
- }
-}
-impl DerefMut for Zombie {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.abstract_monster
- }
-}
-
-#[derive(Debug, Clone)]
-pub struct ZombieHorse {
- pub abstract_animal: AbstractAnimal,
- pub tamed: bool,
- pub eating: bool,
- pub standing: bool,
- pub bred: bool,
- pub saddled: bool,
- pub owner_uuid: Option<Uuid>,
-}
-
-impl ZombieHorse {
- pub fn read(metadata: &mut VecDeque<EntityDataValue>) -> Option<Self> {
- let abstract_animal = AbstractAnimal::read(metadata)?;
- let bitfield = metadata.pop_front()?.into_byte().ok()?;
- let tamed = bitfield & 0x2 != 0;
- let eating = bitfield & 0x10 != 0;
- let standing = bitfield & 0x20 != 0;
- let bred = bitfield & 0x8 != 0;
- let saddled = bitfield & 0x4 != 0;
- let owner_uuid = metadata.pop_front()?.into_optional_uuid().ok()?;
- Some(Self {
- abstract_animal,
- tamed,
- eating,
- standing,
- bred,
- saddled,
- owner_uuid,
- })
- }
-
- pub fn write(&self) -> Vec<EntityDataValue> {
- let mut metadata = Vec::new();
- metadata.extend(self.abstract_animal.write());
- let mut bitfield = 0u8;
- if self.tamed {
- bitfield &= 0x2;
- }
- if self.eating {
- bitfield &= 0x10;
- }
- if self.standing {
- bitfield &= 0x20;
- }
- if self.bred {
- bitfield &= 0x8;
- }
- if self.saddled {
- bitfield &= 0x4;
- }
- metadata.push(EntityDataValue::Byte(bitfield));
- metadata.push(EntityDataValue::OptionalUuid(self.owner_uuid.clone()));
- metadata
- }
-}
-
-impl Default for ZombieHorse {
- fn default() -> Self {
- Self {
- abstract_animal: Default::default(),
- tamed: false,
- eating: false,
- standing: false,
- bred: false,
- saddled: false,
- owner_uuid: None,
- }
- }
-}
-
+ Ok(())
+ }
+}
+
+#[derive(Bundle)]
+pub struct ZombieMetadataBundle {
+ _marker: Zombie,
+ parent: AbstractMonsterMetadataBundle,
+ zombie_baby: ZombieBaby,
+ special_type: SpecialType,
+ drowned_conversion: DrownedConversion,
+}
+impl Default for ZombieMetadataBundle {
+ fn default() -> Self {
+ Self {
+ _marker: Zombie,
+ 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),
+ },
+ },
+ },
+ zombie_baby: ZombieBaby(false),
+ special_type: SpecialType(0),
+ drowned_conversion: DrownedConversion(false),
+ }
+ }
+}
+
+#[derive(Component, Deref, DerefMut)]
+pub struct ZombieHorseTamed(pub bool);
+#[derive(Component, Deref, DerefMut)]
+pub struct ZombieHorseEating(pub bool);
+#[derive(Component, Deref, DerefMut)]
+pub struct ZombieHorseStanding(pub bool);
+#[derive(Component, Deref, DerefMut)]
+pub struct ZombieHorseBred(pub bool);
+#[derive(Component, Deref, DerefMut)]
+pub struct ZombieHorseSaddled(pub bool);
+#[derive(Component, Deref, DerefMut)]
+pub struct ZombieHorseOwnerUuid(pub Option<Uuid>);
+#[derive(Component)]
+pub struct ZombieHorse;
impl ZombieHorse {
- pub fn set_index(&mut self, index: u8, value: EntityDataValue) -> Option<()> {
- match index {
- 0..=16 => self.abstract_animal.set_index(index, value)?,
+ pub fn apply_metadata(
+ entity: &mut azalea_ecs::system::EntityCommands,
+ d: EntityDataItem,
+ ) -> Result<(), UpdateMetadataError> {
+ match d.index {
+ 0..=16 => AbstractAnimal::apply_metadata(entity, d)?,
17 => {
- let bitfield = value.into_byte().ok()?;
- self.tamed = bitfield & 0x2 != 0;
- self.eating = bitfield & 0x10 != 0;
- self.standing = bitfield & 0x20 != 0;
- self.bred = bitfield & 0x8 != 0;
- self.saddled = bitfield & 0x4 != 0;
+ let bitfield = d.value.into_byte()?;
+ entity.insert(ZombieHorseTamed(bitfield & 0x2 != 0));
+ entity.insert(ZombieHorseEating(bitfield & 0x10 != 0));
+ entity.insert(ZombieHorseStanding(bitfield & 0x20 != 0));
+ entity.insert(ZombieHorseBred(bitfield & 0x8 != 0));
+ entity.insert(ZombieHorseSaddled(bitfield & 0x4 != 0));
+ }
+ 18 => {
+ entity.insert(ZombieHorseOwnerUuid(d.value.into_optional_uuid()?));
}
- 18 => self.owner_uuid = value.into_optional_uuid().ok()?,
_ => {}
}
- Some(())
- }
-}
-impl Deref for ZombieHorse {
- type Target = AbstractAnimal;
- fn deref(&self) -> &Self::Target {
- &self.abstract_animal
- }
-}
-impl DerefMut for ZombieHorse {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.abstract_animal
- }
-}
-
-#[derive(Debug, Clone)]
-pub struct ZombieVillager {
- pub zombie: Zombie,
- pub converting: bool,
- pub villager_data: VillagerData,
-}
-
-impl ZombieVillager {
- pub fn read(metadata: &mut VecDeque<EntityDataValue>) -> Option<Self> {
- let zombie = Zombie::read(metadata)?;
- let converting = metadata.pop_front()?.into_boolean().ok()?;
- let villager_data = metadata.pop_front()?.into_villager_data().ok()?;
- Some(Self {
- zombie,
- converting,
- villager_data,
- })
- }
-
- pub fn write(&self) -> Vec<EntityDataValue> {
- let mut metadata = Vec::new();
- metadata.extend(self.zombie.write());
- metadata.push(EntityDataValue::Boolean(self.converting.clone()));
- metadata.push(EntityDataValue::VillagerData(self.villager_data.clone()));
- metadata
- }
-}
-
-impl Default for ZombieVillager {
- fn default() -> Self {
- Self {
- zombie: Default::default(),
- converting: false,
- villager_data: VillagerData {
- kind: azalea_registry::VillagerType::Plains,
- profession: azalea_registry::VillagerProfession::None,
- level: 0,
+ Ok(())
+ }
+}
+
+#[derive(Bundle)]
+pub struct ZombieHorseMetadataBundle {
+ _marker: ZombieHorse,
+ parent: AbstractAnimalMetadataBundle,
+ zombie_horse_tamed: ZombieHorseTamed,
+ zombie_horse_eating: ZombieHorseEating,
+ zombie_horse_standing: ZombieHorseStanding,
+ zombie_horse_bred: ZombieHorseBred,
+ zombie_horse_saddled: ZombieHorseSaddled,
+ zombie_horse_owner_uuid: ZombieHorseOwnerUuid,
+}
+impl Default for ZombieHorseMetadataBundle {
+ fn default() -> Self {
+ Self {
+ _marker: ZombieHorse,
+ parent: AbstractAnimalMetadataBundle {
+ _marker: AbstractAnimal,
+ parent: AbstractAgeableMetadataBundle {
+ _marker: AbstractAgeable,
+ 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),
+ },
+ },
+ abstract_ageable_baby: AbstractAgeableBaby(false),
+ },
},
+ zombie_horse_tamed: ZombieHorseTamed(false),
+ zombie_horse_eating: ZombieHorseEating(false),
+ zombie_horse_standing: ZombieHorseStanding(false),
+ zombie_horse_bred: ZombieHorseBred(false),
+ zombie_horse_saddled: ZombieHorseSaddled(false),
+ zombie_horse_owner_uuid: ZombieHorseOwnerUuid(None),
}
}
}
+#[derive(Component, Deref, DerefMut)]
+pub struct Converting(pub bool);
+#[derive(Component, Deref, DerefMut)]
+pub struct ZombieVillagerVillagerData(pub VillagerData);
+#[derive(Component)]
+pub struct ZombieVillager;
impl ZombieVillager {
- pub fn set_index(&mut self, index: u8, value: EntityDataValue) -> Option<()> {
- match index {
- 0..=18 => self.zombie.set_index(index, value)?,
- 19 => self.converting = value.into_boolean().ok()?,
- 20 => self.villager_data = value.into_villager_data().ok()?,
+ pub fn apply_metadata(
+ entity: &mut azalea_ecs::system::EntityCommands,
+ d: EntityDataItem,
+ ) -> Result<(), UpdateMetadataError> {
+ match d.index {
+ 0..=18 => Zombie::apply_metadata(entity, d)?,
+ 19 => {
+ entity.insert(Converting(d.value.into_boolean()?));
+ }
+ 20 => {
+ entity.insert(ZombieVillagerVillagerData(d.value.into_villager_data()?));
+ }
_ => {}
}
- Some(())
- }
-}
-impl Deref for ZombieVillager {
- type Target = Zombie;
- fn deref(&self) -> &Self::Target {
- &self.zombie
- }
-}
-impl DerefMut for ZombieVillager {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.zombie
- }
-}
-
-#[derive(Debug, Clone)]
-pub struct ZombifiedPiglin {
- pub zombie: Zombie,
-}
-
-impl ZombifiedPiglin {
- pub fn read(metadata: &mut VecDeque<EntityDataValue>) -> Option<Self> {
- let zombie = Zombie::read(metadata)?;
- Some(Self { zombie })
- }
-
- pub fn write(&self) -> Vec<EntityDataValue> {
- let mut metadata = Vec::new();
- metadata.extend(self.zombie.write());
- metadata
- }
-}
-
-impl Default for ZombifiedPiglin {
- fn default() -> Self {
- Self {
- zombie: Default::default(),
+ Ok(())
+ }
+}
+
+#[derive(Bundle)]
+pub struct ZombieVillagerMetadataBundle {
+ _marker: ZombieVillager,
+ parent: ZombieMetadataBundle,
+ converting: Converting,
+ zombie_villager_villager_data: ZombieVillagerVillagerData,
+}
+impl Default for ZombieVillagerMetadataBundle {
+ fn default() -> Self {
+ Self {
+ _marker: ZombieVillager,
+ parent: ZombieMetadataBundle {
+ _marker: Zombie,
+ 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),
+ },
+ },
+ },
+ zombie_baby: ZombieBaby(false),
+ special_type: SpecialType(0),
+ drowned_conversion: DrownedConversion(false),
+ },
+ converting: Converting(false),
+ zombie_villager_villager_data: ZombieVillagerVillagerData(VillagerData {
+ kind: azalea_registry::VillagerKind::Plains,
+ profession: azalea_registry::VillagerProfession::None,
+ level: 0,
+ }),
}
}
}
+#[derive(Component)]
+pub struct ZombifiedPiglin;
impl ZombifiedPiglin {
- pub fn set_index(&mut self, index: u8, value: EntityDataValue) -> Option<()> {
- self.zombie.set_index(index, value)
- }
-}
-impl Deref for ZombifiedPiglin {
- type Target = Zombie;
- fn deref(&self) -> &Self::Target {
- &self.zombie
- }
-}
-impl DerefMut for ZombifiedPiglin {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.zombie
- }
-}
-
-#[derive(Debug, Clone)]
-pub struct AbstractAgeable {
- pub abstract_creature: AbstractCreature,
- pub baby: bool,
-}
-
-impl AbstractAgeable {
- pub fn read(metadata: &mut VecDeque<EntityDataValue>) -> Option<Self> {
- let abstract_creature = AbstractCreature::read(metadata)?;
- let baby = metadata.pop_front()?.into_boolean().ok()?;
- Some(Self {
- abstract_creature,
- baby,
- })
- }
-
- pub fn write(&self) -> Vec<EntityDataValue> {
- let mut metadata = Vec::new();
- metadata.extend(self.abstract_creature.write());
- metadata.push(EntityDataValue::Boolean(self.baby.clone()));
- metadata
- }
-}
-
-impl Default for AbstractAgeable {
- fn default() -> Self {
- Self {
- abstract_creature: Default::default(),
- baby: false,
+ pub fn apply_metadata(
+ entity: &mut azalea_ecs::system::EntityCommands,
+ d: EntityDataItem,
+ ) -> Result<(), UpdateMetadataError> {
+ match d.index {
+ 0..=18 => Zombie::apply_metadata(entity, d)?,
+ _ => {}
+ }
+ Ok(())
+ }
+}
+
+#[derive(Bundle)]
+pub struct ZombifiedPiglinMetadataBundle {
+ _marker: ZombifiedPiglin,
+ parent: ZombieMetadataBundle,
+}
+impl Default for ZombifiedPiglinMetadataBundle {
+ fn default() -> Self {
+ Self {
+ _marker: ZombifiedPiglin,
+ parent: ZombieMetadataBundle {
+ _marker: Zombie,
+ 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),
+ },
+ },
+ },
+ zombie_baby: ZombieBaby(false),
+ special_type: SpecialType(0),
+ drowned_conversion: DrownedConversion(false),
+ },
}
}
}
+#[derive(Component)]
+pub struct AbstractAgeable;
impl AbstractAgeable {
- pub fn set_index(&mut self, index: u8, value: EntityDataValue) -> Option<()> {
- match index {
- 0..=15 => self.abstract_creature.set_index(index, value)?,
- 16 => self.baby = value.into_boolean().ok()?,
+ pub fn apply_metadata(
+ entity: &mut azalea_ecs::system::EntityCommands,
+ d: EntityDataItem,
+ ) -> Result<(), UpdateMetadataError> {
+ match d.index {
+ 0..=15 => AbstractCreature::apply_metadata(entity, d)?,
+ 16 => {
+ entity.insert(AbstractAgeableBaby(d.value.into_boolean()?));
+ }
_ => {}
}
- Some(())
- }
-}
-impl Deref for AbstractAgeable {
- type Target = AbstractCreature;
- fn deref(&self) -> &Self::Target {
- &self.abstract_creature
- }
-}
-impl DerefMut for AbstractAgeable {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.abstract_creature
- }
-}
-
-#[derive(Debug, Clone)]
-pub struct AbstractAnimal {
- pub abstract_ageable: AbstractAgeable,
-}
-
-impl AbstractAnimal {
- pub fn read(metadata: &mut VecDeque<EntityDataValue>) -> Option<Self> {
- let abstract_ageable = AbstractAgeable::read(metadata)?;
- Some(Self { abstract_ageable })
- }
-
- pub fn write(&self) -> Vec<EntityDataValue> {
- let mut metadata = Vec::new();
- metadata.extend(self.abstract_ageable.write());
- metadata
- }
-}
-
-impl Default for AbstractAnimal {
- fn default() -> Self {
- Self {
- abstract_ageable: Default::default(),
+ Ok(())
+ }
+}
+
+#[derive(Bundle)]
+pub struct AbstractAgeableMetadataBundle {
+ _marker: AbstractAgeable,
+ parent: AbstractCreatureMetadataBundle,
+ abstract_ageable_baby: AbstractAgeableBaby,
+}
+impl Default for AbstractAgeableMetadataBundle {
+ fn default() -> Self {
+ Self {
+ _marker: AbstractAgeable,
+ 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),
+ },
+ },
+ abstract_ageable_baby: AbstractAgeableBaby(false),
}
}
}
+#[derive(Component)]
+pub struct AbstractAnimal;
impl AbstractAnimal {
- pub fn set_index(&mut self, index: u8, value: EntityDataValue) -> Option<()> {
- self.abstract_ageable.set_index(index, value)
- }
-}
-impl Deref for AbstractAnimal {
- type Target = AbstractAgeable;
- fn deref(&self) -> &Self::Target {
- &self.abstract_ageable
- }
-}
-impl DerefMut for AbstractAnimal {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.abstract_ageable
- }
-}
-
-#[derive(Debug, Clone)]
-pub struct AbstractCreature {
- pub abstract_insentient: AbstractInsentient,
-}
-
-impl AbstractCreature {
- pub fn read(metadata: &mut VecDeque<EntityDataValue>) -> Option<Self> {
- let abstract_insentient = AbstractInsentient::read(metadata)?;
- Some(Self {
- abstract_insentient,
- })
- }
-
- pub fn write(&self) -> Vec<EntityDataValue> {
- let mut metadata = Vec::new();
- metadata.extend(self.abstract_insentient.write());
- metadata
- }
-}
-
-impl Default for AbstractCreature {
- fn default() -> Self {
- Self {
- abstract_insentient: Default::default(),
+ pub fn apply_metadata(
+ entity: &mut azalea_ecs::system::EntityCommands,
+ d: EntityDataItem,
+ ) -> Result<(), UpdateMetadataError> {
+ match d.index {
+ 0..=16 => AbstractAgeable::apply_metadata(entity, d)?,
+ _ => {}
+ }
+ Ok(())
+ }
+}
+
+#[derive(Bundle)]
+pub struct AbstractAnimalMetadataBundle {
+ _marker: AbstractAnimal,
+ parent: AbstractAgeableMetadataBundle,
+}
+impl Default for AbstractAnimalMetadataBundle {
+ fn default() -> Self {
+ Self {
+ _marker: AbstractAnimal,
+ parent: AbstractAgeableMetadataBundle {
+ _marker: AbstractAgeable,
+ 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),
+ },
+ },
+ abstract_ageable_baby: AbstractAgeableBaby(false),
+ },
}
}
}
+#[derive(Component)]
+pub struct AbstractCreature;
impl AbstractCreature {
- pub fn set_index(&mut self, index: u8, value: EntityDataValue) -> Option<()> {
- self.abstract_insentient.set_index(index, value)
- }
-}
-impl Deref for AbstractCreature {
- type Target = AbstractInsentient;
- fn deref(&self) -> &Self::Target {
- &self.abstract_insentient
- }
-}
-impl DerefMut for AbstractCreature {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.abstract_insentient
- }
-}
-
-#[derive(Debug, Clone)]
-pub struct AbstractEntity {
- pub on_fire: bool,
- pub shift_key_down: bool,
- pub sprinting: bool,
- pub swimming: bool,
- pub currently_glowing: bool,
- pub invisible: bool,
- pub fall_flying: bool,
- pub air_supply: i32,
- pub custom_name: Option<Component>,
- pub custom_name_visible: bool,
- pub silent: bool,
- pub no_gravity: bool,
- pub pose: Pose,
- pub ticks_frozen: i32,
-}
-
-impl AbstractEntity {
- pub fn read(metadata: &mut VecDeque<EntityDataValue>) -> Option<Self> {
- let bitfield = metadata.pop_front()?.into_byte().ok()?;
- let on_fire = bitfield & 0x1 != 0;
- let shift_key_down = bitfield & 0x2 != 0;
- let sprinting = bitfield & 0x8 != 0;
- let swimming = bitfield & 0x10 != 0;
- let currently_glowing = bitfield & 0x40 != 0;
- let invisible = bitfield & 0x20 != 0;
- let fall_flying = bitfield & 0x80 != 0;
- let air_supply = metadata.pop_front()?.into_int().ok()?;
- let custom_name = metadata.pop_front()?.into_optional_component().ok()?;
- let custom_name_visible = metadata.pop_front()?.into_boolean().ok()?;
- let silent = metadata.pop_front()?.into_boolean().ok()?;
- let no_gravity = metadata.pop_front()?.into_boolean().ok()?;
- let pose = metadata.pop_front()?.into_pose().ok()?;
- let ticks_frozen = metadata.pop_front()?.into_int().ok()?;
- Some(Self {
- on_fire,
- shift_key_down,
- sprinting,
- swimming,
- currently_glowing,
- invisible,
- fall_flying,
- air_supply,
- custom_name,
- custom_name_visible,
- silent,
- no_gravity,
- pose,
- ticks_frozen,
- })
- }
-
- pub fn write(&self) -> Vec<EntityDataValue> {
- let mut metadata = Vec::new();
- let mut bitfield = 0u8;
- if self.on_fire {
- bitfield &= 0x1;
- }
- if self.shift_key_down {
- bitfield &= 0x2;
- }
- if self.sprinting {
- bitfield &= 0x8;
- }
- if self.swimming {
- bitfield &= 0x10;
- }
- if self.currently_glowing {
- bitfield &= 0x40;
- }
- if self.invisible {
- bitfield &= 0x20;
- }
- if self.fall_flying {
- bitfield &= 0x80;
- }
- metadata.push(EntityDataValue::Byte(bitfield));
- metadata.push(EntityDataValue::Int(self.air_supply.clone()));
- metadata.push(EntityDataValue::OptionalComponent(self.custom_name.clone()));
- metadata.push(EntityDataValue::Boolean(self.custom_name_visible.clone()));
- metadata.push(EntityDataValue::Boolean(self.silent.clone()));
- metadata.push(EntityDataValue::Boolean(self.no_gravity.clone()));
- metadata.push(EntityDataValue::Pose(self.pose.clone()));
- metadata.push(EntityDataValue::Int(self.ticks_frozen.clone()));
- metadata
- }
-}
-
-impl Default for AbstractEntity {
- fn default() -> Self {
- Self {
- on_fire: false,
- shift_key_down: false,
- sprinting: false,
- swimming: false,
- currently_glowing: false,
- invisible: false,
- fall_flying: false,
- air_supply: Default::default(),
- custom_name: None,
- custom_name_visible: false,
- silent: false,
- no_gravity: false,
- pose: Default::default(),
- ticks_frozen: 0,
+ pub fn apply_metadata(
+ entity: &mut azalea_ecs::system::EntityCommands,
+ d: EntityDataItem,
+ ) -> Result<(), UpdateMetadataError> {
+ match d.index {
+ 0..=15 => AbstractInsentient::apply_metadata(entity, d)?,
+ _ => {}
+ }
+ Ok(())
+ }
+}
+
+#[derive(Bundle)]
+pub struct AbstractCreatureMetadataBundle {
+ _marker: AbstractCreature,
+ parent: AbstractInsentientMetadataBundle,
+}
+impl Default for AbstractCreatureMetadataBundle {
+ fn default() -> Self {
+ Self {
+ _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)]
+pub struct AbstractEntity;
impl AbstractEntity {
- pub fn set_index(&mut self, index: u8, value: EntityDataValue) -> Option<()> {
- match index {
+ pub fn apply_metadata(
+ entity: &mut azalea_ecs::system::EntityCommands,
+ d: EntityDataItem,
+ ) -> Result<(), UpdateMetadataError> {
+ match d.index {
0 => {
- let bitfield = value.into_byte().ok()?;
- self.on_fire = bitfield & 0x1 != 0;
- self.shift_key_down = bitfield & 0x2 != 0;
- self.sprinting = bitfield & 0x8 != 0;
- self.swimming = bitfield & 0x10 != 0;
- self.currently_glowing = bitfield & 0x40 != 0;
- self.invisible = bitfield & 0x20 != 0;
- self.fall_flying = bitfield & 0x80 != 0;
+ let bitfield = d.value.into_byte()?;
+ entity.insert(OnFire(bitfield & 0x1 != 0));
+ entity.insert(ShiftKeyDown(bitfield & 0x2 != 0));
+ entity.insert(Sprinting(bitfield & 0x8 != 0));
+ entity.insert(Swimming(bitfield & 0x10 != 0));
+ entity.insert(CurrentlyGlowing(bitfield & 0x40 != 0));
+ entity.insert(Invisible(bitfield & 0x20 != 0));
+ entity.insert(FallFlying(bitfield & 0x80 != 0));
+ }
+ 1 => {
+ entity.insert(AirSupply(d.value.into_int()?));
+ }
+ 2 => {
+ entity.insert(CustomName(d.value.into_optional_formatted_text()?));
+ }
+ 3 => {
+ entity.insert(CustomNameVisible(d.value.into_boolean()?));
+ }
+ 4 => {
+ entity.insert(Silent(d.value.into_boolean()?));
+ }
+ 5 => {
+ entity.insert(NoGravity(d.value.into_boolean()?));
+ }
+ 6 => {
+ entity.insert(d.value.into_pose()?);
+ }
+ 7 => {
+ entity.insert(TicksFrozen(d.value.into_int()?));
}
- 1 => self.air_supply = value.into_int().ok()?,
- 2 => self.custom_name = value.into_optional_component().ok()?,
- 3 => self.custom_name_visible = value.into_boolean().ok()?,
- 4 => self.silent = value.into_boolean().ok()?,
- 5 => self.no_gravity = value.into_boolean().ok()?,
- 6 => self.pose = value.into_pose().ok()?,
- 7 => self.ticks_frozen = value.into_int().ok()?,
_ => {}
}
- Some(())
+ Ok(())
}
}
-#[derive(Debug, Clone)]
-pub struct AbstractInsentient {
- pub abstract_living: AbstractLiving,
- pub no_ai: bool,
- pub left_handed: bool,
- pub aggressive: bool,
-}
-
-impl AbstractInsentient {
- pub fn read(metadata: &mut VecDeque<EntityDataValue>) -> Option<Self> {
- let abstract_living = AbstractLiving::read(metadata)?;
- let bitfield = metadata.pop_front()?.into_byte().ok()?;
- let no_ai = bitfield & 0x1 != 0;
- let left_handed = bitfield & 0x2 != 0;
- let aggressive = bitfield & 0x4 != 0;
- Some(Self {
- abstract_living,
- no_ai,
- left_handed,
- aggressive,
- })
- }
- pub fn write(&self) -> Vec<EntityDataValue> {
- let mut metadata = Vec::new();
- metadata.extend(self.abstract_living.write());
- let mut bitfield = 0u8;
- if self.no_ai {
- bitfield &= 0x1;
- }
- if self.left_handed {
- bitfield &= 0x2;
- }
- if self.aggressive {
- bitfield &= 0x4;
- }
- metadata.push(EntityDataValue::Byte(bitfield));
- metadata
- }
+#[derive(Bundle)]
+pub struct AbstractEntityMetadataBundle {
+ _marker: AbstractEntity,
+ on_fire: OnFire,
+ shift_key_down: ShiftKeyDown,
+ sprinting: Sprinting,
+ swimming: Swimming,
+ currently_glowing: CurrentlyGlowing,
+ invisible: Invisible,
+ fall_flying: FallFlying,
+ air_supply: AirSupply,
+ custom_name: CustomName,
+ custom_name_visible: CustomNameVisible,
+ silent: Silent,
+ no_gravity: NoGravity,
+ pose: Pose,
+ ticks_frozen: TicksFrozen,
}
-
-impl Default for AbstractInsentient {
+impl Default for AbstractEntityMetadataBundle {
fn default() -> Self {
Self {
- abstract_living: Default::default(),
- no_ai: false,
- left_handed: false,
- aggressive: false,
+ _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)]
+pub struct AbstractInsentient;
impl AbstractInsentient {
- pub fn set_index(&mut self, index: u8, value: EntityDataValue) -> Option<()> {
- match index {
- 0..=14 => self.abstract_living.set_index(index, value)?,
+ pub fn apply_metadata(
+ entity: &mut azalea_ecs::system::EntityCommands,
+ d: EntityDataItem,
+ ) -> Result<(), UpdateMetadataError> {
+ match d.index {
+ 0..=14 => AbstractLiving::apply_metadata(entity, d)?,
15 => {
- let bitfield = value.into_byte().ok()?;
- self.no_ai = bitfield & 0x1 != 0;
- self.left_handed = bitfield & 0x2 != 0;
- self.aggressive = bitfield & 0x4 != 0;
+ let bitfield = d.value.into_byte()?;
+ entity.insert(NoAi(bitfield & 0x1 != 0));
+ entity.insert(LeftHanded(bitfield & 0x2 != 0));
+ entity.insert(Aggressive(bitfield & 0x4 != 0));
}
_ => {}
}
- Some(())
- }
-}
-impl Deref for AbstractInsentient {
- type Target = AbstractLiving;
- fn deref(&self) -> &Self::Target {
- &self.abstract_living
- }
-}
-impl DerefMut for AbstractInsentient {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.abstract_living
- }
-}
-
-#[derive(Debug, Clone)]
-pub struct AbstractLiving {
- pub abstract_entity: AbstractEntity,
- pub auto_spin_attack: bool,
- pub using_item: bool,
- pub health: f32,
- pub effect_color: i32,
- pub effect_ambience: bool,
- pub arrow_count: i32,
- pub stinger_count: i32,
- pub sleeping_pos: Option<BlockPos>,
-}
-
-impl AbstractLiving {
- pub fn read(metadata: &mut VecDeque<EntityDataValue>) -> Option<Self> {
- let abstract_entity = AbstractEntity::read(metadata)?;
- let bitfield = metadata.pop_front()?.into_byte().ok()?;
- let auto_spin_attack = bitfield & 0x4 != 0;
- let using_item = bitfield & 0x1 != 0;
- let health = metadata.pop_front()?.into_float().ok()?;
- let effect_color = metadata.pop_front()?.into_int().ok()?;
- let effect_ambience = metadata.pop_front()?.into_boolean().ok()?;
- let arrow_count = metadata.pop_front()?.into_int().ok()?;
- let stinger_count = metadata.pop_front()?.into_int().ok()?;
- let sleeping_pos = metadata.pop_front()?.into_optional_block_pos().ok()?;
- Some(Self {
- abstract_entity,
- auto_spin_attack,
- using_item,
- health,
- effect_color,
- effect_ambience,
- arrow_count,
- stinger_count,
- sleeping_pos,
- })
- }
-
- pub fn write(&self) -> Vec<EntityDataValue> {
- let mut metadata = Vec::new();
- metadata.extend(self.abstract_entity.write());
- let mut bitfield = 0u8;
- if self.auto_spin_attack {
- bitfield &= 0x4;
- }
- if self.using_item {
- bitfield &= 0x1;
- }
- metadata.push(EntityDataValue::Byte(bitfield));
- metadata.push(EntityDataValue::Float(self.health.clone()));
- metadata.push(EntityDataValue::Int(self.effect_color.clone()));
- metadata.push(EntityDataValue::Boolean(self.effect_ambience.clone()));
- metadata.push(EntityDataValue::Int(self.arrow_count.clone()));
- metadata.push(EntityDataValue::Int(self.stinger_count.clone()));
- metadata.push(EntityDataValue::OptionalBlockPos(self.sleeping_pos.clone()));
- metadata
- }
-}
-
-impl Default for AbstractLiving {
- fn default() -> Self {
- Self {
- abstract_entity: Default::default(),
- auto_spin_attack: false,
- using_item: false,
- health: 1.0,
- effect_color: 0,
- effect_ambience: false,
- arrow_count: 0,
- stinger_count: 0,
- sleeping_pos: None,
+ Ok(())
+ }
+}
+
+#[derive(Bundle)]
+pub struct AbstractInsentientMetadataBundle {
+ _marker: AbstractInsentient,
+ parent: AbstractLivingMetadataBundle,
+ no_ai: NoAi,
+ left_handed: LeftHanded,
+ aggressive: Aggressive,
+}
+impl Default for AbstractInsentientMetadataBundle {
+ fn default() -> Self {
+ Self {
+ _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)]
+pub struct AbstractLiving;
impl AbstractLiving {
- pub fn set_index(&mut self, index: u8, value: EntityDataValue) -> Option<()> {
- match index {
- 0..=7 => self.abstract_entity.set_index(index, value)?,
+ pub fn apply_metadata(
+ entity: &mut azalea_ecs::system::EntityCommands,
+ d: EntityDataItem,
+ ) -> Result<(), UpdateMetadataError> {
+ match d.index {
+ 0..=7 => AbstractEntity::apply_metadata(entity, d)?,
8 => {
- let bitfield = value.into_byte().ok()?;
- self.auto_spin_attack = bitfield & 0x4 != 0;
- self.using_item = bitfield & 0x1 != 0;
+ let bitfield = d.value.into_byte()?;
+ entity.insert(AutoSpinAttack(bitfield & 0x4 != 0));
+ entity.insert(AbstractLivingUsingItem(bitfield & 0x1 != 0));
+ }
+ 9 => {
+ entity.insert(Health(d.value.into_float()?));
+ }
+ 10 => {
+ entity.insert(AbstractLivingEffectColor(d.value.into_int()?));
+ }
+ 11 => {
+ entity.insert(EffectAmbience(d.value.into_boolean()?));
+ }
+ 12 => {
+ entity.insert(ArrowCount(d.value.into_int()?));
+ }
+ 13 => {
+ entity.insert(StingerCount(d.value.into_int()?));
+ }
+ 14 => {
+ entity.insert(SleepingPos(d.value.into_optional_block_pos()?));
}
- 9 => self.health = value.into_float().ok()?,
- 10 => self.effect_color = value.into_int().ok()?,
- 11 => self.effect_ambience = value.into_boolean().ok()?,
- 12 => self.arrow_count = value.into_int().ok()?,
- 13 => self.stinger_count = value.into_int().ok()?,
- 14 => self.sleeping_pos = value.into_optional_block_pos().ok()?,
_ => {}
}
- Some(())
- }
-}
-impl Deref for AbstractLiving {
- type Target = AbstractEntity;
- fn deref(&self) -> &Self::Target {
- &self.abstract_entity
- }
-}
-impl DerefMut for AbstractLiving {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.abstract_entity
- }
-}
-
-#[derive(Debug, Clone)]
-pub struct AbstractMinecart {
- pub abstract_entity: AbstractEntity,
- pub hurt: i32,
- pub hurtdir: i32,
- pub damage: f32,
- pub display_block: i32,
- pub display_offset: i32,
- pub custom_display: bool,
-}
-
-impl AbstractMinecart {
- pub fn read(metadata: &mut VecDeque<EntityDataValue>) -> Option<Self> {
- let abstract_entity = AbstractEntity::read(metadata)?;
- let hurt = metadata.pop_front()?.into_int().ok()?;
- let hurtdir = metadata.pop_front()?.into_int().ok()?;
- let damage = metadata.pop_front()?.into_float().ok()?;
- let display_block = metadata.pop_front()?.into_int().ok()?;
- let display_offset = metadata.pop_front()?.into_int().ok()?;
- let custom_display = metadata.pop_front()?.into_boolean().ok()?;
- Some(Self {
- abstract_entity,
- hurt,
- hurtdir,
- damage,
- display_block,
- display_offset,
- custom_display,
- })
- }
-
- pub fn write(&self) -> Vec<EntityDataValue> {
- let mut metadata = Vec::new();
- metadata.extend(self.abstract_entity.write());
- metadata.push(EntityDataValue::Int(self.hurt.clone()));
- metadata.push(EntityDataValue::Int(self.hurtdir.clone()));
- metadata.push(EntityDataValue::Float(self.damage.clone()));
- metadata.push(EntityDataValue::Int(self.display_block.clone()));
- metadata.push(EntityDataValue::Int(self.display_offset.clone()));
- metadata.push(EntityDataValue::Boolean(self.custom_display.clone()));
- metadata
- }
-}
-
-impl Default for AbstractMinecart {
- fn default() -> Self {
- Self {
- abstract_entity: Default::default(),
- hurt: 0,
- hurtdir: 1,
- damage: 0.0,
- display_block: Default::default(),
- display_offset: 6,
- custom_display: false,
+ Ok(())
+ }
+}
+
+#[derive(Bundle)]
+pub struct AbstractLivingMetadataBundle {
+ _marker: AbstractLiving,
+ parent: AbstractEntityMetadataBundle,
+ auto_spin_attack: AutoSpinAttack,
+ abstract_living_using_item: AbstractLivingUsingItem,
+ health: Health,
+ abstract_living_effect_color: AbstractLivingEffectColor,
+ effect_ambience: EffectAmbience,
+ arrow_count: ArrowCount,
+ stinger_count: StingerCount,
+ sleeping_pos: SleepingPos,
+}
+impl Default for AbstractLivingMetadataBundle {
+ fn default() -> Self {
+ Self {
+ _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),
}
}
}
+#[derive(Component)]
+pub struct AbstractMinecart;
impl AbstractMinecart {
- pub fn set_index(&mut self, index: u8, value: EntityDataValue) -> Option<()> {
- match index {
- 0..=7 => self.abstract_entity.set_index(index, value)?,
- 8 => self.hurt = value.into_int().ok()?,
- 9 => self.hurtdir = value.into_int().ok()?,
- 10 => self.damage = value.into_float().ok()?,
- 11 => self.display_block = value.into_int().ok()?,
- 12 => self.display_offset = value.into_int().ok()?,
- 13 => self.custom_display = value.into_boolean().ok()?,
+ pub fn apply_metadata(
+ entity: &mut azalea_ecs::system::EntityCommands,
+ d: EntityDataItem,
+ ) -> Result<(), UpdateMetadataError> {
+ match d.index {
+ 0..=7 => AbstractEntity::apply_metadata(entity, d)?,
+ 8 => {
+ entity.insert(AbstractMinecartHurt(d.value.into_int()?));
+ }
+ 9 => {
+ entity.insert(AbstractMinecartHurtdir(d.value.into_int()?));
+ }
+ 10 => {
+ entity.insert(AbstractMinecartDamage(d.value.into_float()?));
+ }
+ 11 => {
+ entity.insert(DisplayBlock(d.value.into_int()?));
+ }
+ 12 => {
+ entity.insert(DisplayOffset(d.value.into_int()?));
+ }
+ 13 => {
+ entity.insert(CustomDisplay(d.value.into_boolean()?));
+ }
_ => {}
}
- Some(())
- }
-}
-impl Deref for AbstractMinecart {
- type Target = AbstractEntity;
- fn deref(&self) -> &Self::Target {
- &self.abstract_entity
- }
-}
-impl DerefMut for AbstractMinecart {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.abstract_entity
- }
-}
-
-#[derive(Debug, Clone)]
-pub struct AbstractMonster {
- pub abstract_creature: AbstractCreature,
-}
-
-impl AbstractMonster {
- pub fn read(metadata: &mut VecDeque<EntityDataValue>) -> Option<Self> {
- let abstract_creature = AbstractCreature::read(metadata)?;
- Some(Self { abstract_creature })
- }
-
- pub fn write(&self) -> Vec<EntityDataValue> {
- let mut metadata = Vec::new();
- metadata.extend(self.abstract_creature.write());
- metadata
- }
-}
-
-impl Default for AbstractMonster {
- fn default() -> Self {
- Self {
- abstract_creature: Default::default(),
+ Ok(())
+ }
+}
+
+#[derive(Bundle)]
+pub struct AbstractMinecartMetadataBundle {
+ _marker: AbstractMinecart,
+ parent: AbstractEntityMetadataBundle,
+ abstract_minecart_hurt: AbstractMinecartHurt,
+ abstract_minecart_hurtdir: AbstractMinecartHurtdir,
+ abstract_minecart_damage: AbstractMinecartDamage,
+ display_block: DisplayBlock,
+ display_offset: DisplayOffset,
+ custom_display: CustomDisplay,
+}
+impl Default for AbstractMinecartMetadataBundle {
+ fn default() -> Self {
+ Self {
+ _marker: AbstractMinecart,
+ 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),
+ },
+ abstract_minecart_hurt: AbstractMinecartHurt(0),
+ abstract_minecart_hurtdir: AbstractMinecartHurtdir(1),
+ abstract_minecart_damage: AbstractMinecartDamage(0.0),
+ display_block: DisplayBlock(Default::default()),
+ display_offset: DisplayOffset(6),
+ custom_display: CustomDisplay(false),
}
}
}
+#[derive(Component)]
+pub struct AbstractMonster;
impl AbstractMonster {
- pub fn set_index(&mut self, index: u8, value: EntityDataValue) -> Option<()> {
- self.abstract_creature.set_index(index, value)
- }
-}
-impl Deref for AbstractMonster {
- type Target = AbstractCreature;
- fn deref(&self) -> &Self::Target {
- &self.abstract_creature
- }
-}
-impl DerefMut for AbstractMonster {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.abstract_creature
+ pub fn apply_metadata(
+ entity: &mut azalea_ecs::system::EntityCommands,
+ d: EntityDataItem,
+ ) -> Result<(), UpdateMetadataError> {
+ match d.index {
+ 0..=15 => AbstractCreature::apply_metadata(entity, d)?,
+ _ => {}
+ }
+ Ok(())
+ }
+}
+
+#[derive(Bundle)]
+pub struct AbstractMonsterMetadataBundle {
+ _marker: AbstractMonster,
+ parent: AbstractCreatureMetadataBundle,
+}
+impl Default for AbstractMonsterMetadataBundle {
+ fn default() -> Self {
+ Self {
+ _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(Debug, Clone)]
-pub struct AbstractTameable {
- pub abstract_animal: AbstractAnimal,
- pub tame: bool,
- pub in_sitting_pose: bool,
- pub owneruuid: Option<Uuid>,
-}
-
+#[derive(Component)]
+pub struct AbstractTameable;
impl AbstractTameable {
- pub fn read(metadata: &mut VecDeque<EntityDataValue>) -> Option<Self> {
- let abstract_animal = AbstractAnimal::read(metadata)?;
- let bitfield = metadata.pop_front()?.into_byte().ok()?;
- let tame = bitfield & 0x4 != 0;
- let in_sitting_pose = bitfield & 0x1 != 0;
- let owneruuid = metadata.pop_front()?.into_optional_uuid().ok()?;
- Some(Self {
- abstract_animal,
- tame,
- in_sitting_pose,
- owneruuid,
- })
- }
-
- pub fn write(&self) -> Vec<EntityDataValue> {
- let mut metadata = Vec::new();
- metadata.extend(self.abstract_animal.write());
- let mut bitfield = 0u8;
- if self.tame {
- bitfield &= 0x4;
+ pub fn apply_metadata(
+ entity: &mut azalea_ecs::system::EntityCommands,
+ d: EntityDataItem,
+ ) -> Result<(), UpdateMetadataError> {
+ match d.index {
+ 0..=16 => AbstractAnimal::apply_metadata(entity, d)?,
+ 17 => {
+ let bitfield = d.value.into_byte()?;
+ entity.insert(Tame(bitfield & 0x4 != 0));
+ entity.insert(InSittingPose(bitfield & 0x1 != 0));
+ }
+ 18 => {
+ entity.insert(Owneruuid(d.value.into_optional_uuid()?));
+ }
+ _ => {}
}
- if self.in_sitting_pose {
- bitfield &= 0x1;
+ Ok(())
+ }
+}
+
+#[derive(Bundle)]
+pub struct AbstractTameableMetadataBundle {
+ _marker: AbstractTameable,
+ parent: AbstractAnimalMetadataBundle,
+ tame: Tame,
+ in_sitting_pose: InSittingPose,
+ owneruuid: Owneruuid,
+}
+impl Default for AbstractTameableMetadataBundle {
+ fn default() -> Self {
+ Self {
+ _marker: AbstractTameable,
+ parent: AbstractAnimalMetadataBundle {
+ _marker: AbstractAnimal,
+ parent: AbstractAgeableMetadataBundle {
+ _marker: AbstractAgeable,
+ 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),
+ },
+ },
+ abstract_ageable_baby: AbstractAgeableBaby(false),
+ },
+ },
+ tame: Tame(false),
+ in_sitting_pose: InSittingPose(false),
+ owneruuid: Owneruuid(None),
}
- metadata.push(EntityDataValue::Byte(bitfield));
- metadata.push(EntityDataValue::OptionalUuid(self.owneruuid.clone()));
- metadata
}
}
-impl Default for AbstractTameable {
- fn default() -> Self {
- Self {
- abstract_animal: Default::default(),
- tame: false,
- in_sitting_pose: false,
- owneruuid: None,
+pub fn apply_metadata(
+ entity: &mut azalea_ecs::system::EntityCommands,
+ entity_kind: azalea_registry::EntityKind,
+ items: Vec<EntityDataItem>,
+) -> Result<(), UpdateMetadataError> {
+ match entity_kind {
+ azalea_registry::EntityKind::Allay => {
+ for d in items {
+ Allay::apply_metadata(entity, d)?;
+ }
+ }
+ azalea_registry::EntityKind::AreaEffectCloud => {
+ for d in items {
+ AreaEffectCloud::apply_metadata(entity, d)?;
+ }
+ }
+ azalea_registry::EntityKind::ArmorStand => {
+ for d in items {
+ ArmorStand::apply_metadata(entity, d)?;
+ }
+ }
+ azalea_registry::EntityKind::Arrow => {
+ for d in items {
+ Arrow::apply_metadata(entity, d)?;
+ }
+ }
+ azalea_registry::EntityKind::Axolotl => {
+ for d in items {
+ Axolotl::apply_metadata(entity, d)?;
+ }
+ }
+ azalea_registry::EntityKind::Bat => {
+ for d in items {
+ Bat::apply_metadata(entity, d)?;
+ }
+ }
+ azalea_registry::EntityKind::Bee => {
+ for d in items {
+ Bee::apply_metadata(entity, d)?;
+ }
+ }
+ azalea_registry::EntityKind::Blaze => {
+ for d in items {
+ Blaze::apply_metadata(entity, d)?;
+ }
+ }
+ azalea_registry::EntityKind::Boat => {
+ for d in items {
+ Boat::apply_metadata(entity, d)?;
+ }
+ }
+ azalea_registry::EntityKind::Camel => {
+ for d in items {
+ Camel::apply_metadata(entity, d)?;
+ }
+ }
+ azalea_registry::EntityKind::Cat => {
+ for d in items {
+ Cat::apply_metadata(entity, d)?;
+ }
+ }
+ azalea_registry::EntityKind::CaveSpider => {
+ for d in items {
+ CaveSpider::apply_metadata(entity, d)?;
+ }
+ }
+ azalea_registry::EntityKind::ChestBoat => {
+ for d in items {
+ ChestBoat::apply_metadata(entity, d)?;
+ }
+ }
+ azalea_registry::EntityKind::ChestMinecart => {
+ for d in items {
+ ChestMinecart::apply_metadata(entity, d)?;
+ }
+ }
+ azalea_registry::EntityKind::Chicken => {
+ for d in items {
+ Chicken::apply_metadata(entity, d)?;
+ }
+ }
+ azalea_registry::EntityKind::Cod => {
+ for d in items {
+ Cod::apply_metadata(entity, d)?;
+ }
+ }
+ azalea_registry::EntityKind::CommandBlockMinecart => {
+ for d in items {
+ CommandBlockMinecart::apply_metadata(entity, d)?;
+ }
+ }
+ azalea_registry::EntityKind::Cow => {
+ for d in items {
+ Cow::apply_metadata(entity, d)?;
+ }
+ }
+ azalea_registry::EntityKind::Creeper => {
+ for d in items {
+ Creeper::apply_metadata(entity, d)?;
+ }
+ }
+ azalea_registry::EntityKind::Dolphin => {
+ for d in items {
+ Dolphin::apply_metadata(entity, d)?;
+ }
+ }
+ azalea_registry::EntityKind::Donkey => {
+ for d in items {
+ Donkey::apply_metadata(entity, d)?;
+ }
+ }
+ azalea_registry::EntityKind::DragonFireball => {
+ for d in items {
+ DragonFireball::apply_metadata(entity, d)?;
+ }
+ }
+ azalea_registry::EntityKind::Drowned => {
+ for d in items {
+ Drowned::apply_metadata(entity, d)?;
+ }
+ }
+ azalea_registry::EntityKind::Egg => {
+ for d in items {
+ Egg::apply_metadata(entity, d)?;
+ }
+ }
+ azalea_registry::EntityKind::ElderGuardian => {
+ for d in items {
+ ElderGuardian::apply_metadata(entity, d)?;
+ }
+ }
+ azalea_registry::EntityKind::EndCrystal => {
+ for d in items {
+ EndCrystal::apply_metadata(entity, d)?;
+ }
+ }
+ azalea_registry::EntityKind::EnderDragon => {
+ for d in items {
+ EnderDragon::apply_metadata(entity, d)?;
+ }
+ }
+ azalea_registry::EntityKind::EnderPearl => {
+ for d in items {
+ EnderPearl::apply_metadata(entity, d)?;
+ }
+ }
+ azalea_registry::EntityKind::Enderman => {
+ for d in items {
+ Enderman::apply_metadata(entity, d)?;
+ }
+ }
+ azalea_registry::EntityKind::Endermite => {
+ for d in items {
+ Endermite::apply_metadata(entity, d)?;
+ }
+ }
+ azalea_registry::EntityKind::Evoker => {
+ for d in items {
+ Evoker::apply_metadata(entity, d)?;
+ }
+ }
+ azalea_registry::EntityKind::EvokerFangs => {
+ for d in items {
+ EvokerFangs::apply_metadata(entity, d)?;
+ }
+ }
+ azalea_registry::EntityKind::ExperienceBottle => {
+ for d in items {
+ ExperienceBottle::apply_metadata(entity, d)?;
+ }
+ }
+ azalea_registry::EntityKind::ExperienceOrb => {
+ for d in items {
+ ExperienceOrb::apply_metadata(entity, d)?;
+ }
+ }
+ azalea_registry::EntityKind::EyeOfEnder => {
+ for d in items {
+ EyeOfEnder::apply_metadata(entity, d)?;
+ }
+ }
+ azalea_registry::EntityKind::FallingBlock => {
+ for d in items {
+ FallingBlock::apply_metadata(entity, d)?;
+ }
+ }
+ azalea_registry::EntityKind::Fireball => {
+ for d in items {
+ Fireball::apply_metadata(entity, d)?;
+ }
+ }
+ azalea_registry::EntityKind::FireworkRocket => {
+ for d in items {
+ FireworkRocket::apply_metadata(entity, d)?;
+ }
+ }
+ azalea_registry::EntityKind::FishingBobber => {
+ for d in items {
+ FishingBobber::apply_metadata(entity, d)?;
+ }
+ }
+ azalea_registry::EntityKind::Fox => {
+ for d in items {
+ Fox::apply_metadata(entity, d)?;
+ }
+ }
+ azalea_registry::EntityKind::Frog => {
+ for d in items {
+ Frog::apply_metadata(entity, d)?;
+ }
+ }
+ azalea_registry::EntityKind::FurnaceMinecart => {
+ for d in items {
+ FurnaceMinecart::apply_metadata(entity, d)?;
+ }
+ }
+ azalea_registry::EntityKind::Ghast => {
+ for d in items {
+ Ghast::apply_metadata(entity, d)?;
+ }
+ }
+ azalea_registry::EntityKind::Giant => {
+ for d in items {
+ Giant::apply_metadata(entity, d)?;
+ }
+ }
+ azalea_registry::EntityKind::GlowItemFrame => {
+ for d in items {
+ GlowItemFrame::apply_metadata(entity, d)?;
+ }
+ }
+ azalea_registry::EntityKind::GlowSquid => {
+ for d in items {
+ GlowSquid::apply_metadata(entity, d)?;
+ }
+ }
+ azalea_registry::EntityKind::Goat => {
+ for d in items {
+ Goat::apply_metadata(entity, d)?;
+ }
+ }
+ azalea_registry::EntityKind::Guardian => {
+ for d in items {
+ Guardian::apply_metadata(entity, d)?;
+ }
+ }
+ azalea_registry::EntityKind::Hoglin => {
+ for d in items {
+ Hoglin::apply_metadata(entity, d)?;
+ }
+ }
+ azalea_registry::EntityKind::HopperMinecart => {
+ for d in items {
+ HopperMinecart::apply_metadata(entity, d)?;
+ }
+ }
+ azalea_registry::EntityKind::Horse => {
+ for d in items {
+ Horse::apply_metadata(entity, d)?;
+ }
+ }
+ azalea_registry::EntityKind::Husk => {
+ for d in items {
+ Husk::apply_metadata(entity, d)?;
+ }
+ }
+ azalea_registry::EntityKind::Illusioner => {
+ for d in items {
+ Illusioner::apply_metadata(entity, d)?;
+ }
+ }
+ azalea_registry::EntityKind::IronGolem => {
+ for d in items {
+ IronGolem::apply_metadata(entity, d)?;
+ }
+ }
+ azalea_registry::EntityKind::Item => {
+ for d in items {
+ Item::apply_metadata(entity, d)?;
+ }
+ }
+ azalea_registry::EntityKind::ItemFrame => {
+ for d in items {
+ ItemFrame::apply_metadata(entity, d)?;
+ }
+ }
+ azalea_registry::EntityKind::LeashKnot => {
+ for d in items {
+ LeashKnot::apply_metadata(entity, d)?;
+ }
+ }
+ azalea_registry::EntityKind::LightningBolt => {
+ for d in items {
+ LightningBolt::apply_metadata(entity, d)?;
+ }
+ }
+ azalea_registry::EntityKind::Llama => {
+ for d in items {
+ Llama::apply_metadata(entity, d)?;
+ }
+ }
+ azalea_registry::EntityKind::LlamaSpit => {
+ for d in items {
+ LlamaSpit::apply_metadata(entity, d)?;
+ }
+ }
+ azalea_registry::EntityKind::MagmaCube => {
+ for d in items {
+ MagmaCube::apply_metadata(entity, d)?;
+ }
+ }
+ azalea_registry::EntityKind::Marker => {
+ for d in items {
+ Marker::apply_metadata(entity, d)?;
+ }
+ }
+ azalea_registry::EntityKind::Minecart => {
+ for d in items {
+ Minecart::apply_metadata(entity, d)?;
+ }
+ }
+ azalea_registry::EntityKind::Mooshroom => {
+ for d in items {
+ Mooshroom::apply_metadata(entity, d)?;
+ }
+ }
+ azalea_registry::EntityKind::Mule => {
+ for d in items {
+ Mule::apply_metadata(entity, d)?;
+ }
+ }
+ azalea_registry::EntityKind::Ocelot => {
+ for d in items {
+ Ocelot::apply_metadata(entity, d)?;
+ }
+ }
+ azalea_registry::EntityKind::Painting => {
+ for d in items {
+ Painting::apply_metadata(entity, d)?;
+ }
+ }
+ azalea_registry::EntityKind::Panda => {
+ for d in items {
+ Panda::apply_metadata(entity, d)?;
+ }
+ }
+ azalea_registry::EntityKind::Parrot => {
+ for d in items {
+ Parrot::apply_metadata(entity, d)?;
+ }
+ }
+ azalea_registry::EntityKind::Phantom => {
+ for d in items {
+ Phantom::apply_metadata(entity, d)?;
+ }
+ }
+ azalea_registry::EntityKind::Pig => {
+ for d in items {
+ Pig::apply_metadata(entity, d)?;
+ }
+ }
+ azalea_registry::EntityKind::Piglin => {
+ for d in items {
+ Piglin::apply_metadata(entity, d)?;
+ }
+ }
+ azalea_registry::EntityKind::PiglinBrute => {
+ for d in items {
+ PiglinBrute::apply_metadata(entity, d)?;
+ }
+ }
+ azalea_registry::EntityKind::Pillager => {
+ for d in items {
+ Pillager::apply_metadata(entity, d)?;
+ }
+ }
+ azalea_registry::EntityKind::Player => {
+ for d in items {
+ Player::apply_metadata(entity, d)?;
+ }
+ }
+ azalea_registry::EntityKind::PolarBear => {
+ for d in items {
+ PolarBear::apply_metadata(entity, d)?;
+ }
+ }
+ azalea_registry::EntityKind::Potion => {
+ for d in items {
+ Potion::apply_metadata(entity, d)?;
+ }
+ }
+ azalea_registry::EntityKind::Pufferfish => {
+ for d in items {
+ Pufferfish::apply_metadata(entity, d)?;
+ }
+ }
+ azalea_registry::EntityKind::Rabbit => {
+ for d in items {
+ Rabbit::apply_metadata(entity, d)?;
+ }
+ }
+ azalea_registry::EntityKind::Ravager => {
+ for d in items {
+ Ravager::apply_metadata(entity, d)?;
+ }
+ }
+ azalea_registry::EntityKind::Salmon => {
+ for d in items {
+ Salmon::apply_metadata(entity, d)?;
+ }
+ }
+ azalea_registry::EntityKind::Sheep => {
+ for d in items {
+ Sheep::apply_metadata(entity, d)?;
+ }
+ }
+ azalea_registry::EntityKind::Shulker => {
+ for d in items {
+ Shulker::apply_metadata(entity, d)?;
+ }
+ }
+ azalea_registry::EntityKind::ShulkerBullet => {
+ for d in items {
+ ShulkerBullet::apply_metadata(entity, d)?;
+ }
+ }
+ azalea_registry::EntityKind::Silverfish => {
+ for d in items {
+ Silverfish::apply_metadata(entity, d)?;
+ }
+ }
+ azalea_registry::EntityKind::Skeleton => {
+ for d in items {
+ Skeleton::apply_metadata(entity, d)?;
+ }
+ }
+ azalea_registry::EntityKind::SkeletonHorse => {
+ for d in items {
+ SkeletonHorse::apply_metadata(entity, d)?;
+ }
+ }
+ azalea_registry::EntityKind::Slime => {
+ for d in items {
+ Slime::apply_metadata(entity, d)?;
+ }
+ }
+ azalea_registry::EntityKind::SmallFireball => {
+ for d in items {
+ SmallFireball::apply_metadata(entity, d)?;
+ }
+ }
+ azalea_registry::EntityKind::SnowGolem => {
+ for d in items {
+ SnowGolem::apply_metadata(entity, d)?;
+ }
+ }
+ azalea_registry::EntityKind::Snowball => {
+ for d in items {
+ Snowball::apply_metadata(entity, d)?;
+ }
+ }
+ azalea_registry::EntityKind::SpawnerMinecart => {
+ for d in items {
+ SpawnerMinecart::apply_metadata(entity, d)?;
+ }
+ }
+ azalea_registry::EntityKind::SpectralArrow => {
+ for d in items {
+ SpectralArrow::apply_metadata(entity, d)?;
+ }
+ }
+ azalea_registry::EntityKind::Spider => {
+ for d in items {
+ Spider::apply_metadata(entity, d)?;
+ }
+ }
+ azalea_registry::EntityKind::Squid => {
+ for d in items {
+ Squid::apply_metadata(entity, d)?;
+ }
+ }
+ azalea_registry::EntityKind::Stray => {
+ for d in items {
+ Stray::apply_metadata(entity, d)?;
+ }
+ }
+ azalea_registry::EntityKind::Strider => {
+ for d in items {
+ Strider::apply_metadata(entity, d)?;
+ }
+ }
+ azalea_registry::EntityKind::Tadpole => {
+ for d in items {
+ Tadpole::apply_metadata(entity, d)?;
+ }
+ }
+ azalea_registry::EntityKind::Tnt => {
+ for d in items {
+ Tnt::apply_metadata(entity, d)?;
+ }
+ }
+ azalea_registry::EntityKind::TntMinecart => {
+ for d in items {
+ TntMinecart::apply_metadata(entity, d)?;
+ }
+ }
+ azalea_registry::EntityKind::TraderLlama => {
+ for d in items {
+ TraderLlama::apply_metadata(entity, d)?;
+ }
+ }
+ azalea_registry::EntityKind::Trident => {
+ for d in items {
+ Trident::apply_metadata(entity, d)?;
+ }
+ }
+ azalea_registry::EntityKind::TropicalFish => {
+ for d in items {
+ TropicalFish::apply_metadata(entity, d)?;
+ }
+ }
+ azalea_registry::EntityKind::Turtle => {
+ for d in items {
+ Turtle::apply_metadata(entity, d)?;
+ }
+ }
+ azalea_registry::EntityKind::Vex => {
+ for d in items {
+ Vex::apply_metadata(entity, d)?;
+ }
+ }
+ azalea_registry::EntityKind::Villager => {
+ for d in items {
+ Villager::apply_metadata(entity, d)?;
+ }
+ }
+ azalea_registry::EntityKind::Vindicator => {
+ for d in items {
+ Vindicator::apply_metadata(entity, d)?;
+ }
+ }
+ azalea_registry::EntityKind::WanderingTrader => {
+ for d in items {
+ WanderingTrader::apply_metadata(entity, d)?;
+ }
+ }
+ azalea_registry::EntityKind::Warden => {
+ for d in items {
+ Warden::apply_metadata(entity, d)?;
+ }
+ }
+ azalea_registry::EntityKind::Witch => {
+ for d in items {
+ Witch::apply_metadata(entity, d)?;
+ }
+ }
+ azalea_registry::EntityKind::Wither => {
+ for d in items {
+ Wither::apply_metadata(entity, d)?;
+ }
+ }
+ azalea_registry::EntityKind::WitherSkeleton => {
+ for d in items {
+ WitherSkeleton::apply_metadata(entity, d)?;
+ }
+ }
+ azalea_registry::EntityKind::WitherSkull => {
+ for d in items {
+ WitherSkull::apply_metadata(entity, d)?;
+ }
+ }
+ azalea_registry::EntityKind::Wolf => {
+ for d in items {
+ Wolf::apply_metadata(entity, d)?;
+ }
+ }
+ azalea_registry::EntityKind::Zoglin => {
+ for d in items {
+ Zoglin::apply_metadata(entity, d)?;
+ }
+ }
+ azalea_registry::EntityKind::Zombie => {
+ for d in items {
+ Zombie::apply_metadata(entity, d)?;
+ }
+ }
+ azalea_registry::EntityKind::ZombieHorse => {
+ for d in items {
+ ZombieHorse::apply_metadata(entity, d)?;
+ }
+ }
+ azalea_registry::EntityKind::ZombieVillager => {
+ for d in items {
+ ZombieVillager::apply_metadata(entity, d)?;
+ }
+ }
+ azalea_registry::EntityKind::ZombifiedPiglin => {
+ for d in items {
+ ZombifiedPiglin::apply_metadata(entity, d)?;
+ }
}
}
+ Ok(())
}
-impl AbstractTameable {
- pub fn set_index(&mut self, index: u8, value: EntityDataValue) -> Option<()> {
- match index {
- 0..=16 => self.abstract_animal.set_index(index, value)?,
- 17 => {
- let bitfield = value.into_byte().ok()?;
- self.tame = bitfield & 0x4 != 0;
- self.in_sitting_pose = bitfield & 0x1 != 0;
- }
- 18 => self.owneruuid = value.into_optional_uuid().ok()?,
- _ => {}
- }
- Some(())
- }
-}
-impl Deref for AbstractTameable {
- type Target = AbstractAnimal;
- fn deref(&self) -> &Self::Target {
- &self.abstract_animal
- }
-}
-impl DerefMut for AbstractTameable {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.abstract_animal
- }
-}
-
-#[derive(Debug, Clone, EnumAsInner)]
-pub enum EntityMetadata {
- Allay(Allay),
- AreaEffectCloud(AreaEffectCloud),
- ArmorStand(ArmorStand),
- Arrow(Arrow),
- Axolotl(Axolotl),
- Bat(Bat),
- Bee(Bee),
- Blaze(Blaze),
- Boat(Boat),
- Camel(Camel),
- Cat(Cat),
- CaveSpider(CaveSpider),
- ChestBoat(ChestBoat),
- ChestMinecart(ChestMinecart),
- Chicken(Chicken),
- Cod(Cod),
- CommandBlockMinecart(CommandBlockMinecart),
- Cow(Cow),
- Creeper(Creeper),
- Dolphin(Dolphin),
- Donkey(Donkey),
- DragonFireball(DragonFireball),
- Drowned(Drowned),
- Egg(Egg),
- ElderGuardian(ElderGuardian),
- EndCrystal(EndCrystal),
- EnderDragon(EnderDragon),
- EnderPearl(EnderPearl),
- Enderman(Enderman),
- Endermite(Endermite),
- Evoker(Evoker),
- EvokerFangs(EvokerFangs),
- ExperienceBottle(ExperienceBottle),
- ExperienceOrb(ExperienceOrb),
- EyeOfEnder(EyeOfEnder),
- FallingBlock(FallingBlock),
- Fireball(Fireball),
- FireworkRocket(FireworkRocket),
- FishingBobber(FishingBobber),
- Fox(Fox),
- Frog(Frog),
- FurnaceMinecart(FurnaceMinecart),
- Ghast(Ghast),
- Giant(Giant),
- GlowItemFrame(GlowItemFrame),
- GlowSquid(GlowSquid),
- Goat(Goat),
- Guardian(Guardian),
- Hoglin(Hoglin),
- HopperMinecart(HopperMinecart),
- Horse(Horse),
- Husk(Husk),
- Illusioner(Illusioner),
- IronGolem(IronGolem),
- Item(Item),
- ItemFrame(ItemFrame),
- LeashKnot(LeashKnot),
- LightningBolt(LightningBolt),
- Llama(Llama),
- LlamaSpit(LlamaSpit),
- MagmaCube(MagmaCube),
- Marker(Marker),
- Minecart(Minecart),
- Mooshroom(Mooshroom),
- Mule(Mule),
- Ocelot(Ocelot),
- Painting(Painting),
- Panda(Panda),
- Parrot(Parrot),
- Phantom(Phantom),
- Pig(Pig),
- Piglin(Piglin),
- PiglinBrute(PiglinBrute),
- Pillager(Pillager),
- Player(Player),
- PolarBear(PolarBear),
- Potion(Potion),
- Pufferfish(Pufferfish),
- Rabbit(Rabbit),
- Ravager(Ravager),
- Salmon(Salmon),
- Sheep(Sheep),
- Shulker(Shulker),
- ShulkerBullet(ShulkerBullet),
- Silverfish(Silverfish),
- Skeleton(Skeleton),
- SkeletonHorse(SkeletonHorse),
- Slime(Slime),
- SmallFireball(SmallFireball),
- SnowGolem(SnowGolem),
- Snowball(Snowball),
- SpawnerMinecart(SpawnerMinecart),
- SpectralArrow(SpectralArrow),
- Spider(Spider),
- Squid(Squid),
- Stray(Stray),
- Strider(Strider),
- Tadpole(Tadpole),
- Tnt(Tnt),
- TntMinecart(TntMinecart),
- TraderLlama(TraderLlama),
- Trident(Trident),
- TropicalFish(TropicalFish),
- Turtle(Turtle),
- Vex(Vex),
- Villager(Villager),
- Vindicator(Vindicator),
- WanderingTrader(WanderingTrader),
- Warden(Warden),
- Witch(Witch),
- Wither(Wither),
- WitherSkeleton(WitherSkeleton),
- WitherSkull(WitherSkull),
- Wolf(Wolf),
- Zoglin(Zoglin),
- Zombie(Zombie),
- ZombieHorse(ZombieHorse),
- ZombieVillager(ZombieVillager),
- ZombifiedPiglin(ZombifiedPiglin),
-}
-
-impl From<azalea_registry::EntityType> for EntityMetadata {
- fn from(value: azalea_registry::EntityType) -> Self {
- match value {
- azalea_registry::EntityType::Allay => EntityMetadata::Allay(Allay::default()),
- azalea_registry::EntityType::AreaEffectCloud => {
- EntityMetadata::AreaEffectCloud(AreaEffectCloud::default())
- }
- azalea_registry::EntityType::ArmorStand => {
- EntityMetadata::ArmorStand(ArmorStand::default())
- }
- azalea_registry::EntityType::Arrow => EntityMetadata::Arrow(Arrow::default()),
- azalea_registry::EntityType::Axolotl => EntityMetadata::Axolotl(Axolotl::default()),
- azalea_registry::EntityType::Bat => EntityMetadata::Bat(Bat::default()),
- azalea_registry::EntityType::Bee => EntityMetadata::Bee(Bee::default()),
- azalea_registry::EntityType::Blaze => EntityMetadata::Blaze(Blaze::default()),
- azalea_registry::EntityType::Boat => EntityMetadata::Boat(Boat::default()),
- azalea_registry::EntityType::Camel => EntityMetadata::Camel(Camel::default()),
- azalea_registry::EntityType::Cat => EntityMetadata::Cat(Cat::default()),
- azalea_registry::EntityType::CaveSpider => {
- EntityMetadata::CaveSpider(CaveSpider::default())
- }
- azalea_registry::EntityType::ChestBoat => {
- EntityMetadata::ChestBoat(ChestBoat::default())
- }
- azalea_registry::EntityType::ChestMinecart => {
- EntityMetadata::ChestMinecart(ChestMinecart::default())
- }
- azalea_registry::EntityType::Chicken => EntityMetadata::Chicken(Chicken::default()),
- azalea_registry::EntityType::Cod => EntityMetadata::Cod(Cod::default()),
- azalea_registry::EntityType::CommandBlockMinecart => {
- EntityMetadata::CommandBlockMinecart(CommandBlockMinecart::default())
- }
- azalea_registry::EntityType::Cow => EntityMetadata::Cow(Cow::default()),
- azalea_registry::EntityType::Creeper => EntityMetadata::Creeper(Creeper::default()),
- azalea_registry::EntityType::Dolphin => EntityMetadata::Dolphin(Dolphin::default()),
- azalea_registry::EntityType::Donkey => EntityMetadata::Donkey(Donkey::default()),
- azalea_registry::EntityType::DragonFireball => {
- EntityMetadata::DragonFireball(DragonFireball::default())
- }
- azalea_registry::EntityType::Drowned => EntityMetadata::Drowned(Drowned::default()),
- azalea_registry::EntityType::Egg => EntityMetadata::Egg(Egg::default()),
- azalea_registry::EntityType::ElderGuardian => {
- EntityMetadata::ElderGuardian(ElderGuardian::default())
- }
- azalea_registry::EntityType::EndCrystal => {
- EntityMetadata::EndCrystal(EndCrystal::default())
- }
- azalea_registry::EntityType::EnderDragon => {
- EntityMetadata::EnderDragon(EnderDragon::default())
- }
- azalea_registry::EntityType::EnderPearl => {
- EntityMetadata::EnderPearl(EnderPearl::default())
- }
- azalea_registry::EntityType::Enderman => EntityMetadata::Enderman(Enderman::default()),
- azalea_registry::EntityType::Endermite => {
- EntityMetadata::Endermite(Endermite::default())
- }
- azalea_registry::EntityType::Evoker => EntityMetadata::Evoker(Evoker::default()),
- azalea_registry::EntityType::EvokerFangs => {
- EntityMetadata::EvokerFangs(EvokerFangs::default())
- }
- azalea_registry::EntityType::ExperienceBottle => {
- EntityMetadata::ExperienceBottle(ExperienceBottle::default())
- }
- azalea_registry::EntityType::ExperienceOrb => {
- EntityMetadata::ExperienceOrb(ExperienceOrb::default())
- }
- azalea_registry::EntityType::EyeOfEnder => {
- EntityMetadata::EyeOfEnder(EyeOfEnder::default())
- }
- azalea_registry::EntityType::FallingBlock => {
- EntityMetadata::FallingBlock(FallingBlock::default())
- }
- azalea_registry::EntityType::Fireball => EntityMetadata::Fireball(Fireball::default()),
- azalea_registry::EntityType::FireworkRocket => {
- EntityMetadata::FireworkRocket(FireworkRocket::default())
- }
- azalea_registry::EntityType::FishingBobber => {
- EntityMetadata::FishingBobber(FishingBobber::default())
- }
- azalea_registry::EntityType::Fox => EntityMetadata::Fox(Fox::default()),
- azalea_registry::EntityType::Frog => EntityMetadata::Frog(Frog::default()),
- azalea_registry::EntityType::FurnaceMinecart => {
- EntityMetadata::FurnaceMinecart(FurnaceMinecart::default())
- }
- azalea_registry::EntityType::Ghast => EntityMetadata::Ghast(Ghast::default()),
- azalea_registry::EntityType::Giant => EntityMetadata::Giant(Giant::default()),
- azalea_registry::EntityType::GlowItemFrame => {
- EntityMetadata::GlowItemFrame(GlowItemFrame::default())
- }
- azalea_registry::EntityType::GlowSquid => {
- EntityMetadata::GlowSquid(GlowSquid::default())
- }
- azalea_registry::EntityType::Goat => EntityMetadata::Goat(Goat::default()),
- azalea_registry::EntityType::Guardian => EntityMetadata::Guardian(Guardian::default()),
- azalea_registry::EntityType::Hoglin => EntityMetadata::Hoglin(Hoglin::default()),
- azalea_registry::EntityType::HopperMinecart => {
- EntityMetadata::HopperMinecart(HopperMinecart::default())
- }
- azalea_registry::EntityType::Horse => EntityMetadata::Horse(Horse::default()),
- azalea_registry::EntityType::Husk => EntityMetadata::Husk(Husk::default()),
- azalea_registry::EntityType::Illusioner => {
- EntityMetadata::Illusioner(Illusioner::default())
- }
- azalea_registry::EntityType::IronGolem => {
- EntityMetadata::IronGolem(IronGolem::default())
- }
- azalea_registry::EntityType::Item => EntityMetadata::Item(Item::default()),
- azalea_registry::EntityType::ItemFrame => {
- EntityMetadata::ItemFrame(ItemFrame::default())
- }
- azalea_registry::EntityType::LeashKnot => {
- EntityMetadata::LeashKnot(LeashKnot::default())
- }
- azalea_registry::EntityType::LightningBolt => {
- EntityMetadata::LightningBolt(LightningBolt::default())
- }
- azalea_registry::EntityType::Llama => EntityMetadata::Llama(Llama::default()),
- azalea_registry::EntityType::LlamaSpit => {
- EntityMetadata::LlamaSpit(LlamaSpit::default())
- }
- azalea_registry::EntityType::MagmaCube => {
- EntityMetadata::MagmaCube(MagmaCube::default())
- }
- azalea_registry::EntityType::Marker => EntityMetadata::Marker(Marker::default()),
- azalea_registry::EntityType::Minecart => EntityMetadata::Minecart(Minecart::default()),
- azalea_registry::EntityType::Mooshroom => {
- EntityMetadata::Mooshroom(Mooshroom::default())
- }
- azalea_registry::EntityType::Mule => EntityMetadata::Mule(Mule::default()),
- azalea_registry::EntityType::Ocelot => EntityMetadata::Ocelot(Ocelot::default()),
- azalea_registry::EntityType::Painting => EntityMetadata::Painting(Painting::default()),
- azalea_registry::EntityType::Panda => EntityMetadata::Panda(Panda::default()),
- azalea_registry::EntityType::Parrot => EntityMetadata::Parrot(Parrot::default()),
- azalea_registry::EntityType::Phantom => EntityMetadata::Phantom(Phantom::default()),
- azalea_registry::EntityType::Pig => EntityMetadata::Pig(Pig::default()),
- azalea_registry::EntityType::Piglin => EntityMetadata::Piglin(Piglin::default()),
- azalea_registry::EntityType::PiglinBrute => {
- EntityMetadata::PiglinBrute(PiglinBrute::default())
- }
- azalea_registry::EntityType::Pillager => EntityMetadata::Pillager(Pillager::default()),
- azalea_registry::EntityType::Player => EntityMetadata::Player(Player::default()),
- azalea_registry::EntityType::PolarBear => {
- EntityMetadata::PolarBear(PolarBear::default())
- }
- azalea_registry::EntityType::Potion => EntityMetadata::Potion(Potion::default()),
- azalea_registry::EntityType::Pufferfish => {
- EntityMetadata::Pufferfish(Pufferfish::default())
- }
- azalea_registry::EntityType::Rabbit => EntityMetadata::Rabbit(Rabbit::default()),
- azalea_registry::EntityType::Ravager => EntityMetadata::Ravager(Ravager::default()),
- azalea_registry::EntityType::Salmon => EntityMetadata::Salmon(Salmon::default()),
- azalea_registry::EntityType::Sheep => EntityMetadata::Sheep(Sheep::default()),
- azalea_registry::EntityType::Shulker => EntityMetadata::Shulker(Shulker::default()),
- azalea_registry::EntityType::ShulkerBullet => {
- EntityMetadata::ShulkerBullet(ShulkerBullet::default())
- }
- azalea_registry::EntityType::Silverfish => {
- EntityMetadata::Silverfish(Silverfish::default())
- }
- azalea_registry::EntityType::Skeleton => EntityMetadata::Skeleton(Skeleton::default()),
- azalea_registry::EntityType::SkeletonHorse => {
- EntityMetadata::SkeletonHorse(SkeletonHorse::default())
- }
- azalea_registry::EntityType::Slime => EntityMetadata::Slime(Slime::default()),
- azalea_registry::EntityType::SmallFireball => {
- EntityMetadata::SmallFireball(SmallFireball::default())
- }
- azalea_registry::EntityType::SnowGolem => {
- EntityMetadata::SnowGolem(SnowGolem::default())
- }
- azalea_registry::EntityType::Snowball => EntityMetadata::Snowball(Snowball::default()),
- azalea_registry::EntityType::SpawnerMinecart => {
- EntityMetadata::SpawnerMinecart(SpawnerMinecart::default())
- }
- azalea_registry::EntityType::SpectralArrow => {
- EntityMetadata::SpectralArrow(SpectralArrow::default())
- }
- azalea_registry::EntityType::Spider => EntityMetadata::Spider(Spider::default()),
- azalea_registry::EntityType::Squid => EntityMetadata::Squid(Squid::default()),
- azalea_registry::EntityType::Stray => EntityMetadata::Stray(Stray::default()),
- azalea_registry::EntityType::Strider => EntityMetadata::Strider(Strider::default()),
- azalea_registry::EntityType::Tadpole => EntityMetadata::Tadpole(Tadpole::default()),
- azalea_registry::EntityType::Tnt => EntityMetadata::Tnt(Tnt::default()),
- azalea_registry::EntityType::TntMinecart => {
- EntityMetadata::TntMinecart(TntMinecart::default())
- }
- azalea_registry::EntityType::TraderLlama => {
- EntityMetadata::TraderLlama(TraderLlama::default())
- }
- azalea_registry::EntityType::Trident => EntityMetadata::Trident(Trident::default()),
- azalea_registry::EntityType::TropicalFish => {
- EntityMetadata::TropicalFish(TropicalFish::default())
- }
- azalea_registry::EntityType::Turtle => EntityMetadata::Turtle(Turtle::default()),
- azalea_registry::EntityType::Vex => EntityMetadata::Vex(Vex::default()),
- azalea_registry::EntityType::Villager => EntityMetadata::Villager(Villager::default()),
- azalea_registry::EntityType::Vindicator => {
- EntityMetadata::Vindicator(Vindicator::default())
- }
- azalea_registry::EntityType::WanderingTrader => {
- EntityMetadata::WanderingTrader(WanderingTrader::default())
- }
- azalea_registry::EntityType::Warden => EntityMetadata::Warden(Warden::default()),
- azalea_registry::EntityType::Witch => EntityMetadata::Witch(Witch::default()),
- azalea_registry::EntityType::Wither => EntityMetadata::Wither(Wither::default()),
- azalea_registry::EntityType::WitherSkeleton => {
- EntityMetadata::WitherSkeleton(WitherSkeleton::default())
- }
- azalea_registry::EntityType::WitherSkull => {
- EntityMetadata::WitherSkull(WitherSkull::default())
- }
- azalea_registry::EntityType::Wolf => EntityMetadata::Wolf(Wolf::default()),
- azalea_registry::EntityType::Zoglin => EntityMetadata::Zoglin(Zoglin::default()),
- azalea_registry::EntityType::Zombie => EntityMetadata::Zombie(Zombie::default()),
- azalea_registry::EntityType::ZombieHorse => {
- EntityMetadata::ZombieHorse(ZombieHorse::default())
- }
- azalea_registry::EntityType::ZombieVillager => {
- EntityMetadata::ZombieVillager(ZombieVillager::default())
- }
- azalea_registry::EntityType::ZombifiedPiglin => {
- EntityMetadata::ZombifiedPiglin(ZombifiedPiglin::default())
- }
- }
- }
-}
-
-impl EntityMetadata {
- pub fn set_index(&mut self, index: u8, value: EntityDataValue) -> Option<()> {
- match self {
- EntityMetadata::Allay(entity) => entity.set_index(index, value),
- EntityMetadata::AreaEffectCloud(entity) => entity.set_index(index, value),
- EntityMetadata::ArmorStand(entity) => entity.set_index(index, value),
- EntityMetadata::Arrow(entity) => entity.set_index(index, value),
- EntityMetadata::Axolotl(entity) => entity.set_index(index, value),
- EntityMetadata::Bat(entity) => entity.set_index(index, value),
- EntityMetadata::Bee(entity) => entity.set_index(index, value),
- EntityMetadata::Blaze(entity) => entity.set_index(index, value),
- EntityMetadata::Boat(entity) => entity.set_index(index, value),
- EntityMetadata::Camel(entity) => entity.set_index(index, value),
- EntityMetadata::Cat(entity) => entity.set_index(index, value),
- EntityMetadata::CaveSpider(entity) => entity.set_index(index, value),
- EntityMetadata::ChestBoat(entity) => entity.set_index(index, value),
- EntityMetadata::ChestMinecart(entity) => entity.set_index(index, value),
- EntityMetadata::Chicken(entity) => entity.set_index(index, value),
- EntityMetadata::Cod(entity) => entity.set_index(index, value),
- EntityMetadata::CommandBlockMinecart(entity) => entity.set_index(index, value),
- EntityMetadata::Cow(entity) => entity.set_index(index, value),
- EntityMetadata::Creeper(entity) => entity.set_index(index, value),
- EntityMetadata::Dolphin(entity) => entity.set_index(index, value),
- EntityMetadata::Donkey(entity) => entity.set_index(index, value),
- EntityMetadata::DragonFireball(entity) => entity.set_index(index, value),
- EntityMetadata::Drowned(entity) => entity.set_index(index, value),
- EntityMetadata::Egg(entity) => entity.set_index(index, value),
- EntityMetadata::ElderGuardian(entity) => entity.set_index(index, value),
- EntityMetadata::EndCrystal(entity) => entity.set_index(index, value),
- EntityMetadata::EnderDragon(entity) => entity.set_index(index, value),
- EntityMetadata::EnderPearl(entity) => entity.set_index(index, value),
- EntityMetadata::Enderman(entity) => entity.set_index(index, value),
- EntityMetadata::Endermite(entity) => entity.set_index(index, value),
- EntityMetadata::Evoker(entity) => entity.set_index(index, value),
- EntityMetadata::EvokerFangs(entity) => entity.set_index(index, value),
- EntityMetadata::ExperienceBottle(entity) => entity.set_index(index, value),
- EntityMetadata::ExperienceOrb(entity) => entity.set_index(index, value),
- EntityMetadata::EyeOfEnder(entity) => entity.set_index(index, value),
- EntityMetadata::FallingBlock(entity) => entity.set_index(index, value),
- EntityMetadata::Fireball(entity) => entity.set_index(index, value),
- EntityMetadata::FireworkRocket(entity) => entity.set_index(index, value),
- EntityMetadata::FishingBobber(entity) => entity.set_index(index, value),
- EntityMetadata::Fox(entity) => entity.set_index(index, value),
- EntityMetadata::Frog(entity) => entity.set_index(index, value),
- EntityMetadata::FurnaceMinecart(entity) => entity.set_index(index, value),
- EntityMetadata::Ghast(entity) => entity.set_index(index, value),
- EntityMetadata::Giant(entity) => entity.set_index(index, value),
- EntityMetadata::GlowItemFrame(entity) => entity.set_index(index, value),
- EntityMetadata::GlowSquid(entity) => entity.set_index(index, value),
- EntityMetadata::Goat(entity) => entity.set_index(index, value),
- EntityMetadata::Guardian(entity) => entity.set_index(index, value),
- EntityMetadata::Hoglin(entity) => entity.set_index(index, value),
- EntityMetadata::HopperMinecart(entity) => entity.set_index(index, value),
- EntityMetadata::Horse(entity) => entity.set_index(index, value),
- EntityMetadata::Husk(entity) => entity.set_index(index, value),
- EntityMetadata::Illusioner(entity) => entity.set_index(index, value),
- EntityMetadata::IronGolem(entity) => entity.set_index(index, value),
- EntityMetadata::Item(entity) => entity.set_index(index, value),
- EntityMetadata::ItemFrame(entity) => entity.set_index(index, value),
- EntityMetadata::LeashKnot(entity) => entity.set_index(index, value),
- EntityMetadata::LightningBolt(entity) => entity.set_index(index, value),
- EntityMetadata::Llama(entity) => entity.set_index(index, value),
- EntityMetadata::LlamaSpit(entity) => entity.set_index(index, value),
- EntityMetadata::MagmaCube(entity) => entity.set_index(index, value),
- EntityMetadata::Marker(entity) => entity.set_index(index, value),
- EntityMetadata::Minecart(entity) => entity.set_index(index, value),
- EntityMetadata::Mooshroom(entity) => entity.set_index(index, value),
- EntityMetadata::Mule(entity) => entity.set_index(index, value),
- EntityMetadata::Ocelot(entity) => entity.set_index(index, value),
- EntityMetadata::Painting(entity) => entity.set_index(index, value),
- EntityMetadata::Panda(entity) => entity.set_index(index, value),
- EntityMetadata::Parrot(entity) => entity.set_index(index, value),
- EntityMetadata::Phantom(entity) => entity.set_index(index, value),
- EntityMetadata::Pig(entity) => entity.set_index(index, value),
- EntityMetadata::Piglin(entity) => entity.set_index(index, value),
- EntityMetadata::PiglinBrute(entity) => entity.set_index(index, value),
- EntityMetadata::Pillager(entity) => entity.set_index(index, value),
- EntityMetadata::Player(entity) => entity.set_index(index, value),
- EntityMetadata::PolarBear(entity) => entity.set_index(index, value),
- EntityMetadata::Potion(entity) => entity.set_index(index, value),
- EntityMetadata::Pufferfish(entity) => entity.set_index(index, value),
- EntityMetadata::Rabbit(entity) => entity.set_index(index, value),
- EntityMetadata::Ravager(entity) => entity.set_index(index, value),
- EntityMetadata::Salmon(entity) => entity.set_index(index, value),
- EntityMetadata::Sheep(entity) => entity.set_index(index, value),
- EntityMetadata::Shulker(entity) => entity.set_index(index, value),
- EntityMetadata::ShulkerBullet(entity) => entity.set_index(index, value),
- EntityMetadata::Silverfish(entity) => entity.set_index(index, value),
- EntityMetadata::Skeleton(entity) => entity.set_index(index, value),
- EntityMetadata::SkeletonHorse(entity) => entity.set_index(index, value),
- EntityMetadata::Slime(entity) => entity.set_index(index, value),
- EntityMetadata::SmallFireball(entity) => entity.set_index(index, value),
- EntityMetadata::SnowGolem(entity) => entity.set_index(index, value),
- EntityMetadata::Snowball(entity) => entity.set_index(index, value),
- EntityMetadata::SpawnerMinecart(entity) => entity.set_index(index, value),
- EntityMetadata::SpectralArrow(entity) => entity.set_index(index, value),
- EntityMetadata::Spider(entity) => entity.set_index(index, value),
- EntityMetadata::Squid(entity) => entity.set_index(index, value),
- EntityMetadata::Stray(entity) => entity.set_index(index, value),
- EntityMetadata::Strider(entity) => entity.set_index(index, value),
- EntityMetadata::Tadpole(entity) => entity.set_index(index, value),
- EntityMetadata::Tnt(entity) => entity.set_index(index, value),
- EntityMetadata::TntMinecart(entity) => entity.set_index(index, value),
- EntityMetadata::TraderLlama(entity) => entity.set_index(index, value),
- EntityMetadata::Trident(entity) => entity.set_index(index, value),
- EntityMetadata::TropicalFish(entity) => entity.set_index(index, value),
- EntityMetadata::Turtle(entity) => entity.set_index(index, value),
- EntityMetadata::Vex(entity) => entity.set_index(index, value),
- EntityMetadata::Villager(entity) => entity.set_index(index, value),
- EntityMetadata::Vindicator(entity) => entity.set_index(index, value),
- EntityMetadata::WanderingTrader(entity) => entity.set_index(index, value),
- EntityMetadata::Warden(entity) => entity.set_index(index, value),
- EntityMetadata::Witch(entity) => entity.set_index(index, value),
- EntityMetadata::Wither(entity) => entity.set_index(index, value),
- EntityMetadata::WitherSkeleton(entity) => entity.set_index(index, value),
- EntityMetadata::WitherSkull(entity) => entity.set_index(index, value),
- EntityMetadata::Wolf(entity) => entity.set_index(index, value),
- EntityMetadata::Zoglin(entity) => entity.set_index(index, value),
- EntityMetadata::Zombie(entity) => entity.set_index(index, value),
- EntityMetadata::ZombieHorse(entity) => entity.set_index(index, value),
- EntityMetadata::ZombieVillager(entity) => entity.set_index(index, value),
- EntityMetadata::ZombifiedPiglin(entity) => entity.set_index(index, value),
- }
- }
-}
-
-impl Deref for EntityMetadata {
- type Target = AbstractEntity;
- fn deref(&self) -> &Self::Target {
- match self {
- EntityMetadata::Allay(entity) => entity,
- EntityMetadata::AreaEffectCloud(entity) => entity,
- EntityMetadata::ArmorStand(entity) => entity,
- EntityMetadata::Arrow(entity) => entity,
- EntityMetadata::Axolotl(entity) => entity,
- EntityMetadata::Bat(entity) => entity,
- EntityMetadata::Bee(entity) => entity,
- EntityMetadata::Blaze(entity) => entity,
- EntityMetadata::Boat(entity) => entity,
- EntityMetadata::Camel(entity) => entity,
- EntityMetadata::Cat(entity) => entity,
- EntityMetadata::CaveSpider(entity) => entity,
- EntityMetadata::ChestBoat(entity) => entity,
- EntityMetadata::ChestMinecart(entity) => entity,
- EntityMetadata::Chicken(entity) => entity,
- EntityMetadata::Cod(entity) => entity,
- EntityMetadata::CommandBlockMinecart(entity) => entity,
- EntityMetadata::Cow(entity) => entity,
- EntityMetadata::Creeper(entity) => entity,
- EntityMetadata::Dolphin(entity) => entity,
- EntityMetadata::Donkey(entity) => entity,
- EntityMetadata::DragonFireball(entity) => entity,
- EntityMetadata::Drowned(entity) => entity,
- EntityMetadata::Egg(entity) => entity,
- EntityMetadata::ElderGuardian(entity) => entity,
- EntityMetadata::EndCrystal(entity) => entity,
- EntityMetadata::EnderDragon(entity) => entity,
- EntityMetadata::EnderPearl(entity) => entity,
- EntityMetadata::Enderman(entity) => entity,
- EntityMetadata::Endermite(entity) => entity,
- EntityMetadata::Evoker(entity) => entity,
- EntityMetadata::EvokerFangs(entity) => entity,
- EntityMetadata::ExperienceBottle(entity) => entity,
- EntityMetadata::ExperienceOrb(entity) => entity,
- EntityMetadata::EyeOfEnder(entity) => entity,
- EntityMetadata::FallingBlock(entity) => entity,
- EntityMetadata::Fireball(entity) => entity,
- EntityMetadata::FireworkRocket(entity) => entity,
- EntityMetadata::FishingBobber(entity) => entity,
- EntityMetadata::Fox(entity) => entity,
- EntityMetadata::Frog(entity) => entity,
- EntityMetadata::FurnaceMinecart(entity) => entity,
- EntityMetadata::Ghast(entity) => entity,
- EntityMetadata::Giant(entity) => entity,
- EntityMetadata::GlowItemFrame(entity) => entity,
- EntityMetadata::GlowSquid(entity) => entity,
- EntityMetadata::Goat(entity) => entity,
- EntityMetadata::Guardian(entity) => entity,
- EntityMetadata::Hoglin(entity) => entity,
- EntityMetadata::HopperMinecart(entity) => entity,
- EntityMetadata::Horse(entity) => entity,
- EntityMetadata::Husk(entity) => entity,
- EntityMetadata::Illusioner(entity) => entity,
- EntityMetadata::IronGolem(entity) => entity,
- EntityMetadata::Item(entity) => entity,
- EntityMetadata::ItemFrame(entity) => entity,
- EntityMetadata::LeashKnot(entity) => entity,
- EntityMetadata::LightningBolt(entity) => entity,
- EntityMetadata::Llama(entity) => entity,
- EntityMetadata::LlamaSpit(entity) => entity,
- EntityMetadata::MagmaCube(entity) => entity,
- EntityMetadata::Marker(entity) => entity,
- EntityMetadata::Minecart(entity) => entity,
- EntityMetadata::Mooshroom(entity) => entity,
- EntityMetadata::Mule(entity) => entity,
- EntityMetadata::Ocelot(entity) => entity,
- EntityMetadata::Painting(entity) => entity,
- EntityMetadata::Panda(entity) => entity,
- EntityMetadata::Parrot(entity) => entity,
- EntityMetadata::Phantom(entity) => entity,
- EntityMetadata::Pig(entity) => entity,
- EntityMetadata::Piglin(entity) => entity,
- EntityMetadata::PiglinBrute(entity) => entity,
- EntityMetadata::Pillager(entity) => entity,
- EntityMetadata::Player(entity) => entity,
- EntityMetadata::PolarBear(entity) => entity,
- EntityMetadata::Potion(entity) => entity,
- EntityMetadata::Pufferfish(entity) => entity,
- EntityMetadata::Rabbit(entity) => entity,
- EntityMetadata::Ravager(entity) => entity,
- EntityMetadata::Salmon(entity) => entity,
- EntityMetadata::Sheep(entity) => entity,
- EntityMetadata::Shulker(entity) => entity,
- EntityMetadata::ShulkerBullet(entity) => entity,
- EntityMetadata::Silverfish(entity) => entity,
- EntityMetadata::Skeleton(entity) => entity,
- EntityMetadata::SkeletonHorse(entity) => entity,
- EntityMetadata::Slime(entity) => entity,
- EntityMetadata::SmallFireball(entity) => entity,
- EntityMetadata::SnowGolem(entity) => entity,
- EntityMetadata::Snowball(entity) => entity,
- EntityMetadata::SpawnerMinecart(entity) => entity,
- EntityMetadata::SpectralArrow(entity) => entity,
- EntityMetadata::Spider(entity) => entity,
- EntityMetadata::Squid(entity) => entity,
- EntityMetadata::Stray(entity) => entity,
- EntityMetadata::Strider(entity) => entity,
- EntityMetadata::Tadpole(entity) => entity,
- EntityMetadata::Tnt(entity) => entity,
- EntityMetadata::TntMinecart(entity) => entity,
- EntityMetadata::TraderLlama(entity) => entity,
- EntityMetadata::Trident(entity) => entity,
- EntityMetadata::TropicalFish(entity) => entity,
- EntityMetadata::Turtle(entity) => entity,
- EntityMetadata::Vex(entity) => entity,
- EntityMetadata::Villager(entity) => entity,
- EntityMetadata::Vindicator(entity) => entity,
- EntityMetadata::WanderingTrader(entity) => entity,
- EntityMetadata::Warden(entity) => entity,
- EntityMetadata::Witch(entity) => entity,
- EntityMetadata::Wither(entity) => entity,
- EntityMetadata::WitherSkeleton(entity) => entity,
- EntityMetadata::WitherSkull(entity) => entity,
- EntityMetadata::Wolf(entity) => entity,
- EntityMetadata::Zoglin(entity) => entity,
- EntityMetadata::Zombie(entity) => entity,
- EntityMetadata::ZombieHorse(entity) => entity,
- EntityMetadata::ZombieVillager(entity) => entity,
- EntityMetadata::ZombifiedPiglin(entity) => entity,
- }
- }
-}
-impl DerefMut for EntityMetadata {
- fn deref_mut(&mut self) -> &mut Self::Target {
- match self {
- EntityMetadata::Allay(entity) => entity,
- EntityMetadata::AreaEffectCloud(entity) => entity,
- EntityMetadata::ArmorStand(entity) => entity,
- EntityMetadata::Arrow(entity) => entity,
- EntityMetadata::Axolotl(entity) => entity,
- EntityMetadata::Bat(entity) => entity,
- EntityMetadata::Bee(entity) => entity,
- EntityMetadata::Blaze(entity) => entity,
- EntityMetadata::Boat(entity) => entity,
- EntityMetadata::Camel(entity) => entity,
- EntityMetadata::Cat(entity) => entity,
- EntityMetadata::CaveSpider(entity) => entity,
- EntityMetadata::ChestBoat(entity) => entity,
- EntityMetadata::ChestMinecart(entity) => entity,
- EntityMetadata::Chicken(entity) => entity,
- EntityMetadata::Cod(entity) => entity,
- EntityMetadata::CommandBlockMinecart(entity) => entity,
- EntityMetadata::Cow(entity) => entity,
- EntityMetadata::Creeper(entity) => entity,
- EntityMetadata::Dolphin(entity) => entity,
- EntityMetadata::Donkey(entity) => entity,
- EntityMetadata::DragonFireball(entity) => entity,
- EntityMetadata::Drowned(entity) => entity,
- EntityMetadata::Egg(entity) => entity,
- EntityMetadata::ElderGuardian(entity) => entity,
- EntityMetadata::EndCrystal(entity) => entity,
- EntityMetadata::EnderDragon(entity) => entity,
- EntityMetadata::EnderPearl(entity) => entity,
- EntityMetadata::Enderman(entity) => entity,
- EntityMetadata::Endermite(entity) => entity,
- EntityMetadata::Evoker(entity) => entity,
- EntityMetadata::EvokerFangs(entity) => entity,
- EntityMetadata::ExperienceBottle(entity) => entity,
- EntityMetadata::ExperienceOrb(entity) => entity,
- EntityMetadata::EyeOfEnder(entity) => entity,
- EntityMetadata::FallingBlock(entity) => entity,
- EntityMetadata::Fireball(entity) => entity,
- EntityMetadata::FireworkRocket(entity) => entity,
- EntityMetadata::FishingBobber(entity) => entity,
- EntityMetadata::Fox(entity) => entity,
- EntityMetadata::Frog(entity) => entity,
- EntityMetadata::FurnaceMinecart(entity) => entity,
- EntityMetadata::Ghast(entity) => entity,
- EntityMetadata::Giant(entity) => entity,
- EntityMetadata::GlowItemFrame(entity) => entity,
- EntityMetadata::GlowSquid(entity) => entity,
- EntityMetadata::Goat(entity) => entity,
- EntityMetadata::Guardian(entity) => entity,
- EntityMetadata::Hoglin(entity) => entity,
- EntityMetadata::HopperMinecart(entity) => entity,
- EntityMetadata::Horse(entity) => entity,
- EntityMetadata::Husk(entity) => entity,
- EntityMetadata::Illusioner(entity) => entity,
- EntityMetadata::IronGolem(entity) => entity,
- EntityMetadata::Item(entity) => entity,
- EntityMetadata::ItemFrame(entity) => entity,
- EntityMetadata::LeashKnot(entity) => entity,
- EntityMetadata::LightningBolt(entity) => entity,
- EntityMetadata::Llama(entity) => entity,
- EntityMetadata::LlamaSpit(entity) => entity,
- EntityMetadata::MagmaCube(entity) => entity,
- EntityMetadata::Marker(entity) => entity,
- EntityMetadata::Minecart(entity) => entity,
- EntityMetadata::Mooshroom(entity) => entity,
- EntityMetadata::Mule(entity) => entity,
- EntityMetadata::Ocelot(entity) => entity,
- EntityMetadata::Painting(entity) => entity,
- EntityMetadata::Panda(entity) => entity,
- EntityMetadata::Parrot(entity) => entity,
- EntityMetadata::Phantom(entity) => entity,
- EntityMetadata::Pig(entity) => entity,
- EntityMetadata::Piglin(entity) => entity,
- EntityMetadata::PiglinBrute(entity) => entity,
- EntityMetadata::Pillager(entity) => entity,
- EntityMetadata::Player(entity) => entity,
- EntityMetadata::PolarBear(entity) => entity,
- EntityMetadata::Potion(entity) => entity,
- EntityMetadata::Pufferfish(entity) => entity,
- EntityMetadata::Rabbit(entity) => entity,
- EntityMetadata::Ravager(entity) => entity,
- EntityMetadata::Salmon(entity) => entity,
- EntityMetadata::Sheep(entity) => entity,
- EntityMetadata::Shulker(entity) => entity,
- EntityMetadata::ShulkerBullet(entity) => entity,
- EntityMetadata::Silverfish(entity) => entity,
- EntityMetadata::Skeleton(entity) => entity,
- EntityMetadata::SkeletonHorse(entity) => entity,
- EntityMetadata::Slime(entity) => entity,
- EntityMetadata::SmallFireball(entity) => entity,
- EntityMetadata::SnowGolem(entity) => entity,
- EntityMetadata::Snowball(entity) => entity,
- EntityMetadata::SpawnerMinecart(entity) => entity,
- EntityMetadata::SpectralArrow(entity) => entity,
- EntityMetadata::Spider(entity) => entity,
- EntityMetadata::Squid(entity) => entity,
- EntityMetadata::Stray(entity) => entity,
- EntityMetadata::Strider(entity) => entity,
- EntityMetadata::Tadpole(entity) => entity,
- EntityMetadata::Tnt(entity) => entity,
- EntityMetadata::TntMinecart(entity) => entity,
- EntityMetadata::TraderLlama(entity) => entity,
- EntityMetadata::Trident(entity) => entity,
- EntityMetadata::TropicalFish(entity) => entity,
- EntityMetadata::Turtle(entity) => entity,
- EntityMetadata::Vex(entity) => entity,
- EntityMetadata::Villager(entity) => entity,
- EntityMetadata::Vindicator(entity) => entity,
- EntityMetadata::WanderingTrader(entity) => entity,
- EntityMetadata::Warden(entity) => entity,
- EntityMetadata::Witch(entity) => entity,
- EntityMetadata::Wither(entity) => entity,
- EntityMetadata::WitherSkeleton(entity) => entity,
- EntityMetadata::WitherSkull(entity) => entity,
- EntityMetadata::Wolf(entity) => entity,
- EntityMetadata::Zoglin(entity) => entity,
- EntityMetadata::Zombie(entity) => entity,
- EntityMetadata::ZombieHorse(entity) => entity,
- EntityMetadata::ZombieVillager(entity) => entity,
- EntityMetadata::ZombifiedPiglin(entity) => entity,
+pub fn apply_default_metadata(
+ entity: &mut azalea_ecs::system::EntityCommands,
+ kind: azalea_registry::EntityKind,
+) {
+ match kind {
+ azalea_registry::EntityKind::Allay => {
+ entity.insert(AllayMetadataBundle::default());
+ }
+ azalea_registry::EntityKind::AreaEffectCloud => {
+ entity.insert(AreaEffectCloudMetadataBundle::default());
+ }
+ azalea_registry::EntityKind::ArmorStand => {
+ entity.insert(ArmorStandMetadataBundle::default());
+ }
+ azalea_registry::EntityKind::Arrow => {
+ entity.insert(ArrowMetadataBundle::default());
+ }
+ azalea_registry::EntityKind::Axolotl => {
+ entity.insert(AxolotlMetadataBundle::default());
+ }
+ azalea_registry::EntityKind::Bat => {
+ entity.insert(BatMetadataBundle::default());
+ }
+ azalea_registry::EntityKind::Bee => {
+ entity.insert(BeeMetadataBundle::default());
+ }
+ azalea_registry::EntityKind::Blaze => {
+ entity.insert(BlazeMetadataBundle::default());
+ }
+ azalea_registry::EntityKind::Boat => {
+ entity.insert(BoatMetadataBundle::default());
+ }
+ azalea_registry::EntityKind::Camel => {
+ entity.insert(CamelMetadataBundle::default());
+ }
+ azalea_registry::EntityKind::Cat => {
+ entity.insert(CatMetadataBundle::default());
+ }
+ azalea_registry::EntityKind::CaveSpider => {
+ entity.insert(CaveSpiderMetadataBundle::default());
+ }
+ azalea_registry::EntityKind::ChestBoat => {
+ entity.insert(ChestBoatMetadataBundle::default());
+ }
+ azalea_registry::EntityKind::ChestMinecart => {
+ entity.insert(ChestMinecartMetadataBundle::default());
+ }
+ azalea_registry::EntityKind::Chicken => {
+ entity.insert(ChickenMetadataBundle::default());
+ }
+ azalea_registry::EntityKind::Cod => {
+ entity.insert(CodMetadataBundle::default());
+ }
+ azalea_registry::EntityKind::CommandBlockMinecart => {
+ entity.insert(CommandBlockMinecartMetadataBundle::default());
+ }
+ azalea_registry::EntityKind::Cow => {
+ entity.insert(CowMetadataBundle::default());
+ }
+ azalea_registry::EntityKind::Creeper => {
+ entity.insert(CreeperMetadataBundle::default());
+ }
+ azalea_registry::EntityKind::Dolphin => {
+ entity.insert(DolphinMetadataBundle::default());
+ }
+ azalea_registry::EntityKind::Donkey => {
+ entity.insert(DonkeyMetadataBundle::default());
+ }
+ azalea_registry::EntityKind::DragonFireball => {
+ entity.insert(DragonFireballMetadataBundle::default());
+ }
+ azalea_registry::EntityKind::Drowned => {
+ entity.insert(DrownedMetadataBundle::default());
+ }
+ azalea_registry::EntityKind::Egg => {
+ entity.insert(EggMetadataBundle::default());
+ }
+ azalea_registry::EntityKind::ElderGuardian => {
+ entity.insert(ElderGuardianMetadataBundle::default());
+ }
+ azalea_registry::EntityKind::EndCrystal => {
+ entity.insert(EndCrystalMetadataBundle::default());
+ }
+ azalea_registry::EntityKind::EnderDragon => {
+ entity.insert(EnderDragonMetadataBundle::default());
+ }
+ azalea_registry::EntityKind::EnderPearl => {
+ entity.insert(EnderPearlMetadataBundle::default());
+ }
+ azalea_registry::EntityKind::Enderman => {
+ entity.insert(EndermanMetadataBundle::default());
+ }
+ azalea_registry::EntityKind::Endermite => {
+ entity.insert(EndermiteMetadataBundle::default());
+ }
+ azalea_registry::EntityKind::Evoker => {
+ entity.insert(EvokerMetadataBundle::default());
+ }
+ azalea_registry::EntityKind::EvokerFangs => {
+ entity.insert(EvokerFangsMetadataBundle::default());
+ }
+ azalea_registry::EntityKind::ExperienceBottle => {
+ entity.insert(ExperienceBottleMetadataBundle::default());
+ }
+ azalea_registry::EntityKind::ExperienceOrb => {
+ entity.insert(ExperienceOrbMetadataBundle::default());
+ }
+ azalea_registry::EntityKind::EyeOfEnder => {
+ entity.insert(EyeOfEnderMetadataBundle::default());
+ }
+ azalea_registry::EntityKind::FallingBlock => {
+ entity.insert(FallingBlockMetadataBundle::default());
+ }
+ azalea_registry::EntityKind::Fireball => {
+ entity.insert(FireballMetadataBundle::default());
+ }
+ azalea_registry::EntityKind::FireworkRocket => {
+ entity.insert(FireworkRocketMetadataBundle::default());
+ }
+ azalea_registry::EntityKind::FishingBobber => {
+ entity.insert(FishingBobberMetadataBundle::default());
+ }
+ azalea_registry::EntityKind::Fox => {
+ entity.insert(FoxMetadataBundle::default());
+ }
+ azalea_registry::EntityKind::Frog => {
+ entity.insert(FrogMetadataBundle::default());
+ }
+ azalea_registry::EntityKind::FurnaceMinecart => {
+ entity.insert(FurnaceMinecartMetadataBundle::default());
+ }
+ azalea_registry::EntityKind::Ghast => {
+ entity.insert(GhastMetadataBundle::default());
+ }
+ azalea_registry::EntityKind::Giant => {
+ entity.insert(GiantMetadataBundle::default());
+ }
+ azalea_registry::EntityKind::GlowItemFrame => {
+ entity.insert(GlowItemFrameMetadataBundle::default());
+ }
+ azalea_registry::EntityKind::GlowSquid => {
+ entity.insert(GlowSquidMetadataBundle::default());
+ }
+ azalea_registry::EntityKind::Goat => {
+ entity.insert(GoatMetadataBundle::default());
+ }
+ azalea_registry::EntityKind::Guardian => {
+ entity.insert(GuardianMetadataBundle::default());
+ }
+ azalea_registry::EntityKind::Hoglin => {
+ entity.insert(HoglinMetadataBundle::default());
+ }
+ azalea_registry::EntityKind::HopperMinecart => {
+ entity.insert(HopperMinecartMetadataBundle::default());
+ }
+ azalea_registry::EntityKind::Horse => {
+ entity.insert(HorseMetadataBundle::default());
+ }
+ azalea_registry::EntityKind::Husk => {
+ entity.insert(HuskMetadataBundle::default());
+ }
+ azalea_registry::EntityKind::Illusioner => {
+ entity.insert(IllusionerMetadataBundle::default());
+ }
+ azalea_registry::EntityKind::IronGolem => {
+ entity.insert(IronGolemMetadataBundle::default());
+ }
+ azalea_registry::EntityKind::Item => {
+ entity.insert(ItemMetadataBundle::default());
+ }
+ azalea_registry::EntityKind::ItemFrame => {
+ entity.insert(ItemFrameMetadataBundle::default());
+ }
+ azalea_registry::EntityKind::LeashKnot => {
+ entity.insert(LeashKnotMetadataBundle::default());
+ }
+ azalea_registry::EntityKind::LightningBolt => {
+ entity.insert(LightningBoltMetadataBundle::default());
+ }
+ azalea_registry::EntityKind::Llama => {
+ entity.insert(LlamaMetadataBundle::default());
+ }
+ azalea_registry::EntityKind::LlamaSpit => {
+ entity.insert(LlamaSpitMetadataBundle::default());
+ }
+ azalea_registry::EntityKind::MagmaCube => {
+ entity.insert(MagmaCubeMetadataBundle::default());
+ }
+ azalea_registry::EntityKind::Marker => {
+ entity.insert(MarkerMetadataBundle::default());
+ }
+ azalea_registry::EntityKind::Minecart => {
+ entity.insert(MinecartMetadataBundle::default());
+ }
+ azalea_registry::EntityKind::Mooshroom => {
+ entity.insert(MooshroomMetadataBundle::default());
+ }
+ azalea_registry::EntityKind::Mule => {
+ entity.insert(MuleMetadataBundle::default());
+ }
+ azalea_registry::EntityKind::Ocelot => {
+ entity.insert(OcelotMetadataBundle::default());
+ }
+ azalea_registry::EntityKind::Painting => {
+ entity.insert(PaintingMetadataBundle::default());
+ }
+ azalea_registry::EntityKind::Panda => {
+ entity.insert(PandaMetadataBundle::default());
+ }
+ azalea_registry::EntityKind::Parrot => {
+ entity.insert(ParrotMetadataBundle::default());
+ }
+ azalea_registry::EntityKind::Phantom => {
+ entity.insert(PhantomMetadataBundle::default());
+ }
+ azalea_registry::EntityKind::Pig => {
+ entity.insert(PigMetadataBundle::default());
+ }
+ azalea_registry::EntityKind::Piglin => {
+ entity.insert(PiglinMetadataBundle::default());
+ }
+ azalea_registry::EntityKind::PiglinBrute => {
+ entity.insert(PiglinBruteMetadataBundle::default());
+ }
+ azalea_registry::EntityKind::Pillager => {
+ entity.insert(PillagerMetadataBundle::default());
+ }
+ azalea_registry::EntityKind::Player => {
+ entity.insert(PlayerMetadataBundle::default());
+ }
+ azalea_registry::EntityKind::PolarBear => {
+ entity.insert(PolarBearMetadataBundle::default());
+ }
+ azalea_registry::EntityKind::Potion => {
+ entity.insert(PotionMetadataBundle::default());
+ }
+ azalea_registry::EntityKind::Pufferfish => {
+ entity.insert(PufferfishMetadataBundle::default());
+ }
+ azalea_registry::EntityKind::Rabbit => {
+ entity.insert(RabbitMetadataBundle::default());
+ }
+ azalea_registry::EntityKind::Ravager => {
+ entity.insert(RavagerMetadataBundle::default());
+ }
+ azalea_registry::EntityKind::Salmon => {
+ entity.insert(SalmonMetadataBundle::default());
+ }
+ azalea_registry::EntityKind::Sheep => {
+ entity.insert(SheepMetadataBundle::default());
+ }
+ azalea_registry::EntityKind::Shulker => {
+ entity.insert(ShulkerMetadataBundle::default());
+ }
+ azalea_registry::EntityKind::ShulkerBullet => {
+ entity.insert(ShulkerBulletMetadataBundle::default());
+ }
+ azalea_registry::EntityKind::Silverfish => {
+ entity.insert(SilverfishMetadataBundle::default());
+ }
+ azalea_registry::EntityKind::Skeleton => {
+ entity.insert(SkeletonMetadataBundle::default());
+ }
+ azalea_registry::EntityKind::SkeletonHorse => {
+ entity.insert(SkeletonHorseMetadataBundle::default());
+ }
+ azalea_registry::EntityKind::Slime => {
+ entity.insert(SlimeMetadataBundle::default());
+ }
+ azalea_registry::EntityKind::SmallFireball => {
+ entity.insert(SmallFireballMetadataBundle::default());
+ }
+ azalea_registry::EntityKind::SnowGolem => {
+ entity.insert(SnowGolemMetadataBundle::default());
+ }
+ azalea_registry::EntityKind::Snowball => {
+ entity.insert(SnowballMetadataBundle::default());
+ }
+ azalea_registry::EntityKind::SpawnerMinecart => {
+ entity.insert(SpawnerMinecartMetadataBundle::default());
+ }
+ azalea_registry::EntityKind::SpectralArrow => {
+ entity.insert(SpectralArrowMetadataBundle::default());
+ }
+ azalea_registry::EntityKind::Spider => {
+ entity.insert(SpiderMetadataBundle::default());
+ }
+ azalea_registry::EntityKind::Squid => {
+ entity.insert(SquidMetadataBundle::default());
+ }
+ azalea_registry::EntityKind::Stray => {
+ entity.insert(StrayMetadataBundle::default());
+ }
+ azalea_registry::EntityKind::Strider => {
+ entity.insert(StriderMetadataBundle::default());
+ }
+ azalea_registry::EntityKind::Tadpole => {
+ entity.insert(TadpoleMetadataBundle::default());
+ }
+ azalea_registry::EntityKind::Tnt => {
+ entity.insert(TntMetadataBundle::default());
+ }
+ azalea_registry::EntityKind::TntMinecart => {
+ entity.insert(TntMinecartMetadataBundle::default());
+ }
+ azalea_registry::EntityKind::TraderLlama => {
+ entity.insert(TraderLlamaMetadataBundle::default());
+ }
+ azalea_registry::EntityKind::Trident => {
+ entity.insert(TridentMetadataBundle::default());
+ }
+ azalea_registry::EntityKind::TropicalFish => {
+ entity.insert(TropicalFishMetadataBundle::default());
+ }
+ azalea_registry::EntityKind::Turtle => {
+ entity.insert(TurtleMetadataBundle::default());
+ }
+ azalea_registry::EntityKind::Vex => {
+ entity.insert(VexMetadataBundle::default());
+ }
+ azalea_registry::EntityKind::Villager => {
+ entity.insert(VillagerMetadataBundle::default());
+ }
+ azalea_registry::EntityKind::Vindicator => {
+ entity.insert(VindicatorMetadataBundle::default());
+ }
+ azalea_registry::EntityKind::WanderingTrader => {
+ entity.insert(WanderingTraderMetadataBundle::default());
+ }
+ azalea_registry::EntityKind::Warden => {
+ entity.insert(WardenMetadataBundle::default());
+ }
+ azalea_registry::EntityKind::Witch => {
+ entity.insert(WitchMetadataBundle::default());
+ }
+ azalea_registry::EntityKind::Wither => {
+ entity.insert(WitherMetadataBundle::default());
+ }
+ azalea_registry::EntityKind::WitherSkeleton => {
+ entity.insert(WitherSkeletonMetadataBundle::default());
+ }
+ azalea_registry::EntityKind::WitherSkull => {
+ entity.insert(WitherSkullMetadataBundle::default());
+ }
+ azalea_registry::EntityKind::Wolf => {
+ entity.insert(WolfMetadataBundle::default());
+ }
+ azalea_registry::EntityKind::Zoglin => {
+ entity.insert(ZoglinMetadataBundle::default());
+ }
+ azalea_registry::EntityKind::Zombie => {
+ entity.insert(ZombieMetadataBundle::default());
+ }
+ azalea_registry::EntityKind::ZombieHorse => {
+ entity.insert(ZombieHorseMetadataBundle::default());
+ }
+ azalea_registry::EntityKind::ZombieVillager => {
+ entity.insert(ZombieVillagerMetadataBundle::default());
+ }
+ azalea_registry::EntityKind::ZombifiedPiglin => {
+ entity.insert(ZombifiedPiglinMetadataBundle::default());
}
}
}
diff --git a/azalea-world/src/entity/mod.rs b/azalea-world/src/entity/mod.rs
index 94362f2f..bf758a12 100644
--- a/azalea-world/src/entity/mod.rs
+++ b/azalea-world/src/entity/mod.rs
@@ -1,192 +1,190 @@
+#![allow(clippy::derived_hash_with_manual_eq)]
+
pub mod attributes;
mod data;
mod dimensions;
pub mod metadata;
-use self::attributes::{AttributeInstance, AttributeModifiers};
-pub use self::metadata::EntityMetadata;
-use crate::WeakWorld;
+use crate::ChunkStorage;
+
+use self::{attributes::AttributeInstance, metadata::Health};
+pub use attributes::Attributes;
use azalea_block::BlockState;
-use azalea_core::{BlockPos, Vec3, AABB};
+use azalea_core::{BlockPos, ChunkPos, ResourceLocation, Vec3, AABB};
+use azalea_ecs::{
+ bundle::Bundle,
+ component::Component,
+ entity::Entity,
+ query::Changed,
+ system::{Commands, Query},
+};
pub use data::*;
-pub use dimensions::*;
-use std::marker::PhantomData;
-use std::ops::{Deref, DerefMut};
-use std::ptr::NonNull;
+use derive_more::{Deref, DerefMut};
+pub use dimensions::{update_bounding_box, EntityDimensions};
+use std::fmt::Debug;
use uuid::Uuid;
-/// A reference to an entity in a world.
-#[derive(Debug)]
-pub struct Entity<'d, D = &'d WeakWorld> {
- /// The world this entity is in.
- pub world: D,
- /// The incrementing numerical id of the entity.
- pub id: u32,
- pub data: NonNull<EntityData>,
- _marker: PhantomData<&'d ()>,
+/// An entity ID used by Minecraft. These are not guaranteed to be unique in
+/// shared worlds, that's what [`Entity`] is for.
+#[derive(Component, Copy, Clone, Debug, PartialEq, Eq, Deref, DerefMut)]
+pub struct MinecraftEntityId(pub u32);
+impl std::hash::Hash for MinecraftEntityId {
+ fn hash<H: std::hash::Hasher>(&self, hasher: &mut H) {
+ hasher.write_u32(self.0);
+ }
+}
+impl nohash_hasher::IsEnabled for MinecraftEntityId {}
+pub fn set_rotation(physics: &mut Physics, y_rot: f32, x_rot: f32) {
+ physics.y_rot = y_rot % 360.0;
+ physics.x_rot = x_rot.clamp(-90.0, 90.0) % 360.0;
+ // TODO: minecraft also sets yRotO and xRotO to xRot and yRot ... but
+ // idk what they're used for so
}
-impl<'d, D: Deref<Target = WeakWorld>> Entity<'d, D> {
- pub fn new(world: D, id: u32, data: NonNull<EntityData>) -> Self {
- // TODO: have this be based on the entity type
- Self {
- world,
- id,
- data,
- _marker: PhantomData,
- }
- }
+pub fn move_relative(physics: &mut Physics, speed: f32, acceleration: &Vec3) {
+ let input_vector = input_vector(physics, speed, acceleration);
+ physics.delta += input_vector;
}
-impl<'d, D: Deref<Target = WeakWorld>> Entity<'d, D> {
- /// Sets the position of the entity. This doesn't update the cache in
- /// azalea-world, and should only be used within azalea-world!
- ///
- /// # Safety
- /// Cached position in the world must be updated.
- pub unsafe fn move_unchecked(&mut self, new_pos: Vec3) {
- self.pos = new_pos;
- let bounding_box = self.make_bounding_box();
- self.bounding_box = bounding_box;
+pub fn input_vector(physics: &mut Physics, speed: f32, acceleration: &Vec3) -> Vec3 {
+ let distance = acceleration.length_squared();
+ if distance < 1.0E-7 {
+ return Vec3::default();
}
-
- pub fn set_rotation(&mut self, y_rot: f32, x_rot: f32) {
- self.y_rot = y_rot % 360.0;
- self.x_rot = x_rot.clamp(-90.0, 90.0) % 360.0;
- // TODO: minecraft also sets yRotO and xRotO to xRot and yRot ... but
- // idk what they're used for so
+ let acceleration = if distance > 1.0 {
+ acceleration.normalize()
+ } else {
+ *acceleration
}
-
- pub fn move_relative(&mut self, speed: f32, acceleration: &Vec3) {
- let input_vector = self.input_vector(speed, acceleration);
- self.delta += input_vector;
+ .scale(speed as f64);
+ let y_rot = f32::sin(physics.y_rot * 0.017453292f32);
+ let x_rot = f32::cos(physics.y_rot * 0.017453292f32);
+ Vec3 {
+ x: acceleration.x * (x_rot as f64) - acceleration.z * (y_rot as f64),
+ y: acceleration.y,
+ z: acceleration.z * (x_rot as f64) + acceleration.x * (y_rot as f64),
}
+}
- pub fn input_vector(&self, speed: f32, acceleration: &Vec3) -> Vec3 {
- let distance = acceleration.length_squared();
- if distance < 1.0E-7 {
- return Vec3::default();
- }
- let acceleration = if distance > 1.0 {
- acceleration.normalize()
- } else {
- *acceleration
- }
- .scale(speed as f64);
- let y_rot = f32::sin(self.y_rot * 0.017453292f32);
- let x_rot = f32::cos(self.y_rot * 0.017453292f32);
- Vec3 {
- x: acceleration.x * (x_rot as f64) - acceleration.z * (y_rot as f64),
- y: acceleration.y,
- z: acceleration.z * (x_rot as f64) + acceleration.x * (y_rot as f64),
- }
- }
+/// Get the position of the block below the entity, but a little lower.
+pub fn on_pos_legacy(chunk_storage: &ChunkStorage, position: &Position) -> BlockPos {
+ on_pos(0.2, chunk_storage, position)
+}
- /// Apply the given metadata items to the entity. Everything that isn't
- /// included in items will be left unchanged. If an error occured, None
- /// will be returned.
- ///
- /// TODO: this should be changed to have a proper error.
- pub fn apply_metadata(&mut self, items: &Vec<EntityDataItem>) -> Option<()> {
- for item in items {
- self.metadata.set_index(item.index, item.value.clone())?;
+// int x = Mth.floor(this.position.x);
+// int y = Mth.floor(this.position.y - (double)var1);
+// int z = Mth.floor(this.position.z);
+// BlockPos var5 = new BlockPos(x, y, z);
+// if (this.level.getBlockState(var5).isAir()) {
+// BlockPos var6 = var5.below();
+// BlockState var7 = this.level.getBlockState(var6);
+// if (var7.is(BlockTags.FENCES) || var7.is(BlockTags.WALLS) ||
+// var7.getBlock() instanceof FenceGateBlock) { return var6;
+// }
+// }
+// return var5;
+pub fn on_pos(offset: f32, chunk_storage: &ChunkStorage, pos: &Position) -> BlockPos {
+ let x = pos.x.floor() as i32;
+ let y = (pos.y - offset as f64).floor() as i32;
+ let z = pos.z.floor() as i32;
+ let pos = BlockPos { x, y, z };
+
+ // TODO: check if block below is a fence, wall, or fence gate
+ let block_pos = pos.down(1);
+ let block_state = chunk_storage.get_block_state(&block_pos);
+ if block_state == Some(BlockState::Air) {
+ let block_pos_below = block_pos.down(1);
+ let block_state_below = chunk_storage.get_block_state(&block_pos_below);
+ if let Some(_block_state_below) = block_state_below {
+ // if block_state_below.is_fence()
+ // || block_state_below.is_wall()
+ // || block_state_below.is_fence_gate()
+ // {
+ // return block_pos_below;
+ // }
}
- Some(())
}
+
+ pos
}
-impl<'d, D: Deref<Target = WeakWorld>> Entity<'d, D> {
- #[inline]
- pub fn pos(&self) -> &Vec3 {
- &self.pos
+/// 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)]
+pub struct EntityUuid(Uuid);
+impl Debug for EntityUuid {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ (self.0).fmt(f)
}
+}
- pub fn make_bounding_box(&self) -> AABB {
- self.dimensions.make_bounding_box(self.pos())
+/// The position of the entity right now.
+///
+/// You are free to change this; there's systems that update the indexes
+/// automatically.
+#[derive(Component, Clone, Copy, Debug, Default, PartialEq, Deref, DerefMut)]
+pub struct Position(Vec3);
+impl From<Position> for ChunkPos {
+ fn from(value: Position) -> Self {
+ ChunkPos::from(&value.0)
}
-
- /// Get the position of the block below the entity, but a little lower.
- pub fn on_pos_legacy(&self) -> BlockPos {
- self.on_pos(0.2)
+}
+impl From<Position> for BlockPos {
+ fn from(value: Position) -> Self {
+ BlockPos::from(&value.0)
}
-
- // int x = Mth.floor(this.position.x);
- // int y = Mth.floor(this.position.y - (double)var1);
- // int z = Mth.floor(this.position.z);
- // BlockPos var5 = new BlockPos(x, y, z);
- // if (this.level.getBlockState(var5).isAir()) {
- // BlockPos var6 = var5.below();
- // BlockState var7 = this.level.getBlockState(var6);
- // if (var7.is(BlockTags.FENCES) || var7.is(BlockTags.WALLS) ||
- // var7.getBlock() instanceof FenceGateBlock) { return var6;
- // }
- // }
- // return var5;
- pub fn on_pos(&self, offset: f32) -> BlockPos {
- let x = self.pos().x.floor() as i32;
- let y = (self.pos().y - offset as f64).floor() as i32;
- let z = self.pos().z.floor() as i32;
- let pos = BlockPos { x, y, z };
-
- // TODO: check if block below is a fence, wall, or fence gate
- let block_pos = pos.down(1);
- let block_state = self.world.get_block_state(&block_pos);
- if block_state == Some(BlockState::Air) {
- let block_pos_below = block_pos.down(1);
- let block_state_below = self.world.get_block_state(&block_pos_below);
- if let Some(_block_state_below) = block_state_below {
- // if block_state_below.is_fence()
- // || block_state_below.is_wall()
- // || block_state_below.is_fence_gate()
- // {
- // return block_pos_below;
- // }
- }
- }
-
- pos
+}
+impl From<&Position> for ChunkPos {
+ fn from(value: &Position) -> Self {
+ ChunkPos::from(value.0)
}
}
-
-// impl<
-// 'd,
-// D: Deref<Target = WeakWorld> + Deref<Target = WeakWorld>,
-// D2: Deref<Target = WeakWorld>,
-// > From<Entity<'d, D>> for Entity<'d, D2>
-// {
-// fn from(entity: Entity<'d, D>) -> Entity<'d, D> {
-// Entity {
-// world: entity.world,
-// id: entity.id,
-// data: entity.data,
-// _marker: PhantomData,
-// }
-// }
-// }
-
-impl<D: Deref<Target = WeakWorld>> DerefMut for Entity<'_, D> {
- fn deref_mut(&mut self) -> &mut Self::Target {
- unsafe { self.data.as_mut() }
+impl From<&Position> for BlockPos {
+ fn from(value: &Position) -> Self {
+ BlockPos::from(value.0)
}
}
-impl<D: Deref<Target = WeakWorld>> Deref for Entity<'_, D> {
- type Target = EntityData;
-
- fn deref(&self) -> &Self::Target {
- unsafe { self.data.as_ref() }
+/// The last position of the entity that was sent to the network.
+#[derive(Component, Clone, Copy, Debug, Default, PartialEq, Deref, DerefMut)]
+pub struct LastSentPosition(Vec3);
+impl From<LastSentPosition> for ChunkPos {
+ fn from(value: LastSentPosition) -> Self {
+ ChunkPos::from(&value.0)
+ }
+}
+impl From<LastSentPosition> for BlockPos {
+ fn from(value: LastSentPosition) -> Self {
+ BlockPos::from(&value.0)
+ }
+}
+impl From<&LastSentPosition> for ChunkPos {
+ fn from(value: &LastSentPosition) -> Self {
+ ChunkPos::from(value.0)
+ }
+}
+impl From<&LastSentPosition> for BlockPos {
+ fn from(value: &LastSentPosition) -> Self {
+ BlockPos::from(value.0)
}
}
-#[derive(Debug)]
-pub struct EntityData {
- pub uuid: Uuid,
- /// The position of the entity right now.
- /// This can be changde with unsafe_move, but the correct way is with
- /// world.move_entity
- pos: Vec3,
- /// The position of the entity last tick.
- pub last_pos: Vec3,
+/// The name of the world the entity is in. If two entities share the same world
+/// name, we assume they're in the same world.
+#[derive(Component, Clone, Debug, PartialEq, Deref, DerefMut)]
+pub struct WorldName(ResourceLocation);
+
+/// A component for entities that can jump.
+///
+/// If this is true, the entity will try to jump every tick. (It's equivalent to
+/// the space key being held in vanilla.)
+#[derive(Debug, Component, Deref, DerefMut)]
+pub struct Jumping(bool);
+
+/// The physics data relating to the entity, such as position, velocity, and
+/// bounding box.
+#[derive(Debug, Component)]
+pub struct Physics {
pub delta: Vec3,
/// X acceleration.
@@ -211,97 +209,129 @@ pub struct EntityData {
/// unlike dimensions.
pub bounding_box: AABB,
- /// Whether the entity will try to jump every tick
- /// (equivalent to the space key being held down in vanilla).
- pub jumping: bool,
-
pub has_impulse: bool,
+}
- /// Stores some extra data about the entity, including the entity type.
- pub metadata: EntityMetadata,
+/// Marker component for entities that are dead.
+///
+/// "Dead" means that the entity has 0 health.
+#[derive(Component, Copy, Clone, Default)]
+pub struct Dead;
+
+/// System that adds the [`Dead`] marker component if an entity's health is set
+/// to 0 (or less than 0). This will be present if an entity is doing the death
+/// animation.
+///
+/// Entities that are dead can not be revived.
+/// TODO: fact check this in-game by setting an entity's health to 0 and then
+/// not 0
+pub fn add_dead(mut commands: Commands, query: Query<(Entity, &Health), Changed<Health>>) {
+ for (entity, health) in query.iter() {
+ if **health <= 0.0 {
+ commands.entity(entity).insert(Dead);
+ }
+ }
+}
- /// The attributes and modifiers that the entity has (for example, speed).
- pub attributes: AttributeModifiers,
+/// A component NewType for [`azalea_registry::EntityKind`].
+///
+/// Most of the time, you should be using `azalea_registry::EntityKind`
+/// instead.
+#[derive(Component, Clone, Copy, Debug, PartialEq, Deref)]
+pub struct EntityKind(azalea_registry::EntityKind);
+
+/// A bundle of components that every entity has. This doesn't contain metadata,
+/// that has to be added separately.
+#[derive(Bundle)]
+pub struct EntityBundle {
+ pub kind: EntityKind,
+ pub uuid: EntityUuid,
+ pub world_name: WorldName,
+ pub position: Position,
+ pub last_sent_position: LastSentPosition,
+ pub physics: Physics,
+ pub attributes: Attributes,
+ pub jumping: Jumping,
}
-impl EntityData {
- pub fn new(uuid: Uuid, pos: Vec3, metadata: EntityMetadata) -> Self {
+impl EntityBundle {
+ pub fn new(
+ uuid: Uuid,
+ pos: Vec3,
+ kind: azalea_registry::EntityKind,
+ world_name: ResourceLocation,
+ ) -> Self {
+ // TODO: get correct entity dimensions by having them codegened somewhere
let dimensions = EntityDimensions {
width: 0.6,
height: 1.8,
};
Self {
- uuid,
- pos,
- last_pos: pos,
- delta: Vec3::default(),
-
- xxa: 0.,
- yya: 0.,
- zza: 0.,
+ kind: EntityKind(kind),
+ uuid: EntityUuid(uuid),
+ world_name: WorldName(world_name),
+ position: Position(pos),
+ last_sent_position: LastSentPosition(pos),
+ physics: Physics {
+ delta: Vec3::default(),
- x_rot: 0.,
- y_rot: 0.,
+ xxa: 0.,
+ yya: 0.,
+ zza: 0.,
- y_rot_last: 0.,
- x_rot_last: 0.,
+ x_rot: 0.,
+ y_rot: 0.,
- on_ground: false,
- last_on_ground: false,
+ y_rot_last: 0.,
+ x_rot_last: 0.,
- // TODO: have this be based on the entity type
- bounding_box: dimensions.make_bounding_box(&pos),
- dimensions,
+ on_ground: false,
+ last_on_ground: false,
- has_impulse: false,
+ // TODO: have this be based on the entity type
+ bounding_box: dimensions.make_bounding_box(&pos),
+ dimensions,
- jumping: false,
-
- metadata,
+ has_impulse: false,
+ },
- attributes: AttributeModifiers {
- // TODO: do the correct defaults for everything, some entities have different
- // defaults
+ attributes: Attributes {
+ // TODO: do the correct defaults for everything, some
+ // entities have different defaults
speed: AttributeInstance::new(0.1),
},
- }
- }
- /// Get the position of the entity in the world.
- #[inline]
- pub fn pos(&self) -> &Vec3 {
- &self.pos
- }
-
- /// Convert this &self into a (mutable) pointer.
- ///
- /// # Safety
- /// The entity MUST exist for at least as long as this pointer exists.
- pub unsafe fn as_ptr(&self) -> NonNull<EntityData> {
- // this is cursed
- NonNull::new_unchecked(self as *const EntityData as *mut EntityData)
+ jumping: Jumping(false),
+ }
}
}
-#[cfg(test)]
-mod tests {
- use super::*;
- use crate::PartialWorld;
-
- #[test]
- fn from_mut_entity_to_ref_entity() {
- let mut world = PartialWorld::default();
- let uuid = Uuid::from_u128(100);
- world.add_entity(
- 0,
- EntityData::new(
- uuid,
- Vec3::default(),
- EntityMetadata::Player(metadata::Player::default()),
- ),
- );
- let entity: Entity = world.entity_mut(0).unwrap();
- assert_eq!(entity.uuid, uuid);
- }
+/// A bundle of the components that are always present for a player.
+#[derive(Bundle)]
+pub struct PlayerBundle {
+ pub entity: EntityBundle,
+ pub metadata: metadata::PlayerMetadataBundle,
}
+
+// #[cfg(test)]
+// mod tests {
+// use super::*;
+// use crate::PartialWorld;
+
+// #[test]
+// fn from_mut_entity_to_ref_entity() {
+// let mut world = PartialWorld::default();
+// let uuid = Uuid::from_u128(100);
+// world.add_entity(
+// 0,
+// EntityData::new(
+// uuid,
+// Vec3::default(),
+// EntityMetadata::Player(metadata::Player::default()),
+// ),
+// );
+// let entity: Entity = world.entity_mut(0).unwrap();
+// assert_eq!(entity.uuid, uuid);
+// }
+// }
diff --git a/azalea-world/src/entity_info.rs b/azalea-world/src/entity_info.rs
new file mode 100644
index 00000000..4109e9ce
--- /dev/null
+++ b/azalea-world/src/entity_info.rs
@@ -0,0 +1,357 @@
+use crate::{
+ deduplicate_entities, deduplicate_local_entities,
+ entity::{
+ self, add_dead, update_bounding_box, EntityUuid, MinecraftEntityId, Position, WorldName,
+ },
+ update_entity_by_id_index, update_uuid_index, PartialWorld, WorldContainer,
+};
+use azalea_core::ChunkPos;
+use azalea_ecs::{
+ app::{App, CoreStage, Plugin},
+ component::Component,
+ ecs::Ecs,
+ ecs::EntityMut,
+ entity::Entity,
+ query::{Added, Changed, With, Without},
+ schedule::{IntoSystemDescriptor, SystemSet},
+ system::{Command, Commands, Query, Res, ResMut, Resource},
+};
+use derive_more::{Deref, DerefMut};
+use log::{debug, warn};
+use nohash_hasher::IntMap;
+use parking_lot::RwLock;
+use std::{
+ collections::{HashMap, HashSet},
+ fmt::Debug,
+ sync::Arc,
+};
+use uuid::Uuid;
+
+/// Plugin handling some basic entity functionality.
+pub struct EntityPlugin;
+impl Plugin for EntityPlugin {
+ fn build(&self, app: &mut App) {
+ app.add_system_set(
+ SystemSet::new()
+ .after("tick")
+ .after("packet")
+ .with_system(update_entity_chunk_positions)
+ .with_system(remove_despawned_entities_from_indexes)
+ .with_system(update_bounding_box)
+ .with_system(add_dead)
+ .with_system(
+ add_updates_received
+ .after("deduplicate_entities")
+ .after("deduplicate_local_entities")
+ .label("add_updates_received"),
+ )
+ .with_system(
+ update_uuid_index
+ .label("update_uuid_index")
+ .after("deduplicate_local_entities")
+ .after("deduplicate_entities"),
+ )
+ .with_system(debug_detect_updates_received_on_local_entities)
+ .with_system(
+ update_entity_by_id_index
+ .label("update_entity_by_id_index")
+ .after("deduplicate_entities"),
+ )
+ .with_system(debug_new_entity),
+ )
+ .add_system_set_to_stage(
+ CoreStage::PostUpdate,
+ SystemSet::new()
+ .with_system(deduplicate_entities.label("deduplicate_entities"))
+ .with_system(
+ deduplicate_local_entities
+ .label("deduplicate_local_entities")
+ .before("update_uuid_index")
+ .before("update_entity_by_id_index"),
+ ),
+ )
+ .init_resource::<EntityInfos>();
+ }
+}
+
+fn debug_new_entity(query: Query<Entity, Added<MinecraftEntityId>>) {
+ for entity in query.iter() {
+ debug!("new entity: {:?}", entity);
+ }
+}
+
+// How entity updates are processed (to avoid issues with shared worlds)
+// - each bot contains a map of { entity id: updates received }
+// - the shared world also contains a canonical "true" updates received for each
+// entity
+// - when a client loads an entity, its "updates received" is set to the same as
+// the global "updates received"
+// - when the shared world sees an entity for the first time, the "updates
+// received" is set to 1.
+// - clients can force the shared "updates received" to 0 to make it so certain
+// entities (i.e. other bots in our swarm) don't get confused and updated by
+// other bots
+// - when a client gets an update to an entity, we check if our "updates
+// received" is the same as the shared world's "updates received": if it is,
+// then process the update and increment the client's and shared world's
+// "updates received" if not, then we simply increment our local "updates
+// received" and do nothing else
+
+/// Keep track of certain metadatas that are only relevant for this partial
+/// world.
+#[derive(Debug, Default)]
+pub struct PartialEntityInfos {
+ // note: using MinecraftEntityId for entity ids is acceptable here since
+ // there's no chance of collisions here
+ /// The entity id of the player that owns this partial world. This will
+ /// make [`RelativeEntityUpdate`] pretend the entity doesn't exist so
+ /// it doesn't get modified from outside sources.
+ pub owner_entity: Option<Entity>,
+ /// A counter for each entity that tracks how many updates we've observed
+ /// for it.
+ ///
+ /// This is used for shared worlds (i.e. swarms), to make sure we don't
+ /// update entities twice on accident.
+ pub updates_received: IntMap<MinecraftEntityId, u32>,
+}
+
+impl PartialEntityInfos {
+ pub fn new(owner_entity: Option<Entity>) -> Self {
+ Self {
+ owner_entity,
+ updates_received: IntMap::default(),
+ }
+ }
+}
+
+/// A [`Command`] that applies a "relative update" to an entity, which means
+/// this update won't be run multiple times by different clients in the same
+/// world.
+///
+/// This is used to avoid a bug where when there's multiple clients in the same
+/// world and an entity sends a relative move packet to all clients, its
+/// position gets desynced since the relative move is applied multiple times.
+///
+/// Don't use this unless you actually got an entity update packet that all
+/// other clients within render distance will get too. You usually don't need
+/// this when the change isn't relative either.
+pub struct RelativeEntityUpdate {
+ pub entity: Entity,
+ pub partial_world: Arc<RwLock<PartialWorld>>,
+ // a function that takes the entity and updates it
+ pub update: Box<dyn FnOnce(&mut EntityMut) + Send + Sync>,
+}
+impl Command for RelativeEntityUpdate {
+ fn write(self, world: &mut Ecs) {
+ let partial_entity_infos = &mut self.partial_world.write().entity_infos;
+
+ let mut entity = world.entity_mut(self.entity);
+
+ if Some(self.entity) == partial_entity_infos.owner_entity {
+ // if the entity owns this partial world, it's always allowed to update itself
+ (self.update)(&mut entity);
+ return;
+ };
+
+ let entity_id = *entity.get::<MinecraftEntityId>().unwrap();
+
+ let Some(updates_received) = entity.get_mut::<UpdatesReceived>() else {
+ // a client tried to update another client, which isn't allowed
+ return;
+ };
+
+ let this_client_updates_received = partial_entity_infos
+ .updates_received
+ .get(&entity_id)
+ .copied();
+
+ let can_update = this_client_updates_received.unwrap_or(1) == **updates_received;
+ if can_update {
+ let new_updates_received = this_client_updates_received.unwrap_or(0) + 1;
+ partial_entity_infos
+ .updates_received
+ .insert(entity_id, new_updates_received);
+
+ **entity.get_mut::<UpdatesReceived>().unwrap() = new_updates_received;
+
+ let mut entity = world.entity_mut(self.entity);
+ (self.update)(&mut entity);
+ }
+ }
+}
+
+/// Things that are shared between all the partial worlds.
+#[derive(Resource, Default)]
+pub struct EntityInfos {
+ /// An index of entities by their UUIDs
+ pub(crate) entity_by_uuid: HashMap<Uuid, Entity>,
+}
+
+impl EntityInfos {
+ pub fn new() -> Self {
+ Self {
+ entity_by_uuid: HashMap::default(),
+ }
+ }
+
+ pub fn get_entity_by_uuid(&self, uuid: &Uuid) -> Option<Entity> {
+ self.entity_by_uuid.get(uuid).copied()
+ }
+}
+
+/// Update the chunk position indexes in [`EntityInfos`].
+fn update_entity_chunk_positions(
+ mut query: Query<
+ (
+ Entity,
+ &entity::Position,
+ &mut entity::LastSentPosition,
+ &entity::WorldName,
+ ),
+ Changed<entity::Position>,
+ >,
+ world_container: Res<WorldContainer>,
+) {
+ for (entity, pos, last_pos, world_name) in query.iter_mut() {
+ let world_lock = world_container.get(world_name).unwrap();
+ let mut world = world_lock.write();
+
+ let old_chunk = ChunkPos::from(*last_pos);
+ let new_chunk = ChunkPos::from(*pos);
+
+ if old_chunk != new_chunk {
+ // move the entity from the old chunk to the new one
+ if let Some(entities) = world.entities_by_chunk.get_mut(&old_chunk) {
+ entities.remove(&entity);
+ }
+ world
+ .entities_by_chunk
+ .entry(new_chunk)
+ .or_default()
+ .insert(entity);
+ }
+ }
+}
+/// 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)]
+pub struct LoadedBy(pub HashSet<Entity>);
+
+/// A component that counts the number of times this entity has been modified.
+/// This is used for making sure two clients don't do the same relative update
+/// on an entity.
+///
+/// If an entity is local (i.e. it's a client/localplayer), this component
+/// should NOT be present in the entity.
+#[derive(Component, Debug, Deref, DerefMut)]
+pub struct UpdatesReceived(u32);
+
+#[allow(clippy::type_complexity)]
+pub fn add_updates_received(
+ mut commands: Commands,
+ query: Query<
+ Entity,
+ (
+ Changed<MinecraftEntityId>,
+ (Without<UpdatesReceived>, Without<Local>),
+ ),
+ >,
+) {
+ for entity in query.iter() {
+ // entities always start with 1 update received
+ commands.entity(entity).insert(UpdatesReceived(1));
+ }
+}
+
+/// A marker component that signifies that this entity is "local" and shouldn't
+/// be updated by other clients.
+#[derive(Component)]
+pub struct Local;
+
+/// The [`UpdatesReceived`] component should never be on [`Local`] entities.
+/// This warns if an entity has both components.
+fn debug_detect_updates_received_on_local_entities(
+ query: Query<Entity, (With<Local>, With<UpdatesReceived>)>,
+) {
+ for entity in &query {
+ warn!("Entity {:?} has both Local and UpdatesReceived", entity);
+ }
+}
+
+/// Despawn entities that aren't being loaded by anything.
+fn remove_despawned_entities_from_indexes(
+ mut commands: Commands,
+ mut entity_infos: ResMut<EntityInfos>,
+ world_container: Res<WorldContainer>,
+ query: Query<(Entity, &EntityUuid, &Position, &WorldName, &LoadedBy), Changed<LoadedBy>>,
+) {
+ for (entity, uuid, position, world_name, loaded_by) in &query {
+ let world_lock = world_container.get(world_name).unwrap();
+ let mut world = world_lock.write();
+
+ // if the entity has no references left, despawn it
+ if !loaded_by.is_empty() {
+ continue;
+ }
+
+ // remove the entity from the chunk index
+ let chunk = ChunkPos::from(*position);
+ if let Some(entities_in_chunk) = world.entities_by_chunk.get_mut(&chunk) {
+ if entities_in_chunk.remove(&entity) {
+ // remove the chunk if there's no entities in it anymore
+ if entities_in_chunk.is_empty() {
+ world.entities_by_chunk.remove(&chunk);
+ }
+ } else {
+ warn!("Tried to remove entity from chunk {chunk:?} but the entity was not there.");
+ }
+ } else {
+ warn!("Tried to remove entity from chunk {chunk:?} but the chunk was not found.");
+ }
+ // remove it from the uuid index
+ if entity_infos.entity_by_uuid.remove(uuid).is_none() {
+ warn!("Tried to remove entity {entity:?} from the uuid index but it was not there.");
+ }
+ // and now remove the entity from the ecs
+ commands.entity(entity).despawn();
+ debug!("Despawned entity {entity:?} because it was not loaded by anything.");
+ return;
+ }
+}
+
+impl Debug for EntityInfos {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ f.debug_struct("EntityInfos").finish()
+ }
+}
+
+// #[cfg(test)]
+// mod tests {
+// use crate::entity::metadata;
+
+// use super::*;
+// use azalea_core::Vec3;
+
+// #[test]
+// fn test_store_entity() {
+// let mut storage = PartialEntityInfos::default();
+// assert!(storage.limited_get_by_id(0).is_none());
+// assert!(storage.shared.read().get_by_id(0).is_none());
+
+// let uuid = Uuid::from_u128(100);
+// storage.insert(
+// 0,
+// EntityData::new(
+// uuid,
+// Vec3::default(),
+// EntityMetadata::Player(metadata::Player::default()),
+// ),
+// );
+// assert_eq!(storage.limited_get_by_id(0).unwrap().uuid, uuid);
+// assert_eq!(storage.shared.read().get_by_id(0).unwrap().uuid, uuid);
+
+// storage.remove_by_id(0);
+// assert!(storage.limited_get_by_id(0).is_none());
+// assert!(storage.shared.read().get_by_id(0).is_none());
+// }
+// }
diff --git a/azalea-world/src/entity_storage.rs b/azalea-world/src/entity_storage.rs
deleted file mode 100755
index 1bc1adc5..00000000
--- a/azalea-world/src/entity_storage.rs
+++ /dev/null
@@ -1,416 +0,0 @@
-use crate::entity::EntityData;
-use azalea_core::ChunkPos;
-use log::warn;
-use nohash_hasher::{IntMap, IntSet};
-use parking_lot::RwLock;
-use std::{
- collections::HashMap,
- sync::{Arc, Weak},
-};
-use uuid::Uuid;
-
-// How entity updates are processed (to avoid issues with shared worlds)
-// - each bot contains a map of { entity id: updates received }
-// - the shared world also contains a canonical "true" updates received for each
-// entity
-// - when a client loads an entity, its "updates received" is set to the same as
-// the global "updates received"
-// - when the shared world sees an entity for the first time, the "updates
-// received" is set to 1.
-// - clients can force the shared "updates received" to 0 to make it so certain
-// entities (i.e. other bots in our swarm) don't get confused and updated by
-// other bots
-// - when a client gets an update to an entity, we check if our "updates
-// received" is the same as the shared world's "updates received": if it is,
-// then process the update and increment the client's and shared world's
-// "updates received" if not, then we simply increment our local "updates
-// received" and do nothing else
-
-/// Store a map of entities by ID. To get an iterator over all entities, use
-/// `storage.shared.read().entities` [`WeakEntityStorage::entities`].
-///
-/// This is meant to be used with shared worlds.
-///
-/// You can access the shared storage with `world.shared.read()`.
-#[derive(Debug, Default)]
-pub struct PartialEntityStorage {
- pub shared: Arc<RwLock<WeakEntityStorage>>,
-
- /// The entity id of the player that owns this partial world. This will
- /// make [`PartialWorld::entity_mut`] pretend the entity doesn't exist so
- /// it doesn't get modified from outside sources.
- ///
- /// [`PartialWorld::entity_mut`]: crate::PartialWorld::entity_mut
- pub owner_entity_id: Option<u32>,
- pub updates_received: IntMap<u32, u32>,
- /// Strong references to the entities we have loaded.
- data_by_id: IntMap<u32, Arc<EntityData>>,
-}
-
-/// Weakly store entities in a world. If the entities aren't being referenced
-/// by anything else (like an [`PartialEntityStorage`]), they'll be forgotten.
-#[derive(Debug, Default)]
-pub struct WeakEntityStorage {
- data_by_id: IntMap<u32, Weak<EntityData>>,
- /// An index of all the entity ids we know are in a chunk
- ids_by_chunk: HashMap<ChunkPos, IntSet<u32>>,
- /// An index of entity ids by their UUIDs
- id_by_uuid: HashMap<Uuid, u32>,
-
- pub updates_received: IntMap<u32, u32>,
-}
-
-impl PartialEntityStorage {
- pub fn new(shared: Arc<RwLock<WeakEntityStorage>>, owner_entity_id: Option<u32>) -> Self {
- if let Some(owner_entity_id) = owner_entity_id {
- shared.write().updates_received.insert(owner_entity_id, 0);
- }
- Self {
- shared,
- owner_entity_id,
- updates_received: IntMap::default(),
- data_by_id: IntMap::default(),
- }
- }
-
- /// Add an entity to the storage.
- #[inline]
- pub fn insert(&mut self, id: u32, entity: EntityData) {
- // if the entity is already in the shared world, we don't need to do anything
- if self.shared.read().data_by_id.contains_key(&id) {
- return;
- }
-
- // add the entity to the "indexes"
- let mut shared = self.shared.write();
- shared
- .ids_by_chunk
- .entry(ChunkPos::from(entity.pos()))
- .or_default()
- .insert(id);
- shared.id_by_uuid.insert(entity.uuid, id);
-
- // now store the actual entity data
- let entity = Arc::new(entity);
- shared.data_by_id.insert(id, Arc::downgrade(&entity));
- self.data_by_id.insert(id, entity);
- // set our updates_received to the shared updates_received, unless it's
- // not there in which case set both to 1
- if let Some(&shared_updates_received) = shared.updates_received.get(&id) {
- // 0 means we're never tracking updates for this entity
- if shared_updates_received != 0 || Some(id) == self.owner_entity_id {
- self.updates_received.insert(id, 1);
- }
- } else {
- shared.updates_received.insert(id, 1);
- self.updates_received.insert(id, 1);
- }
- }
-
- /// Remove an entity from this storage by its id. It will only be removed
- /// from the shared storage if there are no other references to it.
- #[inline]
- pub fn remove_by_id(&mut self, id: u32) {
- if let Some(entity) = self.data_by_id.remove(&id) {
- let chunk = ChunkPos::from(entity.pos());
- let uuid = entity.uuid;
- self.updates_received.remove(&id);
- drop(entity);
- // maybe remove it from the storage
- self.shared.write().remove_entity_if_unused(id, uuid, chunk);
- } else {
- warn!("Tried to remove entity with id {id} but it was not found.")
- }
- }
-
- /// Whether the entity with the given id is being loaded by this storage.
- /// If you want to check whether the entity is in the shared storage, use
- /// [`WeakEntityStorage::contains_id`].
- #[inline]
- pub fn limited_contains_id(&self, id: &u32) -> bool {
- self.data_by_id.contains_key(id)
- }
-
- /// Get a reference to an entity by its id, if it's being loaded by this
- /// storage.
- #[inline]
- pub fn limited_get_by_id(&self, id: u32) -> Option<&Arc<EntityData>> {
- self.data_by_id.get(&id)
- }
-
- /// Get a mutable reference to an entity by its id, if it's being loaded by
- /// this storage.
- #[inline]
- pub fn limited_get_mut_by_id(&mut self, id: u32) -> Option<&mut Arc<EntityData>> {
- self.data_by_id.get_mut(&id)
- }
-
- /// Returns whether we're allowed to update this entity (to prevent two
- /// clients in a shared world updating it twice), and acknowleges that
- /// we WILL update it if it's true. Don't call this unless you actually
- /// got an entity update that all other clients within render distance
- /// will get too.
- pub fn maybe_update(&mut self, id: u32) -> bool {
- let this_client_updates_received = self.updates_received.get(&id).copied();
- let shared_updates_received = self.shared.read().updates_received.get(&id).copied();
-
- let can_update = this_client_updates_received == shared_updates_received;
- if can_update {
- let new_updates_received = this_client_updates_received.unwrap_or(0) + 1;
- self.updates_received.insert(id, new_updates_received);
- self.shared
- .write()
- .updates_received
- .insert(id, new_updates_received);
- true
- } else {
- false
- }
- }
-
- /// Get a reference to an entity by its UUID, if it's being loaded by this
- /// storage.
- #[inline]
- pub fn limited_get_by_uuid(&self, uuid: &Uuid) -> Option<&Arc<EntityData>> {
- self.shared
- .read()
- .id_by_uuid
- .get(uuid)
- .and_then(|id| self.data_by_id.get(id))
- }
-
- /// Get a mutable reference to an entity by its UUID, if it's being loaded
- /// by this storage.
- #[inline]
- pub fn limited_get_mut_by_uuid(&mut self, uuid: &Uuid) -> Option<&mut Arc<EntityData>> {
- self.shared
- .read()
- .id_by_uuid
- .get(uuid)
- .and_then(|id| self.data_by_id.get_mut(id))
- }
-
- /// Clear all entities in a chunk. This will not clear them from the
- /// shared storage, unless there are no other references to them.
- pub fn clear_chunk(&mut self, chunk: &ChunkPos) {
- if let Some(entities) = self.shared.read().ids_by_chunk.get(chunk) {
- for id in entities.iter() {
- if let Some(entity) = self.data_by_id.remove(id) {
- let uuid = entity.uuid;
- drop(entity);
- // maybe remove it from the storage
- self.shared
- .write()
- .remove_entity_if_unused(*id, uuid, *chunk);
- }
- }
- // for entity_id in entities {
- // self.remove_by_id(entity_id);
- // }
- }
- }
-
- /// Move an entity from its old chunk to a new chunk.
- #[inline]
- pub fn update_entity_chunk(
- &mut self,
- entity_id: u32,
- old_chunk: &ChunkPos,
- new_chunk: &ChunkPos,
- ) {
- self.shared
- .write()
- .update_entity_chunk(entity_id, old_chunk, new_chunk);
- }
-}
-
-impl WeakEntityStorage {
- pub fn new() -> Self {
- Self {
- data_by_id: IntMap::default(),
- ids_by_chunk: HashMap::default(),
- id_by_uuid: HashMap::default(),
- updates_received: IntMap::default(),
- }
- }
-
- /// Remove an entity from the storage if it has no strong references left.
- /// Returns whether the entity was removed.
- pub fn remove_entity_if_unused(&mut self, id: u32, uuid: Uuid, chunk: ChunkPos) -> bool {
- if self.data_by_id.get(&id).and_then(|e| e.upgrade()).is_some() {
- // if we could get the entity, that means there are still strong
- // references to it
- false
- } else {
- if self.ids_by_chunk.remove(&chunk).is_none() {
- warn!("Tried to remove entity with id {id} from chunk {chunk:?} but it was not found.");
- }
- if self.id_by_uuid.remove(&uuid).is_none() {
- warn!(
- "Tried to remove entity with id {id} from uuid {uuid:?} but it was not found."
- );
- }
- if self.updates_received.remove(&id).is_none() {
- // if this happens it means we weren't tracking the updates_received for the
- // client (bad)
- warn!(
- "Tried to remove entity with id {id} from updates_received but it was not found."
- );
- }
- true
- }
- }
-
- /// Remove a chunk from the storage if the entities in it have no strong
- /// references left.
- pub fn remove_chunk_if_unused(&mut self, chunk: &ChunkPos) {
- if let Some(entities) = self.ids_by_chunk.get(chunk) {
- if entities.is_empty() {
- self.ids_by_chunk.remove(chunk);
- }
- }
- }
-
- /// Get an iterator over all entities in the shared storage. The iterator
- /// is over `Weak<EntityData>`s, so you'll have to manually try to upgrade.
- ///
- /// # Examples
- ///
- /// ```rust
- /// # use std::sync::Arc;
- /// # use azalea_world::{PartialEntityStorage, entity::{EntityData, EntityMetadata, metadata}};
- /// # use azalea_core::Vec3;
- /// # use uuid::Uuid;
- /// #
- /// let mut storage = PartialEntityStorage::default();
- /// storage.insert(
- /// 0,
- /// EntityData::new(
- /// Uuid::nil(),
- /// Vec3::default(),
- /// EntityMetadata::Player(metadata::Player::default()),
- /// ),
- /// );
- /// for entity in storage.shared.read().entities() {
- /// if let Some(entity) = entity.upgrade() {
- /// println!("Entity: {:?}", entity);
- /// }
- /// }
- /// ```
- pub fn entities(&self) -> std::collections::hash_map::Values<'_, u32, Weak<EntityData>> {
- self.data_by_id.values()
- }
-
- /// Whether the entity with the given id is in the shared storage.
- #[inline]
- pub fn contains_id(&self, id: &u32) -> bool {
- self.data_by_id.contains_key(id)
- }
-
- /// Get an entity by its id, if it exists.
- #[inline]
- pub fn get_by_id(&self, id: u32) -> Option<Arc<EntityData>> {
- self.data_by_id.get(&id).and_then(|e| e.upgrade())
- }
-
- /// Get an entity in the shared storage by its UUID, if it exists.
- #[inline]
- pub fn get_by_uuid(&self, uuid: &Uuid) -> Option<Arc<EntityData>> {
- self.id_by_uuid
- .get(uuid)
- .and_then(|id| self.data_by_id.get(id).and_then(|e| e.upgrade()))
- }
-
- pub fn entity_by<F>(&self, mut f: F) -> Option<Arc<EntityData>>
- where
- F: FnMut(&Arc<EntityData>) -> bool,
- {
- for entity in self.entities() {
- if let Some(entity) = entity.upgrade() {
- if f(&entity) {
- return Some(entity);
- }
- }
- }
- None
- }
-
- pub fn entities_by<F>(&self, mut f: F) -> Vec<Arc<EntityData>>
- where
- F: FnMut(&Arc<EntityData>) -> bool,
- {
- let mut entities = Vec::new();
- for entity in self.entities() {
- if let Some(entity) = entity.upgrade() {
- if f(&entity) {
- entities.push(entity);
- }
- }
- }
- entities
- }
-
- pub fn entity_by_in_chunk<F>(&self, chunk: &ChunkPos, mut f: F) -> Option<Arc<EntityData>>
- where
- F: FnMut(&EntityData) -> bool,
- {
- if let Some(entities) = self.ids_by_chunk.get(chunk) {
- for entity_id in entities {
- if let Some(entity) = self.data_by_id.get(entity_id).and_then(|e| e.upgrade()) {
- if f(&entity) {
- return Some(entity);
- }
- }
- }
- }
- None
- }
-
- /// Move an entity from its old chunk to a new chunk.
- #[inline]
- pub fn update_entity_chunk(
- &mut self,
- entity_id: u32,
- old_chunk: &ChunkPos,
- new_chunk: &ChunkPos,
- ) {
- if let Some(entities) = self.ids_by_chunk.get_mut(old_chunk) {
- entities.remove(&entity_id);
- }
- self.ids_by_chunk
- .entry(*new_chunk)
- .or_default()
- .insert(entity_id);
- }
-}
-
-#[cfg(test)]
-mod tests {
- use crate::entity::{metadata, EntityMetadata};
-
- use super::*;
- use azalea_core::Vec3;
-
- #[test]
- fn test_store_entity() {
- let mut storage = PartialEntityStorage::default();
- assert!(storage.limited_get_by_id(0).is_none());
- assert!(storage.shared.read().get_by_id(0).is_none());
-
- let uuid = Uuid::from_u128(100);
- storage.insert(
- 0,
- EntityData::new(
- uuid,
- Vec3::default(),
- EntityMetadata::Player(metadata::Player::default()),
- ),
- );
- assert_eq!(storage.limited_get_by_id(0).unwrap().uuid, uuid);
- assert_eq!(storage.shared.read().get_by_id(0).unwrap().uuid, uuid);
-
- storage.remove_by_id(0);
- assert!(storage.limited_get_by_id(0).is_none());
- assert!(storage.shared.read().get_by_id(0).is_none());
- }
-}
diff --git a/azalea-world/src/lib.rs b/azalea-world/src/lib.rs
index 5ea5db17..88715f44 100644
--- a/azalea-world/src/lib.rs
+++ b/azalea-world/src/lib.rs
@@ -7,16 +7,18 @@ mod bit_storage;
mod chunk_storage;
mod container;
pub mod entity;
-mod entity_storage;
+mod entity_info;
mod palette;
mod world;
use std::backtrace::Backtrace;
pub use bit_storage::BitStorage;
-pub use chunk_storage::{Chunk, ChunkStorage, PartialChunkStorage, WeakChunkStorage};
+pub use chunk_storage::{Chunk, ChunkStorage, PartialChunkStorage};
pub use container::*;
-pub use entity_storage::{PartialEntityStorage, WeakEntityStorage};
+pub use entity_info::{
+ EntityInfos, EntityPlugin, LoadedBy, Local, PartialEntityInfos, RelativeEntityUpdate,
+};
use thiserror::Error;
pub use world::*;
diff --git a/azalea-world/src/palette.rs b/azalea-world/src/palette.rs
index f78b2082..fef5f207 100755
--- a/azalea-world/src/palette.rs
+++ b/azalea-world/src/palette.rs
@@ -87,7 +87,7 @@ impl PalettedContainer {
/// want `.set` instead.
pub fn set_at_index(&mut self, index: usize, value: u32) {
let paletted_value = self.id_for(value);
- self.storage.set(index, paletted_value as u64)
+ self.storage.set(index, paletted_value as u64);
}
/// Sets the id at the given coordinates and return the previous id
diff --git a/azalea-world/src/world.rs b/azalea-world/src/world.rs
index 6a3652f8..5ec2bbac 100644
--- a/azalea-world/src/world.rs
+++ b/azalea-world/src/world.rs
@@ -1,229 +1,237 @@
use crate::{
- entity::{Entity, EntityData},
- Chunk, MoveEntityError, PartialChunkStorage, PartialEntityStorage, WeakChunkStorage,
- WeakEntityStorage,
+ entity::{EntityUuid, MinecraftEntityId, WorldName},
+ entity_info::LoadedBy,
+ ChunkStorage, EntityInfos, Local, PartialChunkStorage, PartialEntityInfos, WorldContainer,
+};
+use azalea_core::ChunkPos;
+use azalea_ecs::{
+ entity::Entity,
+ query::{Changed, With, Without},
+ system::{Commands, Query, Res, ResMut},
+};
+use log::{debug, error, info};
+use nohash_hasher::IntMap;
+use std::fmt::Formatter;
+use std::{
+ collections::{HashMap, HashSet},
+ fmt::Debug,
};
-use azalea_block::BlockState;
-use azalea_buf::BufReadError;
-use azalea_core::{BlockPos, ChunkPos, PositionDelta8, Vec3};
-use parking_lot::RwLock;
-use std::{backtrace::Backtrace, fmt::Debug};
-use std::{fmt::Formatter, io::Cursor, sync::Arc};
-use uuid::Uuid;
/// PartialWorlds are usually owned by clients, and hold strong references to
-/// chunks and entities in [`WeakWorld`]s.
+/// chunks and entities in [`World`]s.
///
/// Basically, they hold the chunks and entities that are within render
/// distance but can still access chunks and entities owned by other
-/// `PartialWorld`s that have the same `WeakWorld`.
+/// `PartialWorld`s that have the same `World`.
///
/// This is primarily useful for having multiple clients in the same world.
pub struct PartialWorld {
- // we just need to keep a strong reference to `shared` so it doesn't get
- // dropped, we don't need to do anything with it
- pub shared: Arc<WeakWorld>,
-
- pub chunk_storage: PartialChunkStorage,
- pub entity_storage: PartialEntityStorage,
-}
-
-/// A world where the chunks are stored as weak pointers. This is used for
-/// shared worlds.
-#[derive(Default, Debug)]
-pub struct WeakWorld {
- pub chunk_storage: Arc<RwLock<WeakChunkStorage>>,
- pub entity_storage: Arc<RwLock<WeakEntityStorage>>,
+ pub chunks: PartialChunkStorage,
+ /// Some metadata about entities, like what entities are in certain chunks.
+ /// This does not contain the entity data itself, that's in the ECS.
+ pub entity_infos: PartialEntityInfos,
}
impl PartialWorld {
- pub fn new(chunk_radius: u32, shared: Arc<WeakWorld>, owner_entity_id: Option<u32>) -> Self {
+ pub fn new(chunk_radius: u32, owner_entity: Option<Entity>) -> Self {
PartialWorld {
- shared: shared.clone(),
- chunk_storage: PartialChunkStorage::new(chunk_radius, shared.chunk_storage.clone()),
- entity_storage: PartialEntityStorage::new(
- shared.entity_storage.clone(),
- owner_entity_id,
- ),
- }
- }
-
- pub fn replace_with_packet_data(
- &mut self,
- pos: &ChunkPos,
- data: &mut Cursor<&[u8]>,
- ) -> Result<(), BufReadError> {
- self.chunk_storage.replace_with_packet_data(pos, data)
- }
-
- pub fn get_chunk(&self, pos: &ChunkPos) -> Option<Arc<RwLock<Chunk>>> {
- self.chunk_storage.get(pos)
- }
-
- pub fn set_chunk(&mut self, pos: &ChunkPos, chunk: Option<Chunk>) -> Result<(), BufReadError> {
- self.chunk_storage
- .set(pos, chunk.map(|c| Arc::new(RwLock::new(c))));
- Ok(())
- }
-
- pub fn update_view_center(&mut self, pos: &ChunkPos) {
- self.chunk_storage.view_center = *pos;
- }
-
- pub fn set_block_state(&mut self, pos: &BlockPos, state: BlockState) -> Option<BlockState> {
- self.chunk_storage.set_block_state(pos, state)
- }
-
- /// Returns a mutable reference to the entity with the given ID.
- pub fn entity_mut(&mut self, id: u32) -> Option<Entity<'_, &WeakWorld>> {
- // no entity for you (we're processing this entity somewhere else)
- if Some(id) != self.entity_storage.owner_entity_id && !self.entity_storage.maybe_update(id)
- {
- return None;
+ chunks: PartialChunkStorage::new(chunk_radius),
+ entity_infos: PartialEntityInfos::new(owner_entity),
}
-
- self.shared.entity(id)
- }
-
- pub fn add_entity(&mut self, id: u32, entity: EntityData) {
- self.entity_storage.insert(id, entity);
- }
-
- pub fn set_entity_pos(&mut self, entity_id: u32, new_pos: Vec3) -> Result<(), MoveEntityError> {
- let mut entity = self
- .entity_mut(entity_id)
- .ok_or_else(|| MoveEntityError::EntityDoesNotExist(Backtrace::capture()))?;
- let old_chunk = ChunkPos::from(entity.pos());
- let new_chunk = ChunkPos::from(&new_pos);
- // this is fine because we update the chunk below
- unsafe { entity.move_unchecked(new_pos) };
- if old_chunk != new_chunk {
- self.entity_storage
- .update_entity_chunk(entity_id, &old_chunk, &new_chunk);
- }
- Ok(())
}
+}
- pub fn move_entity_with_delta(
- &mut self,
- entity_id: u32,
- delta: &PositionDelta8,
- ) -> Result<(), MoveEntityError> {
- let mut entity = self
- .entity_mut(entity_id)
- .ok_or_else(|| MoveEntityError::EntityDoesNotExist(Backtrace::capture()))?;
- let new_pos = entity.pos().with_delta(delta);
-
- let old_chunk = ChunkPos::from(entity.pos());
- let new_chunk = ChunkPos::from(&new_pos);
- // this is fine because we update the chunk below
-
- unsafe { entity.move_unchecked(new_pos) };
- if old_chunk != new_chunk {
- self.entity_storage
- .update_entity_chunk(entity_id, &old_chunk, &new_chunk);
+/// Remove new entities that have the same id as an existing entity, and
+/// increase the reference counts.
+///
+/// This is the reason why spawning entities into the ECS when you get a spawn
+/// entity packet is okay. This system will make sure the new entity gets
+/// combined into the old one.
+#[allow(clippy::type_complexity)]
+pub fn deduplicate_entities(
+ mut commands: Commands,
+ mut query: Query<
+ (Entity, &MinecraftEntityId, &WorldName),
+ (Changed<MinecraftEntityId>, Without<Local>),
+ >,
+ mut loaded_by_query: Query<&mut LoadedBy>,
+ world_container: Res<WorldContainer>,
+) {
+ // if this entity already exists, remove it
+ for (new_entity, id, world_name) in query.iter_mut() {
+ if let Some(world_lock) = world_container.get(world_name) {
+ let world = world_lock.write();
+ if let Some(old_entity) = world.entity_by_id.get(id) {
+ if old_entity == &new_entity {
+ continue;
+ }
+
+ // this entity already exists!!! remove the one we just added but increase
+ // the reference count
+ let new_loaded_by = loaded_by_query
+ .get(new_entity)
+ .unwrap_or_else(|_| panic!(
+ "Entities should always have the LoadedBy component ({new_entity:?} did not)"
+ ))
+ .clone();
+ let old_loaded_by = loaded_by_query.get_mut(*old_entity);
+ // merge them if possible
+ if let Ok(mut old_loaded_by) = old_loaded_by {
+ old_loaded_by.extend(new_loaded_by.iter());
+ }
+ commands.entity(new_entity).despawn();
+ info!(
+ "Entity with id {id:?} / {new_entity:?} already existed in the world, merging it with {old_entity:?}"
+ );
+ break;
+ }
+ } else {
+ error!("Entity was inserted into a world that doesn't exist.");
}
- Ok(())
}
}
-impl WeakWorld {
- pub fn new(height: u32, min_y: i32) -> Self {
- WeakWorld {
- chunk_storage: Arc::new(RwLock::new(WeakChunkStorage::new(height, min_y))),
- entity_storage: Arc::new(RwLock::new(WeakEntityStorage::new())),
+// when a local entity is added, if there was already an entity with the same id
+// then delete the old entity
+#[allow(clippy::type_complexity)]
+pub fn deduplicate_local_entities(
+ mut commands: Commands,
+ mut query: Query<
+ (Entity, &MinecraftEntityId, &WorldName),
+ (Changed<MinecraftEntityId>, With<Local>),
+ >,
+ world_container: Res<WorldContainer>,
+) {
+ // if this entity already exists, remove the old one
+ for (new_entity, id, world_name) in query.iter_mut() {
+ if let Some(world_lock) = world_container.get(world_name) {
+ let world = world_lock.write();
+ if let Some(old_entity) = world.entity_by_id.get(id) {
+ if old_entity == &new_entity {
+ // lol
+ continue;
+ }
+
+ commands.entity(*old_entity).despawn();
+ debug!(
+ "Added local entity {id:?} / {new_entity:?} but already existed in world as {old_entity:?}, despawning {old_entity:?}"
+ );
+ break;
+ }
+ } else {
+ error!("Entity was inserted into a world that doesn't exist.");
}
}
+}
- /// Read the total height of the world. You can add this to [`Self::min_y`]
- /// to get the highest possible y coordinate a block can be placed at.
- pub fn height(&self) -> u32 {
- self.chunk_storage.read().height
- }
-
- /// Get the lowest possible y coordinate a block can be placed at.
- pub fn min_y(&self) -> i32 {
- self.chunk_storage.read().min_y
- }
-
- pub fn entity_data_by_id(&self, id: u32) -> Option<Arc<EntityData>> {
- self.entity_storage.read().get_by_id(id)
- }
-
- /// Returns a entity with the given ID.
- ///
- /// The returned Entity can technically be mutated, but you should avoid
- /// doing any relative mutations.
- pub fn entity(&self, id: u32) -> Option<Entity<&WeakWorld>> {
- let entity_data = self.entity_storage.read().get_by_id(id)?;
- let entity_ptr = unsafe { entity_data.as_ptr() };
- Some(Entity::new(self, id, entity_ptr))
+pub fn update_uuid_index(
+ mut entity_infos: ResMut<EntityInfos>,
+ query: Query<(Entity, &EntityUuid), Changed<EntityUuid>>,
+) {
+ for (entity, &uuid) in query.iter() {
+ // only add it if it doesn't already exist in
+ // entity_infos.entity_by_uuid
+ // if entity_infos.entity_by_uuid.contains_key(&uuid) {
+ // warn!("Entity with UUID {uuid:?} already existed in the world, not adding
+ // to index (ecs id: {entity:?})", uuid=*uuid); continue;
+ // }
+ entity_infos.entity_by_uuid.insert(*uuid, entity);
}
+}
- pub fn entity_by_uuid(&self, uuid: &Uuid) -> Option<Arc<EntityData>> {
- self.entity_storage.read().get_by_uuid(uuid)
- }
+// /// Clear all entities in a chunk. This will not clear them from the
+// /// shared storage unless there are no other references to them.
+// pub fn clear_entities_in_chunk(
+// mut commands: Commands,
+// partial_entity_infos: &mut PartialEntityInfos,
+// chunk: &ChunkPos,
+// world_container: &WorldContainer,
+// world_name: &WorldName,
+// mut query: Query<(&MinecraftEntityId, &mut ReferenceCount)>,
+// ) {
+// let world_lock = world_container.get(world_name).unwrap();
+// let world = world_lock.read();
+
+// if let Some(entities) = world.entities_by_chunk.get(chunk).cloned() {
+// for &entity in &entities {
+// let (id, mut reference_count) = query.get_mut(entity).unwrap();
+// if partial_entity_infos.loaded_entity_ids.remove(id) {
+// // decrease the reference count
+// **reference_count -= 1;
+// }
+// }
+// }
+// }
- pub fn entity_by<F>(&self, mut f: F) -> Option<Arc<EntityData>>
- where
- F: FnMut(&EntityData) -> bool,
- {
- self.entity_storage.read().entity_by(|e| f(e))
- }
-
- pub fn entities_by<F>(&self, mut f: F) -> Vec<Arc<EntityData>>
- where
- F: FnMut(&EntityData) -> bool,
- {
- self.entity_storage.read().entities_by(|e| f(e))
- }
+/// A world where the chunks are stored as weak pointers. This is used for
+/// shared worlds.
+#[derive(Default, Debug)]
+pub struct World {
+ pub chunks: ChunkStorage,
- pub fn set_entity_pos(&self, entity_id: u32, new_pos: Vec3) -> Result<(), MoveEntityError> {
- let mut entity = self
- .entity(entity_id)
- .ok_or_else(|| MoveEntityError::EntityDoesNotExist(Backtrace::capture()))?;
- let old_chunk = ChunkPos::from(entity.pos());
- let new_chunk = ChunkPos::from(&new_pos);
- // this is fine because we update the chunk below
- unsafe { entity.move_unchecked(new_pos) };
- if old_chunk != new_chunk {
- self.entity_storage
- .write()
- .update_entity_chunk(entity_id, &old_chunk, &new_chunk);
- }
- Ok(())
- }
+ /// An index of all the entities we know are in the chunks of the world
+ pub entities_by_chunk: HashMap<ChunkPos, HashSet<Entity>>,
- pub fn get_block_state(&self, pos: &BlockPos) -> Option<BlockState> {
- self.chunk_storage.read().get_block_state(pos)
- }
+ /// An index of Minecraft entity IDs to Azalea ECS entities.
+ pub entity_by_id: IntMap<MinecraftEntityId, Entity>,
+}
- pub fn get_chunk(&self, pos: &ChunkPos) -> Option<Arc<RwLock<Chunk>>> {
- self.chunk_storage.read().get(pos)
+impl World {
+ /// Get an ECS [`Entity`] from a Minecraft entity ID.
+ pub fn entity_by_id(&self, entity_id: &MinecraftEntityId) -> Option<Entity> {
+ self.entity_by_id.get(entity_id).copied()
}
}
impl Debug for PartialWorld {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_struct("World")
- .field("chunk_storage", &self.chunk_storage)
- .field("entity_storage", &self.entity_storage)
- .field("shared", &self.shared)
+ .field("chunk_storage", &self.chunks)
+ .field("entity_storage", &self.entity_infos)
.finish()
}
}
impl Default for PartialWorld {
+ /// Creates a completely self-contained `PartialWorld`. This is only for
+ /// testing and shouldn't be used in actual code!
fn default() -> Self {
let chunk_storage = PartialChunkStorage::default();
- let entity_storage = PartialEntityStorage::default();
+ let entity_storage = PartialEntityInfos::default();
+ Self {
+ chunks: chunk_storage,
+ entity_infos: entity_storage,
+ }
+ }
+}
+
+/// System to keep the entity_by_id index up-to-date.
+pub fn update_entity_by_id_index(
+ mut query: Query<(Entity, &MinecraftEntityId, &WorldName), Changed<MinecraftEntityId>>,
+ world_container: Res<WorldContainer>,
+) {
+ for (entity, id, world_name) in query.iter_mut() {
+ let world_lock = world_container.get(world_name).unwrap();
+ let mut world = world_lock.write();
+ // if let Some(old_entity) = world.entity_by_id.get(id) {
+ // warn!(
+ // "Entity with ID {id:?} already existed in the world, not adding to
+ // index (old ecs id: {old_entity:?} / new ecs id: {entity:?})" );
+ // continue;
+ // }
+ world.entity_by_id.insert(*id, entity);
+ debug!("Added {entity:?} to {world_name:?} with {id:?}.");
+ }
+}
+
+impl From<ChunkStorage> for World {
+ /// Make an empty world from this `ChunkStorage`. This is meant to be a
+ /// convenience function for tests.
+ fn from(chunks: ChunkStorage) -> Self {
Self {
- shared: Arc::new(WeakWorld {
- chunk_storage: chunk_storage.shared.clone(),
- entity_storage: entity_storage.shared.clone(),
- }),
- chunk_storage,
- entity_storage,
+ chunks,
+ entities_by_chunk: HashMap::new(),
+ entity_by_id: IntMap::default(),
}
}
}
diff --git a/azalea/Cargo.toml b/azalea/Cargo.toml
index 27217017..83067d0b 100644
--- a/azalea/Cargo.toml
+++ b/azalea/Cargo.toml
@@ -8,24 +8,30 @@ version = "0.5.0"
[package.metadata.release]
pre-release-replacements = [
- { file = "src/lib.rs", search = "//! `azalea = \"[a-z0-9\\.-]+\"`", replace = "//! `azalea = \"{{version}}\"`" },
+ {file = "src/lib.rs", search = "//! `azalea = \"[a-z0-9\\.-]+\"`", replace = "//! `azalea = \"{{version}}\"`"},
]
[dependencies]
anyhow = "^1.0.65"
async-trait = "0.1.58"
-azalea-block = { version = "0.5.0", path = "../azalea-block" }
-azalea-chat = { version = "0.5.0", path = "../azalea-chat" }
-azalea-client = { version = "0.5.0", path = "../azalea-client" }
-azalea-core = { version = "0.5.0", path = "../azalea-core" }
-azalea-physics = { version = "0.5.0", path = "../azalea-physics" }
-azalea-protocol = { version = "0.5.0", path = "../azalea-protocol" }
-azalea-world = { version = "0.5.0", path = "../azalea-world" }
+azalea-block = {version = "0.5.0", path = "../azalea-block"}
+azalea-chat = {version = "0.5.0", path = "../azalea-chat"}
+azalea-client = {version = "0.5.0", path = "../azalea-client"}
+azalea-core = {version = "0.5.0", path = "../azalea-core"}
+azalea-ecs = { version = "0.5.0", path = "../azalea-ecs" }
+azalea-physics = {version = "0.5.0", path = "../azalea-physics"}
+azalea-protocol = {version = "0.5.0", path = "../azalea-protocol"}
+azalea-registry = {version = "0.5.0", path = "../azalea-registry"}
+azalea-world = {version = "0.5.0", path = "../azalea-world"}
+bevy_tasks = "0.9.1"
+derive_more = {version = "0.99.17", features = ["deref", "deref_mut"]}
futures = "0.3.25"
+futures-lite = "1.12.0"
+iyes_loopless = "0.9.1"
log = "0.4.17"
nohash-hasher = "0.2.0"
num-traits = "0.2.15"
-parking_lot = { version = "^0.12.1", features = ["deadlock_detection"] }
+parking_lot = {version = "^0.12.1", features = ["deadlock_detection"]}
priority-queue = "1.3.0"
thiserror = "^1.0.37"
tokio = "^1.24.2"
diff --git a/azalea/README.md b/azalea/README.md
index afd2feb4..ef822d9f 100755
--- a/azalea/README.md
+++ b/azalea/README.md
@@ -1,4 +1,85 @@
Azalea is a framework for creating Minecraft bots.
-Internally, it's just a wrapper over azalea-client, adding useful functions for making bots.
+Internally, it's just a wrapper over [`azalea_client`], adding useful
+functions for making bots. Because of this, lots 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.
+# Installation
+
+First, install Rust nightly with `rustup install nightly` and `rustup
+default nightly`.
+
+Then, add one of the following lines to your Cargo.toml:
+
+Latest bleeding-edge version:
+`azalea = { git="https://github.com/mat-1/azalea" }`\
+Latest "stable" release:
+`azalea = "0.5.0"`
+
+## Optimization
+
+For faster compile times, make a `.cargo/config.toml` file in your project
+and copy
+[this file](https://github.com/mat-1/azalea/blob/main/.cargo/config.toml)
+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
+```
+
+
+# Examples
+
+```rust,no_run
+A bot that logs chat messages sent in the server to the console.
+
+use azalea::prelude::*;
+use parking_lot::Mutex;
+use std::sync::Arc;
+
+#[tokio::main]
+async fn main() {
+ let account = Account::offline("bot");
+ // or Account::microsoft("example@example.com").await.unwrap();
+
+ azalea::start(azalea::Options {
+ account,
+ address: "localhost",
+ state: State::default(),
+ plugins: plugins![],
+ handle,
+ })
+ .await
+ .unwrap();
+}
+
+#[derive(Default, Clone, Component)]
+pub struct State {}
+
+async fn handle(bot: Client, event: Event, state: State) -> anyhow::Result<()> {
+ match event {
+ Event::Chat(m) => {
+ println!("{}", m.message().to_ansi());
+ }
+ _ => {}
+ }
+
+ Ok(())
+}
+```
+
+# Plugins
+
+Azalea uses [Bevy ECS](https://docs.rs/bevy_ecs) internally to store information about the world and clients. Bevy plugins are more powerful than async handler functions, but more difficult to use. See [pathfinder](azalea/src/pathfinder/mod.rs) as an example of how to make a plugin. You can then use a plugin by adding `.add_plugin(ExamplePlugin)` in the client or swarm builder.
+
+Also note that just because something is an entity in the ECS doesn't mean that it's a Minecraft entity. You can filter for that by having `With<MinecraftEntityId>` as a filter.
+
+See the [https://bevy-cheatbook.github.io/programming/ecs-intro.html](Bevy Cheatbook) to learn more about Bevy ECS (and ECS in general).
+
+[`azalea_client`]: https://docs.rs/azalea-client \ No newline at end of file
diff --git a/azalea/examples/craft_dig_straight_down.rs b/azalea/examples/craft_dig_straight_down.rs
index 864b9809..76979406 100755
--- a/azalea/examples/craft_dig_straight_down.rs
+++ b/azalea/examples/craft_dig_straight_down.rs
@@ -64,8 +64,12 @@ async fn handle(bot: Client, event: Event, state: State) -> anyhow::Result<()> {
crafting_table.close().await;
bot.hold(&pickaxe);
+
loop {
- if let Err(e) = bot.dig(bot.entity().feet_pos().down(1)).await {
+ if let Err(e) = bot
+ .dig(azalea::entity::feet_pos(bot.entity()).down(1))
+ .await
+ {
println!("{:?}", e);
break;
}
diff --git a/azalea/examples/echo.rs b/azalea/examples/echo.rs
index 2093ff4e..f9bafebd 100755
--- a/azalea/examples/echo.rs
+++ b/azalea/examples/echo.rs
@@ -18,7 +18,7 @@ async fn main() {
.unwrap();
}
-#[derive(Default, Clone)]
+#[derive(Default, Clone, Component)]
pub struct State {}
async fn handle(bot: Client, event: Event, _state: State) -> anyhow::Result<()> {
diff --git a/azalea/examples/mine_a_chunk.rs b/azalea/examples/mine_a_chunk.rs
index b48fb99c..72d27ff3 100644
--- a/azalea/examples/mine_a_chunk.rs
+++ b/azalea/examples/mine_a_chunk.rs
@@ -7,7 +7,7 @@ async fn main() {
let mut states = Vec::new();
for i in 0..10 {
- accounts.push(Account::offline(&format!("bot{}", i)));
+ accounts.push(Account::offline(&format!("bot{o}")));
states.push(State::default());
}
diff --git a/azalea/examples/potatobot/autoeat.rs b/azalea/examples/potatobot/autoeat.rs
index 89934fa2..34b418f9 100755
--- a/azalea/examples/potatobot/autoeat.rs
+++ b/azalea/examples/potatobot/autoeat.rs
@@ -1,6 +1,7 @@
//! Automatically eat when we get hungry.
use async_trait::async_trait;
+use azalea::prelude::*;
use azalea::{Client, Event};
use parking_lot::Mutex;
use std::sync::Arc;
@@ -10,7 +11,7 @@ pub struct Plugin {
pub state: State,
}
-#[derive(Default, Clone)]
+#[derive(Default, Clone, Component)]
pub struct State {}
#[async_trait]
diff --git a/azalea/examples/pvp.rs b/azalea/examples/pvp.rs
index 9d2fdc35..28e54f35 100755
--- a/azalea/examples/pvp.rs
+++ b/azalea/examples/pvp.rs
@@ -7,7 +7,7 @@ async fn main() {
let mut states = Vec::new();
for i in 0..10 {
- accounts.push(Account::offline(&format!("bot{}", i)));
+ accounts.push(Account::offline(&format!("bot{i}")));
states.push(State::default());
}
@@ -46,16 +46,16 @@ async fn swarm_handle(
) -> anyhow::Result<()> {
match event {
SwarmEvent::Tick => {
- // choose an arbitrary player within render distance to target
- if let Some(target) = swarm
- .worlds
- .read()
- .entity_by(|e| e.id == "minecraft:player")
+ if let Some(target_entity) =
+ swarm.entity_by::<Player>(|name: &Name| name == "Herobrine")
{
+ let target_bounding_box =
+ swarm.map_entity(target_entity, |bb: &BoundingBox| bb.clone());
+
for (bot, bot_state) in swarm {
- bot.tick_goto_goal(pathfinder::Goals::Reach(target.bounding_box));
+ bot.tick_goto_goal(pathfinder::Goals::Reach(target_bounding_box));
// if target.bounding_box.distance(bot.eyes) < bot.reach_distance() {
- if bot.entity().can_reach(target.bounding_box) {
+ if azalea::entities::can_reach(bot.entity(), target_bounding_box) {
bot.swing();
}
if !bot.using_held_item() && bot.hunger() <= 17 {
diff --git a/azalea/src/bot.rs b/azalea/src/bot.rs
index 0674c692..510449d3 100644
--- a/azalea/src/bot.rs
+++ b/azalea/src/bot.rs
@@ -1,56 +1,115 @@
-use crate::{Client, Event};
-use async_trait::async_trait;
use azalea_core::Vec3;
-use parking_lot::Mutex;
-use std::{f64::consts::PI, sync::Arc};
+use azalea_ecs::{
+ app::{App, Plugin, PluginGroup, PluginGroupBuilder},
+ component::Component,
+ entity::Entity,
+ event::EventReader,
+ query::{With, Without},
+ schedule::IntoSystemDescriptor,
+ system::{Commands, Query},
+ AppTickExt,
+};
+use azalea_world::{
+ entity::{metadata::Player, set_rotation, Jumping, Physics, Position},
+ Local,
+};
+use std::f64::consts::PI;
-#[derive(Clone, Default)]
-pub struct Plugin;
-impl crate::Plugin for Plugin {
- type State = State;
+use crate::pathfinder::PathfinderPlugin;
- fn build(&self) -> State {
- State::default()
+#[derive(Clone, Default)]
+pub struct BotPlugin;
+impl Plugin for BotPlugin {
+ fn build(&self, app: &mut App) {
+ app.add_event::<LookAtEvent>()
+ .add_event::<JumpEvent>()
+ .add_system(insert_bot.before("deduplicate_entities"))
+ .add_system(look_at_listener)
+ .add_system(jump_listener.label("jump_listener").before("ai_step"))
+ .add_tick_system(stop_jumping.after("ai_step"));
}
}
-#[derive(Default, Clone)]
-pub struct State {
- jumping_once: Arc<Mutex<bool>>,
+/// Component for all bots.
+#[derive(Default, Component)]
+pub struct Bot {
+ jumping_once: bool,
+}
+
+/// Insert the [`Bot`] component for any local players that don't have it.
+#[allow(clippy::type_complexity)]
+fn insert_bot(
+ mut commands: Commands,
+ mut query: Query<Entity, (Without<Bot>, With<Local>, With<Player>)>,
+) {
+ for entity in &mut query {
+ commands.entity(entity).insert(Bot::default());
+ }
}
-#[async_trait]
-impl crate::PluginState for State {
- async fn handle(self: Box<Self>, event: Event, mut bot: Client) {
- if let Event::Tick = event {
- if *self.jumping_once.lock() && bot.jumping() {
- *self.jumping_once.lock() = false;
- bot.set_jumping(false);
- }
+fn stop_jumping(mut query: Query<(&mut Jumping, &mut Bot)>) {
+ for (mut jumping, mut bot) in &mut query {
+ if bot.jumping_once && **jumping {
+ bot.jumping_once = false;
+ **jumping = false;
}
}
}
-pub trait BotTrait {
+pub trait BotClientExt {
fn jump(&mut self);
- fn look_at(&mut self, pos: &Vec3);
+ fn look_at(&mut self, pos: Vec3);
}
-impl BotTrait for azalea_client::Client {
+impl BotClientExt for azalea_client::Client {
/// Queue a jump for the next tick.
fn jump(&mut self) {
- self.set_jumping(true);
- let state = self.plugins.get::<State>().unwrap().clone();
- *state.jumping_once.lock() = true;
+ let mut ecs = self.ecs.lock();
+ ecs.send_event(JumpEvent(self.entity));
}
/// Turn the bot's head to look at the coordinate in the world.
- fn look_at(&mut self, pos: &Vec3) {
- let (y_rot, x_rot) = direction_looking_at(self.entity().pos(), pos);
- self.set_rotation(y_rot, x_rot);
+ fn look_at(&mut self, position: Vec3) {
+ let mut ecs = self.ecs.lock();
+ ecs.send_event(LookAtEvent {
+ entity: self.entity,
+ position,
+ });
+ }
+}
+
+/// Event to jump once.
+pub struct JumpEvent(pub Entity);
+
+fn jump_listener(mut query: Query<(&mut Jumping, &mut Bot)>, mut events: EventReader<JumpEvent>) {
+ for event in events.iter() {
+ if let Ok((mut jumping, mut bot)) = query.get_mut(event.0) {
+ **jumping = true;
+ bot.jumping_once = true;
+ }
}
}
+/// Make an entity look towards a certain position in the world.
+pub struct LookAtEvent {
+ pub entity: Entity,
+ /// The position we want the entity to be looking at.
+ pub position: Vec3,
+}
+fn look_at_listener(
+ mut events: EventReader<LookAtEvent>,
+ mut query: Query<(&Position, &mut Physics)>,
+) {
+ for event in events.iter() {
+ if let Ok((position, mut physics)) = query.get_mut(event.entity) {
+ let (y_rot, x_rot) = direction_looking_at(position, &event.position);
+ set_rotation(&mut physics, y_rot, x_rot);
+ }
+ }
+}
+
+/// Return the (`y_rot`, `x_rot`) that would make a client at `current` be
+/// looking at `target`.
fn direction_looking_at(current: &Vec3, target: &Vec3) -> (f32, f32) {
// borrowed from mineflayer's Bot.lookAt because i didn't want to do math
let delta = target - current;
@@ -59,3 +118,15 @@ fn direction_looking_at(current: &Vec3, target: &Vec3) -> (f32, f32) {
let x_rot = f64::atan2(delta.y, ground_distance) * -(180.0 / PI);
(y_rot as f32, x_rot as f32)
}
+
+/// A [`PluginGroup`] for the plugins that add extra bot functionality to the
+/// client.
+pub struct DefaultBotPlugins;
+
+impl PluginGroup for DefaultBotPlugins {
+ fn build(self) -> PluginGroupBuilder {
+ PluginGroupBuilder::start::<Self>()
+ .add(BotPlugin)
+ .add(PathfinderPlugin)
+ }
+}
diff --git a/azalea/src/lib.rs b/azalea/src/lib.rs
index 4ceb5b2e..026a35f5 100644
--- a/azalea/src/lib.rs
+++ b/azalea/src/lib.rs
@@ -1,94 +1,139 @@
-//! Azalea is a framework for creating Minecraft bots.
-//!
-//! Internally, it's just a wrapper over [`azalea_client`], adding useful
-//! functions for making bots. Because of this, lots 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.
-//!
-//! # Installation
-//!
-//! First, install Rust nightly with `rustup install nightly` and `rustup
-//! default nightly`.
-//!
-//! Then, add one of the following lines to your Cargo.toml:
-//!
-//! Latest bleeding-edge version:
-//! `azalea = { git="https://github.com/mat-1/azalea" }`\
-//! Latest "stable" release:
-//! `azalea = "0.5.0"`
-//!
-//! ## Optimization
-//!
-//! For faster compile times, make a `.cargo/config.toml` file in your project
-//! and copy
-//! [this file](https://github.com/mat-1/azalea/blob/main/.cargo/config.toml)
-//! into it.
-//!
-//! For faster performance in debug mode, add
-//! ```toml
-//! [profile.dev]
-//! opt-level = 1
-//! [profile.dev.package."*"]
-//! opt-level = 3
-//! ```
-//! to your Cargo.toml. You may have to install the LLD linker.
-//!
-//! # Examples
-//!
-//! ```rust,no_run
-//! //! A bot that logs chat messages sent in the server to the console.
-//!
-//! use azalea::prelude::*;
-//! use parking_lot::Mutex;
-//! use std::sync::Arc;
-//!
-//! #[tokio::main]
-//! async fn main() {
-//! let account = Account::offline("bot");
-//! // or Account::microsoft("example@example.com").await.unwrap();
-//!
-//! azalea::start(azalea::Options {
-//! account,
-//! address: "localhost",
-//! state: State::default(),
-//! plugins: plugins![],
-//! handle,
-//! })
-//! .await
-//! .unwrap();
-//! }
-//!
-//! #[derive(Default, Clone)]
-//! pub struct State {}
-//!
-//! async fn handle(bot: Client, event: Event, state: State) -> anyhow::Result<()> {
-//! match event {
-//! Event::Chat(m) => {
-//! println!("{}", m.message().to_ansi());
-//! }
-//! _ => {}
-//! }
-//!
-//! Ok(())
-//! }
-//! ```
-//!
-//! [`azalea_client`]: https://docs.rs/azalea-client
-
-#![feature(trait_upcasting)]
+#![doc = include_str!("../README.md")]
#![feature(async_closure)]
-#![allow(incomplete_features)]
mod bot;
pub mod pathfinder;
pub mod prelude;
-mod start;
mod swarm;
-pub use azalea_block::*;
+pub use azalea_block as blocks;
pub use azalea_client::*;
pub use azalea_core::{BlockPos, Vec3};
-pub use start::{start, Options};
+use azalea_ecs::{
+ app::{App, Plugin},
+ component::Component,
+};
+pub use azalea_protocol as protocol;
+pub use azalea_registry::EntityKind;
+pub use azalea_world::{entity, World};
+use bot::DefaultBotPlugins;
+use ecs::app::PluginGroup;
+use futures::Future;
+use protocol::{
+ resolver::{self, ResolverError},
+ ServerAddress,
+};
pub use swarm::*;
+use thiserror::Error;
+use tokio::sync::mpsc;
pub type HandleFn<Fut, S> = fn(Client, Event, S) -> Fut;
+
+#[derive(Error, Debug)]
+pub enum StartError {
+ #[error("Invalid address")]
+ InvalidAddress,
+ #[error(transparent)]
+ ResolveAddress(#[from] ResolverError),
+ #[error("Join error: {0}")]
+ Join(#[from] azalea_client::JoinError),
+}
+
+pub struct ClientBuilder<S, Fut>
+where
+ S: Default + Send + Sync + Clone + 'static,
+ Fut: Future<Output = Result<(), anyhow::Error>>,
+{
+ app: App,
+ /// The function that's called every time a bot receives an [`Event`].
+ handler: Option<HandleFn<Fut, S>>,
+ state: S,
+}
+impl<S, Fut> ClientBuilder<S, Fut>
+where
+ S: Default + Send + Sync + Clone + Component + 'static,
+ Fut: Future<Output = Result<(), anyhow::Error>> + Send + 'static,
+{
+ /// Start building a client that can join the world.
+ #[must_use]
+ pub fn new() -> Self {
+ Self {
+ // we create the app here so plugins can add onto it.
+ // the schedules won't run until [`Self::start`] is called.
+ app: init_ecs_app(),
+
+ handler: None,
+ state: S::default(),
+ }
+ .add_plugins(DefaultBotPlugins)
+ }
+ /// Set the function that's called every time a bot receives an [`Event`].
+ /// This is the way to handle normal per-bot events.
+ ///
+ /// You can only have one client handler, calling this again will replace
+ /// the old client handler function (you can have a client handler and swarm
+ /// handler separately though).
+ #[must_use]
+ pub fn set_handler(mut self, handler: HandleFn<Fut, S>) -> Self {
+ self.handler = Some(handler);
+ self
+ }
+ /// Add a plugin to the client.
+ #[must_use]
+ pub fn add_plugin<T: Plugin>(mut self, plugin: T) -> Self {
+ self.app.add_plugin(plugin);
+ self
+ }
+ /// Add a group of plugins to the client.
+ #[must_use]
+ pub fn add_plugins<T: PluginGroup>(mut self, plugin_group: T) -> Self {
+ self.app.add_plugins(plugin_group);
+ self
+ }
+
+ /// Build this `ClientBuilder` into an actual [`Client`] and join the given
+ /// server.
+ ///
+ /// The `address` argument can be a `&str`, [`ServerAddress`], or anything
+ /// that implements `TryInto<ServerAddress>`.
+ ///
+ /// [`ServerAddress`]: azalea_protocol::ServerAddress
+ pub async fn start(
+ self,
+ account: Account,
+ address: impl TryInto<ServerAddress>,
+ ) -> 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::channel(1);
+ let ecs_lock = start_ecs(self.app, run_schedule_receiver, run_schedule_sender.clone());
+
+ let (bot, mut rx) = Client::start_client(
+ ecs_lock,
+ &account,
+ &address,
+ &resolved_address,
+ 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()));
+ }
+ }
+
+ Ok(())
+ }
+}
+impl<S, Fut> Default for ClientBuilder<S, Fut>
+where
+ S: Default + Send + Sync + Clone + Component + 'static,
+ Fut: Future<Output = Result<(), anyhow::Error>> + Send + 'static,
+{
+ fn default() -> Self {
+ Self::new()
+ }
+}
diff --git a/azalea/src/pathfinder/mod.rs b/azalea/src/pathfinder/mod.rs
index de62e9a7..5246290d 100644
--- a/azalea/src/pathfinder/mod.rs
+++ b/azalea/src/pathfinder/mod.rs
@@ -1,147 +1,255 @@
mod moves;
mod mtdstarlite;
-use crate::{prelude::*, SprintDirection, WalkDirection};
-use crate::{Client, Event};
-use async_trait::async_trait;
+use crate::bot::{JumpEvent, LookAtEvent};
+use crate::{SprintDirection, WalkDirection};
+
+use azalea_client::{StartSprintEvent, StartWalkEvent};
use azalea_core::{BlockPos, CardinalDirection};
-use azalea_world::entity::EntityData;
+use azalea_ecs::{
+ app::{App, Plugin},
+ component::Component,
+ entity::Entity,
+ event::{EventReader, EventWriter},
+ query::{With, Without},
+ schedule::IntoSystemDescriptor,
+ system::{Commands, Query, Res},
+ AppTickExt,
+};
+use azalea_world::entity::metadata::Player;
+use azalea_world::Local;
+use azalea_world::{
+ entity::{Physics, Position, WorldName},
+ WorldContainer,
+};
+use bevy_tasks::{AsyncComputeTaskPool, Task};
+use futures_lite::future;
use log::{debug, error};
use mtdstarlite::Edge;
pub use mtdstarlite::MTDStarLite;
-use parking_lot::Mutex;
use std::collections::VecDeque;
use std::sync::Arc;
#[derive(Clone, Default)]
-pub struct Plugin;
-impl crate::Plugin for Plugin {
- type State = State;
-
- fn build(&self) -> State {
- State::default()
+pub struct PathfinderPlugin;
+impl Plugin for PathfinderPlugin {
+ fn build(&self, app: &mut App) {
+ app.add_event::<GotoEvent>()
+ .add_event::<PathFoundEvent>()
+ .add_tick_system(tick_execute_path.before("walk_listener"))
+ .add_system(goto_listener)
+ .add_system(add_default_pathfinder.after("deduplicate_entities"))
+ .add_system(handle_tasks)
+ .add_system(path_found_listener);
}
}
-#[derive(Default, Clone)]
-pub struct State {
- // pathfinder: Option<MTDStarLite<Node, f32>>,
- pub path: Arc<Mutex<VecDeque<Node>>>,
+/// A component that makes this entity able to pathfind.
+#[derive(Component, Default)]
+pub struct Pathfinder {
+ pub path: VecDeque<Node>,
+}
+#[allow(clippy::type_complexity)]
+fn add_default_pathfinder(
+ mut commands: Commands,
+ mut query: Query<Entity, (Without<Pathfinder>, With<Local>, With<Player>)>,
+) {
+ for entity in &mut query {
+ commands.entity(entity).insert(Pathfinder::default());
+ }
}
-#[async_trait]
-impl crate::PluginState for State {
- async fn handle(self: Box<Self>, event: Event, mut bot: Client) {
- if let Event::Tick = event {
- let mut path = self.path.lock();
+pub trait PathfinderClientExt {
+ fn goto(&self, goal: impl Goal + Send + Sync + 'static);
+}
- if !path.is_empty() {
- tick_execute_path(&mut bot, &mut path);
- }
- }
+impl PathfinderClientExt for azalea_client::Client {
+ fn goto(&self, goal: impl Goal + Send + Sync + 'static) {
+ self.ecs.lock().send_event(GotoEvent {
+ entity: self.entity,
+ goal: Arc::new(goal),
+ });
}
}
-
-pub trait Trait {
- fn goto(&self, goal: impl Goal);
+pub struct GotoEvent {
+ pub entity: Entity,
+ pub goal: Arc<dyn Goal + Send + Sync>,
}
+pub struct PathFoundEvent {
+ pub entity: Entity,
+ pub path: VecDeque<Node>,
+}
+
+#[derive(Component)]
+pub struct ComputePath(Task<Option<PathFoundEvent>>);
-impl Trait for azalea_client::Client {
- fn goto(&self, goal: impl Goal) {
+fn goto_listener(
+ mut commands: Commands,
+ mut events: EventReader<GotoEvent>,
+ mut query: Query<(&Position, &WorldName)>,
+ world_container: Res<WorldContainer>,
+) {
+ let thread_pool = AsyncComputeTaskPool::get();
+
+ for event in events.iter() {
+ let (position, world_name) = query
+ .get_mut(event.entity)
+ .expect("Called goto on an entity that's not in the world");
let start = Node {
- pos: BlockPos::from(self.entity().pos()),
+ pos: BlockPos::from(position),
vertical_vel: VerticalVel::None,
};
- let end = goal.goal_node();
- debug!("start: {start:?}, end: {end:?}");
-
- let possible_moves: Vec<&dyn moves::Move> = vec![
- &moves::ForwardMove(CardinalDirection::North),
- &moves::ForwardMove(CardinalDirection::East),
- &moves::ForwardMove(CardinalDirection::South),
- &moves::ForwardMove(CardinalDirection::West),
- //
- &moves::AscendMove(CardinalDirection::North),
- &moves::AscendMove(CardinalDirection::East),
- &moves::AscendMove(CardinalDirection::South),
- &moves::AscendMove(CardinalDirection::West),
- //
- &moves::DescendMove(CardinalDirection::North),
- &moves::DescendMove(CardinalDirection::East),
- &moves::DescendMove(CardinalDirection::South),
- &moves::DescendMove(CardinalDirection::West),
- //
- &moves::DiagonalMove(CardinalDirection::North),
- &moves::DiagonalMove(CardinalDirection::East),
- &moves::DiagonalMove(CardinalDirection::South),
- &moves::DiagonalMove(CardinalDirection::West),
- ];
-
- let successors = |node: &Node| {
- let mut edges = Vec::new();
-
- let world = &self.world.read().shared;
- for possible_move in possible_moves.iter() {
- edges.push(Edge {
- target: possible_move.next_node(node),
- cost: possible_move.cost(world, node),
- });
+
+ let world_lock = world_container
+ .get(world_name)
+ .expect("Entity tried to pathfind but the entity isn't in a valid world");
+ let end = event.goal.goal_node();
+
+ let goal = event.goal.clone();
+ let entity = event.entity;
+
+ let task = thread_pool.spawn(async move {
+ debug!("start: {start:?}, end: {end:?}");
+
+ let possible_moves: Vec<&dyn moves::Move> = vec![
+ &moves::ForwardMove(CardinalDirection::North),
+ &moves::ForwardMove(CardinalDirection::East),
+ &moves::ForwardMove(CardinalDirection::South),
+ &moves::ForwardMove(CardinalDirection::West),
+ //
+ &moves::AscendMove(CardinalDirection::North),
+ &moves::AscendMove(CardinalDirection::East),
+ &moves::AscendMove(CardinalDirection::South),
+ &moves::AscendMove(CardinalDirection::West),
+ //
+ &moves::DescendMove(CardinalDirection::North),
+ &moves::DescendMove(CardinalDirection::East),
+ &moves::DescendMove(CardinalDirection::South),
+ &moves::DescendMove(CardinalDirection::West),
+ //
+ &moves::DiagonalMove(CardinalDirection::North),
+ &moves::DiagonalMove(CardinalDirection::East),
+ &moves::DiagonalMove(CardinalDirection::South),
+ &moves::DiagonalMove(CardinalDirection::West),
+ ];
+
+ let successors = |node: &Node| {
+ let mut edges = Vec::new();
+
+ let world = world_lock.read();
+ for possible_move in &possible_moves {
+ edges.push(Edge {
+ target: possible_move.next_node(node),
+ cost: possible_move.cost(&world, node),
+ });
+ }
+ edges
+ };
+
+ let mut pf = MTDStarLite::new(
+ start,
+ end,
+ |n| goal.heuristic(n),
+ successors,
+ successors,
+ |n| goal.success(n),
+ );
+
+ let start_time = std::time::Instant::now();
+ let p = pf.find_path();
+ let end_time = std::time::Instant::now();
+ debug!("path: {p:?}");
+ debug!("time: {:?}", end_time - start_time);
+
+ // convert the Option<Vec<Node>> to a VecDeque<Node>
+ if let Some(p) = p {
+ let path = p.into_iter().collect::<VecDeque<_>>();
+ // commands.entity(event.entity).insert(Pathfinder { path: p });
+ Some(PathFoundEvent { entity, path })
+ } else {
+ error!("no path found");
+ None
}
- edges
- };
+ });
- let mut pf = MTDStarLite::new(
- start,
- end,
- |n| goal.heuristic(n),
- successors,
- successors,
- |n| goal.success(n),
- );
-
- let start = std::time::Instant::now();
- let p = pf.find_path();
- let end = std::time::Instant::now();
- debug!("path: {p:?}");
- debug!("time: {:?}", end - start);
-
- let state = self
- .plugins
- .get::<State>()
- .expect("Pathfinder plugin not installed!")
- .clone();
- // convert the Option<Vec<Node>> to a VecDeque<Node>
- if let Some(p) = p {
- *state.path.lock() = p.into_iter().collect();
- } else {
- error!("no path found");
+ commands.spawn(ComputePath(task));
+ }
+}
+
+// poll the tasks and send the PathFoundEvent if they're done
+fn handle_tasks(
+ mut commands: Commands,
+ mut transform_tasks: Query<(Entity, &mut ComputePath)>,
+ mut path_found_events: EventWriter<PathFoundEvent>,
+) {
+ for (entity, mut task) in &mut transform_tasks {
+ if let Some(optional_path_found_event) = future::block_on(future::poll_once(&mut task.0)) {
+ if let Some(path_found_event) = optional_path_found_event {
+ path_found_events.send(path_found_event);
+ }
+
+ // Task is complete, so remove task component from entity
+ commands.entity(entity).remove::<ComputePath>();
}
}
}
-fn tick_execute_path(bot: &mut Client, path: &mut VecDeque<Node>) {
- let target = if let Some(target) = path.front() {
- target
- } else {
- return;
- };
- let center = target.pos.center();
- // println!("going to {center:?} (at {pos:?})", pos = bot.entity().pos());
- bot.look_at(&center);
- bot.sprint(SprintDirection::Forward);
- // check if we should jump
- if target.pos.y > bot.entity().pos().y.floor() as i32 {
- bot.jump();
+// set the path for the target entity when we get the PathFoundEvent
+fn path_found_listener(mut events: EventReader<PathFoundEvent>, mut query: Query<&mut Pathfinder>) {
+ for event in events.iter() {
+ let mut pathfinder = query
+ .get_mut(event.entity)
+ .expect("Path found for an entity that doesn't have a pathfinder");
+ pathfinder.path = event.path.clone();
}
+}
- if target.is_reached(&bot.entity()) {
- // println!("ok target {target:?} reached");
- path.pop_front();
- if path.is_empty() {
- bot.walk(WalkDirection::None);
+fn tick_execute_path(
+ mut query: Query<(Entity, &mut Pathfinder, &Position, &Physics)>,
+ mut look_at_events: EventWriter<LookAtEvent>,
+ mut sprint_events: EventWriter<StartSprintEvent>,
+ mut walk_events: EventWriter<StartWalkEvent>,
+ mut jump_events: EventWriter<JumpEvent>,
+) {
+ for (entity, mut pathfinder, position, physics) in &mut query {
+ loop {
+ let Some(target) = pathfinder.path.front() else {
+ break;
+ };
+ let center = target.pos.center();
+ // println!("going to {center:?} (at {pos:?})", pos = bot.entity().pos());
+ look_at_events.send(LookAtEvent {
+ entity,
+ position: center,
+ });
+ debug!(
+ "tick: pathfinder {entity:?}; going to {:?}; currently at {position:?}",
+ target.pos
+ );
+ sprint_events.send(StartSprintEvent {
+ entity,
+ direction: SprintDirection::Forward,
+ });
+ // check if we should jump
+ if target.pos.y > position.y.floor() as i32 {
+ jump_events.send(JumpEvent(entity));
+ }
+
+ if target.is_reached(position, physics) {
+ // println!("reached target");
+ pathfinder.path.pop_front();
+ if pathfinder.path.is_empty() {
+ // println!("reached goal");
+ walk_events.send(StartWalkEvent {
+ entity,
+ direction: WalkDirection::None,
+ });
+ }
+ // tick again, maybe we already reached the next node!
+ } else {
+ break;
+ }
}
- // tick again, maybe we already reached the next node!
- tick_execute_path(bot, path);
}
}
@@ -172,7 +280,8 @@ pub trait Goal {
impl Node {
/// Returns whether the entity is at the node and should start going to the
/// next node.
- pub fn is_reached(&self, entity: &EntityData) -> bool {
+ #[must_use]
+ pub fn is_reached(&self, position: &Position, physics: &Physics) -> bool {
// println!(
// "entity.delta.y: {} {:?}=={:?}, self.vertical_vel={:?}",
// entity.delta.y,
@@ -180,11 +289,11 @@ impl Node {
// self.pos,
// self.vertical_vel
// );
- BlockPos::from(entity.pos()) == self.pos
+ BlockPos::from(position) == self.pos
&& match self.vertical_vel {
- VerticalVel::NoneMidair => entity.delta.y > -0.1 && entity.delta.y < 0.1,
- VerticalVel::None => entity.on_ground,
- VerticalVel::FallingLittle => entity.delta.y < -0.1,
+ VerticalVel::NoneMidair => physics.delta.y > -0.1 && physics.delta.y < 0.1,
+ VerticalVel::None => physics.on_ground,
+ VerticalVel::FallingLittle => physics.delta.y < -0.1,
}
}
}
diff --git a/azalea/src/pathfinder/moves.rs b/azalea/src/pathfinder/moves.rs
index ccf8ba1a..9bb5c7c1 100644
--- a/azalea/src/pathfinder/moves.rs
+++ b/azalea/src/pathfinder/moves.rs
@@ -1,11 +1,11 @@
use super::{Node, VerticalVel};
use azalea_core::{BlockPos, CardinalDirection};
use azalea_physics::collision::{self, BlockWithShape};
-use azalea_world::WeakWorld;
+use azalea_world::World;
/// whether this block is passable
-fn is_block_passable(pos: &BlockPos, world: &WeakWorld) -> bool {
- if let Some(block) = world.get_block_state(pos) {
+fn is_block_passable(pos: &BlockPos, world: &World) -> bool {
+ if let Some(block) = world.chunks.get_block_state(pos) {
block.shape() == &collision::empty_shape()
} else {
false
@@ -13,8 +13,8 @@ fn is_block_passable(pos: &BlockPos, world: &WeakWorld) -> bool {
}
/// whether this block has a solid hitbox (i.e. we can stand on it)
-fn is_block_solid(pos: &BlockPos, world: &WeakWorld) -> bool {
- if let Some(block) = world.get_block_state(pos) {
+fn is_block_solid(pos: &BlockPos, world: &World) -> bool {
+ if let Some(block) = world.chunks.get_block_state(pos) {
block.shape() == &collision::block_shape()
} else {
false
@@ -22,22 +22,22 @@ fn is_block_solid(pos: &BlockPos, world: &WeakWorld) -> bool {
}
/// Whether this block and the block above are passable
-fn is_passable(pos: &BlockPos, world: &WeakWorld) -> bool {
+fn is_passable(pos: &BlockPos, world: &World) -> bool {
is_block_passable(pos, world) && is_block_passable(&pos.up(1), world)
}
/// Whether we can stand in this position. Checks if the block below is solid,
/// and that the two blocks above that are passable.
-fn is_standable(pos: &BlockPos, world: &WeakWorld) -> bool {
+fn is_standable(pos: &BlockPos, world: &World) -> bool {
is_block_solid(&pos.down(1), world) && is_passable(pos, world)
}
const JUMP_COST: f32 = 0.5;
const WALK_ONE_BLOCK_COST: f32 = 1.0;
-pub trait Move {
- fn cost(&self, world: &WeakWorld, node: &Node) -> f32;
+pub trait Move: Send + Sync {
+ fn cost(&self, world: &World, node: &Node) -> f32;
/// Returns by how much the entity's position should be changed when this
/// move is executed.
fn offset(&self) -> BlockPos;
@@ -51,7 +51,7 @@ pub trait Move {
pub struct ForwardMove(pub CardinalDirection);
impl Move for ForwardMove {
- fn cost(&self, world: &WeakWorld, node: &Node) -> f32 {
+ fn cost(&self, world: &World, node: &Node) -> f32 {
if is_standable(&(node.pos + self.offset()), world)
&& node.vertical_vel == VerticalVel::None
{
@@ -67,7 +67,7 @@ impl Move for ForwardMove {
pub struct AscendMove(pub CardinalDirection);
impl Move for AscendMove {
- fn cost(&self, world: &WeakWorld, node: &Node) -> f32 {
+ fn cost(&self, world: &World, node: &Node) -> f32 {
if node.vertical_vel == VerticalVel::None
&& is_block_passable(&node.pos.up(2), world)
&& is_standable(&(node.pos + self.offset()), world)
@@ -89,7 +89,7 @@ impl Move for AscendMove {
}
pub struct DescendMove(pub CardinalDirection);
impl Move for DescendMove {
- fn cost(&self, world: &WeakWorld, node: &Node) -> f32 {
+ fn cost(&self, world: &World, node: &Node) -> f32 {
// check whether 3 blocks vertically forward are passable
if node.vertical_vel == VerticalVel::None
&& is_standable(&(node.pos + self.offset()), world)
@@ -112,7 +112,7 @@ impl Move for DescendMove {
}
pub struct DiagonalMove(pub CardinalDirection);
impl Move for DiagonalMove {
- fn cost(&self, world: &WeakWorld, node: &Node) -> f32 {
+ fn cost(&self, world: &World, node: &Node) -> f32 {
if node.vertical_vel != VerticalVel::None {
return f32::INFINITY;
}
@@ -151,56 +151,92 @@ mod tests {
use super::*;
use azalea_block::BlockState;
use azalea_core::ChunkPos;
- use azalea_world::{Chunk, PartialWorld};
+ use azalea_world::{Chunk, ChunkStorage, PartialWorld};
#[test]
fn test_is_passable() {
- let mut world = PartialWorld::default();
- world
- .set_chunk(&ChunkPos { x: 0, z: 0 }, Some(Chunk::default()))
- .unwrap();
- world.set_block_state(&BlockPos::new(0, 0, 0), BlockState::Stone);
- world.set_block_state(&BlockPos::new(0, 1, 0), BlockState::Air);
-
- assert_eq!(
- is_block_passable(&BlockPos::new(0, 0, 0), &world.shared),
- false
+ let mut partial_world = PartialWorld::default();
+ let mut chunk_storage = ChunkStorage::default();
+
+ partial_world.chunks.set(
+ &ChunkPos { x: 0, z: 0 },
+ Some(Chunk::default()),
+ &mut chunk_storage,
+ );
+ partial_world.chunks.set_block_state(
+ &BlockPos::new(0, 0, 0),
+ BlockState::Stone,
+ &mut chunk_storage,
);
- assert_eq!(
- is_block_passable(&BlockPos::new(0, 1, 0), &world.shared),
- true
+ partial_world.chunks.set_block_state(
+ &BlockPos::new(0, 1, 0),
+ BlockState::Air,
+ &mut chunk_storage,
);
+
+ let world = chunk_storage.into();
+ assert_eq!(is_block_passable(&BlockPos::new(0, 0, 0), &world), false);
+ assert_eq!(is_block_passable(&BlockPos::new(0, 1, 0), &world), true);
}
#[test]
fn test_is_solid() {
- let mut world = PartialWorld::default();
- world
- .set_chunk(&ChunkPos { x: 0, z: 0 }, Some(Chunk::default()))
- .unwrap();
- world.set_block_state(&BlockPos::new(0, 0, 0), BlockState::Stone);
- world.set_block_state(&BlockPos::new(0, 1, 0), BlockState::Air);
-
- assert_eq!(is_block_solid(&BlockPos::new(0, 0, 0), &world.shared), true);
- assert_eq!(
- is_block_solid(&BlockPos::new(0, 1, 0), &world.shared),
- false
+ let mut partial_world = PartialWorld::default();
+ let mut chunk_storage = ChunkStorage::default();
+ partial_world.chunks.set(
+ &ChunkPos { x: 0, z: 0 },
+ Some(Chunk::default()),
+ &mut chunk_storage,
);
+ partial_world.chunks.set_block_state(
+ &BlockPos::new(0, 0, 0),
+ BlockState::Stone,
+ &mut chunk_storage,
+ );
+ partial_world.chunks.set_block_state(
+ &BlockPos::new(0, 1, 0),
+ BlockState::Air,
+ &mut chunk_storage,
+ );
+
+ let world = chunk_storage.into();
+ assert_eq!(is_block_solid(&BlockPos::new(0, 0, 0), &world), true);
+ assert_eq!(is_block_solid(&BlockPos::new(0, 1, 0), &world), false);
}
#[test]
fn test_is_standable() {
- let mut world = PartialWorld::default();
- world
- .set_chunk(&ChunkPos { x: 0, z: 0 }, Some(Chunk::default()))
- .unwrap();
- world.set_block_state(&BlockPos::new(0, 0, 0), BlockState::Stone);
- world.set_block_state(&BlockPos::new(0, 1, 0), BlockState::Air);
- world.set_block_state(&BlockPos::new(0, 2, 0), BlockState::Air);
- world.set_block_state(&BlockPos::new(0, 3, 0), BlockState::Air);
-
- assert!(is_standable(&BlockPos::new(0, 1, 0), &world.shared));
- assert!(!is_standable(&BlockPos::new(0, 0, 0), &world.shared));
- assert!(!is_standable(&BlockPos::new(0, 2, 0), &world.shared));
+ let mut partial_world = PartialWorld::default();
+ let mut chunk_storage = ChunkStorage::default();
+ partial_world.chunks.set(
+ &ChunkPos { x: 0, z: 0 },
+ Some(Chunk::default()),
+ &mut chunk_storage,
+ );
+ partial_world.chunks.set_block_state(
+ &BlockPos::new(0, 0, 0),
+ BlockState::Stone,
+ &mut chunk_storage,
+ );
+ partial_world.chunks.set_block_state(
+ &BlockPos::new(0, 1, 0),
+ BlockState::Air,
+ &mut chunk_storage,
+ );
+ partial_world.chunks.set_block_state(
+ &BlockPos::new(0, 2, 0),
+ BlockState::Air,
+ &mut chunk_storage,
+ );
+ partial_world.chunks.set_block_state(
+ &BlockPos::new(0, 3, 0),
+ BlockState::Air,
+ &mut chunk_storage,
+ );
+
+ let world = chunk_storage.into();
+ assert!(is_standable(&BlockPos::new(0, 1, 0), &world));
+ assert!(!is_standable(&BlockPos::new(0, 0, 0), &world));
+ assert!(!is_standable(&BlockPos::new(0, 2, 0), &world));
}
}
diff --git a/azalea/src/pathfinder/mtdstarlite.rs b/azalea/src/pathfinder/mtdstarlite.rs
index 50a467df..ce463279 100644
--- a/azalea/src/pathfinder/mtdstarlite.rs
+++ b/azalea/src/pathfinder/mtdstarlite.rs
@@ -3,9 +3,9 @@
//!
//! Future optimization attempt ideas:
//! - Use a different priority queue (e.g. fibonacci heap)
-//! - Use FxHash instead of the default hasher
+//! - Use `FxHash` instead of the default hasher
//! - Have `par` be a raw pointer
-//! - Try borrowing vs copying the Node in several places (like state_mut)
+//! - Try borrowing vs copying the Node in several places (like `state_mut`)
//! - Store edge costs in their own map
use priority_queue::DoublePriorityQueue;
@@ -260,9 +260,7 @@ impl<
// identify a path from sstart to sgoal using the parent pointers
let mut target = self.state(&self.goal).par;
while !(Some(self.start) == target) {
- let this_target = if let Some(this_target) = target {
- this_target
- } else {
+ let Some(this_target) = target else {
break;
};
// hunter follows path from start to goal;
diff --git a/azalea/src/prelude.rs b/azalea/src/prelude.rs
index bedf724f..3d8cc13e 100644
--- a/azalea/src/prelude.rs
+++ b/azalea/src/prelude.rs
@@ -1,7 +1,7 @@
//! The Azalea prelude. Things that are necessary for a bare-bones bot are
//! re-exported here.
-pub use crate::bot::BotTrait;
-pub use crate::pathfinder::Trait;
-pub use crate::{plugins, swarm_plugins, Plugin};
+pub use crate::bot::BotClientExt;
+pub use crate::pathfinder::PathfinderClientExt;
pub use azalea_client::{Account, Client, Event};
+pub use azalea_ecs::component::Component;
diff --git a/azalea/src/start.rs b/azalea/src/start.rs
deleted file mode 100644
index e2374fb8..00000000
--- a/azalea/src/start.rs
+++ /dev/null
@@ -1,136 +0,0 @@
-use crate::{bot, pathfinder, HandleFn};
-use azalea_client::{Account, Client, Plugins};
-use azalea_protocol::ServerAddress;
-use std::{future::Future, sync::Arc};
-use thiserror::Error;
-
-/// A helper macro that generates a [`Plugins`] struct from a list of objects
-/// that implement [`Plugin`].
-///
-/// ```rust,no_run
-/// plugins![azalea_pathfinder::Plugin];
-/// ```
-///
-/// [`Plugin`]: crate::Plugin
-#[macro_export]
-macro_rules! plugins {
- ($($plugin:expr),*) => {
- {
- let mut plugins = azalea::Plugins::new();
- $(
- plugins.add($plugin);
- )*
- plugins
- }
- };
-}
-
-/// The options that are passed to [`azalea::start`].
-///
-/// [`azalea::start`]: crate::start()
-pub struct Options<S, A, Fut>
-where
- A: TryInto<ServerAddress>,
- Fut: Future<Output = Result<(), anyhow::Error>>,
-{
- /// The address of the server that we're connecting to. This can be a
- /// `&str`, [`ServerAddress`], or anything that implements
- /// `TryInto<ServerAddress>`.
- ///
- /// [`ServerAddress`]: azalea_protocol::ServerAddress
- pub address: A,
- /// The account that's going to join the server.
- pub account: Account,
- /// The plugins that are going to be used. Plugins are external crates that
- /// add extra functionality to Azalea. You should use the [`plugins`] macro
- /// for this field.
- ///
- /// ```rust,no_run
- /// plugins![azalea_pathfinder::Plugin]
- /// ```
- pub plugins: Plugins,
- /// A struct that contains the data that you want your bot to remember
- /// across events.
- ///
- /// # Examples
- ///
- /// ```rust
- /// use parking_lot::Mutex;
- /// use std::sync::Arc;
- ///
- /// #[derive(Default, Clone)]
- /// struct State {
- /// farming: Arc<Mutex<bool>>,
- /// }
- /// ```
- pub state: S,
- /// The function that's called whenever we get an event.
- ///
- /// # Examples
- ///
- /// ```rust
- /// use azalea::prelude::*;
- ///
- /// async fn handle(bot: Client, event: Event, state: State) -> anyhow::Result<()> {
- /// Ok(())
- /// }
- /// ```
- pub handle: HandleFn<Fut, S>,
-}
-
-#[derive(Error, Debug)]
-pub enum StartError {
- #[error("Invalid address")]
- InvalidAddress,
- #[error("Join error: {0}")]
- Join(#[from] azalea_client::JoinError),
-}
-
-/// Join a server and start handling events. This function will run forever
-/// until it gets disconnected from the server.
-///
-/// # Examples
-///
-/// ```rust,no_run
-/// let error = azalea::start(azalea::Options {
-/// account,
-/// address: "localhost",
-/// state: State::default(),
-/// plugins: plugins![azalea_pathfinder::Plugin],
-/// handle,
-/// }).await;
-/// ```
-pub async fn start<
- S: Send + Sync + Clone + 'static,
- A: Send + TryInto<ServerAddress>,
- Fut: Future<Output = Result<(), anyhow::Error>> + Send + 'static,
->(
- options: Options<S, A, Fut>,
-) -> Result<(), StartError> {
- let address = match options.address.try_into() {
- Ok(address) => address,
- Err(_) => return Err(StartError::InvalidAddress),
- };
-
- let (mut bot, mut rx) = Client::join(&options.account, address).await?;
-
- let mut plugins = options.plugins;
- // DEFAULT PLUGINS
- plugins.add(bot::Plugin);
- plugins.add(pathfinder::Plugin);
-
- bot.plugins = Arc::new(plugins.build());
-
- let state = options.state;
-
- while let Some(event) = rx.recv().await {
- let cloned_plugins = (*bot.plugins).clone();
- for plugin in cloned_plugins.into_iter() {
- tokio::spawn(plugin.handle(event.clone(), bot.clone()));
- }
-
- tokio::spawn((options.handle)(bot.clone(), event.clone(), state.clone()));
- }
-
- Ok(())
-}
diff --git a/azalea/src/swarm/chat.rs b/azalea/src/swarm/chat.rs
index 4582c59e..a55e9bf6 100644
--- a/azalea/src/swarm/chat.rs
+++ b/azalea/src/swarm/chat.rs
@@ -1,4 +1,4 @@
-//! Implements SwarmEvent::Chat
+//! Implements `SwarmEvent::Chat`.
// How the chat event works (to avoid firing the event multiple times):
// ---
@@ -13,71 +13,81 @@
// in Swarm that's set to the smallest index of all the bots, and we remove all
// messages from the queue that are before that index.
+use azalea_client::{packet_handling::ChatReceivedEvent, ChatPacket};
+use azalea_ecs::{
+ app::{App, Plugin},
+ component::Component,
+ event::{EventReader, EventWriter},
+ schedule::IntoSystemDescriptor,
+ system::{Commands, Query, Res, ResMut, Resource},
+};
+use std::collections::VecDeque;
+
use crate::{Swarm, SwarmEvent};
-use async_trait::async_trait;
-use azalea_client::{ChatPacket, Client, Event};
-use parking_lot::Mutex;
-use std::{collections::VecDeque, sync::Arc};
-use tokio::sync::broadcast::{Receiver, Sender};
#[derive(Clone)]
-pub struct Plugin {
- pub swarm_state: SwarmState,
- pub tx: Sender<ChatPacket>,
-}
-
-impl crate::Plugin for Plugin {
- type State = State;
-
- fn build(&self) -> State {
- State {
- chat_index: Arc::new(Mutex::new(0)),
- swarm_state: self.swarm_state.clone(),
- tx: self.tx.clone(),
- }
+pub struct SwarmChatPlugin;
+impl Plugin for SwarmChatPlugin {
+ fn build(&self, app: &mut App) {
+ app.add_event::<NewChatMessageEvent>()
+ .add_system(chat_listener.label("chat_listener"))
+ .add_system(update_min_index_and_shrink_queue.after("chat_listener"))
+ .insert_resource(GlobalChatState {
+ chat_queue: VecDeque::new(),
+ chat_min_index: 0,
+ });
}
}
-#[derive(Clone)]
-pub struct State {
- pub chat_index: Arc<Mutex<usize>>,
- pub tx: Sender<ChatPacket>,
- pub swarm_state: SwarmState,
+#[derive(Component, Debug)]
+pub struct ClientChatState {
+ pub chat_index: usize,
}
-#[derive(Clone)]
-pub struct SwarmState {
- pub chat_queue: Arc<Mutex<VecDeque<ChatPacket>>>,
- pub chat_min_index: Arc<Mutex<usize>>,
- pub rx: Arc<tokio::sync::Mutex<Receiver<ChatPacket>>>,
+/// A chat message that no other bots have seen yet was received by a bot.
+#[derive(Debug)]
+pub struct NewChatMessageEvent(ChatPacket);
+
+#[derive(Resource)]
+pub struct GlobalChatState {
+ pub chat_queue: VecDeque<ChatPacket>,
+ pub chat_min_index: usize,
}
-impl State {
- pub fn handle_chat(&self, message: ChatPacket) {
+fn chat_listener(
+ mut commands: Commands,
+ mut query: Query<&mut ClientChatState>,
+ mut events: EventReader<ChatReceivedEvent>,
+ mut global_chat_state: ResMut<GlobalChatState>,
+ mut new_chat_messages_events: EventWriter<NewChatMessageEvent>,
+) {
+ for event in events.iter() {
+ let mut client_chat_state = query.get_mut(event.entity);
+ let mut client_chat_index = if let Ok(ref client_chat_state) = client_chat_state {
+ client_chat_state.chat_index
+ } else {
+ // if the client has no chat state, we default to this and insert it at the end
+ global_chat_state.chat_min_index
+ };
+
// When a bot receives a chat messages, it looks into the queue to find the
// earliest instance of the message content that's after the bot's chat index.
// If it finds it, then its personal index is simply updated. Otherwise, fire
// the event and add to the queue.
- let mut chat_queue = self.swarm_state.chat_queue.lock();
- let chat_min_index = self.swarm_state.chat_min_index.lock();
- let mut chat_index = self.chat_index.lock();
-
- if *chat_min_index > *chat_index {
- // if this happens it's because this bot just logged in, so
- // ignore it and let another bot handle it
- println!("chat_min_index ({chat_min_index}) > chat_index ({chat_index})");
- *chat_index = *chat_min_index;
- return;
- }
- let actual_vec_index = *chat_index - *chat_min_index;
+ let actual_vec_index = client_chat_index - global_chat_state.chat_min_index;
// go through the queue and find the first message that's after the bot's index
let mut found = false;
- for (i, past_message) in chat_queue.iter().enumerate().skip(actual_vec_index) {
- if past_message == &message {
+ for (i, past_message) in global_chat_state
+ .chat_queue
+ .iter()
+ .enumerate()
+ .skip(actual_vec_index)
+ {
+ if past_message == &event.packet {
// found the message, update the index
- *chat_index = i + *chat_min_index + 1;
+ client_chat_index = i + global_chat_state.chat_min_index + 1;
found = true;
break;
}
@@ -85,185 +95,157 @@ impl State {
if !found {
// didn't find the message, so fire the swarm event and add to the queue
- self.tx
- .send(message.clone())
- .expect("failed to send chat message to swarm");
- chat_queue.push_back(message);
- *chat_index = chat_queue.len() + *chat_min_index;
+ new_chat_messages_events.send(NewChatMessageEvent(event.packet.clone()));
+ global_chat_state.chat_queue.push_back(event.packet.clone());
+ client_chat_index =
+ global_chat_state.chat_queue.len() + global_chat_state.chat_min_index;
}
- }
-}
-
-#[async_trait]
-impl crate::PluginState for State {
- async fn handle(self: Box<Self>, event: Event, _bot: Client) {
- // we're allowed to access Plugin::swarm_state since it's shared for every bot
- if let Event::Chat(m) = event {
- self.handle_chat(m);
+ if let Ok(ref mut client_chat_state) = client_chat_state {
+ client_chat_state.chat_index = client_chat_index;
+ } else {
+ commands.entity(event.entity).insert(ClientChatState {
+ chat_index: client_chat_index,
+ });
}
}
}
-impl SwarmState {
- pub fn new<S>(swarm: Swarm<S>) -> (Self, Sender<ChatPacket>)
- where
- S: Send + Sync + Clone + 'static,
- {
- let (tx, rx) = tokio::sync::broadcast::channel(1);
-
- let swarm_state = SwarmState {
- chat_queue: Arc::new(Mutex::new(VecDeque::new())),
- chat_min_index: Arc::new(Mutex::new(0)),
- rx: Arc::new(tokio::sync::Mutex::new(rx)),
- };
- tokio::spawn(swarm_state.clone().start(swarm));
-
- (swarm_state, tx)
- }
- async fn start<S>(self, swarm: Swarm<S>)
- where
- S: Send + Sync + Clone + 'static,
- {
- // it should never be locked unless we reused the same plugin for two swarms
- // (bad)
- let mut rx = self.rx.lock().await;
- while let Ok(m) = rx.recv().await {
- swarm.swarm_tx.send(SwarmEvent::Chat(m)).unwrap();
- let bot_states = swarm
- .bot_datas
- .lock()
- .iter()
- .map(|(bot, _)| {
- bot.plugins
- .get::<State>()
- .expect("Chat plugin not installed")
- .clone()
- })
- .collect::<Vec<_>>();
- self.handle_new_chat_message(&bot_states);
+fn update_min_index_and_shrink_queue(
+ query: Query<&ClientChatState>,
+ mut global_chat_state: ResMut<GlobalChatState>,
+ mut events: EventReader<NewChatMessageEvent>,
+ swarm: Option<Res<Swarm>>,
+) {
+ for event in events.iter() {
+ if let Some(swarm) = &swarm {
+ // it should also work if Swarm isn't present (so the tests don't need it)
+ swarm
+ .swarm_tx
+ .send(SwarmEvent::Chat(event.0.clone()))
+ .unwrap();
}
- }
-}
-
-impl SwarmState {
- pub fn handle_new_chat_message(&self, bot_states: &[State]) {
// To make sure the queue doesn't grow too large, we keep a `chat_min_index`
// in Swarm that's set to the smallest index of all the bots, and we remove all
// messages from the queue that are before that index.
- let chat_min_index = *self.chat_min_index.lock();
- let mut new_chat_min_index = usize::MAX;
- for bot_state in bot_states {
- let this_chat_index = *bot_state.chat_index.lock();
+ let mut new_chat_min_index = global_chat_state.chat_min_index;
+ for client_chat_state in query.iter() {
+ let this_chat_index = client_chat_state.chat_index;
if this_chat_index < new_chat_min_index {
new_chat_min_index = this_chat_index;
}
}
- let mut chat_queue = self.chat_queue.lock();
- if chat_min_index > new_chat_min_index {
- println!(
- "chat_min_index ({chat_min_index}) > new_chat_min_index ({new_chat_min_index})"
- );
+ if global_chat_state.chat_min_index > new_chat_min_index {
return;
}
// remove all messages from the queue that are before the min index
- for _ in 0..(new_chat_min_index - chat_min_index) {
- chat_queue.pop_front();
+ for _ in 0..(new_chat_min_index - global_chat_state.chat_min_index) {
+ global_chat_state.chat_queue.pop_front();
}
// update the min index
- *self.chat_min_index.lock() = new_chat_min_index;
+ global_chat_state.chat_min_index = new_chat_min_index;
}
}
#[cfg(test)]
mod tests {
+ use azalea_ecs::{ecs::Ecs, event::Events, system::SystemState};
+
use super::*;
+ 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
+ app.init_resource::<Events<ChatReceivedEvent>>()
+ .init_resource::<Events<NewChatMessageEvent>>()
+ .add_system(chat_listener.label("chat_listener"))
+ .add_system(update_min_index_and_shrink_queue.after("chat_listener"))
+ .insert_resource(GlobalChatState {
+ chat_queue: VecDeque::new(),
+ chat_min_index: 0,
+ });
+ app
+ }
+
+ fn drain_events(ecs: &mut Ecs) -> Vec<ChatPacket> {
+ let mut system_state: SystemState<ResMut<Events<NewChatMessageEvent>>> =
+ SystemState::new(ecs);
+ let mut events = system_state.get_mut(ecs);
+
+ events.drain().map(|e| e.0.clone()).collect::<Vec<_>>()
+ }
+
#[tokio::test]
async fn test_swarm_chat() {
- let (tx, mut rx) = tokio::sync::broadcast::channel(1);
- let swarm_state = SwarmState {
- chat_queue: Arc::new(Mutex::new(VecDeque::new())),
- chat_min_index: Arc::new(Mutex::new(0)),
- rx: Arc::new(tokio::sync::Mutex::new(rx)),
- };
- let mut bot_states = vec![];
- let bot0 = State {
- swarm_state: swarm_state.clone(),
- chat_index: Arc::new(Mutex::new(0)),
- tx: tx.clone(),
- };
- let bot1 = State {
- swarm_state: swarm_state.clone(),
- chat_index: Arc::new(Mutex::new(0)),
- tx: tx.clone(),
- };
- bot_states.push(bot0.clone());
- bot_states.push(bot1.clone());
+ let mut app = make_test_app();
+
+ let bot0 = app.world.spawn_empty().id();
+ let bot1 = app.world.spawn_empty().id();
+
+ app.world.send_event(ChatReceivedEvent {
+ entity: bot0,
+ packet: ChatPacket::new("a"),
+ });
+ app.update();
- bot0.handle_chat(ChatPacket::new("a"));
// the swarm should get the event immediately after the bot gets it
+ assert_eq!(drain_events(&mut app.world), vec![ChatPacket::new("a")]);
assert_eq!(
- swarm_state.rx.lock().await.try_recv(),
- Ok(ChatPacket::new("a"))
+ app.world.get::<ClientChatState>(bot0).unwrap().chat_index,
+ 1
);
- assert_eq!(*bot0.chat_index.lock(), 1);
// and a second bot sending the event shouldn't do anything
- bot1.handle_chat(ChatPacket::new("a"));
- assert!(swarm_state.rx.lock().await.try_recv().is_err());
- assert_eq!(*bot1.chat_index.lock(), 1);
-
- // but if the first one gets it again, it should sent it again
- bot0.handle_chat(ChatPacket::new("a"));
+ app.world.send_event(ChatReceivedEvent {
+ entity: bot1,
+ packet: ChatPacket::new("a"),
+ });
+ app.update();
+ assert_eq!(drain_events(&mut app.world), vec![]);
assert_eq!(
- swarm_state.rx.lock().await.try_recv(),
- Ok(ChatPacket::new("a"))
+ app.world.get::<ClientChatState>(bot1).unwrap().chat_index,
+ 1
);
+ // but if the first one gets it again, it should sent it again
+ app.world.send_event(ChatReceivedEvent {
+ entity: bot0,
+ packet: ChatPacket::new("a"),
+ });
+ app.update();
+ assert_eq!(drain_events(&mut app.world), vec![ChatPacket::new("a")]);
+
// alright and now the second bot got a different chat message and it should be
// sent
- bot1.handle_chat(ChatPacket::new("b"));
- assert_eq!(
- swarm_state.rx.lock().await.try_recv(),
- Ok(ChatPacket::new("b"))
- );
+ app.world.send_event(ChatReceivedEvent {
+ entity: bot1,
+ packet: ChatPacket::new("b"),
+ });
+ app.update();
+ assert_eq!(drain_events(&mut app.world), vec![ChatPacket::new("b")]);
}
#[tokio::test]
async fn test_new_bot() {
- let (tx, mut rx) = tokio::sync::broadcast::channel(1);
- let swarm_state = SwarmState {
- chat_queue: Arc::new(Mutex::new(VecDeque::new())),
- chat_min_index: Arc::new(Mutex::new(0)),
- rx: Arc::new(tokio::sync::Mutex::new(rx)),
- };
- let mut bot_states = vec![];
- let bot0 = State {
- swarm_state: swarm_state.clone(),
- chat_index: Arc::new(Mutex::new(0)),
- tx: tx.clone(),
- };
- bot_states.push(bot0.clone());
+ let mut app = make_test_app();
+
+ let bot0 = app.world.spawn_empty().id();
// bot0 gets a chat message
- bot0.handle_chat(ChatPacket::new("a"));
- assert_eq!(
- swarm_state.rx.lock().await.try_recv(),
- Ok(ChatPacket::new("a"))
- );
- // now a second bot joined and got a different chat message
- let bot1 = State {
- swarm_state: swarm_state.clone(),
- chat_index: Arc::new(Mutex::new(0)),
- tx: tx.clone(),
- };
- bot_states.push(bot1.clone());
- bot1.handle_chat(ChatPacket::new("b"));
- assert_eq!(
- swarm_state.rx.lock().await.try_recv(),
- Ok(ChatPacket::new("b"))
- );
+ app.world.send_event(ChatReceivedEvent {
+ entity: bot0,
+ packet: ChatPacket::new("a"),
+ });
+ app.update();
+ assert_eq!(drain_events(&mut app.world), vec![ChatPacket::new("a")]);
+ let bot1 = app.world.spawn_empty().id();
+ app.world.send_event(ChatReceivedEvent {
+ entity: bot1,
+ packet: ChatPacket::new("b"),
+ });
+ app.update();
+ assert_eq!(drain_events(&mut app.world), vec![ChatPacket::new("b")]);
}
}
diff --git a/azalea/src/swarm/events.rs b/azalea/src/swarm/events.rs
new file mode 100644
index 00000000..81d8c731
--- /dev/null
+++ b/azalea/src/swarm/events.rs
@@ -0,0 +1,45 @@
+use azalea_client::LocalPlayer;
+use azalea_ecs::{
+ app::{App, Plugin},
+ event::EventWriter,
+ query::With,
+ system::{Query, ResMut, Resource},
+};
+use azalea_world::entity::MinecraftEntityId;
+use derive_more::{Deref, DerefMut};
+
+pub struct SwarmPlugin;
+impl Plugin for SwarmPlugin {
+ fn build(&self, app: &mut App) {
+ app.add_event::<SwarmReadyEvent>()
+ .add_system(check_ready)
+ .init_resource::<IsSwarmReady>();
+ }
+}
+
+/// All the bots from the swarm are now in the world.
+pub struct SwarmReadyEvent;
+
+#[derive(Default, Resource, Deref, DerefMut)]
+struct IsSwarmReady(bool);
+
+fn check_ready(
+ query: Query<Option<&MinecraftEntityId>, With<LocalPlayer>>,
+ mut is_swarm_ready: ResMut<IsSwarmReady>,
+ mut ready_events: EventWriter<SwarmReadyEvent>,
+) {
+ // if we already know the swarm is ready, do nothing
+ if **is_swarm_ready {
+ return;
+ }
+ // if all the players are in the world, we're ready
+ for entity_id in query.iter() {
+ if entity_id.is_none() {
+ return;
+ }
+ }
+
+ // all the players are in the world, so we're ready
+ **is_swarm_ready = true;
+ ready_events.send(SwarmReadyEvent);
+}
diff --git a/azalea/src/swarm/mod.rs b/azalea/src/swarm/mod.rs
index 6fc3e40c..9c16bc01 100644
--- a/azalea/src/swarm/mod.rs
+++ b/azalea/src/swarm/mod.rs
@@ -1,41 +1,29 @@
-/// Swarms are a way to conveniently control many bots.
-mod chat;
-mod plugins;
+//! Swarms are a way to conveniently control many bots.
-pub use self::plugins::*;
-use crate::{bot, HandleFn};
-use azalea_client::{Account, ChatPacket, Client, Event, JoinError, Plugins};
+mod chat;
+mod events;
+
+use crate::{bot::DefaultBotPlugins, HandleFn};
+use azalea_client::{init_ecs_app, start_ecs, Account, ChatPacket, Client, Event, JoinError};
+use azalea_ecs::{
+ app::{App, Plugin, PluginGroup, PluginGroupBuilder},
+ component::Component,
+ ecs::Ecs,
+ entity::Entity,
+ system::Resource,
+};
use azalea_protocol::{
- connect::{Connection, ConnectionError},
+ connect::ConnectionError,
resolver::{self, ResolverError},
ServerAddress,
};
-use azalea_world::WeakWorldContainer;
+use azalea_world::WorldContainer;
use futures::future::join_all;
use log::error;
use parking_lot::{Mutex, RwLock};
-use std::{future::Future, net::SocketAddr, sync::Arc, time::Duration};
+use std::{collections::HashMap, future::Future, net::SocketAddr, sync::Arc, time::Duration};
use thiserror::Error;
-use tokio::sync::mpsc::{self, UnboundedSender};
-
-/// A helper macro that generates a [`SwarmPlugins`] struct from a list of
-/// objects that implement [`SwarmPlugin`].
-///
-/// ```rust,no_run
-/// swarm_plugins![azalea_pathfinder::Plugin];
-/// ```
-#[macro_export]
-macro_rules! swarm_plugins {
- ($($plugin:expr),*) => {
- {
- let mut plugins = azalea::SwarmPlugins::new();
- $(
- plugins.add($plugin);
- )*
- plugins
- }
- };
-}
+use tokio::sync::mpsc;
/// 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.
@@ -46,82 +34,295 @@ macro_rules! swarm_plugins {
/// It's used to make the [`Swarm::add`] function work.
///
/// [`azalea::start_swarm`]: fn.start_swarm.html
-#[derive(Clone)]
-pub struct Swarm<S> {
- bot_datas: Arc<Mutex<Vec<(Client, S)>>>,
+#[derive(Clone, Resource)]
+pub struct Swarm {
+ pub ecs_lock: Arc<Mutex<Ecs>>,
+ bots: Arc<Mutex<HashMap<Entity, Client>>>,
+
+ // bot_datas: Arc<Mutex<Vec<(Client, S)>>>,
resolved_address: SocketAddr,
address: ServerAddress,
- pub worlds: Arc<RwLock<WeakWorldContainer>>,
- /// Plugins that are set for new bots
- plugins: Plugins,
+ pub world_container: Arc<RwLock<WorldContainer>>,
- bots_tx: UnboundedSender<(Option<Event>, (Client, S))>,
- swarm_tx: UnboundedSender<SwarmEvent>,
-}
+ bots_tx: mpsc::UnboundedSender<(Option<Event>, Client)>,
+ swarm_tx: mpsc::UnboundedSender<SwarmEvent>,
-/// An event about something that doesn't have to do with a single bot.
-#[derive(Clone, Debug)]
-pub enum SwarmEvent {
- /// All the bots in the swarm have successfully joined the server.
- Login,
- /// The swarm was created. This is only fired once, and it's guaranteed to
- /// be the first event to fire.
- Init,
- /// A bot got disconnected from the server.
- ///
- /// You can implement an auto-reconnect by calling [`Swarm::add`]
- /// with the account from this event.
- Disconnect(Account),
- /// At least one bot received a chat message.
- Chat(ChatPacket),
+ run_schedule_sender: mpsc::Sender<()>,
}
-pub type SwarmHandleFn<Fut, S, SS> = fn(Swarm<S>, SwarmEvent, SS) -> Fut;
-
-/// The options that are passed to [`azalea::start_swarm`].
-///
-/// [`azalea::start_swarm`]: crate::start_swarm()
-pub struct SwarmOptions<S, SS, A, Fut, SwarmFut>
+/// Create a new [`Swarm`].
+pub struct SwarmBuilder<S, SS, Fut, SwarmFut>
where
- A: TryInto<ServerAddress>,
+ S: Default + Send + Sync + Clone + 'static,
+ SS: Default + Send + Sync + Clone + 'static,
Fut: Future<Output = Result<(), anyhow::Error>>,
SwarmFut: Future<Output = Result<(), anyhow::Error>>,
{
- /// The address of the server that we're connecting to. This can be a
- /// `&str`, [`ServerAddress`], or anything that implements
- /// `TryInto<ServerAddress>`.
- ///
- /// [`ServerAddress`]: azalea_protocol::ServerAddress
- pub address: A,
+ app: App,
/// The accounts that are going to join the server.
- pub accounts: Vec<Account>,
- /// The plugins that are going to be used for all the bots.
- ///
- /// You can usually leave this as `plugins![]`.
- pub plugins: Plugins,
- /// The plugins that are going to be used for the swarm.
- ///
- /// You can usually leave this as `swarm_plugins![]`.
- pub swarm_plugins: SwarmPlugins<S>,
+ accounts: Vec<Account>,
/// The individual bot states. This must be the same length as `accounts`,
/// since each bot gets one state.
- pub states: Vec<S>,
+ states: Vec<S>,
/// The state for the overall swarm.
- pub swarm_state: SS,
+ swarm_state: SS,
/// The function that's called every time a bot receives an [`Event`].
- pub handle: HandleFn<Fut, S>,
+ handler: Option<HandleFn<Fut, S>>,
/// The function that's called every time the swarm receives a
/// [`SwarmEvent`].
- pub swarm_handle: SwarmHandleFn<SwarmFut, S, SS>,
+ swarm_handler: Option<SwarmHandleFn<SwarmFut, SS>>,
/// 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.
- pub join_delay: Option<std::time::Duration>,
+ join_delay: Option<std::time::Duration>,
+}
+impl<S, SS, Fut, SwarmFut> SwarmBuilder<S, SS, Fut, SwarmFut>
+where
+ Fut: Future<Output = Result<(), anyhow::Error>> + Send + 'static,
+ SwarmFut: Future<Output = Result<(), anyhow::Error>> + Send + 'static,
+ S: Default + Send + Sync + Clone + Component + 'static,
+ SS: Default + Send + Sync + Clone + Component + 'static,
+{
+ /// Start creating the swarm.
+ #[must_use]
+ pub fn new() -> Self {
+ Self {
+ // we create the app here so plugins can add onto it.
+ // the schedules won't run until [`Self::start`] is called.
+ app: init_ecs_app(),
+
+ accounts: Vec::new(),
+ states: Vec::new(),
+ swarm_state: SS::default(),
+ handler: None,
+ swarm_handler: None,
+ join_delay: None,
+ }
+ .add_plugins(DefaultSwarmPlugins)
+ .add_plugins(DefaultBotPlugins)
+ }
+
+ /// Add a vec of [`Account`]s to the swarm.
+ ///
+ /// 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`].
+ #[must_use]
+ pub fn add_accounts(mut self, accounts: Vec<Account>) -> Self {
+ for account in accounts {
+ self = self.add_account(account);
+ }
+ self
+ }
+ /// Add a single new [`Account`] to the swarm. Use [`Self::add_accounts`] to
+ /// add multiple accounts at a time.
+ ///
+ /// This will make the state for this client be the default, use
+ /// [`Self::add_account_with_state`] to avoid that.
+ #[must_use]
+ pub fn add_account(self, account: Account) -> Self {
+ self.add_account_with_state(account, S::default())
+ }
+ /// Add an account with a custom initial state. Use just
+ /// [`Self::add_account`] to use the Default implementation for the state.
+ #[must_use]
+ pub fn add_account_with_state(mut self, account: Account, state: S) -> Self {
+ self.accounts.push(account);
+ self.states.push(state);
+ self
+ }
+
+ /// Set the function that's called every time a bot receives an [`Event`].
+ /// This is the way to handle normal per-bot events.
+ ///
+ /// You can only have one client handler, calling this again will replace
+ /// the old client handler function (you can have a client handler and swarm
+ /// handler separately though).
+ #[must_use]
+ pub fn set_handler(mut self, handler: HandleFn<Fut, S>) -> Self {
+ self.handler = Some(handler);
+ self
+ }
+ /// Set the function that's called every time the swarm receives a
+ /// [`SwarmEvent`]. This is the way to handle global swarm events.
+ ///
+ /// You can only have one swarm handler, calling this again will replace
+ /// the old swarm handler function (you can have a client handler and swarm
+ /// handler separately though).
+ #[must_use]
+ pub fn set_swarm_handler(mut self, handler: SwarmHandleFn<SwarmFut, SS>) -> Self {
+ self.swarm_handler = Some(handler);
+ self
+ }
+
+ /// Add a plugin to the swarm.
+ #[must_use]
+ pub fn add_plugin<T: Plugin>(mut self, plugin: T) -> Self {
+ self.app.add_plugin(plugin);
+ self
+ }
+ /// Add a group of plugins to the swarm.
+ #[must_use]
+ pub fn add_plugins<T: PluginGroup>(mut self, plugin_group: T) -> Self {
+ self.app.add_plugins(plugin_group);
+ self
+ }
+
+ /// Set how long we should wait between each bot joining the server.
+ ///
+ /// By default, every bot will connect at the same time. If you set this
+ /// field, however, the bots will wait for the previous one to have
+ /// connected and *then* they'll wait the given duration.
+ #[must_use]
+ pub fn join_delay(mut self, delay: std::time::Duration) -> Self {
+ self.join_delay = Some(delay);
+ self
+ }
+
+ /// Build this `SwarmBuilder` into an actual [`Swarm`] and join the given
+ /// server.
+ ///
+ /// The `address` argument can be a `&str`, [`ServerAddress`], or anything
+ /// that implements `TryInto<ServerAddress>`.
+ ///
+ /// [`ServerAddress`]: azalea_protocol::ServerAddress
+ pub async fn start(self, address: impl TryInto<ServerAddress>) -> Result<(), SwarmStartError> {
+ assert_eq!(
+ self.accounts.len(),
+ self.states.len(),
+ "There must be exactly one state per bot."
+ );
+
+ // convert the TryInto<ServerAddress> into a ServerAddress
+ let address: ServerAddress = match address.try_into() {
+ Ok(address) => address,
+ Err(_) => return Err(SwarmStartError::InvalidAddress),
+ };
+
+ // resolve the address
+ let resolved_address = resolver::resolve_address(&address).await?;
+
+ let world_container = Arc::new(RwLock::new(WorldContainer::default()));
+
+ // we can't modify the swarm plugins after this
+ let (bots_tx, mut bots_rx) = mpsc::unbounded_channel();
+ let (swarm_tx, mut swarm_rx) = mpsc::unbounded_channel();
+
+ let (run_schedule_sender, run_schedule_receiver) = mpsc::channel(1);
+ let ecs_lock = start_ecs(self.app, run_schedule_receiver, run_schedule_sender.clone());
+
+ let swarm = Swarm {
+ ecs_lock: ecs_lock.clone(),
+ bots: Arc::new(Mutex::new(HashMap::new())),
+
+ resolved_address,
+ address,
+ world_container,
+
+ bots_tx,
+
+ swarm_tx: swarm_tx.clone(),
+
+ run_schedule_sender,
+ };
+ ecs_lock.lock().insert_resource(swarm.clone());
+
+ // SwarmBuilder (self) isn't Send so we have to take all the things we need out
+ // of it
+ let mut swarm_clone = swarm.clone();
+ let join_delay = self.join_delay;
+ let accounts = self.accounts.clone();
+ let states = self.states.clone();
+
+ let join_task = 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, state.clone())
+ .await;
+ tokio::time::sleep(join_delay).await;
+ }
+ } else {
+ // otherwise, join all at once
+ let swarm_borrow = &swarm_clone;
+ join_all(accounts.iter().zip(states).map(
+ async move |(account, state)| -> Result<(), JoinError> {
+ swarm_borrow
+ .clone()
+ .add_with_exponential_backoff(account, state.clone())
+ .await;
+ Ok(())
+ },
+ ))
+ .await;
+ }
+ });
+
+ let swarm_state = self.swarm_state;
+
+ // Watch swarm_rx and send those events to the swarm_handle.
+ let swarm_clone = swarm.clone();
+ tokio::spawn(async move {
+ while let Some(event) = swarm_rx.recv().await {
+ if let Some(swarm_handler) = self.swarm_handler {
+ tokio::spawn((swarm_handler)(
+ swarm_clone.clone(),
+ event,
+ swarm_state.clone(),
+ ));
+ }
+ }
+ });
+
+ // bot events
+ while let Some((Some(event), bot)) = bots_rx.recv().await {
+ if let Some(handler) = self.handler {
+ let state = bot.component::<S>();
+ tokio::spawn((handler)(bot, event, state));
+ }
+ }
+
+ join_task.abort();
+
+ Ok(())
+ }
+}
+
+impl<S, SS, Fut, SwarmFut> Default for SwarmBuilder<S, SS, Fut, SwarmFut>
+where
+ Fut: Future<Output = Result<(), anyhow::Error>> + Send + 'static,
+ SwarmFut: Future<Output = Result<(), anyhow::Error>> + Send + 'static,
+ S: Default + Send + Sync + Clone + Component + 'static,
+ SS: Default + Send + Sync + Clone + Component + 'static,
+{
+ fn default() -> Self {
+ Self::new()
+ }
+}
+
+/// An event about something that doesn't have to do with a single bot.
+#[derive(Clone, Debug)]
+pub enum SwarmEvent {
+ /// All the bots in the swarm have successfully joined the server.
+ Login,
+ /// The swarm was created. This is only fired once, and it's guaranteed to
+ /// be the first event to fire.
+ Init,
+ /// A bot got disconnected from the server.
+ ///
+ /// You can implement an auto-reconnect by calling [`Swarm::add`]
+ /// with the account from this event.
+ Disconnect(Account),
+ /// At least one bot received a chat message.
+ Chat(ChatPacket),
}
+pub type SwarmHandleFn<Fut, SS> = fn(Swarm, SwarmEvent, SS) -> Fut;
+
#[derive(Error, Debug)]
pub enum SwarmStartError {
#[error("Invalid address")]
@@ -154,7 +355,7 @@ pub enum SwarmStartError {
/// let mut states = Vec::new();
///
/// for i in 0..10 {
-/// accounts.push(Account::offline(&format!("bot{}", i)));
+/// accounts.push(Account::offline(&format!("bot{i}")));
/// states.push(State::default());
/// }
///
@@ -204,193 +405,69 @@ pub enum SwarmStartError {
/// }
/// Ok(())
/// }
-pub async fn start_swarm<
- S: Send + Sync + Clone + 'static,
- SS: Send + Sync + Clone + 'static,
- A: Send + TryInto<ServerAddress>,
- Fut: Future<Output = Result<(), anyhow::Error>> + Send + 'static,
- SwarmFut: Future<Output = Result<(), anyhow::Error>> + Send + 'static,
->(
- options: SwarmOptions<S, SS, A, Fut, SwarmFut>,
-) -> Result<(), SwarmStartError> {
- assert_eq!(
- options.accounts.len(),
- options.states.len(),
- "There must be exactly one state per bot."
- );
-
- // convert the TryInto<ServerAddress> into a ServerAddress
- let address: ServerAddress = match options.address.try_into() {
- Ok(address) => address,
- Err(_) => return Err(SwarmStartError::InvalidAddress),
- };
-
- // resolve the address
- let resolved_address = resolver::resolve_address(&address).await?;
-
- let world_container = Arc::new(RwLock::new(WeakWorldContainer::default()));
-
- let mut plugins = options.plugins;
- let swarm_plugins = options.swarm_plugins;
-
- // DEFAULT CLIENT PLUGINS
- plugins.add(bot::Plugin);
- plugins.add(crate::pathfinder::Plugin);
- // DEFAULT SWARM PLUGINS
-
- // we can't modify the swarm plugins after this
- let (bots_tx, mut bots_rx) = mpsc::unbounded_channel();
- let (swarm_tx, mut swarm_rx) = mpsc::unbounded_channel();
-
- let mut swarm = Swarm {
- bot_datas: Arc::new(Mutex::new(Vec::new())),
-
- resolved_address,
- address,
- worlds: world_container,
- plugins,
-
- bots_tx,
-
- swarm_tx: swarm_tx.clone(),
- };
-
- {
- // the chat plugin is hacky and needs the swarm to be passed like this
- let (chat_swarm_state, chat_tx) = chat::SwarmState::new(swarm.clone());
- swarm.plugins.add(chat::Plugin {
- swarm_state: chat_swarm_state,
- tx: chat_tx,
- });
- }
-
- let swarm_plugins = swarm_plugins.build();
-
- let mut swarm_clone = swarm.clone();
- let join_task = tokio::spawn(async move {
- if let Some(join_delay) = options.join_delay {
- // if there's a join delay, then join one by one
- for (account, state) in options.accounts.iter().zip(options.states) {
- swarm_clone
- .add_with_exponential_backoff(account, state.clone())
- .await;
- tokio::time::sleep(join_delay).await;
- }
- } else {
- let swarm_borrow = &swarm_clone;
- join_all(options.accounts.iter().zip(options.states).map(
- async move |(account, state)| -> Result<(), JoinError> {
- swarm_borrow
- .clone()
- .add_with_exponential_backoff(account, state.clone())
- .await;
- Ok(())
- },
- ))
- .await;
- }
- });
-
- let swarm_state = options.swarm_state;
- let mut internal_state = InternalSwarmState::default();
-
- // Watch swarm_rx and send those events to the plugins and swarm_handle.
- let swarm_clone = swarm.clone();
- let swarm_plugins_clone = swarm_plugins.clone();
- tokio::spawn(async move {
- while let Some(event) = swarm_rx.recv().await {
- for plugin in swarm_plugins_clone.clone().into_iter() {
- tokio::spawn(plugin.handle(event.clone(), swarm_clone.clone()));
- }
- tokio::spawn((options.swarm_handle)(
- swarm_clone.clone(),
- event,
- swarm_state.clone(),
- ));
- }
- });
-
- // bot events
- while let Some((Some(event), (bot, state))) = bots_rx.recv().await {
- // bot event handling
- let cloned_plugins = (*bot.plugins).clone();
- for plugin in cloned_plugins.into_iter() {
- tokio::spawn(plugin.handle(event.clone(), bot.clone()));
- }
-
- // swarm event handling
- // remove this #[allow] when more checks are added
- #[allow(clippy::single_match)]
- match &event {
- Event::Login => {
- internal_state.bots_joined += 1;
- if internal_state.bots_joined == swarm.bot_datas.lock().len() {
- swarm_tx.send(SwarmEvent::Login).unwrap();
- }
- }
- _ => {}
- }
-
- tokio::spawn((options.handle)(bot, event, state));
- }
-
- join_task.abort();
-
- Ok(())
-}
-
-impl<S> Swarm<S>
-where
- S: Send + Sync + Clone + 'static,
-{
+// pub async fn start_swarm<
+// S: Send + Sync + Clone + 'static,
+// SS: Send + Sync + Clone + 'static,
+// A: Send + TryInto<ServerAddress>,
+// Fut: Future<Output = Result<(), anyhow::Error>> + Send + 'static,
+// SwarmFut: Future<Output = Result<(), anyhow::Error>> + Send + 'static,
+// >(
+// options: SwarmOptions<S, SS, A, Fut, SwarmFut>,
+// ) -> Result<(), SwarmStartError> {
+// }
+
+impl Swarm {
/// Add a new account to the swarm. You can remove it later by calling
/// [`Client::disconnect`].
- pub async fn add(&mut self, account: &Account, state: S) -> Result<Client, JoinError> {
- let conn = Connection::new(&self.resolved_address).await?;
- let (conn, game_profile) = Client::handshake(conn, account, &self.address.clone()).await?;
-
+ ///
+ /// # Errors
+ ///
+ /// Returns an `Err` if the bot could not do a handshake successfully.
+ pub async fn add<S: Component + Clone>(
+ &mut self,
+ account: &Account,
+ state: S,
+ ) -> Result<Client, JoinError> {
// tx is moved to the bot so it can send us events
// rx is used to receive events from the bot
- let (tx, mut rx) = mpsc::channel(1);
- let mut bot = Client::new(game_profile, conn, Some(self.worlds.clone()));
- tx.send(Event::Init).await.expect("Failed to send event");
- bot.start_tasks(tx);
+ // 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(run_schedule_receiver, run_schedule_sender.clone());
+ let (bot, mut rx) = Client::start_client(
+ self.ecs_lock.clone(),
+ account,
+ &self.address,
+ &self.resolved_address,
+ self.run_schedule_sender.clone(),
+ )
+ .await?;
+ // add the state to the client
+ {
+ let mut ecs = self.ecs_lock.lock();
+ ecs.entity_mut(bot.entity).insert(state);
+ }
- bot.plugins = Arc::new(self.plugins.clone().build());
+ self.bots.lock().insert(bot.entity, bot.clone());
+ let cloned_bots = self.bots.clone();
let cloned_bots_tx = self.bots_tx.clone();
let cloned_bot = bot.clone();
- let cloned_state = state.clone();
let owned_account = account.clone();
- let bot_datas = self.bot_datas.clone();
let swarm_tx = self.swarm_tx.clone();
- // send the init event immediately so it's the first thing we get
- swarm_tx.send(SwarmEvent::Init).unwrap();
tokio::spawn(async move {
while let Some(event) = rx.recv().await {
// we can't handle events here (since we can't copy the handler),
// they're handled above in start_swarm
- if let Err(e) =
- cloned_bots_tx.send((Some(event), (cloned_bot.clone(), cloned_state.clone())))
- {
+ if let Err(e) = cloned_bots_tx.send((Some(event), cloned_bot.clone())) {
error!("Error sending event to swarm: {e}");
}
}
- // the bot disconnected, so we remove it from the swarm
- let mut bot_datas = bot_datas.lock();
- let index = bot_datas
- .iter()
- .position(|(b, _)| b.profile.uuid == cloned_bot.profile.uuid)
- .expect("bot disconnected but not found in swarm");
- bot_datas.remove(index);
-
+ cloned_bots.lock().remove(&bot.entity);
swarm_tx
.send(SwarmEvent::Disconnect(owned_account))
.unwrap();
});
- self.bot_datas.lock().push((bot.clone(), state.clone()));
-
Ok(bot)
}
@@ -399,7 +476,11 @@ where
///
/// 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(&mut self, account: &Account, state: S) -> Client {
+ pub async fn add_with_exponential_backoff<S: Component + Clone>(
+ &mut self,
+ account: &Account,
+ state: S,
+ ) -> Client {
let mut disconnects = 0;
loop {
match self.add(account, state.clone()).await {
@@ -409,7 +490,7 @@ where
let delay = (Duration::from_secs(5) * 2u32.pow(disconnects))
.min(Duration::from_secs(120));
let username = account.username.clone();
- error!("Error joining {username}: {e}. Waiting {delay:?} and trying again.");
+ error!("Error joining as {username}: {e}. Waiting {delay:?} and trying again.");
tokio::time::sleep(delay).await;
}
}
@@ -417,33 +498,42 @@ where
}
}
-impl<S> IntoIterator for Swarm<S>
-where
- S: Send + Sync + Clone + 'static,
-{
- type Item = (Client, S);
+impl IntoIterator for Swarm {
+ type Item = Client;
type IntoIter = std::vec::IntoIter<Self::Item>;
- /// Iterate over the bots and their states in this swarm.
+ /// Iterate over the bots in this swarm.
///
/// ```rust,no_run
- /// for (bot, state) in swarm {
+ /// for bot in swarm {
+ /// let state = bot.component::<State>();
/// // ...
/// }
/// ```
fn into_iter(self) -> Self::IntoIter {
- self.bot_datas.lock().clone().into_iter()
+ self.bots
+ .lock()
+ .clone()
+ .into_values()
+ .collect::<Vec<_>>()
+ .into_iter()
}
}
-#[derive(Default)]
-struct InternalSwarmState {
- /// The number of bots connected to the server
- pub bots_joined: usize,
-}
-
impl From<ConnectionError> 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;
+
+impl PluginGroup for DefaultSwarmPlugins {
+ fn build(self) -> PluginGroupBuilder {
+ PluginGroupBuilder::start::<Self>()
+ .add(chat::SwarmChatPlugin)
+ .add(events::SwarmPlugin)
+ }
+}
diff --git a/azalea/src/swarm/plugins.rs b/azalea/src/swarm/plugins.rs
deleted file mode 100644
index f92d40e6..00000000
--- a/azalea/src/swarm/plugins.rs
+++ /dev/null
@@ -1,135 +0,0 @@
-use crate::{Swarm, SwarmEvent};
-use async_trait::async_trait;
-use nohash_hasher::NoHashHasher;
-use std::{
- any::{Any, TypeId},
- collections::HashMap,
- hash::BuildHasherDefault,
-};
-
-type U64Hasher = BuildHasherDefault<NoHashHasher<u64>>;
-
-// kind of based on https://docs.rs/http/latest/src/http/extensions.rs.html
-/// A map of plugin ids to [`SwarmPlugin`] trait objects. The client stores
-/// this so we can keep the state for our [`Swarm`] plugins.
-///
-/// If you're using azalea, you should generate this from the `swarm_plugins!`
-/// macro.
-#[derive(Clone, Default)]
-pub struct SwarmPlugins<S> {
- map: Option<HashMap<TypeId, Box<dyn SwarmPlugin<S>>, U64Hasher>>,
-}
-
-#[derive(Clone)]
-pub struct SwarmPluginStates<S> {
- map: Option<HashMap<TypeId, Box<dyn SwarmPluginState<S>>, U64Hasher>>,
-}
-
-impl<S> SwarmPluginStates<S> {
- pub fn get<T: SwarmPluginState<S>>(&self) -> Option<&T> {
- self.map
- .as_ref()
- .and_then(|map| map.get(&TypeId::of::<T>()))
- .and_then(|boxed| (boxed.as_ref() as &dyn Any).downcast_ref::<T>())
- }
-}
-
-impl<S> SwarmPlugins<S>
-where
- S: 'static,
-{
- /// Create a new empty set of plugins.
- pub fn new() -> Self {
- Self { map: None }
- }
-
- /// Add a new plugin to this set.
- pub fn add<T: SwarmPlugin<S>>(&mut self, plugin: T) {
- if self.map.is_none() {
- self.map = Some(HashMap::with_hasher(BuildHasherDefault::default()));
- }
- self.map
- .as_mut()
- .unwrap()
- .insert(TypeId::of::<T>(), Box::new(plugin));
- }
-
- /// Build our plugin states from this set of plugins. Note that if you're
- /// using `azalea` you'll probably never need to use this as it's called
- /// for you.
- pub fn build(self) -> SwarmPluginStates<S> {
- if self.map.is_none() {
- return SwarmPluginStates { map: None };
- }
- let mut map = HashMap::with_hasher(BuildHasherDefault::default());
- for (id, plugin) in self.map.unwrap().into_iter() {
- map.insert(id, plugin.build());
- }
- SwarmPluginStates { map: Some(map) }
- }
-}
-
-impl<S> IntoIterator for SwarmPluginStates<S> {
- type Item = Box<dyn SwarmPluginState<S>>;
- type IntoIter = std::vec::IntoIter<Self::Item>;
-
- /// Iterate over the plugin states.
- fn into_iter(self) -> Self::IntoIter {
- self.map
- .map(|map| map.into_values().collect::<Vec<_>>())
- .unwrap_or_default()
- .into_iter()
- }
-}
-
-/// A `SwarmPluginState` keeps the current state of a plugin for a client. All
-/// the fields must be atomic. Unique `SwarmPluginState`s are built from
-/// [`SwarmPlugin`]s.
-#[async_trait]
-pub trait SwarmPluginState<S>: Send + Sync + SwarmPluginStateClone<S> + Any + 'static {
- async fn handle(self: Box<Self>, event: SwarmEvent, swarm: Swarm<S>);
-}
-
-/// Swarm plugins can keep their own personal state ([`SwarmPluginState`]),
-/// listen to [`SwarmEvent`]s, and add new functions to [`Swarm`].
-pub trait SwarmPlugin<S>: Send + Sync + SwarmPluginClone<S> + Any + 'static {
- fn build(&self) -> Box<dyn SwarmPluginState<S>>;
-}
-
-/// An internal trait that allows SwarmPluginState to be cloned.
-#[doc(hidden)]
-pub trait SwarmPluginStateClone<S> {
- fn clone_box(&self) -> Box<dyn SwarmPluginState<S>>;
-}
-impl<T, S> SwarmPluginStateClone<S> for T
-where
- T: 'static + SwarmPluginState<S> + Clone,
-{
- fn clone_box(&self) -> Box<dyn SwarmPluginState<S>> {
- Box::new(self.clone())
- }
-}
-impl<S> Clone for Box<dyn SwarmPluginState<S>> {
- fn clone(&self) -> Self {
- self.clone_box()
- }
-}
-
-/// An internal trait that allows SwarmPlugin to be cloned.
-#[doc(hidden)]
-pub trait SwarmPluginClone<S> {
- fn clone_box(&self) -> Box<dyn SwarmPlugin<S>>;
-}
-impl<T, S> SwarmPluginClone<S> for T
-where
- T: 'static + SwarmPlugin<S> + Clone,
-{
- fn clone_box(&self) -> Box<dyn SwarmPlugin<S>> {
- Box::new(self.clone())
- }
-}
-impl<S> Clone for Box<dyn SwarmPlugin<S>> {
- fn clone(&self) -> Self {
- self.clone_box()
- }
-}
diff --git a/bot/src/main.rs b/bot/src/main.rs
index 1bd407e8..f748de0b 100644
--- a/bot/src/main.rs
+++ b/bot/src/main.rs
@@ -1,14 +1,19 @@
+#![feature(type_alias_impl_trait)]
+
+use azalea::ecs::query::With;
+use azalea::entity::metadata::Player;
+use azalea::entity::Position;
use azalea::pathfinder::BlockPosGoal;
// use azalea::ClientInformation;
-use azalea::{prelude::*, BlockPos, Swarm, SwarmEvent, WalkDirection};
+use azalea::{prelude::*, BlockPos, GameProfileComponent, Swarm, SwarmEvent, WalkDirection};
use azalea::{Account, Client, Event};
use azalea_protocol::packets::game::serverbound_client_command_packet::ServerboundClientCommandPacket;
use std::time::Duration;
-#[derive(Default, Clone)]
+#[derive(Default, Clone, Component)]
struct State {}
-#[derive(Default, Clone)]
+#[derive(Default, Clone, Component)]
struct SwarmState {}
#[tokio::main]
@@ -48,23 +53,17 @@ async fn main() -> anyhow::Result<()> {
}
loop {
- let e = azalea::start_swarm(azalea::SwarmOptions {
- accounts: accounts.clone(),
- address: "localhost",
-
- states: states.clone(),
- swarm_state: SwarmState::default(),
-
- plugins: plugins![],
- swarm_plugins: swarm_plugins![],
-
- handle,
- swarm_handle,
-
- join_delay: Some(Duration::from_millis(1000)),
- // join_delay: None,
- })
- .await;
+ let e = azalea::SwarmBuilder::new()
+ .add_accounts(accounts.clone())
+ .set_handler(handle)
+ .set_swarm_handler(swarm_handle)
+ .join_delay(Duration::from_millis(1000))
+ .start("localhost")
+ .await;
+ // let e = azalea::ClientBuilder::new()
+ // .set_handler(handle)
+ // .start(Account::offline("bot"), "localhost")
+ // .await;
println!("{e:?}");
}
}
@@ -72,6 +71,7 @@ async fn main() -> anyhow::Result<()> {
async fn handle(mut bot: Client, event: Event, _state: State) -> anyhow::Result<()> {
match event {
Event::Init => {
+ println!("bot init");
// bot.set_client_information(ClientInformation {
// view_distance: 2,
// ..Default::default()
@@ -79,43 +79,76 @@ async fn handle(mut bot: Client, event: Event, _state: State) -> anyhow::Result<
// .await?;
}
Event::Login => {
- bot.chat("Hello world").await?;
+ bot.chat("Hello world");
}
Event::Chat(m) => {
+ println!("client chat message: {}", m.content());
if m.content() == bot.profile.name {
- bot.chat("Bye").await?;
+ bot.chat("Bye");
tokio::time::sleep(Duration::from_millis(50)).await;
- bot.disconnect().await?;
+ bot.disconnect();
}
- let entity = bot
- .world()
- .entity_by_uuid(&uuid::uuid!("6536bfed-8695-48fd-83a1-ecd24cf2a0fd"));
+ let Some(sender) = m.username() else {
+ return Ok(())
+ };
+ // let mut ecs = bot.ecs.lock();
+ // let entity = bot
+ // .ecs
+ // .lock()
+ // .query::<&Player>()
+ // .iter(&mut ecs)
+ // .find(|e| e.name() == Some(sender));
+ // let entity = bot.entity_by::<With<Player>>(|name: &Name| name == sender);
+ let entity = bot.entity_by::<With<Player>, (&GameProfileComponent,)>(
+ |profile: &&GameProfileComponent| {
+ println!("entity {profile:?}");
+ profile.name == sender
+ },
+ );
+ println!("sender entity: {entity:?}");
if let Some(entity) = entity {
- if m.content() == "goto" {
- let target_pos_vec3 = entity.pos();
- let target_pos: BlockPos = target_pos_vec3.into();
- bot.goto(BlockPosGoal::from(target_pos));
- } else if m.content() == "look" {
- let target_pos_vec3 = entity.pos();
- let target_pos: BlockPos = target_pos_vec3.into();
- println!("target_pos: {target_pos:?}");
- bot.look_at(&target_pos.center());
- } else if m.content() == "jump" {
- bot.set_jumping(true);
- } else if m.content() == "walk" {
- bot.walk(WalkDirection::Forward);
- } else if m.content() == "stop" {
- bot.set_jumping(false);
- bot.walk(WalkDirection::None);
- } else if m.content() == "lag" {
- std::thread::sleep(Duration::from_millis(1000));
+ match m.content().as_str() {
+ "whereami" => {
+ let pos = bot.entity_component::<Position>(entity);
+ bot.chat(&format!("You're at {pos:?}",));
+ }
+ "whereareyou" => {
+ let pos = bot.component::<Position>();
+ bot.chat(&format!("I'm at {pos:?}",));
+ }
+ "goto" => {
+ let entity_pos = bot.entity_component::<Position>(entity);
+ let target_pos: BlockPos = entity_pos.into();
+ println!("going to {target_pos:?}");
+ bot.goto(BlockPosGoal::from(target_pos));
+ }
+ "look" => {
+ let entity_pos = bot.entity_component::<Position>(entity);
+ let target_pos: BlockPos = entity_pos.into();
+ println!("target_pos: {target_pos:?}");
+ bot.look_at(target_pos.center());
+ }
+ "jump" => {
+ bot.set_jumping(true);
+ }
+ "walk" => {
+ bot.walk(WalkDirection::Forward);
+ }
+ "stop" => {
+ bot.set_jumping(false);
+ bot.walk(WalkDirection::None);
+ }
+ "lag" => {
+ std::thread::sleep(Duration::from_millis(1000));
+ }
+ _ => {}
}
}
}
Event::Death(_) => {
bot.write_packet(ServerboundClientCommandPacket {
action: azalea_protocol::packets::game::serverbound_client_command_packet::Action::PerformRespawn,
- }.get()).await?;
+ }.get());
}
_ => {}
}
@@ -124,7 +157,7 @@ async fn handle(mut bot: Client, event: Event, _state: State) -> anyhow::Result<
}
async fn swarm_handle(
- mut swarm: Swarm<State>,
+ mut swarm: Swarm,
event: SwarmEvent,
_state: SwarmState,
) -> anyhow::Result<()> {
@@ -137,10 +170,10 @@ async fn swarm_handle(
SwarmEvent::Chat(m) => {
println!("swarm chat message: {}", m.message().to_ansi());
if m.message().to_string() == "<py5> world" {
- for (name, world) in &swarm.worlds.read().worlds {
+ for (name, world) in &swarm.world_container.read().worlds {
println!("world name: {name}");
if let Some(w) = world.upgrade() {
- for chunk_pos in w.chunk_storage.read().chunks.values() {
+ for chunk_pos in w.read().chunks.chunks.values() {
println!("chunk: {chunk_pos:?}");
}
} else {
@@ -149,8 +182,8 @@ async fn swarm_handle(
}
}
if m.message().to_string() == "<py5> hi" {
- for (bot, _) in swarm {
- bot.chat("hello").await?;
+ for bot in swarm {
+ bot.chat("hello");
}
}
}
diff --git a/codegen/lib/code/entity.py b/codegen/lib/code/entity.py
index 6616c8d7..844793f6 100644
--- a/codegen/lib/code/entity.py
+++ b/codegen/lib/code/entity.py
@@ -15,8 +15,8 @@ def generate_entity_metadata(burger_entity_data: dict, mappings: Mappings):
{'name': 'Long', 'type': 'i64'},
{'name': 'Float', 'type': 'f32'},
{'name': 'String', 'type': 'String'},
- {'name': 'Component', 'type': 'Component'},
- {'name': 'OptionalComponent', 'type': 'Option<Component>'},
+ {'name': 'FormattedText', 'type': 'FormattedText'},
+ {'name': 'OptionalFormattedText', 'type': 'Option<FormattedText>'},
{'name': 'ItemStack', 'type': 'Slot'},
{'name': 'Boolean', 'type': 'bool'},
{'name': 'Rotations', 'type': 'Rotations'},
@@ -37,313 +37,426 @@ def generate_entity_metadata(burger_entity_data: dict, mappings: Mappings):
]
code = []
- code.append('// This file is generated from codegen/lib/code/entity.py.')
- code.append("// Don't change it manually!")
- code.append('')
- code.append('#![allow(clippy::clone_on_copy, clippy::derivable_impls)]')
- code.append(
- 'use super::{EntityDataValue, Rotations, VillagerData, OptionalUnsignedInt, Pose};')
- code.append('use azalea_block::BlockState;')
- code.append('use azalea_chat::Component;')
- code.append('use azalea_core::{BlockPos, Direction, Particle, Slot};')
- code.append('use std::{collections::VecDeque, ops::{Deref, DerefMut}};')
- code.append('use uuid::Uuid;')
- code.append('')
-
- entity_structs = []
-
- parent_field_name = None
- for entity_id in burger_entity_data:
- entity_parents = get_entity_parents(entity_id, burger_entity_data)
- entity_metadata = get_entity_metadata(entity_id, burger_entity_data)
- entity_metadata_names = get_entity_metadata_names(
- entity_id, burger_entity_data, mappings)
-
- struct_name: str = upper_first_letter(
- to_camel_case(entity_parents[0].replace('~', '')))
- parent_struct_name: Optional[str] = upper_first_letter(to_camel_case(
- entity_parents[1].replace('~', ''))) if (len(entity_parents) >= 2) else None
- if parent_struct_name:
- parent_field_name = to_snake_case(parent_struct_name)
- if not entity_parents[0].startswith('~'):
- entity_structs.append(struct_name)
+ code.append('''#![allow(clippy::single_match)]
+
+// This file is generated from codegen/lib/code/entity.py.
+// Don't change it manually!
+
+use super::{EntityDataItem, EntityDataValue, OptionalUnsignedInt, Pose, Rotations, VillagerData};
+use azalea_block::BlockState;
+use azalea_chat::FormattedText;
+use azalea_core::{BlockPos, Direction, Particle, Slot};
+use azalea_ecs::{bundle::Bundle, component::Component};
+use derive_more::{Deref, DerefMut};
+use thiserror::Error;
+use uuid::Uuid;
+
+#[derive(Error, Debug)]
+pub enum UpdateMetadataError {
+ #[error("Wrong type ({0:?})")]
+ WrongType(EntityDataValue),
+}
+impl From<EntityDataValue> for UpdateMetadataError {
+ fn from(value: EntityDataValue) -> Self {
+ Self::WrongType(value)
+ }
+}
+''')
+
+ # types that are only ever used in one entity
+ single_use_imported_types = {'particle', 'pose'}
+
+ added_metadata_fields = set()
+
+ # a dict of { entity_id: { field_name: new_name } }
+ field_name_map = {}
+
+ # build the duplicate_field_names set
+ previous_field_names = set()
+ duplicate_field_names = set()
+ for entity_id in burger_entity_data.keys():
+ field_name_map[entity_id] = {}
+ for field_name_or_bitfield in get_entity_metadata_names(entity_id, burger_entity_data, mappings).values():
+ if isinstance(field_name_or_bitfield, str):
+ if field_name_or_bitfield in previous_field_names:
+ duplicate_field_names.add(field_name_or_bitfield)
+ else:
+ previous_field_names.add(field_name_or_bitfield)
+ else:
+ for mask, name in field_name_or_bitfield.items():
+ if name in previous_field_names:
+ duplicate_field_names.add(name)
+ else:
+ previous_field_names.add(name)
+
+ # oh and also just add the entity id to the duplicate field names to
+ # make sure entity names don't clash with field names
+ duplicate_field_names.add(entity_id)
+
+ # make sure these types are only ever made once
+ for name in single_use_imported_types:
+ if name in duplicate_field_names:
+ raise Exception(f'{name} should only exist once')
+
+ # and now figure out what to rename them to
+ for entity_id in burger_entity_data.keys():
+ for index, field_name_or_bitfield in get_entity_metadata_names(entity_id, burger_entity_data, mappings).items():
+ if isinstance(field_name_or_bitfield, str):
+ new_field_name = field_name_or_bitfield
+ if new_field_name == 'type':
+ new_field_name = 'kind'
+ if field_name_or_bitfield in duplicate_field_names:
+ field_name_map[entity_id][
+ field_name_or_bitfield] = f'{entity_id.strip("~")}_{new_field_name}'
+ else:
+ for mask, name in field_name_or_bitfield.items():
+ new_field_name = name
+ if new_field_name == 'type':
+ new_field_name = 'kind'
+ if name in duplicate_field_names:
+ field_name_map[entity_id][name] = f'{entity_id.strip("~")}_{new_field_name}'
+
+ def new_entity(entity_id: str):
+ # note: fields are components
+
+ # if it doesn't start with ~ then also make a marker struct and Query struct for it
+ all_field_names_or_bitfields = []
+ entity_ids_for_all_field_names_or_bitfields = []
+ entity_metadatas = []
+
+ def maybe_rename_field(name: str, index: int) -> str:
+ if name in field_name_map[entity_ids_for_all_field_names_or_bitfields[index]]:
+ return field_name_map[entity_ids_for_all_field_names_or_bitfields[index]][name]
+ return name
+
+ parents = get_entity_parents(entity_id, burger_entity_data)
+ for parent_id in list(reversed(parents)):
+ for index, name_or_bitfield in get_entity_metadata_names(parent_id, burger_entity_data, mappings).items():
+ assert index == len(all_field_names_or_bitfields)
+ all_field_names_or_bitfields.append(name_or_bitfield)
+ entity_ids_for_all_field_names_or_bitfields.append(parent_id)
+ entity_metadatas.extend(get_entity_metadata(
+ parent_id, burger_entity_data))
+ parent_id = parents[1] if len(parents) > 1 else None
+
+ # now add all the fields/component structs
+ for index, name_or_bitfield in enumerate(all_field_names_or_bitfields):
+ # make sure we only ever make these structs once
+ hashable_name_or_bitfield = str(
+ name_or_bitfield) + entity_ids_for_all_field_names_or_bitfields[index]
+ if hashable_name_or_bitfield in added_metadata_fields:
+ continue
+ added_metadata_fields.add(hashable_name_or_bitfield)
- reader_code = []
- writer_code = []
- set_index_code = []
- field_names = []
+ if isinstance(name_or_bitfield, str):
+ # we just use the imported type instead of making our own
+ if name_or_bitfield in single_use_imported_types:
+ continue
- code.append(f'#[derive(Debug, Clone)]')
- code.append(f'pub struct {struct_name} {{')
+ name_or_bitfield = maybe_rename_field(name_or_bitfield, index)
- if parent_struct_name:
- assert parent_field_name
- code.append(f'pub {parent_field_name}: {parent_struct_name},')
- reader_code.append(
- f'let {parent_field_name} = {parent_struct_name}::read(metadata)?;')
- writer_code.append(
- f'metadata.extend(self.{parent_field_name}.write());')
- for index, name_or_bitfield in entity_metadata_names.items():
- if isinstance(name_or_bitfield, str):
- # normal field (can be any type)
- name = name_or_bitfield
- if name == 'type':
- name = 'kind'
- field_names.append(name)
- type_id = next(filter(lambda i: i['index'] == index, entity_metadata))[
+ struct_name = upper_first_letter(
+ to_camel_case(name_or_bitfield))
+ type_id = next(filter(lambda i: i['index'] == index, entity_metadatas))[
'type_id']
metadata_type_data = metadata_types[type_id]
rust_type = metadata_type_data['type']
- type_name = metadata_type_data['name']
- code.append(f'pub {name}: {rust_type},')
- type_name_field = to_snake_case(type_name)
- reader_code.append(
- f'let {name} = metadata.pop_front()?.into_{type_name_field}().ok()?;')
- writer_code.append(
- f'metadata.push(EntityDataValue::{type_name}(self.{name}.clone()));')
-
- # 1 => self.dancing = value.into_boolean().ok()?,
- set_index_code.append(
- f'{index} => self.{name} = value.into_{type_name_field}().ok()?,'
- )
+ code.append(f'#[derive(Component, Deref, DerefMut)]')
+ code.append(f'pub struct {struct_name}(pub {rust_type});')
else:
- # bitfield (sent as a byte, each bit in the byte is used as a boolean)
- reader_code.append(
- 'let bitfield = metadata.pop_front()?.into_byte().ok()?;')
- writer_code.append('let mut bitfield = 0u8;')
- set_index_code.append(f'{index} => {{')
- set_index_code.append(
- f'let bitfield = value.into_byte().ok()?;')
+ # if it's a bitfield just make a struct for each bit
for mask, name in name_or_bitfield.items():
- if name == 'type':
- name = 'kind'
-
- field_names.append(name)
- code.append(f'pub {name}: bool,')
- reader_code.append(f'let {name} = bitfield & {mask} != 0;')
- writer_code.append(
- f'if self.{name} {{ bitfield &= {mask}; }}')
- set_index_code.append(
- f'self.{name} = bitfield & {mask} != 0;')
- writer_code.append(
- 'metadata.push(EntityDataValue::Byte(bitfield));')
- set_index_code.append('},')
+ name = maybe_rename_field(name, index)
+ struct_name = upper_first_letter(to_camel_case(name))
+ code.append(f'#[derive(Component, Deref, DerefMut)]')
+ code.append(f'pub struct {struct_name}(pub bool);')
- code.append('}')
- code.append('')
+ # add the entity struct and Bundle struct
+ struct_name: str = upper_first_letter(
+ to_camel_case(entity_id.lstrip('~')))
+ code.append(f'#[derive(Component)]')
+ code.append(f'pub struct {struct_name};')
- code.append(f'impl {struct_name} {{')
+ parent_struct_name = upper_first_letter(
+ to_camel_case(parent_id.lstrip("~"))) if parent_id else None
+ # impl Allay {
+ # pub fn apply_metadata(
+ # entity: &mut azalea_ecs::system::EntityCommands,
+ # d: EntityDataItem,
+ # ) -> Result<(), UpdateMetadataError> {
+ # match d.index {
+ # 0..=15 => AbstractCreatureBundle::apply_metadata(entity, d)?,
+ # 16 => entity.insert(Dancing(d.value.into_boolean()?)),
+ # 17 => entity.insert(CanDuplicate(d.value.into_boolean()?)),
+ # }
+ # Ok(())
+ # }
+ # }
+ code.append(f'impl {struct_name} {{')
code.append(
- 'pub fn read(metadata: &mut VecDeque<EntityDataValue>) -> Option<Self> {')
- code.extend(reader_code)
-
- self_args = []
- if parent_struct_name:
- self_args.append(
- f'{parent_field_name}')
- self_args.extend(field_names)
- code.append(f'Some(Self {{ {",".join(self_args)} }})')
- code.append('}')
- code.append('')
+ f' pub fn apply_metadata(entity: &mut azalea_ecs::system::EntityCommands, d: EntityDataItem) -> Result<(), UpdateMetadataError> {{')
+ code.append(f' match d.index {{')
+
+ parent_last_index = -1
+ for index, name_or_bitfield in enumerate(all_field_names_or_bitfields):
+ is_from_parent = entity_ids_for_all_field_names_or_bitfields[index] != entity_id
+ if is_from_parent:
+ parent_last_index = index
+ if parent_last_index != -1:
+ code.append(
+ f' 0..={parent_last_index} => {parent_struct_name}::apply_metadata(entity, d)?,')
- code.append('pub fn write(&self) -> Vec<EntityDataValue> {')
- code.append('let mut metadata = Vec::new();')
- code.extend(writer_code)
- code.append('metadata')
- code.append('}')
+ for index, name_or_bitfield in enumerate(all_field_names_or_bitfields):
+ if index <= parent_last_index:
+ continue
+ if isinstance(name_or_bitfield, str):
+ name_or_bitfield = maybe_rename_field(
+ name_or_bitfield, index)
- code.append('}')
- code.append('')
+ field_struct_name = upper_first_letter(
+ to_camel_case(name_or_bitfield))
+ if name_or_bitfield in single_use_imported_types:
+ field_struct_name = ''
- # default
- code.append(f'impl Default for {struct_name} {{')
- code.append('fn default() -> Self {')
- default_fields_code = []
- if parent_struct_name:
- assert parent_field_name
- default_fields_code.append(
- f'{parent_field_name}: Default::default()')
- for index, name_or_bitfield in entity_metadata_names.items():
- default = next(filter(lambda i: i['index'] == index, entity_metadata)).get(
- 'default', 'Default::default()')
- if isinstance(name_or_bitfield, str):
- type_id = next(filter(lambda i: i['index'] == index, entity_metadata))[
+ type_id = next(filter(lambda i: i['index'] == index, entity_metadatas))[
'type_id']
metadata_type_data = metadata_types[type_id]
+ rust_type = metadata_type_data['type']
type_name = metadata_type_data['name']
- # TODO: burger doesn't get the default if it's a complex type
- # like `Rotations`, so entities like armor stands will have the
- # wrong default metadatas. This should be added to Burger.
- if default is None:
- # some types don't have Default implemented
- if type_name == 'CompoundTag':
- default = 'azalea_nbt::Tag::Compound(Default::default())'
- elif type_name == 'CatVariant':
- default = 'azalea_registry::CatVariant::Tabby'
- elif type_name == 'PaintingVariant':
- default = 'azalea_registry::PaintingVariant::Kebab'
- elif type_name == 'FrogVariant':
- default = 'azalea_registry::FrogVariant::Temperate'
- elif type_name == 'VillagerData':
- default = 'VillagerData { kind: azalea_registry::VillagerType::Plains, profession: azalea_registry::VillagerProfession::None, level: 0 }'
- else:
- default = 'Default::default()'
- else:
- if type_name == 'Boolean':
- default = 'true' if default else 'false'
- elif type_name == 'String':
- string_escaped = default.replace('"', '\\"')
- default = f'"{string_escaped}".to_string()'
- elif type_name == 'BlockPos':
- default = f'BlockPos::new{default}'
- elif type_name == 'OptionalBlockPos': # Option<BlockPos>
- default = f'Some(BlockPos::new{default})' if default != 'Empty' else 'None'
- elif type_name == 'OptionalUuid':
- default = f'Some(uuid::uuid!({default}))' if default != 'Empty' else 'None'
- elif type_name == 'OptionalUnsignedInt':
- default = f'OptionalUnsignedInt(Some({default}))' if default != 'Empty' else 'OptionalUnsignedInt(None)'
- elif type_name == 'ItemStack':
- default = f'Slot::Present({default})' if default != 'Empty' else 'Slot::Empty'
- elif type_name == 'BlockState':
- default = f'{default}' if default != 'Empty' else 'BlockState::Air'
- elif type_name == 'OptionalComponent':
- default = f'Some({default})' if default != 'Empty' else 'None'
- elif type_name == 'CompoundTag':
- default = f'azalea_nbt::Tag::Compound({default})' if default != 'Empty' else 'azalea_nbt::Tag::Compound(Default::default())'
-
- print(default, name_or_bitfield, type_name)
- name = name_or_bitfield
- if name == 'type':
- name = 'kind'
- default_fields_code.append(f'{name}: {default}')
+ type_name_field = to_snake_case(type_name)
+ read_field_code = f'{field_struct_name}(d.value.into_{type_name_field}()?)' if field_struct_name else f'd.value.into_{type_name_field}()?'
+ code.append(
+ f' {index} => {{ entity.insert({read_field_code}); }},')
else:
- # if it's a bitfield, we'll have to extract the default for
- # each bool from each bit in the default
+ code.append(f' {index} => {{')
+ code.append(
+ f'let bitfield = d.value.into_byte()?;')
for mask, name in name_or_bitfield.items():
- if name == 'type':
- name = 'kind'
- mask = int(mask, 0)
- field_names.append(name)
- bit_default = 'true' if (default & mask != 0) else 'false'
- default_fields_code.append(f'{name}: {bit_default}')
-
- # Self { abstract_creature: Default::default(), dancing: Default::default(), can_duplicate: Default::default() }
- code.append(f'Self {{ {", ".join(default_fields_code)} }}')
- code.append('}')
+ name = maybe_rename_field(name, index)
+ field_struct_name = upper_first_letter(to_camel_case(name))
+
+ code.append(
+ f'entity.insert({field_struct_name}(bitfield & {mask} != 0));')
+ code.append(' },')
+ code.append(' _ => {}')
+ code.append(' }')
+ code.append(' Ok(())')
+ code.append(' }')
code.append('}')
code.append('')
- # impl Allay {
- # pub fn set_index(&mut self, index: u8, value: EntityDataValue) -> Option<()> {
- # match index {
- # 0..=0 => self.abstract_creature.set_index(index, value),
- # 1 => self.dancing = value.into_boolean().ok()?,
- # 2 => self.can_duplicate = value.into_boolean().ok()?,
- # _ => {}
- # }
- # Some(())
- # }
+ # #[derive(Bundle)]
+ # struct AllayBundle {
+ # health: Health,
+ # ...
+ # dancing: Dancing,
+ # can_duplicate: CanDuplicate,
# }
- code.append(f'impl {struct_name} {{')
+ bundle_struct_name = f'{struct_name}MetadataBundle'
+ code.append(f'')
+ code.append(f'#[derive(Bundle)]')
+ code.append(f'pub struct {bundle_struct_name} {{')
code.append(
- 'pub fn set_index(&mut self, index: u8, value: EntityDataValue) -> Option<()> {')
- if len(entity_metadata_names) > 0:
- code.append('match index {')
- # get the smallest index for this entity
- smallest_index = min(entity_metadata_names.keys())
- if parent_struct_name:
- code.append(
- f'0..={smallest_index-1} => self.{parent_field_name}.set_index(index, value)?,')
- code.extend(set_index_code)
- code.append('_ => {}')
- code.append('}')
- code.append('Some(())')
- elif parent_struct_name:
- code.append(f'self.{parent_field_name}.set_index(index, value)')
- else:
- code.append('Some(())')
- code.append('}')
- code.append('}')
-
- # deref
+ f' _marker: {struct_name},')
if parent_struct_name:
- code.append(f'impl Deref for {struct_name} {{')
- code.append(f'type Target = {parent_struct_name};')
code.append(
- f'fn deref(&self) -> &Self::Target {{ &self.{parent_field_name} }}')
- code.append('}')
- code.append(f'impl DerefMut for {struct_name} {{')
- code.append(
- f'fn deref_mut(&mut self) -> &mut Self::Target {{ &mut self.{parent_field_name} }}')
- code.append('}')
- code.append('')
-
- # make the EntityMetadata enum from entity_structs
- code.append(f'#[derive(Debug, Clone)]')
- code.append('pub enum EntityMetadata {')
- for struct_name in entity_structs:
- code.append(f'{struct_name}({struct_name}),')
- code.append('}')
- code.append('')
+ f' parent: {parent_struct_name}MetadataBundle,')
+ for index, name_or_bitfield in get_entity_metadata_names(entity_id, burger_entity_data, mappings).items():
+ if isinstance(name_or_bitfield, str):
+ name_or_bitfield = maybe_rename_field(
+ name_or_bitfield, index)
+ struct_name = upper_first_letter(
+ to_camel_case(name_or_bitfield))
+ code.append(
+ f' {name_or_bitfield}: {struct_name},')
+ else:
+ for mask, name in name_or_bitfield.items():
+ name = maybe_rename_field(name, index)
+
+ struct_name = upper_first_letter(to_camel_case(name))
+ code.append(f' {name}: {struct_name},')
+ code.append('}')
- # impl From<azalea_registry::EntityType> for EntityMetadata {
- code.append('impl From<azalea_registry::EntityType> for EntityMetadata {')
- code.append('fn from(value: azalea_registry::EntityType) -> Self {')
- code.append('match value {')
- # azalea_registry::EntityType::Allay => EntityMetadata::Allay(Allay::default()),
- for struct_name in entity_structs:
+ # impl Default for AllayBundle {
+ # fn default() -> Self {
+ # Self {
+ # _marker: Allay,
+ # parent: AbstractCreatureBundle {
+ # on_fire: OnFire(false),
+ # shift_key_down: ShiftKeyDown(false),
+ # },
+ # sprinting: Sprinting(false),
+ # swimming: Swimming(false)
+ # }
+ # }
+ # }
+ code.append(f'impl Default for {bundle_struct_name} {{')
code.append(
- f'azalea_registry::EntityType::{struct_name} => EntityMetadata::{struct_name}({struct_name}::default()),')
- code.append('}')
- code.append('}')
- code.append('}')
- code.append('')
+ ' fn default() -> Self {')
- # impl EntityMetadata
- # pub fn set_index(&mut self, index: u8, value: EntityDataValue)
- code.append('impl EntityMetadata {')
+ def generate_fields(this_entity_id: str):
+ # on_fire: OnFire(false),
+ # shift_key_down: ShiftKeyDown(false),
+
+ # _marker
+ this_entity_struct_name = upper_first_letter(
+ to_camel_case(this_entity_id.lstrip('~')))
+ code.append(
+ f' _marker: {this_entity_struct_name},')
+
+ # if it has a parent, put it (do recursion)
+ # parent: AbstractCreatureBundle { ... },
+ this_entity_parent_ids = get_entity_parents(
+ this_entity_id, burger_entity_data)
+ this_entity_parent_id = this_entity_parent_ids[1] if len(
+ this_entity_parent_ids) > 1 else None
+ if this_entity_parent_id:
+ bundle_struct_name = upper_first_letter(
+ to_camel_case(this_entity_parent_id.lstrip('~'))) + 'MetadataBundle'
+ code.append(
+ f' parent: {bundle_struct_name} {{')
+ generate_fields(this_entity_parent_id)
+ code.append(
+ ' },')
+
+ for index, name_or_bitfield in get_entity_metadata_names(this_entity_id, burger_entity_data, mappings).items():
+ default = next(filter(lambda i: i['index'] == index, entity_metadatas)).get(
+ 'default', 'Default::default()')
+ if isinstance(name_or_bitfield, str):
+ type_id = next(filter(lambda i: i['index'] == index, entity_metadatas))[
+ 'type_id']
+ metadata_type_data = metadata_types[type_id]
+ type_name = metadata_type_data['name']
+
+ name = maybe_rename_field(name_or_bitfield, index)
+
+ # TODO: burger doesn't get the default if it's a complex type
+ # like `Rotations`, so entities like armor stands will have the
+ # wrong default metadatas. This should be added to Burger.
+ if default is None:
+ # some types don't have Default implemented
+ if type_name == 'CompoundTag':
+ default = 'azalea_nbt::Tag::Compound(Default::default())'
+ elif type_name == 'CatVariant':
+ default = 'azalea_registry::CatVariant::Tabby'
+ elif type_name == 'PaintingVariant':
+ default = 'azalea_registry::PaintingVariant::Kebab'
+ elif type_name == 'FrogVariant':
+ default = 'azalea_registry::FrogVariant::Temperate'
+ elif type_name == 'VillagerData':
+ default = 'VillagerData { kind: azalea_registry::VillagerKind::Plains, profession: azalea_registry::VillagerProfession::None, level: 0 }'
+ else:
+ default = f'{type_name}::default()' if name in single_use_imported_types else 'Default::default()'
+ else:
+ if type_name == 'Boolean':
+ default = 'true' if default else 'false'
+ elif type_name == 'String':
+ string_escaped = default.replace('"', '\\"')
+ default = f'"{string_escaped}".to_string()'
+ elif type_name == 'BlockPos':
+ default = f'BlockPos::new{default}'
+ elif type_name == 'OptionalBlockPos': # Option<BlockPos>
+ default = f'Some(BlockPos::new{default})' if default != 'Empty' else 'None'
+ elif type_name == 'OptionalUuid':
+ default = f'Some(uuid::uuid!({default}))' if default != 'Empty' else 'None'
+ elif type_name == 'OptionalUnsignedInt':
+ default = f'OptionalUnsignedInt(Some({default}))' if default != 'Empty' else 'OptionalUnsignedInt(None)'
+ elif type_name == 'ItemStack':
+ default = f'Slot::Present({default})' if default != 'Empty' else 'Slot::Empty'
+ elif type_name == 'BlockState':
+ default = f'{default}' if default != 'Empty' else 'BlockState::Air'
+ elif type_name == 'OptionalFormattedText':
+ default = f'Some({default})' if default != 'Empty' else 'None'
+ elif type_name == 'CompoundTag':
+ default = f'azalea_nbt::Tag::Compound({default})' if default != 'Empty' else 'azalea_nbt::Tag::Compound(Default::default())'
+ if name in single_use_imported_types:
+ code.append(f' {name}: {default},')
+ else:
+ code.append(
+ f' {name}: {upper_first_letter(to_camel_case(name))}({default}),')
+ else:
+ # if it's a bitfield, we'll have to extract the default for
+ # each bool from each bit in the default
+ for mask, name in name_or_bitfield.items():
+ name = maybe_rename_field(name, index)
+ mask = int(mask, 0)
+ bit_default = 'true' if (
+ default & mask != 0) else 'false'
+ code.append(
+ f' {name}: {upper_first_letter(to_camel_case(name))}({bit_default}),')
+ code.append(' Self {')
+ generate_fields(entity_id)
+ code.append(' }')
+ code.append(' }')
+ code.append('}')
+ code.append('')
+
+ # parent_field_name = None
+ for entity_id in burger_entity_data:
+ new_entity(entity_id)
+
+ # and now make the main apply_metadata
+ # pub fn apply_metadata(
+ # entity: &mut azalea_ecs::system::EntityCommands,
+ # items: Vec<EntityDataItem>,
+ # ) -> Result<(), UpdateMetadataError> {
+ # if entity.contains::<Allay>() {
+ # for d in items {
+ # Allay::apply_metadata(entity, d)?;
+ # }
+ # return Ok(());
+ # }
+ #
+ # Ok(())
+ # }
code.append(
- 'pub fn set_index(&mut self, index: u8, value: EntityDataValue) -> Option<()> {')
- code.append('match self {')
- # EntityMetadata::Allay(allay) => allay.set_index(index, value),
- for struct_name in entity_structs:
+ f'''pub fn apply_metadata(
+ entity: &mut azalea_ecs::system::EntityCommands,
+ entity_kind: azalea_registry::EntityKind,
+ items: Vec<EntityDataItem>,
+) -> Result<(), UpdateMetadataError> {{
+ match entity_kind {{''')
+ for entity_id in burger_entity_data:
+ if entity_id.startswith('~'):
+ # not actually an entity
+ continue
+ struct_name: str = upper_first_letter(to_camel_case(entity_id))
code.append(
- f'EntityMetadata::{struct_name}(entity) => entity.set_index(index, value),')
- code.append('}')
- code.append('}')
+ f' azalea_registry::EntityKind::{struct_name} => {{')
+ code.append(' for d in items {')
+ code.append(
+ f' {struct_name}::apply_metadata(entity, d)?;')
+ code.append(' }')
+ code.append(' },')
+ code.append(' }')
+ code.append(' Ok(())')
code.append('}')
code.append('')
- # impl Deref for EntityMetadata {
- # type Target = AbstractEntity;
- # fn deref(&self) -> &Self::Target {
- # match self {
- # EntityMetadata::Allay(entity) => entity,
- # _ => {}
+ # pub fn apply_default_metadata(entity: &mut azalea_ecs::system::EntityCommands, kind: azalea_registry::EntityKind) {
+ # match kind {
+ # azalea_registry::EntityKind::AreaEffectCloud => {
+ # entity.insert(AreaEffectCloudMetadataBundle::default());
# }
# }
# }
- code.append('impl Deref for EntityMetadata {')
- code.append('type Target = AbstractEntity;')
- code.append('fn deref(&self) -> &Self::Target {')
- code.append('match self {')
- for struct_name in entity_structs:
+ code.append(
+ 'pub fn apply_default_metadata(entity: &mut azalea_ecs::system::EntityCommands, kind: azalea_registry::EntityKind) {')
+ code.append(' match kind {')
+ for entity_id in burger_entity_data:
+ if entity_id.startswith('~'):
+ # not actually an entity
+ continue
+ struct_name: str = upper_first_letter(to_camel_case(entity_id))
code.append(
- f'EntityMetadata::{struct_name}(entity) => entity,')
- code.append('}')
- code.append('}')
- code.append('}')
- code.append('impl DerefMut for EntityMetadata {')
- code.append('fn deref_mut(&mut self) -> &mut Self::Target {')
- code.append('match self {')
- for struct_name in entity_structs:
+ f' azalea_registry::EntityKind::{struct_name} => {{')
code.append(
- f'EntityMetadata::{struct_name}(entity) => entity,')
- code.append('}')
- code.append('}')
+ f' entity.insert({struct_name}MetadataBundle::default());')
+ code.append(' },')
+ code.append(' }')
code.append('}')
code.append('')
@@ -378,6 +491,8 @@ def get_entity_metadata(entity_id: str, burger_entity_data: dict):
})
return entity_useful_metadata
+# returns a dict of {index: (name or bitfield)}
+
def get_entity_metadata_names(entity_id: str, burger_entity_data: dict, mappings: Mappings):
entity_metadata = burger_entity_data[entity_id]['metadata']
diff --git a/codegen/lib/code/registry.py b/codegen/lib/code/registry.py
index 86f5f02d..1e9d9f43 100755
--- a/codegen/lib/code/registry.py
+++ b/codegen/lib/code/registry.py
@@ -56,7 +56,14 @@ impl<T: Registry> McBufWritable for OptionalRegistry<T> {
# Air => "minecraft:air",
# Stone => "minecraft:stone"
# });
+
+ if registry_name.endswith('_type'):
+ # change _type to _kind because that's Rustier (and because _type
+ # is a reserved keyword)
+ registry_name = registry_name[:-5] + '_kind'
+
registry_struct_name = to_camel_case(registry_name.split(':')[1])
+
code.append(f'registry!({registry_struct_name}, {{')
registry_entries = sorted(
registry['entries'].items(), key=lambda x: x[1]['protocol_id'])
diff --git a/codegen/lib/code/utils.py b/codegen/lib/code/utils.py
index 5550cdb2..fe4aca7f 100755
--- a/codegen/lib/code/utils.py
+++ b/codegen/lib/code/utils.py
@@ -44,8 +44,8 @@ def burger_type_to_rust_type(burger_type, field_name: Optional[str] = None, inst
field_type_rs = 'String'
elif burger_type == 'chatcomponent':
- field_type_rs = 'Component'
- uses.add('azalea_chat::Component')
+ field_type_rs = 'FormattedText'
+ uses.add('azalea_chat::FormattedText')
elif burger_type == 'identifier':
field_type_rs = 'ResourceLocation'
uses.add('azalea_core::ResourceLocation')