From 5c90387b02cf548e21ea939ccd7f71538abcafb1 Mon Sep 17 00:00:00 2001 From: Alexander Medvedev Date: Thu, 7 May 2026 19:35:17 +0200 Subject: [PATCH] refactor: move block entities from pumpkin-world -> pumpkin --- Cargo.lock | 425 +++++++++--------- Cargo.toml | 2 +- .../src/beacon_screen_handler.rs | 136 ++++++ .../furnace_like_screen_handler.rs | 2 +- .../src/furnace_like/furnace_like_slot.rs | 4 +- pumpkin-inventory/src/lib.rs | 1 + .../src/java/client/play/chunk_data.rs | 22 +- pumpkin-protocol/src/java/server/play/mod.rs | 2 + .../src/java/server/play/set_beacon.rs | 12 + pumpkin-util/src/math/boundingbox.rs | 43 ++ pumpkin-world/src/block/entities.rs | 11 + pumpkin-world/src/block/entities/mod.rs | 209 --------- pumpkin-world/src/block/viewer.rs | 79 +--- pumpkin-world/src/chunk/format/mod.rs | 53 +-- pumpkin-world/src/chunk/mod.rs | 6 +- pumpkin-world/src/chunk_system/chunk_state.rs | 16 +- .../feature/features/monster_room.rs | 20 +- .../nether_fortress/bridge_platform.rs | 22 +- .../structures/stronghold/portal_room.rs | 18 +- pumpkin-world/src/level.rs | 8 +- pumpkin-world/src/world.rs | 103 +---- pumpkin/Cargo.toml | 2 +- pumpkin/src/block/blocks/barrel.rs | 2 +- pumpkin/src/block/blocks/beacon.rs | 69 +++ pumpkin/src/block/blocks/bed.rs | 2 +- pumpkin/src/block/blocks/blast_furnace.rs | 13 +- pumpkin/src/block/blocks/brewing_stand.rs | 4 +- pumpkin/src/block/blocks/chests.rs | 8 +- .../src/block/blocks/chiseled_bookshelf.rs | 6 +- pumpkin/src/block/blocks/command.rs | 7 +- pumpkin/src/block/blocks/end_portal.rs | 2 +- pumpkin/src/block/blocks/ender_chest.rs | 5 +- pumpkin/src/block/blocks/furnace.rs | 12 +- pumpkin/src/block/blocks/hopper.rs | 2 +- pumpkin/src/block/blocks/jukebox.rs | 2 +- pumpkin/src/block/blocks/lectern.rs | 2 +- pumpkin/src/block/blocks/mod.rs | 2 + pumpkin/src/block/blocks/piston/piston.rs | 7 +- pumpkin/src/block/blocks/redstone/bell.rs | 2 +- .../src/block/blocks/redstone/comparator.rs | 6 +- .../blocks/redstone/daylight_detector.rs | 2 +- pumpkin/src/block/blocks/redstone/dropper.rs | 4 +- pumpkin/src/block/blocks/shulker_box.rs | 2 +- pumpkin/src/block/blocks/signs.rs | 2 +- pumpkin/src/block/blocks/smoker.rs | 11 +- pumpkin/src/block/blocks/spawner.rs | 2 +- .../src/block/entities/barrel.rs | 23 +- pumpkin/src/block/entities/beacon.rs | 345 ++++++++++++++ .../src/block/entities/bed.rs | 0 .../src/block/entities/bell.rs | 7 +- .../src/block/entities/blasting_furnace.rs | 2 +- .../src/block/entities/brewing_stand.rs | 73 +-- .../src/block/entities/chest.rs | 0 .../block/entities/chest_like_block_entity.rs | 49 +- .../src/block/entities/chiseled_bookshelf.rs | 8 +- .../src/block/entities/command_block.rs | 0 .../src/block/entities/comparator.rs | 0 .../src/block/entities/daylight_detector.rs | 15 +- .../src/block/entities/dropper.rs | 2 +- .../src/block/entities/end_portal.rs | 0 .../src/block/entities/ender_chest.rs | 19 +- .../src/block/entities/furnace.rs | 2 +- .../entities/furnace_like_block_entity.rs | 49 +- .../src/block/entities/hopper.rs | 29 +- .../src/block/entities/jukebox.rs | 9 +- .../src/block/entities/lectern.rs | 12 +- .../src/block/entities/mob_spawner.rs | 18 +- pumpkin/src/block/entities/mod.rs | 207 +++++++++ .../src/block/entities/piston.rs | 9 +- .../src/block/entities/shulker_box.rs | 25 +- .../src/block/entities/sign.rs | 4 +- .../src/block/entities/smoker.rs | 2 +- .../src/block/entities/trapped_chest.rs | 0 pumpkin/src/block/mod.rs | 2 + pumpkin/src/block/registry.rs | 3 +- pumpkin/src/block/viewer.rs | 83 ++++ pumpkin/src/command/commands/particle.rs | 2 +- pumpkin/src/command/commands/summon.rs | 2 +- pumpkin/src/command/mod.rs | 4 +- pumpkin/src/item/items/dye.rs | 6 +- pumpkin/src/item/items/glowing_ink_sac.rs | 2 +- pumpkin/src/item/items/honeycomb.rs | 4 +- pumpkin/src/item/items/ink_sac.rs | 2 +- pumpkin/src/item/items/spawn_egg.rs | 2 +- pumpkin/src/net/java/play.rs | 4 +- .../loader/wasm/wasm_host/wit/v0_1/world.rs | 2 +- pumpkin/src/world/mod.rs | 295 ++++-------- 87 files changed, 1514 insertions(+), 1180 deletions(-) create mode 100644 pumpkin-inventory/src/beacon_screen_handler.rs create mode 100644 pumpkin-protocol/src/java/server/play/set_beacon.rs create mode 100644 pumpkin-world/src/block/entities.rs delete mode 100644 pumpkin-world/src/block/entities/mod.rs create mode 100644 pumpkin/src/block/blocks/beacon.rs rename {pumpkin-world => pumpkin}/src/block/entities/barrel.rs (92%) create mode 100644 pumpkin/src/block/entities/beacon.rs rename {pumpkin-world => pumpkin}/src/block/entities/bed.rs (100%) rename {pumpkin-world => pumpkin}/src/block/entities/bell.rs (94%) rename {pumpkin-world => pumpkin}/src/block/entities/blasting_furnace.rs (98%) rename {pumpkin-world => pumpkin}/src/block/entities/brewing_stand.rs (89%) rename {pumpkin-world => pumpkin}/src/block/entities/chest.rs (100%) rename {pumpkin-world => pumpkin}/src/block/entities/chest_like_block_entity.rs (86%) rename {pumpkin-world => pumpkin}/src/block/entities/chiseled_bookshelf.rs (97%) rename {pumpkin-world => pumpkin}/src/block/entities/command_block.rs (100%) rename {pumpkin-world => pumpkin}/src/block/entities/comparator.rs (100%) rename {pumpkin-world => pumpkin}/src/block/entities/daylight_detector.rs (88%) rename {pumpkin-world => pumpkin}/src/block/entities/dropper.rs (98%) rename {pumpkin-world => pumpkin}/src/block/entities/end_portal.rs (100%) rename {pumpkin-world => pumpkin}/src/block/entities/ender_chest.rs (86%) rename {pumpkin-world => pumpkin}/src/block/entities/furnace.rs (98%) rename {pumpkin-world => pumpkin}/src/block/entities/furnace_like_block_entity.rs (94%) rename {pumpkin-world => pumpkin}/src/block/entities/hopper.rs (94%) rename {pumpkin-world => pumpkin}/src/block/entities/jukebox.rs (96%) rename {pumpkin-world => pumpkin}/src/block/entities/lectern.rs (93%) rename {pumpkin-world => pumpkin}/src/block/entities/mob_spawner.rs (93%) create mode 100644 pumpkin/src/block/entities/mod.rs rename {pumpkin-world => pumpkin}/src/block/entities/piston.rs (95%) rename {pumpkin-world => pumpkin}/src/block/entities/shulker_box.rs (91%) rename {pumpkin-world => pumpkin}/src/block/entities/sign.rs (98%) rename {pumpkin-world => pumpkin}/src/block/entities/smoker.rs (98%) rename {pumpkin-world => pumpkin}/src/block/entities/trapped_chest.rs (100%) create mode 100644 pumpkin/src/block/viewer.rs diff --git a/Cargo.lock b/Cargo.lock index 823dd7ce4..4d94291b1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -217,12 +217,6 @@ version = "1.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - [[package]] name = "bitflags" version = "2.11.1" @@ -611,9 +605,9 @@ dependencies = [ [[package]] name = "cpubits" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ef0c543070d296ea414df2dd7625d1b24866ce206709d8a4a424f28377f5861" +checksum = "15b85f9c39137c3a891689859392b1bd49812121d0d61c9caf00d46ed5ce06ae" [[package]] name = "cpufeatures" @@ -635,27 +629,27 @@ dependencies = [ [[package]] name = "cranelift-assembler-x64" -version = "0.131.0" +version = "0.131.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6edb5bdd1af46714e3224a017fabbbd57f70df4e840eb5ad6a7429dc456119d6" +checksum = "f8628cc4ba7f88a9205a7ee42327697abc61195a1e3d92cfae172d6a946e722e" dependencies = [ "cranelift-assembler-x64-meta", ] [[package]] name = "cranelift-assembler-x64-meta" -version = "0.131.0" +version = "0.131.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a819599186e1b1a1f88d464e06045696afc7aa3e0cc018aa0b2999cb63d1d088" +checksum = "d582754487e6c9a065a91c42ccf1bdd8d5977af33468dac5ae9bec0ce88acb3e" dependencies = [ "cranelift-srcgen", ] [[package]] name = "cranelift-bforest" -version = "0.131.0" +version = "0.131.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36e2c152d488e03c87b913bc2ed3414416eb1e0d66d61b49af60bf456a9665c7" +checksum = "fb59c81ace12ee7c33074db7903d4d75d1f40b28cd3e8e6f491de57b29129eb9" dependencies = [ "cranelift-entity", "wasmtime-internal-core", @@ -663,9 +657,9 @@ dependencies = [ [[package]] name = "cranelift-bitset" -version = "0.131.0" +version = "0.131.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6559d4fbc253d1396e1f6beeae57fa88a244f02aaf0cde2a735afd3492d9b2e" +checksum = "f25c06993a681be9cf3140798a3d4ac5bec955e7444416a2fdc87fda8567285d" dependencies = [ "serde", "serde_derive", @@ -674,9 +668,9 @@ dependencies = [ [[package]] name = "cranelift-codegen" -version = "0.131.0" +version = "0.131.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96d9315d98d6e0a64454d4c83be2ee0e8055c3f80c3b2d7bcad7079f281a06ff" +checksum = "27b61f95c5a211918f5d336254a61a488b36a5818de47a868e8c4658dce9cccc" dependencies = [ "bumpalo", "cranelift-assembler-x64", @@ -702,9 +696,9 @@ dependencies = [ [[package]] name = "cranelift-codegen-meta" -version = "0.131.0" +version = "0.131.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d89c00a88081c55e3087c45bebc77e0cc973de2d7b44ef6a943c7122647b89f5" +checksum = "0b85aa822fce72080d041d7c2cf7c3f5c6ecdea7afae68379ba4ef85269c4fa5" dependencies = [ "cranelift-assembler-x64-meta", "cranelift-codegen-shared", @@ -715,24 +709,24 @@ dependencies = [ [[package]] name = "cranelift-codegen-shared" -version = "0.131.0" +version = "0.131.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "879f77c497a1eb6273482aa1ac3b23cb8563ff04edb39ed5dfcfd28c8deff8f5" +checksum = "833eb9fc89326cd072cc19e96892f09b5692c0dfe17cd4da2858ba30c2cd85c0" [[package]] name = "cranelift-control" -version = "0.131.0" +version = "0.131.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "498dc1f17a6910c88316d49c7176d8fa97cf10c30859c32a266040449317f963" +checksum = "9d005320f487e6e8a3edcc7f2fd4f43fcc9946d1013bf206ea649789ac1617fc" dependencies = [ "arbitrary", ] [[package]] name = "cranelift-entity" -version = "0.131.0" +version = "0.131.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2acba797f6a46042ce82aaf7680d0c3567fe2001e238db9df649fd104a2727f" +checksum = "5e62ef34c6e720f347a79ece043e8584e242d168911da640bac654a33a6aaaf5" dependencies = [ "cranelift-bitset", "serde", @@ -742,9 +736,9 @@ dependencies = [ [[package]] name = "cranelift-frontend" -version = "0.131.0" +version = "0.131.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dca3df1d107d98d88f159ad1d5eaa2d5cdb678b3d5bcfadc6fc83d8ebb448ea" +checksum = "dfa2ad00399dd47e7e7e33cb1dc23b0e39ed9dcd01e8f026fc37af91655031b8" dependencies = [ "cranelift-codegen", "log", @@ -754,15 +748,15 @@ dependencies = [ [[package]] name = "cranelift-isle" -version = "0.131.0" +version = "0.131.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f62dd18116d88bed649871feceda79dad7b59cc685ea8998c2b3e64d0e689602" +checksum = "02c51975ed217b4e8e5a7fd11e9ec83a96104bdff311dddcb505d1d8a9fd7fc6" [[package]] name = "cranelift-native" -version = "0.131.0" +version = "0.131.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f843b80360d7fdf61a6124642af7597f6d55724cf521210c34af8a1c66daca6e" +checksum = "f9b1889e00da9729d8f8525f3c12998ded86ea709058ff844ebe00b97548de0e" dependencies = [ "cranelift-codegen", "libc", @@ -771,9 +765,9 @@ dependencies = [ [[package]] name = "cranelift-srcgen" -version = "0.131.0" +version = "0.131.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "090ee5de58c6f17eb5e3a5ae8cf1695c7efea04ec4dd0ecba6a5b996c9bad7dc" +checksum = "d5a8f82fd5124f009f72167e60139245cd3b56cfd4b53050f22110c48c5f4da1" [[package]] name = "crc-fast" @@ -936,9 +930,9 @@ dependencies = [ [[package]] name = "crypto-common" -version = "0.1.7" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ "generic-array", "typenum", @@ -1035,15 +1029,15 @@ checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer 0.10.4", "const-oid 0.9.6", - "crypto-common 0.1.7", + "crypto-common 0.1.6", "subtle", ] [[package]] name = "digest" -version = "0.11.2" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4850db49bf08e663084f7fb5c87d202ef91a3907271aff24a94eb97ff039153c" +checksum = "f1dd6dbb5841937940781866fa1281a1ff7bd3bf827091440879f9994983d5c2" dependencies = [ "block-buffer 0.12.0", "const-oid 0.10.2", @@ -1072,6 +1066,16 @@ dependencies = [ "winapi", ] +[[package]] +name = "dispatch2" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e0e367e4e7da84520dedcac1901e4da967309406d1e51017ae1abfb97adbd38" +dependencies = [ + "bitflags", + "objc2", +] + [[package]] name = "displaydoc" version = "0.2.5" @@ -1384,7 +1388,7 @@ version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "25234f20a3ec0a962a61770cfe39ecf03cb529a6e474ad8cff025ed497eda557" dependencies = [ - "bitflags 2.11.1", + "bitflags", "debugid", "rustc-hash", "serde", @@ -1394,9 +1398,9 @@ dependencies = [ [[package]] name = "generic-array" -version = "0.14.7" +version = "0.14.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +checksum = "4bb6743198531e02858aeaea5398fcc883e71851fcbcb5a2f773e2fb6cb1edf2" dependencies = [ "typenum", "version_check", @@ -1465,9 +1469,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.4.13" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f44da3a8150a6703ed5d34e164b875fd14c2cdab9af1252a9a1020bde2bdc54" +checksum = "171fefbc92fe4a4de27e0698d6a5b392d6a0e333506bc49133760b3bcf948733" dependencies = [ "atomic-waker", "bytes", @@ -1596,7 +1600,7 @@ version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6303bc9732ae41b04cb554b844a762b4115a61bfaa81e3e83050991eeb56863f" dependencies = [ - "digest 0.11.2", + "digest 0.11.3", ] [[package]] @@ -1874,7 +1878,7 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd5b3eaf1a28b758ac0faa5a4254e8ab2705605496f1b1f3fbbc3988ad73d199" dependencies = [ - "bitflags 2.11.1", + "bitflags", "inotify-sys", "libc", ] @@ -1975,10 +1979,12 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.95" +version = "0.3.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2964e92d1d9dc3364cae4d718d93f227e3abb088e747d92e0395bfdedf1c12ca" +checksum = "67df7112613f8bfd9150013a0314e196f4800d3201ae742489d999db2f979f08" dependencies = [ + "cfg-if", + "futures-util", "once_cell", "wasm-bindgen", ] @@ -1995,11 +2001,11 @@ dependencies = [ [[package]] name = "kqueue-sys" -version = "1.0.4" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed9625ffda8729b85e45cf04090035ac368927b8cebc34898e7c120f52e4838b" +checksum = "a7b65860415f949f23fa882e669f2dbd4a0f0eeb1acdd56790b30494afd7da2f" dependencies = [ - "bitflags 1.3.2", + "bitflags", "libc", ] @@ -2009,12 +2015,6 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" -[[package]] -name = "leb128" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6cc46bac87ef8093eed6f272babb833b6443374399985ac8ed28471ee0918545" - [[package]] name = "leb128fmt" version = "0.1.0" @@ -2228,7 +2228,7 @@ version = "0.31.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5d6d0705320c1e6ba1d912b5e37cf18071b6c2e9b7fa8215a1e8a7651966f5d3" dependencies = [ - "bitflags 2.11.1", + "bitflags", "cfg-if", "cfg_aliases", "libc", @@ -2250,7 +2250,7 @@ version = "8.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4d3d07927151ff8575b7087f245456e549fea62edf0ec4e565a5ee50c8402bc3" dependencies = [ - "bitflags 2.11.1", + "bitflags", "fsevent-sys", "inotify", "kqueue", @@ -2268,7 +2268,7 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42b8cfee0e339a0337359f3c88165702ac6e600dc01c0cc9579a92d62b08477a" dependencies = [ - "bitflags 2.11.1", + "bitflags", ] [[package]] @@ -2343,13 +2343,40 @@ dependencies = [ "libc", ] +[[package]] +name = "objc2" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a12a8ed07aefc768292f076dc3ac8c48f3781c8f2d5851dd3d98950e8c5a89f" +dependencies = [ + "objc2-encode", +] + [[package]] name = "objc2-core-foundation" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536" dependencies = [ - "bitflags 2.11.1", + "bitflags", + "dispatch2", + "objc2", +] + +[[package]] +name = "objc2-encode" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33" + +[[package]] +name = "objc2-foundation" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272" +dependencies = [ + "bitflags", + "objc2", ] [[package]] @@ -2362,6 +2389,17 @@ dependencies = [ "objc2-core-foundation", ] +[[package]] +name = "objc2-open-directory" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb82bed227edf5201dfedf072bba4015a33d3d4a98519837295a90f0a23f676d" +dependencies = [ + "objc2", + "objc2-core-foundation", + "objc2-foundation", +] + [[package]] name = "object" version = "0.39.1" @@ -2562,18 +2600,18 @@ dependencies = [ [[package]] name = "pin-project" -version = "1.1.11" +version = "1.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1749c7ed4bcaf4c3d0a3efc28538844fb29bcdd7d2b67b2be7e20ba861ff517" +checksum = "cbf0d9e68100b3a7989b4901972f265cd542e560a3a8a724e1e20322f4d06ce9" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.1.11" +version = "1.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b20ed30f105399776b9c883e68e536ef602a16ae6f596d2c473591d6ad64c6" +checksum = "a990e22f43e84855daf260dded30524ef4a9021cc7541c26540500a50b624389" dependencies = [ "proc-macro2", "quote", @@ -2764,9 +2802,9 @@ dependencies = [ [[package]] name = "pulley-interpreter" -version = "44.0.0" +version = "44.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df866b7fd522992ccc6682e58b2741cc7972b163b661db24c4328f4c914cb09d" +checksum = "b9326e3a0093d170582cf64ed9e4cf253b8aac155ec4a294ff62330450bbf094" dependencies = [ "cranelift-bitset", "log", @@ -2776,9 +2814,9 @@ dependencies = [ [[package]] name = "pulley-macros" -version = "44.0.0" +version = "44.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7dfa8354acc622b3857e1bb1a4e4315d3bc1a44ad31d5653c3e87c0da9306d7" +checksum = "00c6433917e3789605b1f4cd2a589f637ff17212344e7fa5ba99544625ba52c7" dependencies = [ "proc-macro2", "quote", @@ -2791,7 +2829,7 @@ version = "0.1.0-dev+26.1" dependencies = [ "arc-swap", "base64 0.22.1", - "bitflags 2.11.1", + "bitflags", "bytes", "colored", "console-subscriber", @@ -2941,7 +2979,7 @@ version = "0.1.0-dev+26.1" dependencies = [ "aes", "async-compression", - "bitflags 2.11.1", + "bitflags", "bytes", "cfb8", "flate2", @@ -2990,7 +3028,7 @@ dependencies = [ name = "pumpkin-world" version = "0.1.0-dev+26.1" dependencies = [ - "bitflags 2.11.1", + "bitflags", "bytes", "criterion", "crossbeam", @@ -3126,7 +3164,7 @@ version = "0.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" dependencies = [ - "bitflags 2.11.1", + "bitflags", ] [[package]] @@ -3216,11 +3254,11 @@ dependencies = [ "const-oid 0.10.2", "crypto-bigint 0.7.3", "crypto-primes", - "digest 0.11.2", + "digest 0.11.3", "pkcs1", "pkcs8 0.11.0", "rand_core 0.10.1", - "signature 3.0.0-rc.10", + "signature 3.0.0", "spki 0.8.0", "zeroize", ] @@ -3262,7 +3300,7 @@ version = "0.38.44" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" dependencies = [ - "bitflags 2.11.1", + "bitflags", "errno", "libc", "linux-raw-sys 0.4.15", @@ -3275,7 +3313,7 @@ version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" dependencies = [ - "bitflags 2.11.1", + "bitflags", "errno", "libc", "linux-raw-sys 0.12.1", @@ -3294,9 +3332,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.39" +version = "0.23.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c2c118cb077cca2822033836dfb1b975355dfb784b5e8da48f7b6c5db74e60e" +checksum = "ef86cd5876211988985292b91c96a8f2d298df24e75989a43a3c73f2d4d8168b" dependencies = [ "log", "once_cell", @@ -3339,7 +3377,7 @@ version = "18.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4a990b25f351b25139ddc7f21ee3f6f56f86d6846b74ac8fad3a719a287cd4a0" dependencies = [ - "bitflags 2.11.1", + "bitflags", "cfg-if", "clipboard-win", "home", @@ -3478,9 +3516,9 @@ dependencies = [ [[package]] name = "serdect" -version = "0.4.2" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9af4a3e75ebd5599b30d4de5768e00b5095d518a79fefc3ecbaf77e665d1ec06" +checksum = "66cf8fedced2fcf12406bcb34223dffb92eaf34908ede12fed414c82b7f00b3e" dependencies = [ "base16ct 1.0.0", "serde", @@ -3494,7 +3532,7 @@ checksum = "aacc4cc499359472b4abe1bf11d0b12e688af9a805fa5e3016f9a386dc2d0214" dependencies = [ "cfg-if", "cpufeatures 0.3.0", - "digest 0.11.2", + "digest 0.11.3", ] [[package]] @@ -3516,7 +3554,7 @@ checksum = "446ba717509524cb3f22f17ecc096f10f4822d76ab5c0b9822c5f9c284e825f4" dependencies = [ "cfg-if", "cpufeatures 0.3.0", - "digest 0.11.2", + "digest 0.11.3", ] [[package]] @@ -3556,11 +3594,11 @@ dependencies = [ [[package]] name = "signature" -version = "3.0.0-rc.10" +version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f1880df446116126965eeec169136b2e0251dba37c6223bcc819569550edea3" +checksum = "28d567dcbaf0049cb8ac2608a76cd95ff9e4412e1899d389ee400918ca7537f5" dependencies = [ - "digest 0.11.2", + "digest 0.11.3", "rand_core 0.10.1", ] @@ -3572,9 +3610,9 @@ checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214" [[package]] name = "siphasher" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2aa850e253778c88a04c3d7323b043aeda9d3e30d5971937c1855769763678e" +checksum = "8ee5873ec9cce0195efcb7a4e9507a04cd49aec9c83d0389df45b1ef7ba2e649" [[package]] name = "slab" @@ -3693,15 +3731,16 @@ dependencies = [ [[package]] name = "sysinfo" -version = "0.38.4" +version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92ab6a2f8bfe508deb3c6406578252e491d299cbbf3bc0529ecc3313aee4a52f" +checksum = "cd9f9fe3d2b7b75cf4f2805e5b9926e8ac47146667b16b86298c4a8bf08cc469" dependencies = [ "libc", "memchr", "ntapi", "objc2-core-foundation", "objc2-io-kit", + "objc2-open-directory", "windows", ] @@ -3711,7 +3750,7 @@ version = "0.27.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cc4592f674ce18521c2a81483873a49596655b179f71c5e05d10c1fe66c78745" dependencies = [ - "bitflags 2.11.1", + "bitflags", "cap-fs-ext", "cap-std", "fd-lock", @@ -3865,9 +3904,9 @@ dependencies = [ [[package]] name = "tokio" -version = "1.52.1" +version = "1.52.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b67dee974fe86fd92cc45b7a95fdd2f99a36a6d7b0d431a231178d3d670bbcc6" +checksum = "110a78583f19d5cdb2c5ccf321d1290344e71313c6c37d43520d386027d18386" dependencies = [ "bytes", "libc", @@ -3981,9 +4020,9 @@ checksum = "756daf9b1013ebe47a8776667b466417e2d4c5679d441c26230efd9ef78692db" [[package]] name = "tonic" -version = "0.14.5" +version = "0.14.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fec7c61a0695dc1887c1b53952990f3ad2e3a31453e1f49f10e75424943a93ec" +checksum = "ac2a5518c70fa84342385732db33fb3f44bc4cc748936eb5833d2df34d6445ef" dependencies = [ "async-trait", "axum", @@ -4010,9 +4049,9 @@ dependencies = [ [[package]] name = "tonic-prost" -version = "0.14.5" +version = "0.14.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a55376a0bbaa4975a3f10d009ad763d8f4108f067c7c2e74f3001fb49778d309" +checksum = "50849f68853be452acf590cde0b146665b8d507b3b8af17261df47e02c209ea0" dependencies = [ "bytes", "prost", @@ -4321,9 +4360,9 @@ dependencies = [ [[package]] name = "wasm-bindgen" -version = "0.2.118" +version = "0.2.121" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bf938a0bacb0469e83c1e148908bd7d5a6010354cf4fb73279b7447422e3a89" +checksum = "49ace1d07c165b0864824eee619580c4689389afa9dc9ed3a4c75040d82e6790" dependencies = [ "cfg-if", "once_cell", @@ -4334,9 +4373,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.118" +version = "0.2.121" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eeff24f84126c0ec2db7a449f0c2ec963c6a49efe0698c4242929da037ca28ed" +checksum = "8e68e6f4afd367a562002c05637acb8578ff2dea1943df76afb9e83d177c8578" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -4344,9 +4383,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.118" +version = "0.2.121" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d08065faf983b2b80a79fd87d8254c409281cf7de75fc4b773019824196c904" +checksum = "d95a9ec35c64b2a7cb35d3fead40c4238d0940c86d107136999567a4703259f2" dependencies = [ "bumpalo", "proc-macro2", @@ -4357,9 +4396,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.118" +version = "0.2.121" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fd04d9e306f1907bd13c6361b5c6bfc7b3b3c095ed3f8a9246390f8dbdee129" +checksum = "c4e0100b01e9f0d03189a92b96772a1fb998639d981193d7dbab487302513441" dependencies = [ "unicode-ident", ] @@ -4411,6 +4450,16 @@ dependencies = [ "wasmparser 0.247.0", ] +[[package]] +name = "wasm-encoder" +version = "0.248.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac92cf547bc18d27ecc521015c08c353b4f18b84ab388bb6d1b6b682c620d9b6" +dependencies = [ + "leb128fmt", + "wasmparser 0.248.0", +] + [[package]] name = "wasm-metadata" version = "0.244.0" @@ -4441,7 +4490,7 @@ version = "0.244.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" dependencies = [ - "bitflags 2.11.1", + "bitflags", "hashbrown 0.15.5", "indexmap", "semver", @@ -4453,7 +4502,7 @@ version = "0.246.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "71cde4757396defafd25417cfb36aa3161027d06d865b0c24baaae229aac005d" dependencies = [ - "bitflags 2.11.1", + "bitflags", "hashbrown 0.16.1", "indexmap", "semver", @@ -4466,7 +4515,7 @@ version = "0.247.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e6fb4c2bee46c5ea4d40f8cdb5c131725cd976718ec56f1c8e82fbde5fa2a80" dependencies = [ - "bitflags 2.11.1", + "bitflags", "hashbrown 0.17.0", "indexmap", "semver", @@ -4478,7 +4527,7 @@ version = "0.248.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aa4439c5eee9df71ee0c6efb37f63b1fcb1fec38f85f5142c54e7ed05d33091a" dependencies = [ - "bitflags 2.11.1", + "bitflags", "hashbrown 0.17.0", "indexmap", "semver", @@ -4498,13 +4547,13 @@ dependencies = [ [[package]] name = "wasmtime" -version = "44.0.0" +version = "44.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fca3f777dfb4db45915f95eeb25cac7f2eeb268797a27e5eb78b072618135c7f" +checksum = "372db8bbad8ec962038101f75ab2c3ffcd18797d7d3ae877a58ab9873cd0c4bd" dependencies = [ "addr2line", "async-trait", - "bitflags 2.11.1", + "bitflags", "bumpalo", "cc", "cfg-if", @@ -4551,9 +4600,9 @@ dependencies = [ [[package]] name = "wasmtime-environ" -version = "44.0.0" +version = "44.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c5ca1af838cec374931242d07af5d354aedf63f297f95b3625ac863e516ef67" +checksum = "1e15aa0d1545e48d9b25ca604e9e27b4cd6d5886d30ac5787b57b3a2daf85b57" dependencies = [ "anyhow", "cpp_demangle", @@ -4582,9 +4631,9 @@ dependencies = [ [[package]] name = "wasmtime-internal-cache" -version = "44.0.0" +version = "44.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2004f7c86ebeb116550655377cdf16dbf7b03ae5aa6b4b1c1458cfa23aaa306" +checksum = "5441170843ac2ab28a1d7646b04a93a46d63bd4083274fd246c6a80189b37767" dependencies = [ "base64 0.22.1", "directories-next", @@ -4602,9 +4651,9 @@ dependencies = [ [[package]] name = "wasmtime-internal-component-macro" -version = "44.0.0" +version = "44.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58b31927f7b613d8fe019609744e226f6458d8aa5e6289e92fbbc60e521cd026" +checksum = "c136cb0d2d47850d6d04a58157130ac98b0df4c17626cd30b083d26b607b7027" dependencies = [ "anyhow", "proc-macro2", @@ -4617,15 +4666,15 @@ dependencies = [ [[package]] name = "wasmtime-internal-component-util" -version = "44.0.0" +version = "44.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc29e3478928b93979831ba02a997ce7f707c673ce47180d643091cf4fa4f561" +checksum = "49df3d3b4fa2119c6fd161e475b4e21aaefb51d082353b922b433bea37facc65" [[package]] name = "wasmtime-internal-core" -version = "44.0.0" +version = "44.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "816a61a75275c6be435131fc625a4f5956daf24d9f9f59443e81cbef228929b3" +checksum = "8f2c7fa6523647262bfb4095dbdf4087accefe525813e783f81a0c682f418ce4" dependencies = [ "anyhow", "hashbrown 0.16.1", @@ -4635,9 +4684,9 @@ dependencies = [ [[package]] name = "wasmtime-internal-cranelift" -version = "44.0.0" +version = "44.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69ceb5e079877e7e4565c1e2d86d9db889175d55f7ca0001315576d08c71e634" +checksum = "98c032f422e39061dfc43f32190c0a3526b04161ec4867f362958f3fe9d1fe29" dependencies = [ "cfg-if", "cranelift-codegen", @@ -4662,9 +4711,9 @@ dependencies = [ [[package]] name = "wasmtime-internal-fiber" -version = "44.0.0" +version = "44.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e18f8bb05d25e0d4cca7278147c9f9e2f26f66886ef754b562bf729128f1e537" +checksum = "d8dd76d80adf450cc260ba58f23c28030401930b19149695b1d121f7d621e791" dependencies = [ "cc", "cfg-if", @@ -4677,9 +4726,9 @@ dependencies = [ [[package]] name = "wasmtime-internal-jit-debug" -version = "44.0.0" +version = "44.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "357f1070b31154ee463937b477ca0b2962bf450b40fc59799bef2f656b15da73" +checksum = "ab453cc600b28ee5d3f9495aa6d4cb2c81eda40903e9287296b548fba8b2391d" dependencies = [ "cc", "object", @@ -4689,9 +4738,9 @@ dependencies = [ [[package]] name = "wasmtime-internal-jit-icache-coherence" -version = "44.0.0" +version = "44.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fd683a94490bf755d016a09697b0955602c50106b1ded97d16983ab2ded9fed" +checksum = "6a1859e920871515d324fb9757c3e448d6ed1512ca6ccdff14b6e016505d6ada" dependencies = [ "cfg-if", "libc", @@ -4701,9 +4750,9 @@ dependencies = [ [[package]] name = "wasmtime-internal-unwinder" -version = "44.0.0" +version = "44.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4471746ce113c3c1862ce2c0674acb35399a4b3ed3ef4531dc087f333c74f064" +checksum = "f1dfe405bd6adb1386d935a30f16a236bd4ef0d3c383e7cbbab98d063c9d9b73" dependencies = [ "cfg-if", "cranelift-codegen", @@ -4714,9 +4763,9 @@ dependencies = [ [[package]] name = "wasmtime-internal-versioned-export-macros" -version = "44.0.0" +version = "44.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6af582ec18b674bf7a17775d6fbfbddfcc143f0edbd89c9c1778239c8aa92ed" +checksum = "2a9b9165fc45d42c81edfe3e9cb458e58720594ad5db6553c4079ea041a4a581" dependencies = [ "proc-macro2", "quote", @@ -4725,9 +4774,9 @@ dependencies = [ [[package]] name = "wasmtime-internal-winch" -version = "44.0.0" +version = "44.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d31be8916bb60ea756d2f0ae1f634d9258442aa71e773c893e2f4cead30501b5" +checksum = "95f439b70ba3855a8c808d2cd798eef79bcd389f78aa48a8a694ea8e2904410c" dependencies = [ "cranelift-codegen", "gimli", @@ -4742,12 +4791,12 @@ dependencies = [ [[package]] name = "wasmtime-internal-wit-bindgen" -version = "44.0.0" +version = "44.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2150e63d502ab2d64754e5abe8eb737ae674b7dd4ad53144fd16bbeceaf4a19" +checksum = "17c7ced16dc16d2027f9f8d3a503e191dcce0f53fe9218e7990135b31f8f6fdb" dependencies = [ "anyhow", - "bitflags 2.11.1", + "bitflags", "heck", "indexmap", "wit-parser 0.246.2", @@ -4755,12 +4804,12 @@ dependencies = [ [[package]] name = "wasmtime-wasi" -version = "44.0.0" +version = "44.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83f5109b4fd619b9796b9c9901de59d83e3575cd1226c1a36d1901371f43db28" +checksum = "0d3d57dd833d0c3ea2016a2aa54c6c517bf8dad9e79d8a593b0252c12bc961e3" dependencies = [ "async-trait", - "bitflags 2.11.1", + "bitflags", "bytes", "cap-fs-ext", "cap-net-ext", @@ -4779,15 +4828,14 @@ dependencies = [ "url", "wasmtime", "wasmtime-wasi-io", - "wiggle", "windows-sys 0.61.2", ] [[package]] name = "wasmtime-wasi-io" -version = "44.0.0" +version = "44.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74ebe14c586e98d2fdc32c76ca0005ef28348e98ed737e776d378b3b0cc2afd0" +checksum = "6650bb4c61012b2221e751b7bc1162c7fd11bd1bc29e0714ad6ca463777a3422" dependencies = [ "async-trait", "bytes", @@ -4798,33 +4846,24 @@ dependencies = [ [[package]] name = "wast" -version = "35.0.2" +version = "248.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ef140f1b49946586078353a453a1d28ba90adfc54dde75710bc1931de204d68" -dependencies = [ - "leb128", -] - -[[package]] -name = "wast" -version = "247.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "579d2d47eb33b0cdf9b14723cb115f1e1b7d6e77aac6f0816e5b7c7aeaa418ff" +checksum = "acc54622ed5a5cddafcdf152043f9d4aed54d4a653d686b7dfe874809fca99d7" dependencies = [ "bumpalo", "leb128fmt", "memchr", "unicode-width", - "wasm-encoder 0.247.0", + "wasm-encoder 0.248.0", ] [[package]] name = "wat" -version = "1.247.0" +version = "1.248.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3f4091c56437e86f2b57fa2fac72c4f528957a605b3f44f7c0b3b19a17ac5ee" +checksum = "d75cd9e510603909748e6ebab89f27cd04472c1d9d85a3c88a7a6fc51a1a7934" dependencies = [ - "wast 247.0.0", + "wast", ] [[package]] @@ -4836,46 +4875,6 @@ dependencies = [ "rustls-pki-types", ] -[[package]] -name = "wiggle" -version = "44.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89cff414ef7dce0cc1cf8a033ff80d3f38e3987c37e3efeec7926ecb5ffaaae6" -dependencies = [ - "bitflags 2.11.1", - "thiserror 2.0.18", - "tracing", - "wasmtime", - "wasmtime-environ", - "wiggle-macro", -] - -[[package]] -name = "wiggle-generate" -version = "44.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccf9dc7272b151a9616a2699e7f94ea1d4ae253b47b63a79fbc8f38e2cca5fa6" -dependencies = [ - "heck", - "proc-macro2", - "quote", - "syn", - "wasmtime-environ", - "witx", -] - -[[package]] -name = "wiggle-macro" -version = "44.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa7c29fcf738630cba4e35f1805da5e42dde20ee9809ee9202b0648ae671602f" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "wiggle-generate", -] - [[package]] name = "winapi" version = "0.3.9" @@ -4909,9 +4908,9 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "winch-codegen" -version = "44.0.0" +version = "44.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9339858ad222412200fd8b1af9e270712201aaec440c7618991443af3446481f" +checksum = "6da7c536f3cfe5ff63537f795902fed56b8b5adcc7a87843a86dd8d4e57a7946" dependencies = [ "cranelift-assembler-x64", "cranelift-codegen", @@ -5219,7 +5218,7 @@ version = "0.36.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f3fd376f71958b862e7afb20cfe5a22830e1963462f3a17f49d82a6c1d1f42d" dependencies = [ - "bitflags 2.11.1", + "bitflags", "windows-sys 0.59.0", ] @@ -5238,7 +5237,7 @@ version = "0.57.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" dependencies = [ - "bitflags 2.11.1", + "bitflags", "wit-bindgen-rust-macro 0.57.1", ] @@ -5334,7 +5333,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" dependencies = [ "anyhow", - "bitflags 2.11.1", + "bitflags", "indexmap", "log", "serde", @@ -5353,7 +5352,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d567162a6b9843080e5e0053f696623ff694bae8ae017c9ec536d1873bbe3d8" dependencies = [ "anyhow", - "bitflags 2.11.1", + "bitflags", "indexmap", "log", "serde", @@ -5421,18 +5420,6 @@ dependencies = [ "wasmparser 0.247.0", ] -[[package]] -name = "witx" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e366f27a5cabcddb2706a78296a40b8fcc451e1a6aba2fc1d94b4a01bdaaef4b" -dependencies = [ - "anyhow", - "log", - "thiserror 1.0.69", - "wast 35.0.2", -] - [[package]] name = "writeable" version = "0.6.3" diff --git a/Cargo.toml b/Cargo.toml index 5b707f1d1..8675853dd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -179,7 +179,7 @@ ureq = "3.3.0" notify = "8.2.0" wasmtime = "44.0" -wasmtime-wasi = "44.0" +wasmtime-wasi = { version = "44.0", default-features = false, features = ["p2"] } wit-bindgen = "0.57" wasmparser = "0.248" diff --git a/pumpkin-inventory/src/beacon_screen_handler.rs b/pumpkin-inventory/src/beacon_screen_handler.rs new file mode 100644 index 000000000..2e80e4b7b --- /dev/null +++ b/pumpkin-inventory/src/beacon_screen_handler.rs @@ -0,0 +1,136 @@ +use std::{any::Any, sync::Arc}; + +use pumpkin_data::{item_stack::ItemStack, screen::WindowType}; +use pumpkin_world::inventory::Inventory; + +use crate::{ + player::player_inventory::PlayerInventory, + screen_handler::{ + InventoryPlayer, ItemStackFuture, ScreenHandler, ScreenHandlerBehaviour, + ScreenHandlerFuture, + }, + slot::NormalSlot, +}; + +/// Creates a beacon container screen handler. +/// +/// Beacons feature a single payment slot and a specialized UI for selecting status effects. +pub async fn create_beacon_handler( + sync_id: u8, + player_inventory: &Arc, + inventory: Arc, +) -> BeaconScreenHandler { + BeaconScreenHandler::new(sync_id, player_inventory, inventory).await +} + +/// Screen handler specifically for Beacon blocks. +pub struct BeaconScreenHandler { + /// The beacon's inventory (contains exactly 1 slot for payment). + pub inventory: Arc, + /// Core screen handler behavior (slots, sync ID, listeners). + behaviour: ScreenHandlerBehaviour, +} + +impl BeaconScreenHandler { + /// Creates a new beacon screen handler. + async fn new( + sync_id: u8, + player_inventory: &Arc, + inventory: Arc, + ) -> Self { + let mut handler = Self { + inventory: inventory.clone(), + behaviour: ScreenHandlerBehaviour::new(sync_id, Some(WindowType::Beacon)), + }; + + inventory.on_open().await; + + // Add the single payment slot for the beacon (slot 0) + handler.add_slot(Arc::new(NormalSlot::new(handler.inventory.clone(), 0))); + + // Add the player's inventory slots (27 slots + 9 hotbar) + let player_inventory_arc: Arc = player_inventory.clone(); + handler.add_player_slots(&player_inventory_arc); + + handler + } +} + +impl ScreenHandler for BeaconScreenHandler { + fn as_any(&self) -> &dyn Any { + self + } + + fn as_any_mut(&mut self) -> &mut dyn Any { + self + } + + fn get_behaviour(&self) -> &ScreenHandlerBehaviour { + &self.behaviour + } + + fn get_behaviour_mut(&mut self) -> &mut ScreenHandlerBehaviour { + &mut self.behaviour + } + + fn on_closed<'a>(&'a mut self, player: &'a dyn InventoryPlayer) -> ScreenHandlerFuture<'a, ()> { + Box::pin(async move { + self.default_on_closed(player).await; + self.inventory.on_close().await; + }) + } + + /// Quick move logic specifically for the beacon UI. + /// + /// - From beacon payment slot (0): Move to player inventory + /// - From player inventory (1+): Move to beacon payment slot + fn quick_move<'a>( + &'a mut self, + _player: &'a dyn InventoryPlayer, + slot_index: i32, + ) -> ItemStackFuture<'a> { + Box::pin(async move { + let mut stack_left = ItemStack::EMPTY.clone(); + let slot = self.get_behaviour().slots[slot_index as usize].clone(); + + if slot.has_stack().await { + let slot_stack_lock = slot.get_stack().await; + let slot_stack_guard = slot_stack_lock.lock().await; + stack_left = slot_stack_guard.clone(); + drop(slot_stack_guard); + + let mut slot_stack_mut = slot_stack_lock.lock().await; + + if slot_index == 0 { + // Move from the single beacon slot to the player inventory (slots 1 to end) + if !self + .insert_item( + &mut slot_stack_mut, + 1, + self.get_behaviour().slots.len() as i32, + true, + ) + .await + { + return ItemStack::EMPTY.clone(); + } + } else { + // Move from player inventory into the beacon payment slot (slot 0) + if !self.insert_item(&mut slot_stack_mut, 0, 1, false).await { + return ItemStack::EMPTY.clone(); + } + } + + if slot_stack_mut.is_empty() { + drop(slot_stack_mut); + slot.set_stack(ItemStack::EMPTY.clone()).await; + } else { + drop(slot_stack_mut); + slot.mark_dirty().await; + } + } + + stack_left + }) + } +} diff --git a/pumpkin-inventory/src/furnace_like/furnace_like_screen_handler.rs b/pumpkin-inventory/src/furnace_like/furnace_like_screen_handler.rs index 10174d1d6..549f61a85 100644 --- a/pumpkin-inventory/src/furnace_like/furnace_like_screen_handler.rs +++ b/pumpkin-inventory/src/furnace_like/furnace_like_screen_handler.rs @@ -20,7 +20,7 @@ use std::{any::Any, pin::Pin, sync::Arc}; use pumpkin_data::{fuels::is_fuel, item_stack::ItemStack, screen::WindowType}; use pumpkin_world::{ - block::entities::{PropertyDelegate, furnace_like_block_entity::ExperienceContainer}, + block::entities::{ExperienceContainer, PropertyDelegate}, inventory::Inventory, }; diff --git a/pumpkin-inventory/src/furnace_like/furnace_like_slot.rs b/pumpkin-inventory/src/furnace_like/furnace_like_slot.rs index 524cba88e..ea25feb20 100644 --- a/pumpkin-inventory/src/furnace_like/furnace_like_slot.rs +++ b/pumpkin-inventory/src/furnace_like/furnace_like_slot.rs @@ -9,9 +9,7 @@ use std::sync::{Arc, atomic::AtomicU8}; use pumpkin_data::{fuels::is_fuel, item::Item}; -use pumpkin_world::{ - block::entities::furnace_like_block_entity::ExperienceContainer, inventory::Inventory, -}; +use pumpkin_world::{block::entities::ExperienceContainer, inventory::Inventory}; use tracing::debug; diff --git a/pumpkin-inventory/src/lib.rs b/pumpkin-inventory/src/lib.rs index 17009b17e..1b9e8c3bb 100644 --- a/pumpkin-inventory/src/lib.rs +++ b/pumpkin-inventory/src/lib.rs @@ -31,6 +31,7 @@ //! [`SyncHandler`]: sync_handler::SyncHandler pub mod anvil; +pub mod beacon_screen_handler; pub mod brewing; pub mod container_click; pub mod crafting; diff --git a/pumpkin-protocol/src/java/client/play/chunk_data.rs b/pumpkin-protocol/src/java/client/play/chunk_data.rs index dff8c18c0..4ef828f7e 100644 --- a/pumpkin-protocol/src/java/client/play/chunk_data.rs +++ b/pumpkin-protocol/src/java/client/play/chunk_data.rs @@ -5,7 +5,6 @@ use pumpkin_data::block_state_remap::remap_block_state_for_version; use pumpkin_data::packet::CURRENT_MC_VERSION; use pumpkin_data::packet::clientbound::PLAY_LEVEL_CHUNK_WITH_LIGHT; use pumpkin_macros::java_packet; -use pumpkin_nbt::END_ID; use pumpkin_util::math::position::get_local_cord; use pumpkin_util::version::MinecraftVersion; use pumpkin_world::chunk::format::LightContainer; @@ -195,23 +194,26 @@ impl ClientPacket for CChunkData<'_> { let block_entities = self .0 - .block_entities + .pending_block_entities .lock() .map_err(|_| WritingError::Message("block_entities lock poisoned".into()))?; write.write_var_int(&VarInt(block_entities.len() as i32))?; - for block_entity in block_entities.values() { - let pos = block_entity.get_position(); + for (pos, nbt) in block_entities.iter() { let local_xz = ((get_local_cord(pos.0.x) & 0xF) << 4) | (get_local_cord(pos.0.z) & 0xF); write.write_u8(local_xz as u8)?; write.write_i16_be(pos.0.y as i16)?; - write.write_var_int(&VarInt(block_entity.get_id() as i32))?; - if let Some(nbt) = block_entity.chunk_data_nbt() { - write.write_nbt(nbt.into())?; - } else { - write.write_u8(END_ID)?; - } + let id = nbt.get_string("id").map_or(0, |id_str| { + let name = id_str.split(':').next_back().unwrap_or(id_str); + pumpkin_data::block_properties::BLOCK_ENTITY_TYPES + .iter() + .position(|&n| n == name) + .unwrap_or(0) + }); + + write.write_var_int(&VarInt(id as i32))?; + write.write_nbt(nbt.clone().into())?; } { diff --git a/pumpkin-protocol/src/java/server/play/mod.rs b/pumpkin-protocol/src/java/server/play/mod.rs index 12969aa73..0cd9b25e1 100644 --- a/pumpkin-protocol/src/java/server/play/mod.rs +++ b/pumpkin-protocol/src/java/server/play/mod.rs @@ -34,6 +34,7 @@ mod recipe_book_change_settings; mod recipe_book_seen_recipe; mod rename_item; mod select_trade; +mod set_beacon; mod set_command_block; mod set_creative_slot; mod set_held_item; @@ -78,6 +79,7 @@ pub use recipe_book_change_settings::*; pub use recipe_book_seen_recipe::*; pub use rename_item::*; pub use select_trade::*; +pub use set_beacon::*; pub use set_command_block::*; pub use set_creative_slot::*; pub use set_held_item::*; diff --git a/pumpkin-protocol/src/java/server/play/set_beacon.rs b/pumpkin-protocol/src/java/server/play/set_beacon.rs new file mode 100644 index 000000000..3312abec8 --- /dev/null +++ b/pumpkin-protocol/src/java/server/play/set_beacon.rs @@ -0,0 +1,12 @@ +use pumpkin_data::packet::serverbound::PLAY_SET_BEACON; +use pumpkin_macros::java_packet; +use serde::Deserialize; + +use crate::codec::var_int::VarInt; + +#[derive(Deserialize)] +#[java_packet(PLAY_SET_BEACON)] +pub struct SSetBeacon { + pub primary_effect: Option, + pub secondary_effect: Option, +} diff --git a/pumpkin-util/src/math/boundingbox.rs b/pumpkin-util/src/math/boundingbox.rs index 0785f403b..e868d3e60 100644 --- a/pumpkin-util/src/math/boundingbox.rs +++ b/pumpkin-util/src/math/boundingbox.rs @@ -94,6 +94,49 @@ impl BoundingBox { } } + /// Expands this bounding box towards a specific direction. + /// + /// If a provided value is negative, it extends the minimum boundary along that axis. + /// If a provided value is positive, it extends the maximum boundary along that axis. + /// + /// # Arguments + /// * `x` – Amount to expand towards on the X axis. + /// * `y` – Amount to expand towards on the Y axis. + /// * `z` – Amount to expand towards on the Z axis. + #[must_use] + pub fn expand_towards(&self, x: f64, y: f64, z: f64) -> Self { + let mut min_x = self.min.x; + let mut min_y = self.min.y; + let mut min_z = self.min.z; + + let mut max_x = self.max.x; + let mut max_y = self.max.y; + let mut max_z = self.max.z; + + if x < 0.0 { + min_x += x; + } else if x > 0.0 { + max_x += x; + } + + if y < 0.0 { + min_y += y; + } else if y > 0.0 { + max_y += y; + } + + if z < 0.0 { + min_z += z; + } else if z > 0.0 { + max_z += z; + } + + Self { + min: Vector3::new(min_x, min_y, min_z), + max: Vector3::new(max_x, max_y, max_z), + } + } + /// Expands this box uniformly along all axes. /// /// # Arguments diff --git a/pumpkin-world/src/block/entities.rs b/pumpkin-world/src/block/entities.rs new file mode 100644 index 000000000..9e6d71fb6 --- /dev/null +++ b/pumpkin-world/src/block/entities.rs @@ -0,0 +1,11 @@ +pub trait PropertyDelegate: Sync + Send { + fn get_property(&self, _index: i32) -> i32; + fn set_property(&self, _index: i32, _value: i32); + fn get_properties_size(&self) -> i32; +} + +/// Trait for extracting smelting experience from cooking block entities. +pub trait ExperienceContainer: Send + Sync { + /// Extract and reset accumulated experience, returning the total as an integer + fn extract_experience(&self) -> i32; +} diff --git a/pumpkin-world/src/block/entities/mod.rs b/pumpkin-world/src/block/entities/mod.rs deleted file mode 100644 index ba6323dde..000000000 --- a/pumpkin-world/src/block/entities/mod.rs +++ /dev/null @@ -1,209 +0,0 @@ -use std::pin::Pin; -use std::{any::Any, sync::Arc}; - -use barrel::BarrelBlockEntity; -use bed::BedBlockEntity; -use brewing_stand::BrewingStandBlockEntity; -use chest::ChestBlockEntity; -use comparator::ComparatorBlockEntity; -use daylight_detector::DaylightDetectorBlockEntity; -use end_portal::EndPortalBlockEntity; -use furnace::FurnaceBlockEntity; -use furnace_like_block_entity::ExperienceContainer; -use piston::PistonBlockEntity; -use pumpkin_data::{Block, block_properties::BLOCK_ENTITY_TYPES}; -use pumpkin_nbt::compound::NbtCompound; -use pumpkin_util::math::position::BlockPos; -use sign::SignBlockEntity; -use trapped_chest::TrappedChestBlockEntity; - -use crate::block::entities::bell::BellBlockEntity; -use crate::block::entities::blasting_furnace::BlastingFurnaceBlockEntity; -use crate::block::entities::command_block::CommandBlockEntity; -use crate::block::entities::ender_chest::EnderChestBlockEntity; -use crate::block::entities::hopper::HopperBlockEntity; -use crate::block::entities::jukebox::JukeboxBlockEntity; -use crate::block::entities::lectern::LecternBlockEntity; -use crate::block::entities::mob_spawner::MobSpawnerBlockEntity; -use crate::block::entities::shulker_box::ShulkerBoxBlockEntity; -use crate::block::entities::smoker::SmokerBlockEntity; -use crate::{ - BlockStateId, block::entities::chiseled_bookshelf::ChiseledBookshelfBlockEntity, - block::entities::dropper::DropperBlockEntity, inventory::Inventory, world::SimpleWorld, -}; - -pub mod barrel; -pub mod bed; -pub mod bell; -pub mod blasting_furnace; -pub mod brewing_stand; -pub mod chest; -pub mod chest_like_block_entity; -pub mod chiseled_bookshelf; -pub mod command_block; -pub mod comparator; -pub mod daylight_detector; -pub mod dropper; -pub mod end_portal; -pub mod ender_chest; -pub mod furnace; -pub mod furnace_like_block_entity; -pub mod hopper; -pub mod jukebox; -pub mod lectern; -pub mod mob_spawner; -pub mod piston; -pub mod shulker_box; -pub mod sign; -pub mod smoker; -pub mod trapped_chest; - -//TODO: We need a mark_dirty for chests -pub trait BlockEntity: Any + Send + Sync { - fn write_nbt<'a>( - &'a self, - nbt: &'a mut NbtCompound, - ) -> Pin + Send + 'a>>; - fn from_nbt(nbt: &NbtCompound, position: BlockPos) -> Self - where - Self: Sized; - fn tick<'a>( - &'a self, - _world: &'a Arc, - ) -> Pin + Send + 'a>> { - Box::pin(async {}) - } - fn resource_location(&self) -> &'static str; - fn get_position(&self) -> BlockPos; - fn write_internal<'a>( - &'a self, - nbt: &'a mut NbtCompound, - ) -> Pin + Send + 'a>> { - Box::pin(async move { - nbt.put_string("id", self.resource_location().to_string()); - let position = self.get_position(); - nbt.put_int("x", position.0.x); - nbt.put_int("y", position.0.y); - nbt.put_int("z", position.0.z); - self.write_nbt(nbt).await; - }) - } - fn get_id(&self) -> u32 { - pumpkin_data::block_properties::BLOCK_ENTITY_TYPES - .iter() - .position(|block_entity_name| { - *block_entity_name == self.resource_location().split(':').next_back().unwrap() - }) - .unwrap() as u32 - } - - /// Obtain NBT data for sending to the client in [`ChunkData`](crate::chunk::ChunkData) - fn chunk_data_nbt(&self) -> Option { - None - } - - fn get_inventory(self: Arc) -> Option> { - None - } - fn set_block_state(&mut self, _block_state: BlockStateId) {} - fn on_block_replaced<'a>( - self: Arc, - world: Arc, - position: BlockPos, - ) -> Pin + Send + 'a>> - where - Self: 'a, - { - Box::pin(async move { - if let Some(inventory) = self.get_inventory() { - // Assuming scatter_inventory is an async method on SimpleWorld - world.scatter_inventory(&position, &inventory).await; - } - }) - } - fn is_dirty(&self) -> bool { - false - } - - fn clear_dirty(&self) { - // Default implementation does nothing - // Override in implementations that have a dirty flag - } - - fn as_any(&self) -> &dyn Any; - fn to_property_delegate(self: Arc) -> Option> { - None - } - fn to_experience_container(self: Arc) -> Option> { - None - } -} - -#[must_use] -pub fn block_entity_from_generic(nbt: &NbtCompound) -> T { - let x = nbt.get_int("x").unwrap(); - let y = nbt.get_int("y").unwrap(); - let z = nbt.get_int("z").unwrap(); - T::from_nbt(nbt, BlockPos::new(x, y, z)) -} - -#[must_use] -pub fn block_entity_from_nbt(nbt: &NbtCompound) -> Option> { - Some(match nbt.get_string("id").unwrap() { - ChestBlockEntity::ID => Arc::new(block_entity_from_generic::(nbt)), - TrappedChestBlockEntity::ID => { - Arc::new(block_entity_from_generic::(nbt)) - } - EnderChestBlockEntity::ID => { - Arc::new(block_entity_from_generic::(nbt)) - } - JukeboxBlockEntity::ID => Arc::new(block_entity_from_generic::(nbt)), - SignBlockEntity::ID => Arc::new(block_entity_from_generic::(nbt)), - BedBlockEntity::ID => Arc::new(block_entity_from_generic::(nbt)), - ComparatorBlockEntity::ID => { - Arc::new(block_entity_from_generic::(nbt)) - } - BarrelBlockEntity::ID => Arc::new(block_entity_from_generic::(nbt)), - HopperBlockEntity::ID => Arc::new(block_entity_from_generic::(nbt)), - MobSpawnerBlockEntity::ID => { - Arc::new(block_entity_from_generic::(nbt)) - } - DropperBlockEntity::ID => Arc::new(block_entity_from_generic::(nbt)), - ShulkerBoxBlockEntity::ID => { - Arc::new(block_entity_from_generic::(nbt)) - } - PistonBlockEntity::ID => Arc::new(block_entity_from_generic::(nbt)), - EndPortalBlockEntity::ID => { - Arc::new(block_entity_from_generic::(nbt)) - } - ChiseledBookshelfBlockEntity::ID => Arc::new(block_entity_from_generic::< - ChiseledBookshelfBlockEntity, - >(nbt)), - FurnaceBlockEntity::ID => Arc::new(block_entity_from_generic::(nbt)), - CommandBlockEntity::ID => Arc::new(block_entity_from_generic::(nbt)), - BlastingFurnaceBlockEntity::ID => { - Arc::new(block_entity_from_generic::(nbt)) - } - SmokerBlockEntity::ID => Arc::new(block_entity_from_generic::(nbt)), - DaylightDetectorBlockEntity::ID => Arc::new(block_entity_from_generic::< - DaylightDetectorBlockEntity, - >(nbt)), - BrewingStandBlockEntity::ID => { - Arc::new(block_entity_from_generic::(nbt)) - } - BellBlockEntity::ID => Arc::new(block_entity_from_generic::(nbt)), - LecternBlockEntity::ID => Arc::new(block_entity_from_generic::(nbt)), - _ => return None, - }) -} - -#[must_use] -pub fn has_block_block_entity(block: &Block) -> bool { - BLOCK_ENTITY_TYPES.contains(&block.name) -} - -pub trait PropertyDelegate: Sync + Send { - fn get_property(&self, _index: i32) -> i32; - fn set_property(&self, _index: i32, _value: i32); - fn get_properties_size(&self) -> i32; -} diff --git a/pumpkin-world/src/block/viewer.rs b/pumpkin-world/src/block/viewer.rs index f05c5803c..f4f5242d7 100644 --- a/pumpkin-world/src/block/viewer.rs +++ b/pumpkin-world/src/block/viewer.rs @@ -1,19 +1,9 @@ -use std::{ - pin::Pin, - sync::{ - Arc, - atomic::{AtomicU16, Ordering}, - }, -}; - -use pumpkin_util::math::position::BlockPos; - -use crate::{block::entities::BlockEntity, world::SimpleWorld}; +use std::sync::atomic::{AtomicU16, Ordering}; #[derive(Debug)] pub struct ViewerCountTracker { - old: AtomicU16, - current: AtomicU16, + pub old: AtomicU16, + pub current: AtomicU16, } impl Default for ViewerCountTracker { @@ -43,67 +33,4 @@ impl ViewerCountTracker { pub fn get_viewer_count(&self) -> u16 { self.current.load(Ordering::Relaxed) } - - pub async fn update_viewer_count( - &self, - entity: &T, - world: &Arc, - position: &BlockPos, - ) where - T: BlockEntity + ViewerCountListener + 'static, - { - let current = self.current.load(Ordering::Relaxed); - let old = self.old.swap(current, Ordering::Relaxed); - if old != current { - match (old, current) { - (n, 0) if n > 0 => { - entity.on_container_close(world, position).await; - // TODO: world.emitGameEvent(player, GameEvent.CONTAINER_CLOSE, pos); - // TODO: this.maxBlockInteractionRange = 0.0; - } - (0, n) if n > 0 => { - entity.on_container_open(world, position).await; - // TODO: world.emitGameEvent(player, GameEvent.CONTAINER_OPEN, pos); - // TODO: scheduleBlockTick(world, pos, state); - } - _ => {} // Ignore - } - - entity - .on_viewer_count_update(world, position, old, current) - .await; - } - - // TODO: Requires players - } -} - -pub type ViewerFuture<'a, T> = Pin + Send + 'a>>; - -pub trait ViewerCountListener: Send + Sync { - fn on_container_open<'a>( - &'a self, - _world: &'a Arc, - _position: &'a BlockPos, - ) -> ViewerFuture<'a, ()> { - Box::pin(async {}) - } - - fn on_container_close<'a>( - &'a self, - _world: &'a Arc, - _position: &'a BlockPos, - ) -> ViewerFuture<'a, ()> { - Box::pin(async {}) - } - - fn on_viewer_count_update<'a>( - &'a self, - _world: &'a Arc, - _position: &'a BlockPos, - _old: u16, - _new: u16, - ) -> ViewerFuture<'a, ()> { - Box::pin(async {}) - } } diff --git a/pumpkin-world/src/chunk/format/mod.rs b/pumpkin-world/src/chunk/format/mod.rs index e4328d7fd..6998659aa 100644 --- a/pumpkin-world/src/chunk/format/mod.rs +++ b/pumpkin-world/src/chunk/format/mod.rs @@ -8,7 +8,6 @@ use std::{ }; use bytes::Bytes; -use futures::future::join_all; use pumpkin_data::{Block, chunk::ChunkStatus, fluid::Fluid}; use pumpkin_nbt::{compound::NbtCompound, nbt_long_array}; use rustc_hash::FxHashMap; @@ -16,7 +15,6 @@ use tokio::sync::Mutex; use tracing::debug; use crate::{ - block::entities::block_entity_from_nbt, chunk::{ ChunkEntityData, ChunkReadingError, ChunkSerializingError, format::anvil::{SingleChunkDataSerializer, WORLD_DATA_VERSION}, @@ -26,6 +24,7 @@ use crate::{ level::LevelFolder, tick::{ScheduledTick, scheduler::ChunkTickScheduler}, }; +use pumpkin_util::math::position::BlockPos; use pumpkin_util::math::vector2::Vector2; use serde::{Deserialize, Serialize}; @@ -67,36 +66,11 @@ impl Dirtiable for ChunkData { #[inline] fn mark_dirty(&self, flag: bool) { self.dirty.store(flag, Ordering::Relaxed); - - if flag { - return; - } - - // When marking chunk as clean, also clear all block entity dirty flags - if let Ok(block_entities) = self.block_entities.lock() { - for block_entity in block_entities.values() { - block_entity.clear_dirty(); - } - } } #[inline] fn is_dirty(&self) -> bool { - // Check if chunk itself is dirty - if self.dirty.load(Ordering::Relaxed) { - return true; - } - - // Also check if any block entities are dirty (e.g., inventory changes) - if let Ok(block_entities) = self.block_entities.lock() { - for block_entity in block_entities.values() { - if block_entity.is_dirty() { - return true; - } - } - } - - false + self.dirty.load(Ordering::Relaxed) } } @@ -180,12 +154,14 @@ impl ChunkData { dirty: AtomicBool::new(false), block_ticks: ChunkTickScheduler::from_iter(chunk_data.block_ticks), fluid_ticks: ChunkTickScheduler::from_iter(chunk_data.fluid_ticks), - block_entities: { + pending_block_entities: { let mut block_entities = FxHashMap::default(); for nbt in chunk_data.block_entities { - let block_entity = block_entity_from_nbt(&nbt); - if let Some(block_entity) = block_entity { - block_entities.insert(block_entity.get_position(), block_entity); + if let Some(x) = nbt.get_int("x") + && let Some(y) = nbt.get_int("y") + && let Some(z) = nbt.get_int("z") + { + block_entities.insert(BlockPos::new(x, y, z), nbt); } } std::sync::Mutex::new(block_entities) @@ -202,20 +178,11 @@ impl ChunkData { .light_populated .load(std::sync::atomic::Ordering::Relaxed); - let entities_to_serialize = { - let entities_guard = self.block_entities.lock().unwrap(); + let block_entities_nbt = { + let entities_guard = self.pending_block_entities.lock().unwrap(); entities_guard.values().cloned().collect::>() }; - let block_entities_nbt = join_all(entities_to_serialize.into_iter().map( - |block_entity| async move { - let mut nbt = NbtCompound::new(); - block_entity.write_internal(&mut nbt).await; - nbt - }, - )) - .await; - fn extract_light_ref(light: Option<&LightContainer>) -> Option<&[u8]> { match light { Some(LightContainer::Full(data)) => Some(data.as_ref()), diff --git a/pumpkin-world/src/chunk/mod.rs b/pumpkin-world/src/chunk/mod.rs index d780c9eeb..96883a916 100644 --- a/pumpkin-world/src/chunk/mod.rs +++ b/pumpkin-world/src/chunk/mod.rs @@ -1,5 +1,4 @@ use crate::BlockStateId; -use crate::block::entities::BlockEntity; use crate::chunk::format::LightContainer; use crate::tick::scheduler::ChunkTickScheduler; use palette::{BiomePalette, BlockPalette, has_random_ticking_fluid}; @@ -8,12 +7,13 @@ use pumpkin_data::chunk::ChunkStatus; use pumpkin_data::fluid::Fluid; use pumpkin_data::tag::Block::MINECRAFT_LEAVES; use pumpkin_data::{Block, BlockState}; +use pumpkin_nbt::compound::NbtCompound; use pumpkin_nbt::nbt_long_array; use pumpkin_util::math::position::BlockPos; use rustc_hash::FxHashMap; use serde::{Deserialize, Serialize}; use std::sync::RwLock; -use std::sync::{Arc, atomic::AtomicBool}; +use std::sync::atomic::AtomicBool; use thiserror::Error; use tokio::sync::Mutex; @@ -76,7 +76,7 @@ pub struct ChunkData { pub z: i32, pub block_ticks: ChunkTickScheduler<&'static Block>, pub fluid_ticks: ChunkTickScheduler<&'static Fluid>, - pub block_entities: std::sync::Mutex>>, + pub pending_block_entities: std::sync::Mutex>, pub light_engine: std::sync::Mutex, pub light_populated: AtomicBool, pub status: ChunkStatus, diff --git a/pumpkin-world/src/chunk_system/chunk_state.rs b/pumpkin-world/src/chunk_system/chunk_state.rs index 309493f8b..57b02b74d 100644 --- a/pumpkin-world/src/chunk_system/chunk_state.rs +++ b/pumpkin-world/src/chunk_system/chunk_state.rs @@ -1,4 +1,3 @@ -use crate::block::entities::block_entity_from_nbt; use crate::chunk::{ChunkData, ChunkLight, ChunkSections}; use crate::generation::biome_coords; use pumpkin_config::lighting::LightingEngineConfig; @@ -225,7 +224,7 @@ impl Chunk { z: 0, block_ticks: Default::default(), fluid_ticks: Default::default(), - block_entities: Default::default(), + pending_block_entities: Default::default(), light_engine: Mutex::new(ChunkLight::default()), light_populated: AtomicBool::new(false), status: ChunkStatus::Empty, @@ -298,11 +297,14 @@ impl Chunk { && *lighting_config == LightingEngineConfig::Default; // Convert pending block entities from structure generation to actual block entities - let mut block_entities = FxHashMap::default(); + let mut pending_block_entities = FxHashMap::default(); for nbt in proto_chunk.pending_block_entities { - if let Some(block_entity) = block_entity_from_nbt(&nbt) { - let pos = block_entity.get_position(); - block_entities.insert(pos, block_entity); + if let Some(x) = nbt.get_int("x") + && let Some(y) = nbt.get_int("y") + && let Some(z) = nbt.get_int("z") + { + pending_block_entities + .insert(pumpkin_util::math::position::BlockPos::new(x, y, z), nbt); } } @@ -316,7 +318,7 @@ impl Chunk { dirty: AtomicBool::new(true), block_ticks: Default::default(), fluid_ticks: Default::default(), - block_entities: Mutex::new(block_entities), + pending_block_entities: Mutex::new(pending_block_entities), status: proto_chunk.stage.into(), blending_data: proto_chunk.blending_data.clone(), }; diff --git a/pumpkin-world/src/generation/feature/features/monster_room.rs b/pumpkin-world/src/generation/feature/features/monster_room.rs index 4c088866a..d9568557f 100644 --- a/pumpkin-world/src/generation/feature/features/monster_room.rs +++ b/pumpkin-world/src/generation/feature/features/monster_room.rs @@ -1,13 +1,11 @@ -use pumpkin_data::{Block, BlockDirection, entity::EntityType}; +use pumpkin_data::{Block, BlockDirection}; use pumpkin_nbt::compound::NbtCompound; use pumpkin_util::{ math::{position::BlockPos, vector3::Vector3}, random::{RandomGenerator, RandomImpl}, }; -use crate::{ - block::entities::mob_spawner::MobSpawnerBlockEntity, generation::proto_chunk::GenerationCache, -}; +use crate::generation::proto_chunk::GenerationCache; /// The three mob types that can appear in a dungeon spawner. /// @@ -148,9 +146,19 @@ impl DungeonFeature { // TODO: set spawner entity type let mob = DUNGEON_MOBS[random.next_bounded_i32(DUNGEON_MOBS.len() as i32) as usize]; chunk.set_block_state(&pos.0, Block::SPAWNER.default_state); - let spawner_block_entity = MobSpawnerBlockEntity::new(pos, EntityType::from_name(mob)); + let mut entity_nbt = NbtCompound::new(); - spawner_block_entity.write_nbt(&mut entity_nbt); + entity_nbt.put_string("id", "minecraft:mob_spawner".to_string()); + entity_nbt.put_int("x", pos.0.x); + entity_nbt.put_int("y", pos.0.y); + entity_nbt.put_int("z", pos.0.z); + + let mut spawn_entry = NbtCompound::new(); + let mut entity_nbt_inner = NbtCompound::new(); + entity_nbt_inner.put_string("id", mob.to_string()); + spawn_entry.put_compound("entity", entity_nbt_inner); + entity_nbt.put_compound("SpawnData", spawn_entry); + chunk.add_block_entity(&pos.0, entity_nbt); true diff --git a/pumpkin-world/src/generation/structure/structures/nether_fortress/bridge_platform.rs b/pumpkin-world/src/generation/structure/structures/nether_fortress/bridge_platform.rs index 14e474b01..ac7c4669e 100644 --- a/pumpkin-world/src/generation/structure/structures/nether_fortress/bridge_platform.rs +++ b/pumpkin-world/src/generation/structure/structures/nether_fortress/bridge_platform.rs @@ -1,18 +1,12 @@ use pumpkin_data::{ Block, block_properties::{BlockProperties, OakFenceLikeProperties}, - entity::EntityType, }; use pumpkin_nbt::compound::NbtCompound; -use pumpkin_util::{ - BlockDirection, - math::{block_box::BlockBox, position::BlockPos}, - random::RandomGenerator, -}; +use pumpkin_util::{BlockDirection, math::block_box::BlockBox, random::RandomGenerator}; use crate::{ ProtoChunk, - block::entities::mob_spawner::MobSpawnerBlockEntity, generation::structure::{ piece::StructurePieceType, structures::{ @@ -147,10 +141,18 @@ impl StructurePieceBase for BridgePlatformPiece { spawner_pos.z, Block::SPAWNER.default_state, ); - let spawner_block_entity = - MobSpawnerBlockEntity::new(BlockPos(spawner_pos), Some(&EntityType::BLAZE)); let mut entity_nbt = NbtCompound::new(); - spawner_block_entity.write_nbt(&mut entity_nbt); + entity_nbt.put_string("id", "minecraft:mob_spawner".to_string()); + entity_nbt.put_int("x", spawner_pos.x); + entity_nbt.put_int("y", spawner_pos.y); + entity_nbt.put_int("z", spawner_pos.z); + + let mut spawn_entry = NbtCompound::new(); + let mut entity_nbt_inner = NbtCompound::new(); + entity_nbt_inner.put_string("id", "minecraft:blaze".to_string()); + spawn_entry.put_compound("entity", entity_nbt_inner); + entity_nbt.put_compound("SpawnData", spawn_entry); + chunk.add_block_entity(entity_nbt); } } diff --git a/pumpkin-world/src/generation/structure/structures/stronghold/portal_room.rs b/pumpkin-world/src/generation/structure/structures/stronghold/portal_room.rs index e5084b81c..b7ace36ea 100644 --- a/pumpkin-world/src/generation/structure/structures/stronghold/portal_room.rs +++ b/pumpkin-world/src/generation/structure/structures/stronghold/portal_room.rs @@ -4,18 +4,16 @@ use pumpkin_data::{ BlockProperties, EndPortalFrameLikeProperties, HorizontalFacing, OakFenceLikeProperties, OakStairsLikeProperties, }, - entity::EntityType, }; use pumpkin_nbt::compound::NbtCompound; use pumpkin_util::{ BlockDirection, - math::{block_box::BlockBox, position::BlockPos}, + math::block_box::BlockBox, random::{RandomGenerator, RandomImpl}, }; use crate::{ ProtoChunk, - block::entities::mob_spawner::MobSpawnerBlockEntity, generation::structure::{ piece::StructurePieceType, structures::{ @@ -358,10 +356,18 @@ impl StructurePieceBase for PortalRoomPiece { self.spawner_placed = true; let spawner = Block::SPAWNER.default_state; inner.add_block(chunk, spawner, 5, 3, 6, &box_limit); - let spawner_block_entity = - MobSpawnerBlockEntity::new(BlockPos(pos), Some(&EntityType::SILVERFISH)); let mut entity_nbt = NbtCompound::new(); - spawner_block_entity.write_nbt(&mut entity_nbt); + entity_nbt.put_string("id", "minecraft:mob_spawner".to_string()); + entity_nbt.put_int("x", pos.x); + entity_nbt.put_int("y", pos.y); + entity_nbt.put_int("z", pos.z); + + let mut spawn_entry = NbtCompound::new(); + let mut entity_nbt_inner = NbtCompound::new(); + entity_nbt_inner.put_string("id", "minecraft:silverfish".to_string()); + spawn_entry.put_compound("entity", entity_nbt_inner); + entity_nbt.put_compound("SpawnData", spawn_entry); + chunk.add_block_entity(entity_nbt); } } diff --git a/pumpkin-world/src/level.rs b/pumpkin-world/src/level.rs index 436df4b14..8dc371c4e 100644 --- a/pumpkin-world/src/level.rs +++ b/pumpkin-world/src/level.rs @@ -5,7 +5,7 @@ use crate::generation::generator::VanillaGenerator; use crate::lighting::DynamicLightEngine; use crate::{ BlockStateId, - block::{RawBlockState, entities::BlockEntity}, + block::RawBlockState, chunk::{ ChunkData, ChunkEntityData, ChunkReadingError, format::anvil::AnvilChunkFile, @@ -110,7 +110,6 @@ pub struct TickData { pub block_ticks: Vec>, pub fluid_ticks: Vec>, pub random_ticks: Vec, - pub block_entities: Vec>, } #[derive(Clone, Copy)] @@ -446,7 +445,6 @@ impl Level { block_ticks: Vec::new(), fluid_ticks: Vec::new(), random_ticks: Vec::with_capacity(active_chunks.len() * 3), - block_entities: Vec::new(), }; // 1. Process active chunks (random ticks, block entities) @@ -457,10 +455,6 @@ impl Level { let chunk_z_base = chunk.z * 16; let section_count = chunk.section.count; - ticks - .block_entities - .extend(chunk.block_entities.lock().unwrap().values().cloned()); - // Use the bitmask to skip sections let mask = chunk.section.randomly_ticking_mask.load(Ordering::Relaxed); if mask != 0 { diff --git a/pumpkin-world/src/world.rs b/pumpkin-world/src/world.rs index 4ff339572..59c629a94 100644 --- a/pumpkin-world/src/world.rs +++ b/pumpkin-world/src/world.rs @@ -1,17 +1,9 @@ use std::pin::Pin; -use std::sync::Arc; -use crate::block::entities::BlockEntity; -use crate::{BlockStateId, inventory::Inventory, level::Level}; +use crate::BlockStateId; use bitflags::bitflags; -use pumpkin_data::dimension::Dimension; -use pumpkin_data::entity::EntityType; -use pumpkin_data::sound::{Sound, SoundCategory}; -use pumpkin_data::world::WorldEvent; -use pumpkin_data::{Block, BlockDirection, BlockState}; -use pumpkin_util::math::boundingbox::BoundingBox; +use pumpkin_data::{Block, BlockState}; use pumpkin_util::math::position::BlockPos; -use pumpkin_util::math::vector3::Vector3; use thiserror::Error; bitflags! { @@ -63,97 +55,6 @@ impl std::fmt::Display for GetBlockError { pub type WorldFuture<'a, T> = Pin + Send + 'a>>; -pub trait SimpleWorld: BlockAccessor + Send + Sync { - fn set_block_state( - self: Arc, - position: &BlockPos, - block_state_id: BlockStateId, - flags: BlockFlags, - ) -> WorldFuture<'_, BlockStateId>; - - fn update_neighbor<'a>( - self: Arc, - neighbor_block_pos: &'a BlockPos, - source_block: &'a pumpkin_data::Block, - ) -> WorldFuture<'a, ()>; - - fn update_neighbors( - self: Arc, - block_pos: &BlockPos, - except: Option, - ) -> WorldFuture<'_, ()>; - - fn is_space_empty(&self, bounding_box: BoundingBox) -> WorldFuture<'_, bool>; - - fn spawn_from_type( - self: Arc, - entity_type: &'static EntityType, - position: Vector3, - ) -> WorldFuture<'static, ()>; - - fn add_synced_block_event(&self, pos: BlockPos, r#type: u8, data: u8) -> WorldFuture<'_, ()>; - - fn sync_world_event( - &self, - world_event: WorldEvent, - position: BlockPos, - data: i32, - ) -> WorldFuture<'_, ()>; - - fn remove_block_entity<'a>(&'a self, block_pos: &'a BlockPos) -> WorldFuture<'a, ()>; - - fn get_block_entity<'a>( - &'a self, - block_pos: &'a BlockPos, - ) -> WorldFuture<'a, Option>>; - - fn get_world_age(&self) -> WorldFuture<'_, i64>; - - fn get_time_of_day(&self) -> WorldFuture<'_, i64>; - - fn get_level(&self) -> WorldFuture<'_, &Arc>; - - fn get_dimension(&self) -> WorldFuture<'_, &Dimension>; - - fn play_sound<'a>( - &'a self, - sound: Sound, - category: SoundCategory, - position: &'a Vector3, - ) -> WorldFuture<'a, ()>; - - fn play_sound_fine<'a>( - &'a self, - sound: Sound, - category: SoundCategory, - position: &'a Vector3, - volume: f32, - pitch: f32, - ) -> WorldFuture<'a, ()>; - - /* ItemScatterer */ - fn scatter_inventory<'a>( - self: Arc, - position: &'a BlockPos, - inventory: &'a Arc, - ) -> WorldFuture<'a, ()>; - - /// Spawn experience orbs at the given position with the specified amount - fn spawn_experience_orbs( - self: Arc, - position: Vector3, - amount: u32, - ) -> WorldFuture<'static, ()>; - - /// `Block.updateFromNeighbourShapes`: updates a block state by calling - /// `get_state_for_neighbor_update` on itself for each of the 6 directions. - fn update_from_neighbor_shapes( - self: Arc, - block_state_id: BlockStateId, - position: &BlockPos, - ) -> WorldFuture<'_, BlockStateId>; -} - pub trait BlockRegistryExt: Send + Sync { fn can_place_at( &self, diff --git a/pumpkin/Cargo.toml b/pumpkin/Cargo.toml index 3ddc1a853..3f1c48f75 100644 --- a/pumpkin/Cargo.toml +++ b/pumpkin/Cargo.toml @@ -92,7 +92,7 @@ rustc-hash.workspace = true tokio-util = { workspace = true, features = ["rt"] } # System details & Rustc version -sysinfo = "0.38" +sysinfo = "0.39" rustc_version_runtime = "0.3" flate2.workspace = true diff --git a/pumpkin/src/block/blocks/barrel.rs b/pumpkin/src/block/blocks/barrel.rs index e8366c351..f56ef1658 100644 --- a/pumpkin/src/block/blocks/barrel.rs +++ b/pumpkin/src/block/blocks/barrel.rs @@ -6,6 +6,7 @@ use crate::block::{ {BlockBehaviour, NormalUseArgs}, }; +use crate::block::entities::barrel::BarrelBlockEntity; use pumpkin_data::block_properties::{BarrelLikeProperties, BlockProperties}; use pumpkin_data::translation; use pumpkin_inventory::generic_container_screen_handler::create_generic_9x3; @@ -16,7 +17,6 @@ use pumpkin_inventory::screen_handler::{ use pumpkin_macros::pumpkin_block; use pumpkin_util::text::TextComponent; use pumpkin_world::BlockStateId; -use pumpkin_world::block::entities::barrel::BarrelBlockEntity; use pumpkin_world::inventory::Inventory; use tokio::sync::Mutex; diff --git a/pumpkin/src/block/blocks/beacon.rs b/pumpkin/src/block/blocks/beacon.rs new file mode 100644 index 000000000..7baeb581b --- /dev/null +++ b/pumpkin/src/block/blocks/beacon.rs @@ -0,0 +1,69 @@ +use std::sync::Arc; +use tokio::sync::Mutex; + +use crate::block::entities::BlockEntity; +use pumpkin_data::translation; +use pumpkin_inventory::player::player_inventory::PlayerInventory; +use pumpkin_inventory::screen_handler::{ + BoxFuture, InventoryPlayer, ScreenHandlerFactory, SharedScreenHandler, +}; +use pumpkin_macros::pumpkin_block; +use pumpkin_util::text::TextComponent; +use pumpkin_world::inventory::Inventory; + +use crate::block::registry::BlockActionResult; +use crate::block::{BlockBehaviour, BlockFuture, NormalUseArgs}; + +// Create the factory just like ChestScreenFactory +struct BeaconScreenFactory(Arc); + +impl ScreenHandlerFactory for BeaconScreenFactory { + fn create_screen_handler<'a>( + &'a self, + sync_id: u8, + player_inventory: &'a Arc, + _player: &'a dyn InventoryPlayer, + ) -> BoxFuture<'a, Option> { + Box::pin(async move { + // Assumes create_beacon_handler exists in your generic_container_screen_handler equivalent + use pumpkin_inventory::beacon_screen_handler::create_beacon_handler; + + let concrete_handler = + create_beacon_handler(sync_id, player_inventory, self.0.clone()).await; + let concrete_arc = Arc::new(Mutex::new(concrete_handler)); + + Some(concrete_arc as SharedScreenHandler) + }) + } + + fn get_display_name(&self) -> TextComponent { + TextComponent::translate_cross( + translation::java::CONTAINER_BEACON, + translation::bedrock::CONTAINER_BEACON, + &[], + ) + } +} + +#[pumpkin_block("minecraft:beacon")] +pub struct BeaconBlock; + +impl BlockBehaviour for BeaconBlock { + fn normal_use<'a>(&'a self, args: NormalUseArgs<'a>) -> BlockFuture<'a, BlockActionResult> { + Box::pin(async move { + let block_entity = args.world.get_block_entity(args.position).await; + + // Extract the inventory from the entity + let Some(inventory) = block_entity.and_then(BlockEntity::get_inventory) else { + return BlockActionResult::Fail; + }; + + // Open the screen using the factory + args.player + .open_handled_screen(&BeaconScreenFactory(inventory), Some(*args.position)) + .await; + + BlockActionResult::Success + }) + } +} diff --git a/pumpkin/src/block/blocks/bed.rs b/pumpkin/src/block/blocks/bed.rs index baa847b6c..a569d42c5 100644 --- a/pumpkin/src/block/blocks/bed.rs +++ b/pumpkin/src/block/blocks/bed.rs @@ -1,5 +1,6 @@ use std::sync::Arc; +use crate::block::entities::bed::BedBlockEntity; use pumpkin_data::Block; use pumpkin_data::block_properties::BedPart; use pumpkin_data::block_properties::BlockProperties; @@ -11,7 +12,6 @@ use pumpkin_util::GameMode; use pumpkin_util::math::position::BlockPos; use pumpkin_util::text::TextComponent; use pumpkin_world::BlockStateId; -use pumpkin_world::block::entities::bed::BedBlockEntity; use pumpkin_world::world::BlockFlags; use crate::block::BlockFuture; diff --git a/pumpkin/src/block/blocks/blast_furnace.rs b/pumpkin/src/block/blocks/blast_furnace.rs index 3034e48a4..4dc521dd6 100644 --- a/pumpkin/src/block/blocks/blast_furnace.rs +++ b/pumpkin/src/block/blocks/blast_furnace.rs @@ -1,5 +1,9 @@ use std::sync::Arc; +use crate::block::entities::{ + PropertyDelegate, blasting_furnace::BlastingFurnaceBlockEntity, + furnace_like_block_entity::ExperienceContainer, +}; use pumpkin_data::{ block_properties::{BlockProperties, FurnaceLikeProperties}, screen::WindowType, @@ -12,14 +16,7 @@ use pumpkin_inventory::{ }; use pumpkin_macros::pumpkin_block; use pumpkin_util::text::TextComponent; -use pumpkin_world::{ - BlockStateId, - block::entities::{ - PropertyDelegate, blasting_furnace::BlastingFurnaceBlockEntity, - furnace_like_block_entity::ExperienceContainer, - }, - inventory::Inventory, -}; +use pumpkin_world::{BlockStateId, inventory::Inventory}; use tokio::sync::Mutex; use crate::{ diff --git a/pumpkin/src/block/blocks/brewing_stand.rs b/pumpkin/src/block/blocks/brewing_stand.rs index 320eaf3a9..336c36674 100644 --- a/pumpkin/src/block/blocks/brewing_stand.rs +++ b/pumpkin/src/block/blocks/brewing_stand.rs @@ -6,17 +6,17 @@ use crate::block::{ {BlockBehaviour, NormalUseArgs}, }; +use crate::block::entities::brewing_stand::BrewingStandBlockEntity; use pumpkin_data::translation; use pumpkin_inventory::player::player_inventory::PlayerInventory; use pumpkin_inventory::screen_handler::{BoxFuture, ScreenHandlerFactory, SharedScreenHandler}; use pumpkin_macros::pumpkin_block; use pumpkin_util::text::TextComponent; -use pumpkin_world::block::entities::brewing_stand::BrewingStandBlockEntity; use pumpkin_world::inventory::Inventory; struct BrewingScreenFactory( Arc, - Arc, + Arc, ); impl ScreenHandlerFactory for BrewingScreenFactory { diff --git a/pumpkin/src/block/blocks/chests.rs b/pumpkin/src/block/blocks/chests.rs index 592af3cd1..69b85ceea 100644 --- a/pumpkin/src/block/blocks/chests.rs +++ b/pumpkin/src/block/blocks/chests.rs @@ -1,5 +1,7 @@ use std::sync::Arc; +use crate::block::entities::BlockEntity; +use crate::block::entities::chest::ChestBlockEntity; use futures::future::join; use pumpkin_data::block_properties::{ BlockProperties, ChestLikeProperties, ChestType, HorizontalFacing, @@ -16,8 +18,6 @@ use pumpkin_macros::{pumpkin_block, pumpkin_block_from_tag}; use pumpkin_util::math::position::BlockPos; use pumpkin_util::text::TextComponent; use pumpkin_world::BlockStateId; -use pumpkin_world::block::entities::BlockEntity; -use pumpkin_world::block::entities::chest::ChestBlockEntity; use pumpkin_world::inventory::Inventory; use pumpkin_world::world::BlockFlags; use tokio::sync::Mutex; @@ -445,7 +445,7 @@ impl BlockBehaviour for TrappedChestBlock { } fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> { - use pumpkin_world::block::entities::trapped_chest::TrappedChestBlockEntity; + use crate::block::entities::trapped_chest::TrappedChestBlockEntity; Box::pin(placed_chest_impl(args, TrappedChestBlockEntity::new)) } @@ -469,7 +469,7 @@ impl BlockBehaviour for TrappedChestBlock { args: GetRedstonePowerArgs<'a>, ) -> BlockFuture<'a, u8> { Box::pin(async move { - use pumpkin_world::block::entities::trapped_chest::TrappedChestBlockEntity; + use crate::block::entities::trapped_chest::TrappedChestBlockEntity; // Get viewer count from this chest let viewer_count = if let Some(block_entity) = diff --git a/pumpkin/src/block/blocks/chiseled_bookshelf.rs b/pumpkin/src/block/blocks/chiseled_bookshelf.rs index e8c2948f6..8e951b010 100644 --- a/pumpkin/src/block/blocks/chiseled_bookshelf.rs +++ b/pumpkin/src/block/blocks/chiseled_bookshelf.rs @@ -2,6 +2,7 @@ use std::sync::{Arc, atomic::Ordering}; use pumpkin_macros::pumpkin_block; +use crate::block::entities::chiseled_bookshelf::ChiseledBookshelfBlockEntity; use crate::{ block::{ BlockBehaviour, BlockFuture, BlockHitResult, GetComparatorOutputArgs, NormalUseArgs, @@ -20,10 +21,7 @@ use pumpkin_data::{ }; use pumpkin_inventory::screen_handler::InventoryPlayer; use pumpkin_util::math::{position::BlockPos, vector2::Vector2}; -use pumpkin_world::{ - BlockStateId, block::entities::chiseled_bookshelf::ChiseledBookshelfBlockEntity, - inventory::Inventory, -}; +use pumpkin_world::{BlockStateId, inventory::Inventory}; use tokio::sync::Mutex; #[pumpkin_block("minecraft:chiseled_bookshelf")] diff --git a/pumpkin/src/block/blocks/command.rs b/pumpkin/src/block/blocks/command.rs index fcd0c01a4..1db1093ab 100644 --- a/pumpkin/src/block/blocks/command.rs +++ b/pumpkin/src/block/blocks/command.rs @@ -1,6 +1,7 @@ use std::sync::{Arc, atomic::Ordering}; use super::redstone::block_receives_redstone_power; +use crate::block::entities::{BlockEntity, command_block::CommandBlockEntity}; use crate::command::CommandSender; use crate::{ block::{ @@ -16,11 +17,7 @@ use pumpkin_data::{ block_properties::{BlockProperties, CommandBlockLikeProperties, Facing}, }; use pumpkin_util::{GameMode, PermissionLvl, math::position::BlockPos}; -use pumpkin_world::{ - BlockStateId, - block::entities::{BlockEntity, command_block::CommandBlockEntity}, - tick::TickPriority, -}; +use pumpkin_world::{BlockStateId, tick::TickPriority}; use tracing::warn; pub struct CommandBlock; diff --git a/pumpkin/src/block/blocks/end_portal.rs b/pumpkin/src/block/blocks/end_portal.rs index 8d249a8f5..ad672b96a 100644 --- a/pumpkin/src/block/blocks/end_portal.rs +++ b/pumpkin/src/block/blocks/end_portal.rs @@ -4,9 +4,9 @@ use crate::block::BlockBehaviour; use crate::block::BlockFuture; use crate::block::OnEntityCollisionArgs; use crate::block::PlacedArgs; +use crate::block::entities::end_portal::EndPortalBlockEntity; use pumpkin_data::dimension::Dimension; use pumpkin_macros::pumpkin_block; -use pumpkin_world::block::entities::end_portal::EndPortalBlockEntity; #[pumpkin_block("minecraft:end_portal")] pub struct EndPortalBlock; diff --git a/pumpkin/src/block/blocks/ender_chest.rs b/pumpkin/src/block/blocks/ender_chest.rs index d053e18bf..a9444d78d 100644 --- a/pumpkin/src/block/blocks/ender_chest.rs +++ b/pumpkin/src/block/blocks/ender_chest.rs @@ -1,5 +1,6 @@ use std::sync::Arc; +use crate::block::entities::ender_chest::EnderChestBlockEntity; use crate::block::{ BlockBehaviour, BlockFuture, NormalUseArgs, OnPlaceArgs, OnSyncedBlockEventArgs, PlacedArgs, registry::BlockActionResult, @@ -15,9 +16,7 @@ use pumpkin_inventory::{ use pumpkin_macros::pumpkin_block; use pumpkin_util::math::position::BlockPos; use pumpkin_util::text::TextComponent; -use pumpkin_world::{ - BlockStateId, block::entities::ender_chest::EnderChestBlockEntity, inventory::Inventory, -}; +use pumpkin_world::{BlockStateId, inventory::Inventory}; use tokio::sync::Mutex; struct EnderChestScreenFactory(Arc); diff --git a/pumpkin/src/block/blocks/furnace.rs b/pumpkin/src/block/blocks/furnace.rs index 5197957fd..d0971fb47 100644 --- a/pumpkin/src/block/blocks/furnace.rs +++ b/pumpkin/src/block/blocks/furnace.rs @@ -1,5 +1,8 @@ use std::sync::Arc; +use crate::block::entities::{ + PropertyDelegate, furnace::FurnaceBlockEntity, furnace_like_block_entity::ExperienceContainer, +}; use pumpkin_data::{ block_properties::{BlockProperties, FurnaceLikeProperties}, screen::WindowType, @@ -12,14 +15,7 @@ use pumpkin_inventory::{ }; use pumpkin_macros::pumpkin_block; use pumpkin_util::text::TextComponent; -use pumpkin_world::{ - BlockStateId, - block::entities::{ - PropertyDelegate, furnace::FurnaceBlockEntity, - furnace_like_block_entity::ExperienceContainer, - }, - inventory::Inventory, -}; +use pumpkin_world::{BlockStateId, inventory::Inventory}; use tokio::sync::Mutex; use crate::{ diff --git a/pumpkin/src/block/blocks/hopper.rs b/pumpkin/src/block/blocks/hopper.rs index 2b65f4237..3e507cb2a 100644 --- a/pumpkin/src/block/blocks/hopper.rs +++ b/pumpkin/src/block/blocks/hopper.rs @@ -8,6 +8,7 @@ use crate::block::{ }; use crate::world::World; +use crate::block::entities::hopper::HopperBlockEntity; use pumpkin_data::block_properties::{BlockProperties, FacingHopper}; use pumpkin_data::{Block, BlockDirection, translation}; use pumpkin_inventory::generic_container_screen_handler::create_hopper; @@ -19,7 +20,6 @@ use pumpkin_macros::pumpkin_block; use pumpkin_util::math::position::BlockPos; use pumpkin_util::text::TextComponent; use pumpkin_world::BlockStateId; -use pumpkin_world::block::entities::hopper::HopperBlockEntity; use pumpkin_world::inventory::Inventory; use pumpkin_world::world::BlockFlags; use tokio::sync::Mutex; diff --git a/pumpkin/src/block/blocks/jukebox.rs b/pumpkin/src/block/blocks/jukebox.rs index 79593183d..0a615bfd4 100644 --- a/pumpkin/src/block/blocks/jukebox.rs +++ b/pumpkin/src/block/blocks/jukebox.rs @@ -1,5 +1,6 @@ use std::sync::Arc; +use crate::block::entities::jukebox::JukeboxBlockEntity; use crate::block::registry::BlockActionResult; use crate::block::{ BlockBehaviour, BlockFuture, BrokenArgs, EmitsRedstonePowerArgs, GetComparatorOutputArgs, @@ -19,7 +20,6 @@ use pumpkin_data::{ use pumpkin_macros::pumpkin_block; use pumpkin_util::math::position::BlockPos; use pumpkin_util::math::vector3::Vector3; -use pumpkin_world::block::entities::jukebox::JukeboxBlockEntity; use pumpkin_world::world::BlockFlags; use rand::{RngExt, rng}; diff --git a/pumpkin/src/block/blocks/lectern.rs b/pumpkin/src/block/blocks/lectern.rs index d79b310a5..0f413e8eb 100644 --- a/pumpkin/src/block/blocks/lectern.rs +++ b/pumpkin/src/block/blocks/lectern.rs @@ -1,5 +1,6 @@ use std::sync::Arc; +use crate::block::entities::lectern::LecternBlockEntity; use crate::block::registry::BlockActionResult; use crate::block::{ BlockBehaviour, BlockFuture, BrokenArgs, NormalUseArgs, OnPlaceArgs, PlacedArgs, @@ -15,7 +16,6 @@ use pumpkin_macros::pumpkin_block; use pumpkin_util::math::position::BlockPos; use pumpkin_util::math::vector3::Vector3; use pumpkin_world::BlockStateId; -use pumpkin_world::block::entities::lectern::LecternBlockEntity; use pumpkin_world::inventory::Inventory; use pumpkin_world::world::BlockFlags; diff --git a/pumpkin/src/block/blocks/mod.rs b/pumpkin/src/block/blocks/mod.rs index cfffb4efc..9977a0bc6 100644 --- a/pumpkin/src/block/blocks/mod.rs +++ b/pumpkin/src/block/blocks/mod.rs @@ -97,3 +97,5 @@ pub mod tnt; // Misc / abstract pub mod abstract_wall_mounting; + +pub mod beacon; diff --git a/pumpkin/src/block/blocks/piston/piston.rs b/pumpkin/src/block/blocks/piston/piston.rs index 074e6ee91..ec8e1d384 100644 --- a/pumpkin/src/block/blocks/piston/piston.rs +++ b/pumpkin/src/block/blocks/piston/piston.rs @@ -1,5 +1,6 @@ use std::sync::Arc; +use crate::block::entities::{has_block_block_entity, piston::PistonBlockEntity}; use pumpkin_data::{ Block, BlockDirection, BlockState, FacingExt, block_properties::{ @@ -9,11 +10,7 @@ use pumpkin_data::{ sound::{Sound, SoundCategory}, }; use pumpkin_util::math::position::BlockPos; -use pumpkin_world::{ - BlockStateId, - block::entities::{has_block_block_entity, piston::PistonBlockEntity}, - world::BlockFlags, -}; +use pumpkin_world::{BlockStateId, world::BlockFlags}; use rand::RngExt; use rustc_hash::FxHashMap; diff --git a/pumpkin/src/block/blocks/redstone/bell.rs b/pumpkin/src/block/blocks/redstone/bell.rs index 0ebe3cb3f..396186429 100644 --- a/pumpkin/src/block/blocks/redstone/bell.rs +++ b/pumpkin/src/block/blocks/redstone/bell.rs @@ -1,6 +1,7 @@ use crate::Arc; use crate::block::blocks::abstract_wall_mounting::WallMountedBlock; use crate::block::blocks::redstone::block_receives_redstone_power; +use crate::block::entities::bell::BellBlockEntity; use crate::block::registry::BlockActionResult; use crate::block::{ BlockBehaviour, BlockFuture, BlockHitResult, BrokenArgs, CanPlaceAtArgs, NormalUseArgs, @@ -18,7 +19,6 @@ use pumpkin_data::{HorizontalFacingExt, tag}; use pumpkin_macros::pumpkin_block; use pumpkin_util::math::position::BlockPos; use pumpkin_world::BlockStateId; -use pumpkin_world::block::entities::bell::BellBlockEntity; use pumpkin_world::world::BlockFlags; async fn ring_bell( diff --git a/pumpkin/src/block/blocks/redstone/comparator.rs b/pumpkin/src/block/blocks/redstone/comparator.rs index b214a7d0b..3afbcf43a 100644 --- a/pumpkin/src/block/blocks/redstone/comparator.rs +++ b/pumpkin/src/block/blocks/redstone/comparator.rs @@ -1,5 +1,6 @@ use std::sync::{Arc, atomic::Ordering}; +use crate::block::entities::comparator::ComparatorBlockEntity; use pumpkin_data::{ Block, BlockDirection, BlockState, block_properties::{ @@ -9,10 +10,7 @@ use pumpkin_data::{ }; use pumpkin_macros::pumpkin_block; use pumpkin_util::math::{boundingbox::BoundingBox, position::BlockPos}; -use pumpkin_world::{ - BlockStateId, block::entities::comparator::ComparatorBlockEntity, tick::TickPriority, - world::BlockFlags, -}; +use pumpkin_world::{BlockStateId, tick::TickPriority, world::BlockFlags}; use crate::{ block::{ diff --git a/pumpkin/src/block/blocks/redstone/daylight_detector.rs b/pumpkin/src/block/blocks/redstone/daylight_detector.rs index f99153c0c..90fd7030d 100644 --- a/pumpkin/src/block/blocks/redstone/daylight_detector.rs +++ b/pumpkin/src/block/blocks/redstone/daylight_detector.rs @@ -1,9 +1,9 @@ use std::sync::Arc; +use crate::block::entities::daylight_detector::DaylightDetectorBlockEntity; use pumpkin_data::{Block, block_properties::BlockProperties}; use pumpkin_macros::pumpkin_block; use pumpkin_util::math::position::BlockPos; -use pumpkin_world::block::entities::daylight_detector::DaylightDetectorBlockEntity; use pumpkin_world::world::BlockFlags; use crate::block::{ diff --git a/pumpkin/src/block/blocks/redstone/dropper.rs b/pumpkin/src/block/blocks/redstone/dropper.rs index e6f221b15..2ee399037 100644 --- a/pumpkin/src/block/blocks/redstone/dropper.rs +++ b/pumpkin/src/block/blocks/redstone/dropper.rs @@ -7,6 +7,8 @@ use crate::block::{ use crate::entity::Entity; use crate::entity::item::ItemEntity; +use crate::block::entities::dropper::DropperBlockEntity; +use crate::block::entities::hopper::HopperBlockEntity; use pumpkin_data::block_properties::{BlockProperties, Facing}; use pumpkin_data::entity::EntityType; use pumpkin_data::world::WorldEvent; @@ -20,8 +22,6 @@ use pumpkin_macros::pumpkin_block; use pumpkin_util::math::vector3::Vector3; use pumpkin_util::text::TextComponent; use pumpkin_world::BlockStateId; -use pumpkin_world::block::entities::dropper::DropperBlockEntity; -use pumpkin_world::block::entities::hopper::HopperBlockEntity; use pumpkin_world::inventory::Inventory; use pumpkin_world::tick::TickPriority; use pumpkin_world::world::BlockFlags; diff --git a/pumpkin/src/block/blocks/shulker_box.rs b/pumpkin/src/block/blocks/shulker_box.rs index f2d2aebbb..223e4dbb8 100644 --- a/pumpkin/src/block/blocks/shulker_box.rs +++ b/pumpkin/src/block/blocks/shulker_box.rs @@ -6,6 +6,7 @@ use crate::block::{ {BlockBehaviour, NormalUseArgs}, }; +use crate::block::entities::shulker_box::ShulkerBoxBlockEntity; use pumpkin_data::block_properties::BlockProperties; use pumpkin_data::tag::{self}; use pumpkin_data::translation; @@ -16,7 +17,6 @@ use pumpkin_inventory::screen_handler::{ }; use pumpkin_util::text::TextComponent; use pumpkin_world::BlockStateId; -use pumpkin_world::block::entities::shulker_box::ShulkerBoxBlockEntity; use pumpkin_world::inventory::Inventory; use tokio::sync::Mutex; diff --git a/pumpkin/src/block/blocks/signs.rs b/pumpkin/src/block/blocks/signs.rs index fd8e234a7..bacfeacf4 100644 --- a/pumpkin/src/block/blocks/signs.rs +++ b/pumpkin/src/block/blocks/signs.rs @@ -1,6 +1,7 @@ use std::sync::Arc; use std::sync::atomic::Ordering; +use crate::block::entities::sign::SignBlockEntity; use pumpkin_data::Block; use pumpkin_data::BlockDirection; use pumpkin_data::block_properties::EnumVariants; @@ -10,7 +11,6 @@ use pumpkin_macros::pumpkin_block_from_tag; use pumpkin_util::math::position::BlockPos; use pumpkin_util::math::vector3::Vector3; use pumpkin_world::BlockStateId; -use pumpkin_world::block::entities::sign::SignBlockEntity; use uuid::Uuid; use crate::block::BlockBehaviour; diff --git a/pumpkin/src/block/blocks/smoker.rs b/pumpkin/src/block/blocks/smoker.rs index 73e942a25..b38627a6e 100644 --- a/pumpkin/src/block/blocks/smoker.rs +++ b/pumpkin/src/block/blocks/smoker.rs @@ -1,5 +1,8 @@ use std::sync::Arc; +use crate::block::entities::{ + PropertyDelegate, furnace_like_block_entity::ExperienceContainer, smoker::SmokerBlockEntity, +}; use pumpkin_data::{ block_properties::{BlockProperties, FurnaceLikeProperties}, screen::WindowType, @@ -12,13 +15,7 @@ use pumpkin_inventory::{ }; use pumpkin_macros::pumpkin_block; use pumpkin_util::text::TextComponent; -use pumpkin_world::{ - BlockStateId, - block::entities::{ - PropertyDelegate, furnace_like_block_entity::ExperienceContainer, smoker::SmokerBlockEntity, - }, - inventory::Inventory, -}; +use pumpkin_world::{BlockStateId, inventory::Inventory}; use tokio::sync::Mutex; use crate::{ diff --git a/pumpkin/src/block/blocks/spawner.rs b/pumpkin/src/block/blocks/spawner.rs index 0b682dc38..0ec1c53e9 100644 --- a/pumpkin/src/block/blocks/spawner.rs +++ b/pumpkin/src/block/blocks/spawner.rs @@ -1,7 +1,7 @@ use std::sync::Arc; +use crate::block::entities::mob_spawner::MobSpawnerBlockEntity; use pumpkin_macros::pumpkin_block; -use pumpkin_world::block::entities::mob_spawner::MobSpawnerBlockEntity; use crate::block::{BlockBehaviour, BlockFuture, PlacedArgs}; diff --git a/pumpkin-world/src/block/entities/barrel.rs b/pumpkin/src/block/entities/barrel.rs similarity index 92% rename from pumpkin-world/src/block/entities/barrel.rs rename to pumpkin/src/block/entities/barrel.rs index 11da7dca0..4bde3dc5f 100644 --- a/pumpkin-world/src/block/entities/barrel.rs +++ b/pumpkin/src/block/entities/barrel.rs @@ -17,12 +17,14 @@ use std::{ }; use tokio::sync::Mutex; -use crate::block::viewer::{ViewerCountListener, ViewerCountTracker, ViewerFuture}; -use crate::inventory::InventoryFuture; -use crate::inventory::{ +use crate::block::viewer::{ + ViewerCountListener, ViewerCountTracker, ViewerCountTrackerExt, ViewerFuture, +}; +use crate::world::{BlockFlags, World}; +use pumpkin_world::inventory::InventoryFuture; +use pumpkin_world::inventory::{ split_stack, {Clearable, Inventory}, }; -use crate::world::{BlockFlags, SimpleWorld}; use super::BlockEntity; @@ -67,10 +69,7 @@ impl BlockEntity for BarrelBlockEntity { self.write_inventory_nbt(nbt, true) } - fn tick<'a>( - &'a self, - world: &'a Arc, - ) -> Pin + Send + 'a>> { + fn tick<'a>(&'a self, world: &'a Arc) -> Pin + Send + 'a>> { Box::pin(async move { self.viewers .update_viewer_count::(self, world, &self.position) @@ -98,7 +97,7 @@ impl BlockEntity for BarrelBlockEntity { impl ViewerCountListener for BarrelBlockEntity { fn on_container_open<'a>( &'a self, - world: &'a Arc, + world: &'a Arc, _position: &'a BlockPos, ) -> ViewerFuture<'a, ()> { Box::pin(async move { @@ -109,7 +108,7 @@ impl ViewerCountListener for BarrelBlockEntity { fn on_container_close<'a>( &'a self, - world: &'a Arc, + world: &'a Arc, _position: &'a BlockPos, ) -> ViewerFuture<'a, ()> { Box::pin(async move { @@ -133,7 +132,7 @@ impl BarrelBlockEntity { } } - async fn set_open(&self, world: &Arc, open: bool) { + async fn set_open(&self, world: &Arc, open: bool) { let state = world.get_block_state(&self.position).await; let mut properties = BarrelLikeProperties::from_state_id(state.id, &Block::BARREL); @@ -149,7 +148,7 @@ impl BarrelBlockEntity { .await; } - async fn play_sound(&self, world: &Arc, sound: Sound) { + async fn play_sound(&self, world: &Arc, sound: Sound) { let mut rng = Xoroshiro::from_seed(get_seed()); let state = world.get_block_state(&self.position).await; diff --git a/pumpkin/src/block/entities/beacon.rs b/pumpkin/src/block/entities/beacon.rs new file mode 100644 index 000000000..638bb1f79 --- /dev/null +++ b/pumpkin/src/block/entities/beacon.rs @@ -0,0 +1,345 @@ +use futures::Future; +use pumpkin_data::data_component_impl::IDSetContent; +use pumpkin_data::tag::Taggable; +use std::any::Any; +use std::pin::Pin; +use std::sync::Arc; +use std::sync::atomic::{AtomicBool, AtomicI32, Ordering}; + +use pumpkin_data::effect::StatusEffect; +use pumpkin_data::item_stack::ItemStack; +use pumpkin_nbt::compound::NbtCompound; +use pumpkin_util::math::boundingbox::BoundingBox; +use pumpkin_util::math::position::BlockPos; +use tokio::sync::Mutex; + +use crate::block::entities::BlockEntity; +use crate::world::World; +use pumpkin_world::inventory::{Clearable, Inventory, InventoryFuture}; + +pub struct BeaconBlockEntity { + pub position: BlockPos, + pub primary_effect: AtomicI32, + pub secondary_effect: AtomicI32, + pub levels: AtomicI32, + pub dirty: AtomicBool, + pub payment: Arc>, + + // Vanilla Parity Fields + pub custom_name: Mutex>, + pub lock_key: Mutex>, + pub last_check_y: AtomicI32, +} + +impl BeaconBlockEntity { + pub const ID: &'static str = "minecraft:beacon"; + + // ContainerData Property Constants + pub const DATA_LEVELS: usize = 0; + pub const DATA_PRIMARY: usize = 1; + pub const DATA_SECONDARY: usize = 2; + pub const NUM_DATA_VALUES: usize = 3; + + #[must_use] + pub fn new(position: BlockPos) -> Self { + Self { + position, + primary_effect: AtomicI32::new(-1), + secondary_effect: AtomicI32::new(-1), + levels: AtomicI32::new(0), + dirty: AtomicBool::new(false), + payment: Arc::new(Mutex::new(ItemStack::EMPTY.clone())), + custom_name: Mutex::new(None), + lock_key: Mutex::new(None), + last_check_y: AtomicI32::new(position.0.y - 1), + } + } + + /// Replicates the Java `ContainerData` used to sync values to the `BeaconMenu` + pub fn get_data(&self, id: usize) -> i32 { + match id { + Self::DATA_LEVELS => self.levels.load(Ordering::Relaxed), + Self::DATA_PRIMARY => self.primary_effect.load(Ordering::Relaxed), + Self::DATA_SECONDARY => self.secondary_effect.load(Ordering::Relaxed), + _ => 0, + } + } + + pub fn set_data(&self, id: usize, value: i32) { + match id { + Self::DATA_LEVELS => self.levels.store(value, Ordering::Relaxed), + Self::DATA_PRIMARY => self.primary_effect.store(value, Ordering::Relaxed), + Self::DATA_SECONDARY => self.secondary_effect.store(value, Ordering::Relaxed), + _ => {} + } + self.mark_dirty(); + } + + /// Replicates Java's `updateBase` logic + async fn update_base(&self, world: &Arc) -> i32 { + let mut levels = 0; + let x = self.position.0.x; + let y = self.position.0.y; + let z = self.position.0.z; + + for step in 1..=4 { + let ly = y - step; + if ly < world.dimension.min_y { + break; + } + + let mut is_ok = true; + for lx in (x - step)..=(x + step) { + for lz in (z - step)..=(z + step) { + let pos = BlockPos::new(lx, ly, lz); + let block = world.get_block(&pos).await; + if !block.has_tag(&pumpkin_data::tag::Block::MINECRAFT_BEACON_BASE_BLOCKS) { + is_ok = false; + break; + } + } + if !is_ok { + break; + } + } + + if !is_ok { + break; + } + levels = step; + } + levels + } + + /// Replicates Java's `applyEffects` bounding box mapping and duration mapping + async fn apply_effects(&self, world: &Arc, levels: i32) { + if levels <= 0 { + return; + } + + let primary_id = self.primary_effect.load(Ordering::Relaxed); + let secondary_id = self.secondary_effect.load(Ordering::Relaxed); + + if primary_id <= 0 { + return; + } + + let primary_effect = StatusEffect::from_id(primary_id as u16); + let secondary_effect = if secondary_id > 0 { + StatusEffect::from_id(secondary_id as u16) + } else { + None + }; + + // Vanilla: expandTowards(0.0, level.getHeight(), 0.0) -> Reaches across the entire Y axis + let range = (levels * 10 + 10) as f64; + let pos = self.position.0.to_f64(); + + // Use the dimension height for vanilla parity (usually 384.0 in modern versions) + let world_height = world.dimension.height as f64; + + let bounding_box = BoundingBox::new(pos, pos.add_raw(1.0, 1.0, 1.0)) + .expand(range, range, range) + .expand_towards(0.0, world_height, 0.0); + + let players = world.get_players_at_box(&bounding_box); + + let duration_ticks = (9 + levels * 2) * 20; + let base_amp = i32::from(levels >= 4 && primary_id == secondary_id); + + for player in players { + if let Some(effect) = primary_effect { + player + .add_effect(pumpkin_data::potion::Effect { + effect_type: effect, + duration: duration_ticks, + amplifier: base_amp as u8, + ambient: true, + show_particles: true, + show_icon: true, + blend: false, + }) + .await; + } + + if levels >= 4 + && primary_id != secondary_id + && let Some(effect) = secondary_effect + { + player + .add_effect(pumpkin_data::potion::Effect { + effect_type: effect, + duration: duration_ticks, + amplifier: 0, + ambient: true, + show_particles: true, + show_icon: true, + blend: false, + }) + .await; + } + } + } +} + +impl BlockEntity for BeaconBlockEntity { + fn resource_location(&self) -> &'static str { + Self::ID + } + + fn get_position(&self) -> BlockPos { + self.position + } + + fn from_nbt(nbt: &NbtCompound, position: BlockPos) -> Self + where + Self: Sized, + { + // Aligning to strict vanilla NBT tags + let primary = nbt.get_int("primary_effect").unwrap_or(-1); + let secondary = nbt.get_int("secondary_effect").unwrap_or(-1); + let levels = nbt.get_int("Levels").unwrap_or(0); // Vanilla uses capital L + let custom_name = nbt + .get_string("CustomName") + .map(std::string::ToString::to_string); + let lock_key = nbt.get_string("Lock").map(std::string::ToString::to_string); + + Self { + position, + primary_effect: AtomicI32::new(primary), + secondary_effect: AtomicI32::new(secondary), + levels: AtomicI32::new(levels), + dirty: AtomicBool::new(false), + payment: Arc::new(Mutex::new(ItemStack::EMPTY.clone())), + custom_name: Mutex::new(custom_name), + lock_key: Mutex::new(lock_key), + last_check_y: AtomicI32::new(position.0.y - 1), + } + } + + fn write_nbt<'a>( + &'a self, + nbt: &'a mut NbtCompound, + ) -> Pin + Send + 'a>> { + Box::pin(async move { + nbt.put_int( + "primary_effect", + self.primary_effect.load(Ordering::Relaxed), + ); + nbt.put_int( + "secondary_effect", + self.secondary_effect.load(Ordering::Relaxed), + ); + nbt.put_int("Levels", self.levels.load(Ordering::Relaxed)); + + if let Some(name) = &*self.custom_name.lock().await { + nbt.put_string("CustomName", name.clone()); + } + if let Some(lock) = &*self.lock_key.lock().await { + nbt.put_string("Lock", lock.clone()); + } + }) + } + + fn tick<'a>(&'a self, world: &'a Arc) -> Pin + Send + 'a>> { + Box::pin(async move { + // Check properties every 80 ticks matching Java + if world.get_time_of_day().await % 80 == 0 { + let levels = self.update_base(world).await; + self.levels.store(levels, Ordering::Relaxed); + + // TODO: Beam Section validation (scanning upward to heightmap to check for sky visibility) + // is typically checked here before applying effects in Vanilla. + + if levels > 0 { + self.apply_effects(world, levels).await; + } + } + }) + } + + fn as_any(&self) -> &dyn std::any::Any { + self + } + + fn get_inventory(self: Arc) -> Option> { + Some(self as Arc) + } +} + +impl Inventory for BeaconBlockEntity { + fn size(&self) -> usize { + 1 + } + + fn is_empty(&self) -> InventoryFuture<'_, bool> { + Box::pin(async move { self.payment.lock().await.is_empty() }) + } + + fn get_stack(&self, slot: usize) -> InventoryFuture<'_, Arc>> { + Box::pin(async move { + if slot == 0 { + self.payment.clone() + } else { + Arc::new(Mutex::new(ItemStack::EMPTY.clone())) + } + }) + } + + fn remove_stack(&self, slot: usize) -> InventoryFuture<'_, ItemStack> { + Box::pin(async move { + if slot == 0 { + let mut removed = ItemStack::EMPTY.clone(); + let mut guard = self.payment.lock().await; + std::mem::swap(&mut removed, &mut *guard); + self.mark_dirty(); + removed + } else { + ItemStack::EMPTY.clone() + } + }) + } + + fn remove_stack_specific(&self, slot: usize, amount: u8) -> InventoryFuture<'_, ItemStack> { + Box::pin(async move { + if slot == 0 { + let mut stack = self.payment.lock().await; + if stack.is_empty() { + return ItemStack::EMPTY.clone(); + } + let res = stack.split(amount); + self.mark_dirty(); + res + } else { + ItemStack::EMPTY.clone() + } + }) + } + + fn set_stack(&self, slot: usize, stack: ItemStack) -> InventoryFuture<'_, ()> { + Box::pin(async move { + if slot == 0 { + *self.payment.lock().await = stack; + self.mark_dirty(); + } + }) + } + + fn mark_dirty(&self) { + self.dirty.store(true, Ordering::Relaxed); + } + + fn as_any(&self) -> &dyn Any { + self + } +} + +impl Clearable for BeaconBlockEntity { + fn clear(&self) -> Pin + Send + '_>> { + Box::pin(async move { + if let Ok(mut payment) = self.payment.try_lock() { + *payment = ItemStack::EMPTY.clone(); + } + }) + } +} diff --git a/pumpkin-world/src/block/entities/bed.rs b/pumpkin/src/block/entities/bed.rs similarity index 100% rename from pumpkin-world/src/block/entities/bed.rs rename to pumpkin/src/block/entities/bed.rs diff --git a/pumpkin-world/src/block/entities/bell.rs b/pumpkin/src/block/entities/bell.rs similarity index 94% rename from pumpkin-world/src/block/entities/bell.rs rename to pumpkin/src/block/entities/bell.rs index a9f59f39e..adade4690 100644 --- a/pumpkin-world/src/block/entities/bell.rs +++ b/pumpkin/src/block/entities/bell.rs @@ -1,5 +1,5 @@ use crate::block::entities::BlockEntity; -use crate::world::SimpleWorld; +use crate::world::World; use crossbeam::atomic::AtomicCell; use pumpkin_data::block_properties::HorizontalFacing; use pumpkin_data::sound::{Sound, SoundCategory}; @@ -61,10 +61,7 @@ impl BlockEntity for BellBlockEntity { Self::new(position) } - fn tick<'a>( - &'a self, - world: &'a Arc, - ) -> Pin + Send + 'a>> { + fn tick<'a>(&'a self, world: &'a Arc) -> Pin + Send + 'a>> { Box::pin(async move { if self.ringing.load() { self.ring_ticks.fetch_add(1); diff --git a/pumpkin-world/src/block/entities/blasting_furnace.rs b/pumpkin/src/block/entities/blasting_furnace.rs similarity index 98% rename from pumpkin-world/src/block/entities/blasting_furnace.rs rename to pumpkin/src/block/entities/blasting_furnace.rs index f9714a09f..1268652c4 100644 --- a/pumpkin-world/src/block/entities/blasting_furnace.rs +++ b/pumpkin/src/block/entities/blasting_furnace.rs @@ -1,5 +1,5 @@ -use crate::inventory::Inventory; use pumpkin_data::{block_properties::BlockProperties, item_stack::ItemStack}; +use pumpkin_world::inventory::Inventory; use std::{ array::from_fn, diff --git a/pumpkin-world/src/block/entities/brewing_stand.rs b/pumpkin/src/block/entities/brewing_stand.rs similarity index 89% rename from pumpkin-world/src/block/entities/brewing_stand.rs rename to pumpkin/src/block/entities/brewing_stand.rs index 204c648fa..f5b9b3eff 100644 --- a/pumpkin-world/src/block/entities/brewing_stand.rs +++ b/pumpkin/src/block/entities/brewing_stand.rs @@ -8,7 +8,6 @@ use std::sync::{ }; use crate::block::entities::PropertyDelegate; -use crate::inventory::Inventory; use pumpkin_data::block_properties::BlockProperties; use pumpkin_data::data_component_impl::DataComponentImpl; use pumpkin_data::item::Item; @@ -19,6 +18,7 @@ use pumpkin_data::tag::{self, Taggable}; use pumpkin_nbt::compound::NbtCompound; use pumpkin_util::math::position::BlockPos; use pumpkin_util::math::vector3::Vector3; +use pumpkin_world::inventory::Inventory; use tokio::sync::Mutex; pub struct BrewingStandBlockEntity { @@ -50,12 +50,11 @@ impl BrewingStandBlockEntity { } /// Check if the current ingredient matches the stored ingredient - async fn ingredient_matches(&self, ingredient: &ItemStack) -> bool { - if let Some(stored) = *self.ingredient_item.lock().unwrap() { - !ingredient.is_empty() && ingredient.get_item().id == stored.id - } else { - false - } + fn ingredient_matches(&self, ingredient: &ItemStack) -> bool { + self.ingredient_item + .lock() + .unwrap() + .is_some_and(|stored| !ingredient.is_empty() && ingredient.get_item().id == stored.id) } /// Check if any potion slot has a valid recipe with the ingredient @@ -101,7 +100,7 @@ impl BrewingStandBlockEntity { } /// Perform brewing on all valid potion slots - async fn do_brew(&self, world: &Arc, ingredient: &ItemStack) { + async fn do_brew(&self, world: &Arc, ingredient: &ItemStack) { let ingredient_id = ingredient.get_item().id; // Apply recipes to each slot @@ -120,18 +119,19 @@ impl BrewingStandBlockEntity { { let new_item = recipe.to(); let potion_comp = slot.get_data_component::().cloned(); - let new_stack = if let Some(pc) = potion_comp { - ItemStack::new_with_component( - slot.item_count, - new_item, - vec![( - pumpkin_data::data_component::DataComponent::PotionContents, - Some(pc.to_dyn()), - )], - ) - } else { - ItemStack::new(slot.item_count, new_item) - }; + let new_stack = potion_comp.map_or_else( + || ItemStack::new(slot.item_count, new_item), + |pc| { + ItemStack::new_with_component( + slot.item_count, + new_item, + vec![( + pumpkin_data::data_component::DataComponent::PotionContents, + Some(pc.to_dyn()), + )], + ) + }, + ); new_stack_opt = Some(new_stack); break; } @@ -203,12 +203,12 @@ impl BrewingStandBlockEntity { } } -impl crate::inventory::Inventory for BrewingStandBlockEntity { +impl pumpkin_world::inventory::Inventory for BrewingStandBlockEntity { fn size(&self) -> usize { Self::INVENTORY_SIZE } - fn is_empty(&self) -> crate::inventory::InventoryFuture<'_, bool> { + fn is_empty(&self) -> pumpkin_world::inventory::InventoryFuture<'_, bool> { Box::pin(async move { for slot in &self.items { if !slot.lock().await.is_empty() { @@ -222,11 +222,14 @@ impl crate::inventory::Inventory for BrewingStandBlockEntity { fn get_stack( &self, slot: usize, - ) -> crate::inventory::InventoryFuture<'_, Arc>> { + ) -> pumpkin_world::inventory::InventoryFuture<'_, Arc>> { Box::pin(async move { self.items[slot].clone() }) } - fn remove_stack(&self, slot: usize) -> crate::inventory::InventoryFuture<'_, ItemStack> { + fn remove_stack( + &self, + slot: usize, + ) -> pumpkin_world::inventory::InventoryFuture<'_, ItemStack> { Box::pin(async move { let mut removed = ItemStack::EMPTY.clone(); let mut guard = self.items[slot].lock().await; @@ -239,7 +242,7 @@ impl crate::inventory::Inventory for BrewingStandBlockEntity { &self, slot: usize, amount: u8, - ) -> crate::inventory::InventoryFuture<'_, ItemStack> { + ) -> pumpkin_world::inventory::InventoryFuture<'_, ItemStack> { Box::pin(async move { let mut guard = self.items[slot].lock().await; let mut taken = ItemStack::EMPTY.clone(); @@ -258,18 +261,18 @@ impl crate::inventory::Inventory for BrewingStandBlockEntity { &self, slot: usize, stack: ItemStack, - ) -> crate::inventory::InventoryFuture<'_, ()> { + ) -> pumpkin_world::inventory::InventoryFuture<'_, ()> { Box::pin(async move { *self.items[slot].lock().await = stack; self.mark_dirty(); }) } - fn on_open(&self) -> crate::inventory::InventoryFuture<'_, ()> { + fn on_open(&self) -> pumpkin_world::inventory::InventoryFuture<'_, ()> { Box::pin(async move {}) } - fn on_close(&self) -> crate::inventory::InventoryFuture<'_, ()> { + fn on_close(&self) -> pumpkin_world::inventory::InventoryFuture<'_, ()> { Box::pin(async move {}) } @@ -307,7 +310,7 @@ impl crate::inventory::Inventory for BrewingStandBlockEntity { } } -impl crate::inventory::Clearable for BrewingStandBlockEntity { +impl pumpkin_world::inventory::Clearable for BrewingStandBlockEntity { fn clear(&self) -> Pin + Send + '_>> { Box::pin(async move { for slot in &self.items { @@ -368,15 +371,15 @@ impl crate::block::entities::BlockEntity for BrewingStandBlockEntity { fn write_nbt<'a>( &'a self, - _nbt: &'a mut NbtCompound, + nbt: &'a mut NbtCompound, ) -> Pin + Send + 'a>> { Box::pin(async move { // Persist brew state - _nbt.put_int("BrewTime", self.brew_time.load(Ordering::Relaxed)); - _nbt.put_int("Fuel", self.fuel.load(Ordering::Relaxed)); + nbt.put_int("BrewTime", self.brew_time.load(Ordering::Relaxed)); + nbt.put_int("Fuel", self.fuel.load(Ordering::Relaxed)); // Save inventory contents to NBT - self.write_inventory_nbt(_nbt, true).await; + self.write_inventory_nbt(nbt, true).await; }) } @@ -406,7 +409,7 @@ impl crate::block::entities::BlockEntity for BrewingStandBlockEntity { fn tick<'a>( &'a self, - world: &'a Arc, + world: &'a Arc, ) -> Pin + Send + 'a>> { Box::pin(async move { // Refill fuel counter from fuel item if needed @@ -442,7 +445,7 @@ impl crate::block::entities::BlockEntity for BrewingStandBlockEntity { if is_done_brewing && brewable { // Brewing complete self.do_brew(world, &ingredient).await; - } else if !brewable || !self.ingredient_matches(&ingredient).await { + } else if !brewable || !self.ingredient_matches(&ingredient) { // Cancel brewing self.brew_time.store(0, Ordering::Relaxed); self.mark_dirty(); diff --git a/pumpkin-world/src/block/entities/chest.rs b/pumpkin/src/block/entities/chest.rs similarity index 100% rename from pumpkin-world/src/block/entities/chest.rs rename to pumpkin/src/block/entities/chest.rs diff --git a/pumpkin-world/src/block/entities/chest_like_block_entity.rs b/pumpkin/src/block/entities/chest_like_block_entity.rs similarity index 86% rename from pumpkin-world/src/block/entities/chest_like_block_entity.rs rename to pumpkin/src/block/entities/chest_like_block_entity.rs index 365f50a7f..af868b486 100644 --- a/pumpkin-world/src/block/entities/chest_like_block_entity.rs +++ b/pumpkin/src/block/entities/chest_like_block_entity.rs @@ -18,7 +18,7 @@ macro_rules! impl_block_entity_for_chest { where Self: Sized, { - use $crate::inventory::Inventory; + use pumpkin_world::inventory::Inventory; let chest = Self { position, @@ -36,7 +36,7 @@ macro_rules! impl_block_entity_for_chest { &'a self, nbt: &'a mut pumpkin_nbt::compound::NbtCompound, ) -> std::pin::Pin + Send + 'a>> { - use $crate::inventory::Inventory; + use pumpkin_world::inventory::Inventory; // Write inventory data to NBT self.write_inventory_nbt(nbt, true) @@ -44,16 +44,20 @@ macro_rules! impl_block_entity_for_chest { fn tick<'a>( &'a self, - world: &'a Arc, + world: &'a Arc<$crate::world::World>, ) -> std::pin::Pin + Send + 'a>> { Box::pin(async move { - self.viewers - .update_viewer_count::(self, world, &self.position) - .await; + $crate::block::viewer::ViewerCountTrackerExt::update_viewer_count::<$struct_name>( + &self.viewers, + self, + world, + &self.position, + ) + .await; }) } - fn get_inventory(self: Arc) -> Option> { + fn get_inventory(self: Arc) -> Option> { Some(self) } @@ -77,12 +81,12 @@ macro_rules! impl_block_entity_for_chest { #[macro_export] macro_rules! impl_inventory_for_chest { ($struct_name:ty) => { - impl $crate::inventory::Inventory for $struct_name { + impl pumpkin_world::inventory::Inventory for $struct_name { fn size(&self) -> usize { self.items.len() } - fn is_empty(&self) -> $crate::inventory::InventoryFuture<'_, bool> { + fn is_empty(&self) -> pumpkin_world::inventory::InventoryFuture<'_, bool> { Box::pin(async move { for slot in &self.items { if !slot.lock().await.is_empty() { @@ -97,14 +101,14 @@ macro_rules! impl_inventory_for_chest { fn get_stack( &self, slot: usize, - ) -> $crate::inventory::InventoryFuture<'_, Arc>> { + ) -> pumpkin_world::inventory::InventoryFuture<'_, Arc>> { Box::pin(async move { self.items[slot].clone() }) } fn remove_stack( &self, slot: usize, - ) -> $crate::inventory::InventoryFuture<'_, ItemStack> { + ) -> pumpkin_world::inventory::InventoryFuture<'_, ItemStack> { Box::pin(async move { let mut removed = ItemStack::EMPTY.clone(); let mut guard = self.items[slot].lock().await; @@ -118,9 +122,10 @@ macro_rules! impl_inventory_for_chest { &self, slot: usize, amount: u8, - ) -> $crate::inventory::InventoryFuture<'_, ItemStack> { + ) -> pumpkin_world::inventory::InventoryFuture<'_, ItemStack> { Box::pin(async move { - let res = $crate::inventory::split_stack(&self.items, slot, amount).await; + let res = + pumpkin_world::inventory::split_stack(&self.items, slot, amount).await; self.mark_dirty(); res }) @@ -130,20 +135,20 @@ macro_rules! impl_inventory_for_chest { &self, slot: usize, stack: ItemStack, - ) -> $crate::inventory::InventoryFuture<'_, ()> { + ) -> pumpkin_world::inventory::InventoryFuture<'_, ()> { Box::pin(async move { *self.items[slot].lock().await = stack; self.mark_dirty(); }) } - fn on_open(&self) -> $crate::inventory::InventoryFuture<'_, ()> { + fn on_open(&self) -> pumpkin_world::inventory::InventoryFuture<'_, ()> { Box::pin(async move { self.viewers.open_container(); }) } - fn on_close(&self) -> $crate::inventory::InventoryFuture<'_, ()> { + fn on_close(&self) -> pumpkin_world::inventory::InventoryFuture<'_, ()> { Box::pin(async move { self.viewers.close_container(); }) @@ -164,7 +169,7 @@ macro_rules! impl_inventory_for_chest { #[macro_export] macro_rules! impl_clearable_for_chest { ($struct_name:ty) => { - impl $crate::inventory::Clearable for $struct_name { + impl pumpkin_world::inventory::Clearable for $struct_name { fn clear( &self, ) -> std::pin::Pin + Send + '_>> { @@ -172,7 +177,7 @@ macro_rules! impl_clearable_for_chest { for slot in &self.items { *slot.lock().await = ItemStack::EMPTY.clone(); } - <$struct_name as $crate::inventory::Inventory>::mark_dirty(self); + <$struct_name as pumpkin_world::inventory::Inventory>::mark_dirty(self); }) } } @@ -189,7 +194,7 @@ macro_rules! impl_viewer_count_listener_for_chest { impl $crate::block::viewer::ViewerCountListener for $struct_name { fn on_container_open<'a>( &'a self, - world: &'a Arc, + world: &'a Arc<$crate::world::World>, _position: &'a pumpkin_util::math::position::BlockPos, ) -> $crate::block::viewer::ViewerFuture<'a, ()> { Box::pin(async move { @@ -200,7 +205,7 @@ macro_rules! impl_viewer_count_listener_for_chest { fn on_container_close<'a>( &'a self, - world: &'a Arc, + world: &'a Arc<$crate::world::World>, _position: &'a pumpkin_util::math::position::BlockPos, ) -> $crate::block::viewer::ViewerFuture<'a, ()> { Box::pin(async move { @@ -211,7 +216,7 @@ macro_rules! impl_viewer_count_listener_for_chest { fn on_viewer_count_update<'a>( &'a self, - world: &'a Arc, + world: &'a Arc<$crate::world::World>, position: &'a pumpkin_util::math::position::BlockPos, old: u16, new: u16, @@ -271,7 +276,7 @@ macro_rules! impl_chest_helper_methods { async fn play_sound( &self, - world: &Arc, + world: &Arc<$crate::world::World>, sound: pumpkin_data::sound::Sound, ) { let mut rng = pumpkin_util::random::xoroshiro128::Xoroshiro::from_seed( diff --git a/pumpkin-world/src/block/entities/chiseled_bookshelf.rs b/pumpkin/src/block/entities/chiseled_bookshelf.rs similarity index 97% rename from pumpkin-world/src/block/entities/chiseled_bookshelf.rs rename to pumpkin/src/block/entities/chiseled_bookshelf.rs index 702d7657a..e952d3a7a 100644 --- a/pumpkin-world/src/block/entities/chiseled_bookshelf.rs +++ b/pumpkin/src/block/entities/chiseled_bookshelf.rs @@ -15,12 +15,12 @@ use std::{ use tokio::sync::Mutex; use tracing::warn; -use crate::inventory::InventoryFuture; use crate::{ block::entities::BlockEntity, - inventory::{Clearable, Inventory, split_stack}, - world::{BlockFlags, SimpleWorld}, + world::{BlockFlags, World}, }; +use pumpkin_world::inventory::InventoryFuture; +use pumpkin_world::inventory::{Clearable, Inventory, split_stack}; pub struct ChiseledBookshelfBlockEntity { pub position: BlockPos, @@ -108,7 +108,7 @@ impl ChiseledBookshelfBlockEntity { pub async fn update_state( &self, mut properties: ChiseledBookshelfLikeProperties, - world: Arc, + world: Arc, slot: i8, ) { if slot >= 0 && slot < self.items.len() as i8 { diff --git a/pumpkin-world/src/block/entities/command_block.rs b/pumpkin/src/block/entities/command_block.rs similarity index 100% rename from pumpkin-world/src/block/entities/command_block.rs rename to pumpkin/src/block/entities/command_block.rs diff --git a/pumpkin-world/src/block/entities/comparator.rs b/pumpkin/src/block/entities/comparator.rs similarity index 100% rename from pumpkin-world/src/block/entities/comparator.rs rename to pumpkin/src/block/entities/comparator.rs diff --git a/pumpkin-world/src/block/entities/daylight_detector.rs b/pumpkin/src/block/entities/daylight_detector.rs similarity index 88% rename from pumpkin-world/src/block/entities/daylight_detector.rs rename to pumpkin/src/block/entities/daylight_detector.rs index ab79b1927..2b174c296 100644 --- a/pumpkin-world/src/block/entities/daylight_detector.rs +++ b/pumpkin/src/block/entities/daylight_detector.rs @@ -5,7 +5,7 @@ use pumpkin_data::block_properties::BlockProperties; use pumpkin_nbt::compound::NbtCompound; use pumpkin_util::math::position::BlockPos; -use crate::world::{BlockFlags, SimpleWorld}; +use crate::world::{BlockFlags, World}; use super::BlockEntity; @@ -42,12 +42,9 @@ impl BlockEntity for DaylightDetectorBlockEntity { self } - fn tick<'a>( - &'a self, - world: &'a Arc, - ) -> Pin + Send + 'a>> { + fn tick<'a>(&'a self, world: &'a Arc) -> Pin + Send + 'a>> { Box::pin(async { - if world.get_world_age().await % 20 == 0 && world.get_dimension().await.has_skylight { + if world.get_world_age().await % 20 == 0 && world.dimension.has_skylight { Self::update_power(world, &self.position).await; } }) @@ -62,13 +59,13 @@ impl DaylightDetectorBlockEntity { Self { position } } - pub async fn update_power(world: &Arc, block_pos: &BlockPos) { + pub async fn update_power(world: &Arc, block_pos: &BlockPos) { use std::f32::consts::PI; let (block, state) = world.get_block_and_state(block_pos).await; let mut props = DaylightDetectorProperties::from_state_id(state.id, block); - let level = world.get_level().await; + let level = world.level.clone(); let inverted = props.inverted; @@ -102,7 +99,7 @@ impl DaylightDetectorBlockEntity { let sky_light_level = level .light_engine - .get_sky_light_level(level, block_pos) + .get_sky_light_level(&level, block_pos) .await; let mut power = sky_light_level - ambient_darkness; diff --git a/pumpkin-world/src/block/entities/dropper.rs b/pumpkin/src/block/entities/dropper.rs similarity index 98% rename from pumpkin-world/src/block/entities/dropper.rs rename to pumpkin/src/block/entities/dropper.rs index 24a3a1e44..056abbb48 100644 --- a/pumpkin-world/src/block/entities/dropper.rs +++ b/pumpkin/src/block/entities/dropper.rs @@ -1,8 +1,8 @@ use crate::block::entities::BlockEntity; -use crate::inventory::{Clearable, Inventory, InventoryFuture, split_stack}; use pumpkin_data::item_stack::ItemStack; use pumpkin_nbt::compound::NbtCompound; use pumpkin_util::math::position::BlockPos; +use pumpkin_world::inventory::{Clearable, Inventory, InventoryFuture, split_stack}; use rand::{RngExt, rng}; use std::any::Any; use std::array::from_fn; diff --git a/pumpkin-world/src/block/entities/end_portal.rs b/pumpkin/src/block/entities/end_portal.rs similarity index 100% rename from pumpkin-world/src/block/entities/end_portal.rs rename to pumpkin/src/block/entities/end_portal.rs diff --git a/pumpkin-world/src/block/entities/ender_chest.rs b/pumpkin/src/block/entities/ender_chest.rs similarity index 86% rename from pumpkin-world/src/block/entities/ender_chest.rs rename to pumpkin/src/block/entities/ender_chest.rs index fb5f6ae50..232b829c6 100644 --- a/pumpkin-world/src/block/entities/ender_chest.rs +++ b/pumpkin/src/block/entities/ender_chest.rs @@ -7,8 +7,10 @@ use std::any::Any; use std::pin::Pin; use std::sync::Arc; -use crate::block::viewer::{ViewerCountListener, ViewerCountTracker, ViewerFuture}; -use crate::world::SimpleWorld; +use crate::block::viewer::{ + ViewerCountListener, ViewerCountTracker, ViewerCountTrackerExt, ViewerFuture, +}; +use crate::world::World; use super::BlockEntity; @@ -45,10 +47,7 @@ impl BlockEntity for EnderChestBlockEntity { Box::pin(async {}) } - fn tick<'a>( - &'a self, - world: &'a Arc, - ) -> Pin + Send + 'a>> { + fn tick<'a>(&'a self, world: &'a Arc) -> Pin + Send + 'a>> { Box::pin(async move { self.viewers .update_viewer_count::(self, world, &self.position) @@ -64,7 +63,7 @@ impl BlockEntity for EnderChestBlockEntity { impl ViewerCountListener for EnderChestBlockEntity { fn on_container_open<'a>( &'a self, - world: &'a Arc, + world: &'a Arc, _position: &'a BlockPos, ) -> ViewerFuture<'a, ()> { Box::pin(async move { @@ -74,7 +73,7 @@ impl ViewerCountListener for EnderChestBlockEntity { fn on_container_close<'a>( &'a self, - world: &'a Arc, + world: &'a Arc, _position: &'a BlockPos, ) -> ViewerFuture<'a, ()> { Box::pin(async move { @@ -84,7 +83,7 @@ impl ViewerCountListener for EnderChestBlockEntity { fn on_viewer_count_update<'a>( &'a self, - world: &'a Arc, + world: &'a Arc, position: &'a BlockPos, _old: u16, new: u16, @@ -114,7 +113,7 @@ impl EnderChestBlockEntity { self.viewers.clone() } - async fn play_sound(&self, world: &Arc, sound: Sound) { + async fn play_sound(&self, world: &Arc, sound: Sound) { let mut rng = Xoroshiro::from_seed(get_seed()); world diff --git a/pumpkin-world/src/block/entities/furnace.rs b/pumpkin/src/block/entities/furnace.rs similarity index 98% rename from pumpkin-world/src/block/entities/furnace.rs rename to pumpkin/src/block/entities/furnace.rs index 7dc163187..a98272c50 100644 --- a/pumpkin-world/src/block/entities/furnace.rs +++ b/pumpkin/src/block/entities/furnace.rs @@ -1,5 +1,5 @@ -use crate::inventory::Inventory; use pumpkin_data::block_properties::BlockProperties; +use pumpkin_world::inventory::Inventory; use std::{ array::from_fn, diff --git a/pumpkin-world/src/block/entities/furnace_like_block_entity.rs b/pumpkin/src/block/entities/furnace_like_block_entity.rs similarity index 94% rename from pumpkin-world/src/block/entities/furnace_like_block_entity.rs rename to pumpkin/src/block/entities/furnace_like_block_entity.rs index c2dbcb9ef..8f66fe065 100644 --- a/pumpkin-world/src/block/entities/furnace_like_block_entity.rs +++ b/pumpkin/src/block/entities/furnace_like_block_entity.rs @@ -3,18 +3,11 @@ use std::sync::Arc; use pumpkin_data::{item_stack::ItemStack, recipes::CookingRecipe}; use tokio::sync::Mutex; -use crate::{ - block::entities::{BlockEntity, PropertyDelegate}, - inventory::{Clearable, Inventory}, -}; +use crate::block::entities::{BlockEntity, PropertyDelegate}; +pub use pumpkin_world::block::entities::ExperienceContainer; +use pumpkin_world::inventory::{Clearable, Inventory}; /// Trait for extracting smelting experience from cooking block entities. -/// This is a separate dyn-compatible trait since `CookingBlockEntityBase` is not. -pub trait ExperienceContainer: Send + Sync { - /// Extract and reset accumulated experience, returning the total as an integer - fn extract_experience(&self) -> i32; -} - pub trait CookingBlockEntityBase: Sync + Send + Inventory + PropertyDelegate + BlockEntity + Clearable { @@ -130,10 +123,7 @@ macro_rules! impl_cooking_block_entity_base { recipe: Option<&pumpkin_data::recipes::CookingRecipe>, max_count: u8, ) -> bool { - let recipe = match recipe { - Some(cooking_recipe) => cooking_recipe, - None => return false, - }; + let Some(recipe) = recipe else { return false }; let top_item_stack = self.items[0].lock().await; let is_top_items_empty = top_item_stack.is_empty(); @@ -165,11 +155,10 @@ macro_rules! impl_cooking_block_entity_base { if let Some(recipe) = recipe { if can_accept_output { let mut side_items = self.items[2].lock().await; - let output_item = match pumpkin_data::item::Item::from_registry_key( + let Some(output_item) = pumpkin_data::item::Item::from_registry_key( recipe.result.id.strip_prefix("minecraft:").unwrap(), - ) { - Some(item) => item, - None => return false, + ) else { + return false; }; let output_item_stack = ItemStack::new(recipe.result.count, output_item); @@ -243,7 +232,7 @@ macro_rules! impl_property_delegate_for_cooking { #[macro_export] macro_rules! impl_clearable_for_cooking { ($struct_name:ty) => { - impl $crate::inventory::Clearable for $struct_name { + impl pumpkin_world::inventory::Clearable for $struct_name { fn clear(&self) -> std::pin::Pin + Send + '_>> { Box::pin(async move { for slot in self.items.iter() { @@ -273,12 +262,12 @@ macro_rules! impl_experience_container_for_cooking { #[macro_export] macro_rules! impl_inventory_for_cooking { ($struct_name:ty) => { - impl $crate::inventory::Inventory for $struct_name { + impl pumpkin_world::inventory::Inventory for $struct_name { fn size(&self) -> usize { self.items.len() } - fn is_empty(&self) -> $crate::inventory::InventoryFuture<'_, bool> { + fn is_empty(&self) -> pumpkin_world::inventory::InventoryFuture<'_, bool> { Box::pin(async move { for slot in self.items.iter() { if !slot.lock().await.is_empty() { @@ -292,14 +281,14 @@ macro_rules! impl_inventory_for_cooking { fn get_stack( &self, slot: usize, - ) -> $crate::inventory::InventoryFuture<'_, Arc>> { + ) -> pumpkin_world::inventory::InventoryFuture<'_, Arc>> { Box::pin(async move { self.items[slot].clone() }) } fn remove_stack( &self, slot: usize, - ) -> $crate::inventory::InventoryFuture<'_, ItemStack> { + ) -> pumpkin_world::inventory::InventoryFuture<'_, ItemStack> { Box::pin(async move { let mut removed = ItemStack::EMPTY.clone(); let mut guard = self.items[slot].lock().await; @@ -313,9 +302,10 @@ macro_rules! impl_inventory_for_cooking { &self, slot: usize, amount: u8, - ) -> $crate::inventory::InventoryFuture<'_, ItemStack> { + ) -> pumpkin_world::inventory::InventoryFuture<'_, ItemStack> { Box::pin(async move { - let res = $crate::inventory::split_stack(&self.items, slot, amount).await; + let res = + pumpkin_world::inventory::split_stack(&self.items, slot, amount).await; self.mark_dirty(); res }) @@ -325,7 +315,7 @@ macro_rules! impl_inventory_for_cooking { &self, slot: usize, stack: ItemStack, - ) -> $crate::inventory::InventoryFuture<'_, ()> { + ) -> pumpkin_world::inventory::InventoryFuture<'_, ()> { Box::pin(async move { let furnace_stack = self.get_stack(slot).await; let mut furnace_stack = furnace_stack.lock().await; @@ -370,9 +360,10 @@ macro_rules! impl_inventory_for_cooking { macro_rules! impl_block_entity_for_cooking { ($struct_name:ty,$recipe_kind:expr) => { impl $crate::block::entities::BlockEntity for $struct_name { + #[expect(clippy::too_many_lines)] fn tick<'a>( &'a self, - world: &'a Arc, + world: &'a Arc<$crate::world::World>, ) -> std::pin::Pin + Send + 'a>> { Box::pin(async move { let is_burning = self.is_burning(); @@ -586,7 +577,9 @@ macro_rules! impl_block_entity_for_cooking { }) } - fn get_inventory(self: Arc) -> Option> { + fn get_inventory( + self: Arc, + ) -> Option> { Some(self) } diff --git a/pumpkin-world/src/block/entities/hopper.rs b/pumpkin/src/block/entities/hopper.rs similarity index 94% rename from pumpkin-world/src/block/entities/hopper.rs rename to pumpkin/src/block/entities/hopper.rs index cb1ee4c54..08ebf2de2 100644 --- a/pumpkin-world/src/block/entities/hopper.rs +++ b/pumpkin/src/block/entities/hopper.rs @@ -1,7 +1,6 @@ -use crate::BlockStateId; use crate::block::entities::BlockEntity; -use crate::inventory::{Clearable, Inventory, InventoryFuture, split_stack}; -use crate::world::SimpleWorld; +use crate::entity::experience_orb::ExperienceOrbEntity; +use crate::world::World; use pumpkin_data::block_properties::{BlockProperties, FacingHopper, HopperLikeProperties}; use pumpkin_data::item_stack::ItemStack; use pumpkin_data::tag::Taggable; @@ -10,6 +9,8 @@ use pumpkin_nbt::compound::NbtCompound; use pumpkin_nbt::tag::NbtTag; use pumpkin_util::math::position::BlockPos; use pumpkin_util::math::vector3::Vector3; +use pumpkin_world::BlockStateId; +use pumpkin_world::inventory::{Clearable, Inventory, InventoryFuture, split_stack}; use std::any::Any; use std::array::from_fn; use std::pin::Pin; @@ -71,10 +72,7 @@ impl BlockEntity for HopperBlockEntity { hopper } - fn tick<'a>( - &'a self, - world: &'a Arc, - ) -> Pin + Send + 'a>> { + fn tick<'a>(&'a self, world: &'a Arc) -> Pin + Send + 'a>> { Box::pin(async move { self.ticked_game_time .store(world.get_world_age().await, Ordering::Relaxed); @@ -134,12 +132,13 @@ impl HopperBlockEntity { ticked_game_time: AtomicI64::new(0), } } - async fn try_move_items(&self, state: &HopperLikeProperties, world: &Arc) { + async fn try_move_items(&self, state: &HopperLikeProperties, world: &Arc) { if self.cooldown_time.load(Ordering::Relaxed) <= 0 && state.enabled { - let mut success = false; - if !self.is_empty().await { - success = self.eject_items(world).await; - } + let mut success = if self.is_empty().await { + false + } else { + self.eject_items(world).await + }; if !self.inventory_full().await { success |= self.suck_in_items(world).await; } @@ -160,7 +159,7 @@ impl HopperBlockEntity { true } - async fn suck_in_items(&self, world: &Arc) -> bool { + async fn suck_in_items(&self, world: &Arc) -> bool { // TODO getEntityContainer let pos_up = &self.position.up(); if let Some(entity) = world.get_block_entity(pos_up).await @@ -184,7 +183,7 @@ impl HopperBlockEntity { let xp = experience_container.extract_experience(); if xp > 0 { let pos = self.position.to_f64(); - world.clone().spawn_experience_orbs(pos, xp as u32).await; + ExperienceOrbEntity::spawn(world, pos, xp as u32).await; } } return true; @@ -202,7 +201,7 @@ impl HopperBlockEntity { false } - async fn eject_items(&self, world: &Arc) -> bool { + async fn eject_items(&self, world: &Arc) -> bool { // TODO getEntityContainer if let Some(entity) = world diff --git a/pumpkin-world/src/block/entities/jukebox.rs b/pumpkin/src/block/entities/jukebox.rs similarity index 96% rename from pumpkin-world/src/block/entities/jukebox.rs rename to pumpkin/src/block/entities/jukebox.rs index a4fabd16a..356732f38 100644 --- a/pumpkin-world/src/block/entities/jukebox.rs +++ b/pumpkin/src/block/entities/jukebox.rs @@ -10,8 +10,8 @@ use pumpkin_util::math::position::BlockPos; use tokio::sync::Mutex; use crate::block::entities::BlockEntity; -use crate::inventory::{Clearable, Inventory, InventoryFuture}; -use crate::world::SimpleWorld; +use crate::world::World; +use pumpkin_world::inventory::{Clearable, Inventory, InventoryFuture}; /// Matches vanilla's `JukeboxBlockEntity` pub struct JukeboxBlockEntity { @@ -77,10 +77,7 @@ impl BlockEntity for JukeboxBlockEntity { }) } - fn tick<'a>( - &'a self, - _world: &'a Arc, - ) -> Pin + Send + 'a>> { + fn tick<'a>(&'a self, _world: &'a Arc) -> Pin + Send + 'a>> { Box::pin(async move { // Increment ticks if we're playing let song_length = self.song_length_ticks.load(Ordering::Relaxed); diff --git a/pumpkin-world/src/block/entities/lectern.rs b/pumpkin/src/block/entities/lectern.rs similarity index 93% rename from pumpkin-world/src/block/entities/lectern.rs rename to pumpkin/src/block/entities/lectern.rs index d4fe81375..3e3a822d2 100644 --- a/pumpkin-world/src/block/entities/lectern.rs +++ b/pumpkin/src/block/entities/lectern.rs @@ -11,10 +11,8 @@ use std::{ }; use tokio::sync::Mutex; -use crate::{ - block::entities::BlockEntity, - inventory::{Clearable, Inventory, InventoryFuture}, -}; +use crate::block::entities::BlockEntity; +use pumpkin_world::inventory::{Clearable, Inventory, InventoryFuture}; pub struct LecternBlockEntity { pub position: BlockPos, @@ -38,8 +36,10 @@ impl BlockEntity for LecternBlockEntity { let book = nbt .get_compound("Book") .and_then(ItemStack::read_item_stack) - .map(|stack| Arc::new(Mutex::new(stack))) - .unwrap_or_else(|| Arc::new(Mutex::new(ItemStack::EMPTY.clone()))); + .map_or_else( + || Arc::new(Mutex::new(ItemStack::EMPTY.clone())), + |stack| Arc::new(Mutex::new(stack)), + ); Self { position, diff --git a/pumpkin-world/src/block/entities/mob_spawner.rs b/pumpkin/src/block/entities/mob_spawner.rs similarity index 93% rename from pumpkin-world/src/block/entities/mob_spawner.rs rename to pumpkin/src/block/entities/mob_spawner.rs index 56107be92..13626a069 100644 --- a/pumpkin-world/src/block/entities/mob_spawner.rs +++ b/pumpkin/src/block/entities/mob_spawner.rs @@ -15,7 +15,7 @@ use pumpkin_util::math::{ vector3::Vector3, }; -use crate::{block::entities::BlockEntity, world::SimpleWorld}; +use crate::{block::entities::BlockEntity, world::World}; pub struct MobSpawnerBlockEntity { pub position: BlockPos, @@ -69,7 +69,7 @@ impl MobSpawnerBlockEntity { } impl MobSpawnerBlockEntity { - async fn update_spawns(&self, world: &Arc) { + async fn update_spawns(&self, world: &Arc) { let min_delay = self.min_delay; let max_delay = self.max_delay; @@ -98,10 +98,7 @@ impl BlockEntity for MobSpawnerBlockEntity { self.position } - fn tick<'a>( - &'a self, - world: &'a Arc, - ) -> Pin + Send + 'a>> { + fn tick<'a>(&'a self, world: &'a Arc) -> Pin + Send + 'a>> { Box::pin(async move { if let Some(entity_type) = &self.entity_type.load() { if self.delay.load(Ordering::Relaxed) == -1 { @@ -140,7 +137,14 @@ impl BlockEntity for MobSpawnerBlockEntity { { continue; } - world.clone().spawn_from_type(entity_type, spawn_pos).await; + let entity = crate::entity::r#type::from_type( + entity_type, + spawn_pos, + world, + uuid::Uuid::new_v4(), + ) + .await; + world.spawn_entity(entity).await; world .sync_world_event(WorldEvent::ParticlesMobblockSpawn, self.position, 0) .await; diff --git a/pumpkin/src/block/entities/mod.rs b/pumpkin/src/block/entities/mod.rs new file mode 100644 index 000000000..a36125b60 --- /dev/null +++ b/pumpkin/src/block/entities/mod.rs @@ -0,0 +1,207 @@ +use std::pin::Pin; +use std::{any::Any, sync::Arc}; + +use pumpkin_data::{Block, block_properties::BLOCK_ENTITY_TYPES}; +use pumpkin_nbt::compound::NbtCompound; +use pumpkin_util::math::position::BlockPos; + +use crate::world::World; +use pumpkin_world::BlockStateId; +use pumpkin_world::inventory::Inventory; + +pub mod barrel; +pub mod beacon; +pub mod bed; +pub mod bell; +pub mod blasting_furnace; +pub mod brewing_stand; +pub mod chest; +pub mod chest_like_block_entity; +pub mod chiseled_bookshelf; +pub mod command_block; +pub mod comparator; +pub mod daylight_detector; +pub mod dropper; +pub mod end_portal; +pub mod ender_chest; +pub mod furnace; +pub mod furnace_like_block_entity; +pub mod hopper; +pub mod jukebox; +pub mod lectern; +pub mod mob_spawner; +pub mod piston; +pub mod shulker_box; +pub mod sign; +pub mod smoker; +pub mod trapped_chest; + +pub use furnace_like_block_entity::ExperienceContainer; +pub use pumpkin_world::block::entities::PropertyDelegate; + +//TODO: We need a mark_dirty for chests +pub trait BlockEntity: Any + Send + Sync { + fn write_nbt<'a>( + &'a self, + nbt: &'a mut NbtCompound, + ) -> Pin + Send + 'a>>; + fn from_nbt(nbt: &NbtCompound, position: BlockPos) -> Self + where + Self: Sized; + fn tick<'a>(&'a self, _world: &'a Arc) -> Pin + Send + 'a>> { + Box::pin(async {}) + } + fn resource_location(&self) -> &'static str; + fn get_position(&self) -> BlockPos; + fn write_internal<'a>( + &'a self, + nbt: &'a mut NbtCompound, + ) -> Pin + Send + 'a>> { + Box::pin(async move { + nbt.put_string("id", self.resource_location().to_string()); + let position = self.get_position(); + nbt.put_int("x", position.0.x); + nbt.put_int("y", position.0.y); + nbt.put_int("z", position.0.z); + self.write_nbt(nbt).await; + }) + } + fn get_id(&self) -> u32 { + pumpkin_data::block_properties::BLOCK_ENTITY_TYPES + .iter() + .position(|block_entity_name| { + *block_entity_name == self.resource_location().split(':').next_back().unwrap() + }) + .unwrap() as u32 + } + + /// Obtain NBT data for sending to the client in [`ChunkData`](crate::chunk::ChunkData) + fn chunk_data_nbt(&self) -> Option { + None + } + + fn get_inventory(self: Arc) -> Option> { + None + } + fn set_block_state(&mut self, _block_state: BlockStateId) {} + fn on_block_replaced<'a>( + self: Arc, + world: Arc, + position: BlockPos, + ) -> Pin + Send + 'a>> + where + Self: 'a, + { + Box::pin(async move { + if let Some(inventory) = self.get_inventory() { + // Assuming scatter_inventory is an async method on World + world.scatter_inventory(&position, &inventory).await; + } + }) + } + fn is_dirty(&self) -> bool { + false + } + + fn clear_dirty(&self) { + // Default implementation does nothing + // Override in implementations that have a dirty flag + } + + fn as_any(&self) -> &dyn Any; + fn to_property_delegate(self: Arc) -> Option> { + None + } + fn to_experience_container(self: Arc) -> Option> { + None + } +} + +#[must_use] +pub fn block_entity_from_generic(nbt: &NbtCompound) -> T { + let x = nbt.get_int("x").unwrap(); + let y = nbt.get_int("y").unwrap(); + let z = nbt.get_int("z").unwrap(); + T::from_nbt(nbt, BlockPos::new(x, y, z)) +} + +#[must_use] +pub fn block_entity_from_nbt(nbt: &NbtCompound) -> Option> { + let id = nbt.get_string("id")?; + let x = nbt.get_int("x")?; + let y = nbt.get_int("y")?; + let z = nbt.get_int("z")?; + let pos = BlockPos::new(x, y, z); + match id { + barrel::BarrelBlockEntity::ID => { + Some(Arc::new(barrel::BarrelBlockEntity::from_nbt(nbt, pos))) + } + chest::ChestBlockEntity::ID => Some(Arc::new(chest::ChestBlockEntity::from_nbt(nbt, pos))), + trapped_chest::TrappedChestBlockEntity::ID => Some(Arc::new( + trapped_chest::TrappedChestBlockEntity::from_nbt(nbt, pos), + )), + ender_chest::EnderChestBlockEntity::ID => Some(Arc::new( + ender_chest::EnderChestBlockEntity::from_nbt(nbt, pos), + )), + furnace::FurnaceBlockEntity::ID => { + Some(Arc::new(furnace::FurnaceBlockEntity::from_nbt(nbt, pos))) + } + blasting_furnace::BlastingFurnaceBlockEntity::ID => Some(Arc::new( + blasting_furnace::BlastingFurnaceBlockEntity::from_nbt(nbt, pos), + )), + smoker::SmokerBlockEntity::ID => { + Some(Arc::new(smoker::SmokerBlockEntity::from_nbt(nbt, pos))) + } + brewing_stand::BrewingStandBlockEntity::ID => Some(Arc::new( + brewing_stand::BrewingStandBlockEntity::from_nbt(nbt, pos), + )), + hopper::HopperBlockEntity::ID => { + Some(Arc::new(hopper::HopperBlockEntity::from_nbt(nbt, pos))) + } + jukebox::JukeboxBlockEntity::ID => { + Some(Arc::new(jukebox::JukeboxBlockEntity::from_nbt(nbt, pos))) + } + mob_spawner::MobSpawnerBlockEntity::ID => Some(Arc::new( + mob_spawner::MobSpawnerBlockEntity::from_nbt(nbt, pos), + )), + sign::SignBlockEntity::ID => Some(Arc::new(sign::SignBlockEntity::from_nbt(nbt, pos))), + piston::PistonBlockEntity::ID => { + Some(Arc::new(piston::PistonBlockEntity::from_nbt(nbt, pos))) + } + chiseled_bookshelf::ChiseledBookshelfBlockEntity::ID => Some(Arc::new( + chiseled_bookshelf::ChiseledBookshelfBlockEntity::from_nbt(nbt, pos), + )), + dropper::DropperBlockEntity::ID => { + Some(Arc::new(dropper::DropperBlockEntity::from_nbt(nbt, pos))) + } + command_block::CommandBlockEntity::ID => Some(Arc::new( + command_block::CommandBlockEntity::from_nbt(nbt, pos), + )), + comparator::ComparatorBlockEntity::ID => Some(Arc::new( + comparator::ComparatorBlockEntity::from_nbt(nbt, pos), + )), + daylight_detector::DaylightDetectorBlockEntity::ID => Some(Arc::new( + daylight_detector::DaylightDetectorBlockEntity::from_nbt(nbt, pos), + )), + end_portal::EndPortalBlockEntity::ID => Some(Arc::new( + end_portal::EndPortalBlockEntity::from_nbt(nbt, pos), + )), + beacon::BeaconBlockEntity::ID => { + Some(Arc::new(beacon::BeaconBlockEntity::from_nbt(nbt, pos))) + } + bed::BedBlockEntity::ID => Some(Arc::new(bed::BedBlockEntity::from_nbt(nbt, pos))), + bell::BellBlockEntity::ID => Some(Arc::new(bell::BellBlockEntity::from_nbt(nbt, pos))), + shulker_box::ShulkerBoxBlockEntity::ID => Some(Arc::new( + shulker_box::ShulkerBoxBlockEntity::from_nbt(nbt, pos), + )), + lectern::LecternBlockEntity::ID => { + Some(Arc::new(lectern::LecternBlockEntity::from_nbt(nbt, pos))) + } + _ => None, + } +} + +#[must_use] +pub fn has_block_block_entity(block: &Block) -> bool { + BLOCK_ENTITY_TYPES.contains(&block.name) +} diff --git a/pumpkin-world/src/block/entities/piston.rs b/pumpkin/src/block/entities/piston.rs similarity index 95% rename from pumpkin-world/src/block/entities/piston.rs rename to pumpkin/src/block/entities/piston.rs index d530d22f2..693c58071 100644 --- a/pumpkin-world/src/block/entities/piston.rs +++ b/pumpkin/src/block/entities/piston.rs @@ -5,7 +5,7 @@ use pumpkin_data::{Block, BlockDirection, BlockState}; use pumpkin_nbt::compound::NbtCompound; use pumpkin_util::math::position::BlockPos; -use crate::world::{BlockFlags, SimpleWorld}; +use crate::world::{BlockFlags, World}; use super::BlockEntity; @@ -22,7 +22,7 @@ pub struct PistonBlockEntity { impl PistonBlockEntity { pub const ID: &'static str = "minecraft:piston"; - pub async fn finish(&self, world: Arc) { + pub async fn finish(&self, world: Arc) { if self.last_progress.load() < 1.0 { let pos = self.position; world.remove_block_entity(&pos).await; @@ -61,10 +61,7 @@ impl BlockEntity for PistonBlockEntity { self.position } - fn tick<'a>( - &'a self, - world: &'a Arc, - ) -> Pin + Send + 'a>> { + fn tick<'a>(&'a self, world: &'a Arc) -> Pin + Send + 'a>> { Box::pin(async move { let current_progress = self.current_progress.load(); self.last_progress.store(current_progress); diff --git a/pumpkin-world/src/block/entities/shulker_box.rs b/pumpkin/src/block/entities/shulker_box.rs similarity index 91% rename from pumpkin-world/src/block/entities/shulker_box.rs rename to pumpkin/src/block/entities/shulker_box.rs index fc180cbc3..b7fbf3883 100644 --- a/pumpkin-world/src/block/entities/shulker_box.rs +++ b/pumpkin/src/block/entities/shulker_box.rs @@ -13,12 +13,14 @@ use std::{ }; use tokio::sync::Mutex; -use crate::block::viewer::{ViewerCountListener, ViewerCountTracker, ViewerFuture}; -use crate::inventory::InventoryFuture; -use crate::inventory::{ +use crate::block::viewer::{ + ViewerCountListener, ViewerCountTracker, ViewerCountTrackerExt, ViewerFuture, +}; +use crate::world::World; +use pumpkin_world::inventory::InventoryFuture; +use pumpkin_world::inventory::{ split_stack, {Clearable, Inventory}, }; -use crate::world::SimpleWorld; use super::BlockEntity; @@ -63,10 +65,7 @@ impl BlockEntity for ShulkerBoxBlockEntity { self.write_inventory_nbt(nbt, true) } - fn tick<'a>( - &'a self, - world: &'a Arc, - ) -> Pin + Send + 'a>> { + fn tick<'a>(&'a self, world: &'a Arc) -> Pin + Send + 'a>> { Box::pin(async move { self.viewers .update_viewer_count::(self, world, &self.position) @@ -76,7 +75,7 @@ impl BlockEntity for ShulkerBoxBlockEntity { fn on_block_replaced<'a>( self: Arc, - _world: Arc, + _world: Arc, _position: BlockPos, ) -> Pin + Send + 'a>> where @@ -107,7 +106,7 @@ impl BlockEntity for ShulkerBoxBlockEntity { impl ViewerCountListener for ShulkerBoxBlockEntity { fn on_container_open<'a>( &'a self, - world: &'a Arc, + world: &'a Arc, position: &'a BlockPos, ) -> ViewerFuture<'a, ()> { Box::pin(async move { @@ -119,7 +118,7 @@ impl ViewerCountListener for ShulkerBoxBlockEntity { fn on_container_close<'a>( &'a self, - world: &'a Arc, + world: &'a Arc, position: &'a BlockPos, ) -> ViewerFuture<'a, ()> { Box::pin(async move { @@ -131,7 +130,7 @@ impl ViewerCountListener for ShulkerBoxBlockEntity { fn on_viewer_count_update<'a>( &'a self, - world: &'a Arc, + world: &'a Arc, position: &'a BlockPos, _old: u16, new: u16, @@ -159,7 +158,7 @@ impl ShulkerBoxBlockEntity { } } - async fn play_sound(&self, world: &Arc, position: &BlockPos, sound: Sound) { + async fn play_sound(&self, world: &Arc, position: &BlockPos, sound: Sound) { let mut rng = Xoroshiro::from_seed(get_seed()); world diff --git a/pumpkin-world/src/block/entities/sign.rs b/pumpkin/src/block/entities/sign.rs similarity index 98% rename from pumpkin-world/src/block/entities/sign.rs rename to pumpkin/src/block/entities/sign.rs index f922a9f9d..d543dd4ff 100644 --- a/pumpkin-world/src/block/entities/sign.rs +++ b/pumpkin/src/block/entities/sign.rs @@ -139,11 +139,12 @@ impl Default for Text { Self { has_glowing_text: AtomicBool::new(false), color: AtomicI8::new(DyeColor::default() as i8), - messages: Default::default(), + messages: Arc::default(), } } } +#[allow(clippy::fallible_impl_from)] impl From for NbtTag { fn from(value: Text) -> Self { let mut nbt = NbtCompound::new(); @@ -166,6 +167,7 @@ impl From for NbtTag { } } +#[allow(clippy::fallible_impl_from)] impl From for Text { fn from(tag: NbtTag) -> Self { let nbt = tag.extract_compound().unwrap(); diff --git a/pumpkin-world/src/block/entities/smoker.rs b/pumpkin/src/block/entities/smoker.rs similarity index 98% rename from pumpkin-world/src/block/entities/smoker.rs rename to pumpkin/src/block/entities/smoker.rs index 8dd27a19f..680c93cf4 100644 --- a/pumpkin-world/src/block/entities/smoker.rs +++ b/pumpkin/src/block/entities/smoker.rs @@ -1,5 +1,5 @@ -use crate::inventory::Inventory; use pumpkin_data::{block_properties::BlockProperties, item_stack::ItemStack}; +use pumpkin_world::inventory::Inventory; use std::{ array::from_fn, diff --git a/pumpkin-world/src/block/entities/trapped_chest.rs b/pumpkin/src/block/entities/trapped_chest.rs similarity index 100% rename from pumpkin-world/src/block/entities/trapped_chest.rs rename to pumpkin/src/block/entities/trapped_chest.rs diff --git a/pumpkin/src/block/mod.rs b/pumpkin/src/block/mod.rs index cf57facf4..b1770fbf9 100644 --- a/pumpkin/src/block/mod.rs +++ b/pumpkin/src/block/mod.rs @@ -13,8 +13,10 @@ use std::sync::Arc; use std::sync::atomic::Ordering; pub mod blocks; +pub mod entities; pub mod fluid; pub mod registry; +pub mod viewer; use crate::block::registry::BlockActionResult; use crate::entity::EntityBase; diff --git a/pumpkin/src/block/registry.rs b/pumpkin/src/block/registry.rs index f2253b998..49ea4e22e 100644 --- a/pumpkin/src/block/registry.rs +++ b/pumpkin/src/block/registry.rs @@ -1,8 +1,8 @@ use crate::block::blocks::anvil::AnvilBlock; - use crate::block::blocks::banners::BannerBlock; use crate::block::blocks::barrel::BarrelBlock; use crate::block::blocks::barrier::BarrierBlock; +use crate::block::blocks::beacon::BeaconBlock; use crate::block::blocks::bed::BedBlock; use crate::block::blocks::brewing_stand::BrewingStandBlock; use crate::block::blocks::cake::CakeBlock; @@ -179,6 +179,7 @@ pub fn default_registry() -> Arc { // Blocks manager.register(AnvilBlock); + manager.register(BeaconBlock); manager.register(BedBlock); manager.register(SaplingBlock); manager.register(CactusBlock); diff --git a/pumpkin/src/block/viewer.rs b/pumpkin/src/block/viewer.rs new file mode 100644 index 000000000..71de5b27d --- /dev/null +++ b/pumpkin/src/block/viewer.rs @@ -0,0 +1,83 @@ +use std::{ + pin::Pin, + sync::{Arc, atomic::Ordering}, +}; + +use pumpkin_util::math::position::BlockPos; + +use crate::{block::entities::BlockEntity, world::World}; + +pub use pumpkin_world::block::viewer::ViewerCountTracker; + +pub trait ViewerCountTrackerExt { + fn update_viewer_count<'a, T>( + &'a self, + entity: &'a T, + world: &'a Arc, + position: &'a BlockPos, + ) -> Pin + Send + 'a>> + where + T: BlockEntity + ViewerCountListener + 'static; +} + +impl ViewerCountTrackerExt for ViewerCountTracker { + fn update_viewer_count<'a, T>( + &'a self, + entity: &'a T, + world: &'a Arc, + position: &'a BlockPos, + ) -> Pin + Send + 'a>> + where + T: BlockEntity + ViewerCountListener + 'static, + { + Box::pin(async move { + let current = self.current.load(Ordering::Relaxed); + let old = self.old.swap(current, Ordering::Relaxed); + if old != current { + match (old, current) { + (n, 0) if n > 0 => { + entity.on_container_close(world, position).await; + } + (0, n) if n > 0 => { + entity.on_container_open(world, position).await; + } + _ => {} // Ignore + } + + entity + .on_viewer_count_update(world, position, old, current) + .await; + } + }) + } +} + +pub type ViewerFuture<'a, T> = Pin + Send + 'a>>; + +pub trait ViewerCountListener: Send + Sync { + fn on_container_open<'a>( + &'a self, + _world: &'a Arc, + _position: &'a BlockPos, + ) -> ViewerFuture<'a, ()> { + Box::pin(async {}) + } + + fn on_container_close<'a>( + &'a self, + _world: &'a Arc, + _position: &'a BlockPos, + ) -> ViewerFuture<'a, ()> { + Box::pin(async {}) + } + + fn on_viewer_count_update<'a>( + &'a self, + _world: &'a Arc, + _position: &'a BlockPos, + _old: u16, + _new: u16, + ) -> ViewerFuture<'a, ()> { + Box::pin(async {}) + } +} diff --git a/pumpkin/src/command/commands/particle.rs b/pumpkin/src/command/commands/particle.rs index d1bb9188c..129f2912d 100644 --- a/pumpkin/src/command/commands/particle.rs +++ b/pumpkin/src/command/commands/particle.rs @@ -1,3 +1,4 @@ +use crate::block::entities::BlockEntity; use crate::command::{ CommandError, CommandExecutor, CommandResult, CommandSender, args::{ @@ -7,7 +8,6 @@ use crate::command::{ tree::{CommandTree, builder::argument}, }; use pumpkin_util::{math::vector3::Vector3, text::TextComponent}; -use pumpkin_world::block::entities::BlockEntity; const NAMES: [&str; 1] = ["particle"]; const DESCRIPTION: &str = "Spawns a Particle at position."; diff --git a/pumpkin/src/command/commands/summon.rs b/pumpkin/src/command/commands/summon.rs index f9e94d752..cf6a871c5 100644 --- a/pumpkin/src/command/commands/summon.rs +++ b/pumpkin/src/command/commands/summon.rs @@ -13,7 +13,7 @@ use pumpkin_data::translation; use pumpkin_util::{math::vector3::Vector3, text::TextComponent}; use uuid::Uuid; -use pumpkin_world::block::entities::BlockEntity; +use crate::block::entities::BlockEntity; const NAMES: [&str; 1] = ["summon"]; diff --git a/pumpkin/src/command/mod.rs b/pumpkin/src/command/mod.rs index 999ef72b6..4f61f3cd5 100644 --- a/pumpkin/src/command/mod.rs +++ b/pumpkin/src/command/mod.rs @@ -8,6 +8,8 @@ use crate::server::Server; use crate::world::World; use args::ConsumedArgs; +use crate::block::entities::BlockEntity; +use crate::block::entities::command_block::CommandBlockEntity; use crate::command::context::command_source::CommandSource; use crate::entity::EntityBase; use dispatcher::CommandError; @@ -21,8 +23,6 @@ use pumpkin_util::math::vector3::Vector3; use pumpkin_util::permission::{PermissionDefault, PermissionLvl}; use pumpkin_util::text::TextComponent; use pumpkin_util::translation::Locale; -use pumpkin_world::block::entities::BlockEntity; -use pumpkin_world::block::entities::command_block::CommandBlockEntity; pub mod args; pub mod argument_builder; diff --git a/pumpkin/src/item/items/dye.rs b/pumpkin/src/item/items/dye.rs index d8cfc1ab2..d9d7cd585 100644 --- a/pumpkin/src/item/items/dye.rs +++ b/pumpkin/src/item/items/dye.rs @@ -1,11 +1,11 @@ use std::sync::Arc; -use pumpkin_data::tag; -use pumpkin_util::GameMode; -use pumpkin_world::block::entities::{ +use crate::block::entities::{ BlockEntity, sign::{DyeColor, Text}, }; +use pumpkin_data::tag; +use pumpkin_util::GameMode; use crate::{ block::{UseWithItemArgs, registry::BlockActionResult}, diff --git a/pumpkin/src/item/items/glowing_ink_sac.rs b/pumpkin/src/item/items/glowing_ink_sac.rs index d305c8350..c836921ea 100644 --- a/pumpkin/src/item/items/glowing_ink_sac.rs +++ b/pumpkin/src/item/items/glowing_ink_sac.rs @@ -1,7 +1,7 @@ use std::sync::{Arc, atomic::Ordering}; +use crate::block::entities::{BlockEntity, sign::Text}; use pumpkin_data::item::Item; -use pumpkin_world::block::entities::{BlockEntity, sign::Text}; use crate::{ block::{UseWithItemArgs, registry::BlockActionResult}, diff --git a/pumpkin/src/item/items/honeycomb.rs b/pumpkin/src/item/items/honeycomb.rs index 474439879..b2abaa739 100644 --- a/pumpkin/src/item/items/honeycomb.rs +++ b/pumpkin/src/item/items/honeycomb.rs @@ -3,6 +3,8 @@ use std::sync::Arc; use std::sync::atomic::Ordering; use crate::block::UseWithItemArgs; +use crate::block::entities::BlockEntity; +use crate::block::entities::sign::SignBlockEntity; use crate::block::registry::BlockActionResult; use crate::entity::player::Player; use crate::item::{ItemBehaviour, ItemMetadata}; @@ -17,8 +19,6 @@ use pumpkin_data::world::WorldEvent; use pumpkin_data::{Block, tag}; use pumpkin_util::math::position::BlockPos; use pumpkin_util::math::vector3::Vector3; -use pumpkin_world::block::entities::BlockEntity; -use pumpkin_world::block::entities::sign::SignBlockEntity; use pumpkin_world::world::BlockFlags; pub struct HoneyCombItem; diff --git a/pumpkin/src/item/items/ink_sac.rs b/pumpkin/src/item/items/ink_sac.rs index 04153fe10..448195e5c 100644 --- a/pumpkin/src/item/items/ink_sac.rs +++ b/pumpkin/src/item/items/ink_sac.rs @@ -1,7 +1,7 @@ use std::sync::{Arc, atomic::Ordering}; +use crate::block::entities::{BlockEntity, sign::Text}; use pumpkin_data::item::Item; -use pumpkin_world::block::entities::{BlockEntity, sign::Text}; use crate::{ block::{UseWithItemArgs, registry::BlockActionResult}, diff --git a/pumpkin/src/item/items/spawn_egg.rs b/pumpkin/src/item/items/spawn_egg.rs index 8ea607ec1..d61a03c02 100644 --- a/pumpkin/src/item/items/spawn_egg.rs +++ b/pumpkin/src/item/items/spawn_egg.rs @@ -1,5 +1,6 @@ use std::pin::Pin; +use crate::block::entities::mob_spawner::MobSpawnerBlockEntity; use crate::entity::player::Player; use crate::entity::r#type::from_type; use crate::item::{ItemBehaviour, ItemMetadata}; @@ -10,7 +11,6 @@ use pumpkin_data::{Block, BlockDirection}; use pumpkin_util::math::position::BlockPos; use pumpkin_util::math::vector3::Vector3; use pumpkin_util::math::wrap_degrees; -use pumpkin_world::block::entities::mob_spawner::MobSpawnerBlockEntity; use uuid::Uuid; pub struct SpawnEggItem; diff --git a/pumpkin/src/net/java/play.rs b/pumpkin/src/net/java/play.rs index 6d3aa2823..afe70d0c6 100644 --- a/pumpkin/src/net/java/play.rs +++ b/pumpkin/src/net/java/play.rs @@ -33,6 +33,8 @@ use crate::plugin::player::player_move::PlayerMoveEvent; use crate::plugin::player::player_toggle_flight_event::PlayerToggleFlightEvent; use crate::plugin::player::player_toggle_sneak_event::PlayerToggleSneakEvent; +use crate::block::entities::command_block::CommandBlockEntity; +use crate::block::entities::sign::SignBlockEntity; use crate::plugin::player::player_toggle_sprint_event::PlayerToggleSprintEvent; use crate::server::{Server, seasonal_events}; use crate::world::{World, chunker}; @@ -71,8 +73,6 @@ use pumpkin_util::math::vector3::Vector3; use pumpkin_util::math::{polynomial_rolling_hash, position::BlockPos, wrap_degrees}; use pumpkin_util::text::color::NamedColor; use pumpkin_util::{GameMode, text::TextComponent}; -use pumpkin_world::block::entities::command_block::CommandBlockEntity; -use pumpkin_world::block::entities::sign::SignBlockEntity; use pumpkin_world::world::BlockFlags; use tokio::sync::Mutex; diff --git a/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1/world.rs b/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1/world.rs index 8d81841f7..8a297777b 100644 --- a/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1/world.rs +++ b/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1/world.rs @@ -1,7 +1,7 @@ use pumpkin_data::BlockDirection as InternalBlockDirection; use pumpkin_data::block_state::PistonBehavior; use pumpkin_util::math::position::BlockPos; -use pumpkin_world::world::{BlockFlags, SimpleWorld}; +use pumpkin_world::world::BlockFlags; use std::sync::Arc; use wasmtime::component::Resource; diff --git a/pumpkin/src/world/mod.rs b/pumpkin/src/world/mod.rs index 40b95d965..ab8533de7 100644 --- a/pumpkin/src/world/mod.rs +++ b/pumpkin/src/world/mod.rs @@ -1,3 +1,5 @@ +use crate::block::entities::BlockEntity; +use dashmap::DashMap; use pumpkin_protocol::bedrock::client::level_event::{CLevelEvent, LevelEvent}; use pumpkin_protocol::codec::data_component::data_to_proto_sound; use std::pin::Pin; @@ -18,9 +20,7 @@ pub mod time; use crate::block::RandomTickArgs; use crate::world::chunker::is_within_view_distance; use crate::world::{chunker::get_view_distance, loot::LootContextParameters}; -use crate::{ - block::BlockEvent, entity::experience_orb::ExperienceOrbEntity, entity::item::ItemEntity, -}; +use crate::{block::BlockEvent, entity::item::ItemEntity}; use crate::{ block::{ self, @@ -120,14 +120,13 @@ use pumpkin_util::{ random::{RandomImpl, get_seed, xoroshiro128::Xoroshiro}, }; use pumpkin_world::inventory::Clearable; -use pumpkin_world::world::{GetBlockError, WorldFuture}; +use pumpkin_world::world::GetBlockError; use pumpkin_world::{ - BlockStateId, CURRENT_BEDROCK_MC_VERSION, biome, block::entities::BlockEntity, - chunk::io::Dirtiable, inventory::Inventory, world::SimpleWorld, + BlockStateId, CURRENT_BEDROCK_MC_VERSION, biome, chunk::io::Dirtiable, inventory::Inventory, }; use pumpkin_world::{chunk::ChunkData, world::BlockAccessor}; use pumpkin_world::{level::Level, tick::TickPriority}; -use pumpkin_world::{world::BlockFlags, world_info::LevelData}; +pub use pumpkin_world::{world::BlockFlags, world_info::LevelData}; use rand::seq::SliceRandom; use rand::{RngExt, rng}; use scoreboard::Scoreboard; @@ -213,6 +212,7 @@ pub struct World { pub dragon_fight: Option>, pub spawn_state: ArcSwap, pub active_chunks: ArcSwap>>, + pub block_entities: DashMap>, } impl PartialEq for World { @@ -260,6 +260,7 @@ impl World { spawn_state: ArcSwap::new(Arc::new(SpawnState::empty())), active_chunks: ArcSwap::new(Arc::new(FxHashSet::default())), server, + block_entities: DashMap::new(), } } @@ -777,6 +778,7 @@ impl World { .await; } + #[expect(clippy::too_many_lines)] pub async fn tick(self: &Arc, server: Arc) { let start = tokio::time::Instant::now(); @@ -853,6 +855,28 @@ impl World { } let entity_elapsed = entity_start.elapsed(); + let block_entity_start = tokio::time::Instant::now(); + let block_entities: Vec> = self + .block_entities + .iter() + .map(|e| e.value().clone()) + .collect(); + let block_entity_count = block_entities.len(); + + let mut block_entity_tasks = tokio::task::JoinSet::new(); + for block_entity in block_entities { + let world_clone = self.clone(); + block_entity_tasks.spawn(async move { + block_entity.tick(&world_clone).await; + }); + } + while let Some(res) = block_entity_tasks.join_next().await { + if let Err(e) = res { + error!("Block entity tick panicked: {:?}", e); + } + } + let block_entity_elapsed = block_entity_start.elapsed(); + //self.level.chunk_loading.lock().unwrap().send_change(); // Tick the End dragon fight (only on THE_END worlds). @@ -863,13 +887,15 @@ impl World { let total_elapsed = start.elapsed(); if total_elapsed.as_millis() > 50 { debug!( - "Slow Tick [{}ms]: Chunks: {:?} | Players({}): {:?} | Entities({}): {:?}", + "Slow Tick [{}ms]: Chunks: {:?} | Players({}): {:?} | Entities({}): {:?} | Block Entities({}): {:?}", total_elapsed.as_millis(), chunk_elapsed, player_count, player_elapsed, entity_count, entity_elapsed, + block_entity_count, + block_entity_elapsed, ); } } @@ -974,7 +1000,6 @@ impl World { } } - #[expect(clippy::too_many_lines)] pub async fn tick_chunks(self: &Arc) { let active_chunks = self.active_chunks.load(); let tick_data = self.level.get_tick_data(&active_chunks); @@ -1031,16 +1056,6 @@ impl World { } } - let world_simple: Arc = self.clone(); - let mut block_entity_tasks = JoinSet::new(); - for block_entity in tick_data.block_entities { - let world_simple = world_simple.clone(); - block_entity_tasks.spawn(async move { - block_entity.tick(&world_simple).await; - }); - } - while block_entity_tasks.join_next().await.is_some() {} - let spawn_state = self.spawn_state.load(); // TODO gamerule this.spawnEnemies || this.spawnFriendlies @@ -1486,6 +1501,14 @@ impl World { spawn_for_chunk(self, chunk_pos, chunk, spawn_state, spawn_list).await; } + pub async fn get_world_age(&self) -> i64 { + self.level_time.lock().await.world_age + } + + pub async fn get_time_of_day(&self) -> i64 { + self.level_time.lock().await.time_of_day + } + pub async fn set_time_of_day(&self, time: i64) { let mut level_time = self.level_time.lock().await; level_time.set_time(time); @@ -3306,8 +3329,7 @@ impl World { && old_block.default_state.block_entity_type != u16::MAX && let Some(entity) = self.get_block_entity(position).await { - let world: Arc = self.clone(); - entity.on_block_replaced(world, *position).await; + entity.on_block_replaced(self.clone(), *position).await; self.remove_block_entity(position).await; } @@ -3843,6 +3865,32 @@ impl World { } } + pub async fn update_from_neighbor_shapes( + self: &Arc, + state_id: BlockStateId, + pos: &BlockPos, + ) -> BlockStateId { + let mut current_state_id = state_id; + let block = Block::from_state_id(state_id); + for direction in BlockDirection::all() { + let neighbor_pos = pos.offset(direction.to_offset()); + let neighbor_state_id = self.get_block_state_id(&neighbor_pos).await; + current_state_id = self + .block_registry + .get_state_for_neighbor_update( + self, + block, + current_state_id, + pos, + direction, + &neighbor_pos, + neighbor_state_id, + ) + .await; + } + current_state_id + } + pub async fn replace_with_state_for_neighbor_update( self: &Arc, block_pos: &BlockPos, @@ -3882,12 +3930,11 @@ impl World { } } + #[expect(clippy::unused_async)] pub async fn get_block_entity(&self, block_pos: &BlockPos) -> Option> { - self.level - .get_or_fetch_chunk(block_pos.chunk_position(), |chunk| { - chunk.block_entities.lock().unwrap().get(block_pos).cloned() - }) - .await + self.block_entities + .get(block_pos) + .map(|e| e.value().clone()) } pub async fn add_block_entity(&self, block_entity: Arc) { @@ -3909,32 +3956,22 @@ impl World { .await; } + self.block_entities.insert(block_pos, block_entity.clone()); self.level .get_or_fetch_chunk(chunk_pos, |chunk| { - chunk - .block_entities - .lock() - .unwrap() - .insert(block_pos, block_entity.clone()); chunk.mark_dirty(true); }) .await; } pub async fn remove_block_entity(&self, block_pos: &BlockPos) { - self.level - .get_or_fetch_chunk(block_pos.chunk_position(), |chunk| { - if chunk - .block_entities - .lock() - .unwrap() - .remove(block_pos) - .is_some() - { + if self.block_entities.remove(block_pos).is_some() { + self.level + .get_or_fetch_chunk(block_pos.chunk_position(), |chunk| { chunk.mark_dirty(true); - } - }) - .await; + }) + .await; + } } pub async fn update_block_entity(&self, block_entity: &Arc) { @@ -4237,178 +4274,6 @@ impl World { } } -impl pumpkin_world::world::SimpleWorld for World { - fn set_block_state( - self: Arc, - position: &BlockPos, - block_state_id: BlockStateId, - flags: BlockFlags, - ) -> WorldFuture<'_, BlockStateId> { - Box::pin(async move { Self::set_block_state(&self, position, block_state_id, flags).await }) - } - - fn update_neighbor<'a>( - self: Arc, - neighbor_block_pos: &'a BlockPos, - source_block: &'a pumpkin_data::Block, - ) -> WorldFuture<'a, ()> { - Box::pin(async move { - Self::update_neighbor(&self, neighbor_block_pos, source_block).await; - }) - } - - fn update_neighbors( - self: Arc, - block_pos: &BlockPos, - except: Option, - ) -> WorldFuture<'_, ()> { - Box::pin(async move { - Self::update_neighbors(&self, block_pos, except).await; - }) - } - - fn is_space_empty(&self, bounding_box: BoundingBox) -> WorldFuture<'_, bool> { - Box::pin(async move { self.is_space_empty(bounding_box).await }) - } - - fn add_synced_block_event(&self, pos: BlockPos, r#type: u8, data: u8) -> WorldFuture<'_, ()> { - Box::pin(async move { - self.add_synced_block_event(pos, r#type, data).await; - }) - } - - fn sync_world_event( - &self, - world_event: WorldEvent, - position: BlockPos, - data: i32, - ) -> WorldFuture<'_, ()> { - Box::pin(async move { - self.sync_world_event(world_event, position, data).await; - }) - } - - fn spawn_from_type( - self: Arc, - entity_type: &'static EntityType, - position: Vector3, - ) -> WorldFuture<'static, ()> { - Box::pin(async move { - let mob = from_type(entity_type, position, &self, Uuid::new_v4()).await; - self.spawn_entity(mob).await; - }) - } - - fn remove_block_entity<'a>(&'a self, block_pos: &'a BlockPos) -> WorldFuture<'a, ()> { - Box::pin(async move { - self.remove_block_entity(block_pos).await; - }) - } - - fn get_block_entity<'a>( - &'a self, - block_pos: &'a BlockPos, - ) -> WorldFuture<'a, Option>> { - Box::pin(async move { self.get_block_entity(block_pos).await }) - } - - fn get_world_age(&self) -> WorldFuture<'_, i64> { - Box::pin(async move { - // Note: MutexGuard must be released before returning the future's result. - let level_time_guard = self.level_time.lock().await; - level_time_guard.world_age - }) - } - - fn get_time_of_day(&self) -> WorldFuture<'_, i64> { - Box::pin(async move { - let level_time_guard = self.level_time.lock().await; - level_time_guard.query_daytime() - }) - } - - fn get_level(&self) -> WorldFuture<'_, &Arc> { - Box::pin(async move { &self.level }) - } - - fn get_dimension(&self) -> WorldFuture<'_, &Dimension> { - Box::pin(async move { &self.dimension }) - } - - fn play_sound<'a>( - &'a self, - sound: Sound, - category: SoundCategory, - position: &'a Vector3, - ) -> WorldFuture<'a, ()> { - Box::pin(async move { - self.play_sound(sound, category, position).await; - }) - } - - fn play_sound_fine<'a>( - &'a self, - sound: Sound, - category: SoundCategory, - position: &'a Vector3, - volume: f32, - pitch: f32, - ) -> WorldFuture<'a, ()> { - Box::pin(async move { - self.play_sound_fine(sound, category, position, volume, pitch) - .await; - }) - } - - fn scatter_inventory<'a>( - self: Arc, - position: &'a BlockPos, - inventory: &'a Arc, - ) -> WorldFuture<'a, ()> { - Box::pin(async move { - Self::scatter_inventory(&self, position, inventory).await; - }) - } - - fn spawn_experience_orbs( - self: Arc, - position: Vector3, - amount: u32, - ) -> WorldFuture<'static, ()> { - Box::pin(async move { - ExperienceOrbEntity::spawn(&self, position, amount).await; - }) - } - - fn update_from_neighbor_shapes( - self: Arc, - block_state_id: BlockStateId, - position: &BlockPos, - ) -> WorldFuture<'_, BlockStateId> { - Box::pin(async move { - let block = Block::from_state_id(block_state_id); - let mut state_id = block_state_id; - for direction in BlockDirection::update_order() { - let neighbor_pos = position.offset(direction.to_offset()); - let neighbor_state_id = self.get_block_state_id(&neighbor_pos).await; - state_id = self - .block_registry - .get_state_for_neighbor_update( - &self, - block, - state_id, - position, - direction, - &neighbor_pos, - neighbor_state_id, - ) - .await; - } - state_id - }) - } -} - impl BlockAccessor for World { fn get_block<'a>( &'a self,