From 6eb02f65b903b8b48359cedb0d69ddf241e64913 Mon Sep 17 00:00:00 2001 From: Alexander Medvedev Date: Fri, 28 Aug 2026 13:31:28 +0200 Subject: [PATCH] chore: more fixes --- Cargo.lock | 29 +- Cargo.toml | 6 +- .../enchanting/enchanting_screen_handler.rs | 14 +- .../pumpkin-inventory/src/screen_handler.rs | 2 +- crates/pumpkin-inventory/src/sync_handler.rs | 3 +- crates/pumpkin-macros/src/lib.rs | 82 +++++ crates/pumpkin-protocol/fuzz/Cargo.lock | 32 -- .../fuzz/fuzz_targets/decoder_bedrock.rs | 8 +- .../fuzz/fuzz_targets/encoder_bedrock.rs | 2 +- .../fuzz/fuzz_targets/encoder_java.rs | 4 +- crates/pumpkin-util/src/math/pool.rs | 5 +- crates/pumpkin-world/benches/chunk.rs | 37 +- crates/pumpkin-world/benches/chunk_gen.rs | 3 - .../benches/chunk_gen_concurrent.rs | 2 - crates/pumpkin-world/benches/chunk_io.rs | 18 +- .../pumpkin-world/src/chunk/format/anvil.rs | 45 ++- .../pumpkin-world/src/chunk/format/linear.rs | 35 +- crates/pumpkin-world/src/chunk/format/pump.rs | 64 ++-- .../src/chunk_system/generation.rs | 40 +-- .../src/chunk_system/schedule.rs | 27 +- .../src/chunk_system/worker_logic.rs | 56 ++-- .../src/generation/blender/blending_data.rs | 4 +- .../src/generation/blender/mod.rs | 12 +- .../generation/feature/configured_features.rs | 24 +- .../feature/features/coral/coral_claw.rs | 4 - .../feature/features/drip_stone/cluster.rs | 9 +- .../feature/features/tree/foliage/acacia.rs | 2 +- .../feature/features/tree/foliage/blob.rs | 2 +- .../feature/features/tree/foliage/bush.rs | 2 +- .../feature/features/tree/foliage/dark_oak.rs | 2 +- .../feature/features/tree/foliage/fancy.rs | 2 +- .../feature/features/tree/foliage/jungle.rs | 6 +- .../features/tree/foliage/mega_pine.rs | 2 +- .../feature/features/tree/foliage/mod.rs | 20 +- .../feature/features/tree/foliage/pine.rs | 2 +- .../features/tree/foliage/random_spread.rs | 3 +- .../generation/feature/features/tree/mod.rs | 19 +- crates/pumpkin-world/src/lib.rs | 29 +- crates/pumpkin/src/block/blocks/end_portal.rs | 2 +- crates/pumpkin/src/block/blocks/fire/fire.rs | 10 +- .../pumpkin/src/block/blocks/nether_portal.rs | 4 +- .../src/block/blocks/plant/mushroom_plant.rs | 9 +- crates/pumpkin/src/block/blocks/vine.rs | 2 - crates/pumpkin/src/block/fluid/lava.rs | 6 +- crates/pumpkin/src/block/registry.rs | 21 +- crates/pumpkin/src/command/commands/mod.rs | 29 +- crates/pumpkin/src/command/commands/raid.rs | 2 +- crates/pumpkin/src/command/dispatcher.rs | 9 +- .../src/entity/ai/goal/follow_owner.rs | 2 +- crates/pumpkin/src/entity/ai/goal/mod.rs | 3 +- .../src/entity/ai/goal/move_to_target_pos.rs | 68 +++- .../src/entity/ai/goal/move_towards_target.rs | 208 ++++++++++++ .../src/entity/ai/goal/track_target.rs | 4 +- .../ai/pathfinder/walk_node_evaluator.rs | 28 +- .../pumpkin/src/entity/boss/ender_dragon.rs | 9 +- crates/pumpkin/src/entity/combat.rs | 2 +- crates/pumpkin/src/entity/effect/raid_omen.rs | 2 +- crates/pumpkin/src/entity/experience_orb.rs | 2 +- crates/pumpkin/src/entity/falling.rs | 4 +- crates/pumpkin/src/entity/item.rs | 13 +- crates/pumpkin/src/entity/living.rs | 6 +- crates/pumpkin/src/entity/mob/equipment.rs | 12 +- crates/pumpkin/src/entity/mob/shulker.rs | 6 +- crates/pumpkin/src/entity/mod.rs | 69 ++-- .../pumpkin/src/entity/passive/iron_golem.rs | 10 +- crates/pumpkin/src/entity/player.rs | 190 +++++++---- crates/pumpkin/src/entity/projectile/egg.rs | 4 +- .../src/entity/projectile/ender_pearl.rs | 4 +- .../pumpkin/src/entity/projectile/fireball.rs | 7 +- .../src/entity/projectile/firework_rocket.rs | 4 +- .../src/entity/projectile/fishing_bobber.rs | 6 +- .../src/entity/projectile/lingering_potion.rs | 4 +- .../src/entity/projectile/llama_spit.rs | 4 +- crates/pumpkin/src/entity/projectile/mod.rs | 13 +- .../src/entity/projectile/small_fireball.rs | 4 +- .../pumpkin/src/entity/projectile/snowball.rs | 4 +- .../src/entity/projectile/splash_potion.rs | 4 +- .../src/entity/projectile/wind_charge.rs | 5 +- .../src/entity/projectile/wither_skull.rs | 4 +- crates/pumpkin/src/entity/tnt.rs | 4 +- crates/pumpkin/src/item/items/egg.rs | 3 +- crates/pumpkin/src/item/items/ender_pearl.rs | 2 +- crates/pumpkin/src/item/items/potions.rs | 7 +- crates/pumpkin/src/item/items/snowball.rs | 2 +- crates/pumpkin/src/item/items/wind_charge.rs | 2 +- crates/pumpkin/src/lib.rs | 50 ++- crates/pumpkin/src/net/bedrock/login/login.rs | 4 +- crates/pumpkin/src/net/bedrock/login/mod.rs | 25 +- crates/pumpkin/src/net/bedrock/mod.rs | 145 ++++---- crates/pumpkin/src/net/bedrock/nethernet.rs | 29 +- .../src/net/bedrock/nethernet/discovery.rs | 2 +- .../pumpkin/src/net/bedrock/play/animate.rs | 2 +- .../net/bedrock/play/block_pick_request.rs | 26 +- .../src/net/bedrock/play/container_close.rs | 17 +- crates/pumpkin/src/net/bedrock/play/emote.rs | 2 +- .../src/net/bedrock/play/emote_list.rs | 2 +- .../src/net/bedrock/play/interaction.rs | 12 +- .../src/net/bedrock/play/inventory_action.rs | 50 ++- .../net/bedrock/play/item_stack_request.rs | 10 +- .../src/net/bedrock/play/mob_equipment.rs | 32 +- .../net/bedrock/play/modal_form_response.rs | 4 +- .../src/net/bedrock/play/player_action.rs | 15 +- .../src/net/bedrock/play/player_auth_input.rs | 13 +- .../net/bedrock/play/player_block_action.rs | 7 +- .../net/bedrock/play/request_chunk_radius.rs | 18 +- .../pumpkin/src/net/bedrock/play/respawn.rs | 7 +- crates/pumpkin/src/net/bedrock/status.rs | 2 +- .../src/net/java/config/known_packs.rs | 6 +- crates/pumpkin/src/net/java/config/mod.rs | 2 +- .../src/net/java/login/encryption_response.rs | 9 +- .../pumpkin/src/net/java/login/known_packs.rs | 2 +- .../src/net/java/login/login_acknowledged.rs | 8 +- crates/pumpkin/src/net/java/login/mod.rs | 1 - crates/pumpkin/src/net/java/mod.rs | 315 ++++++++---------- crates/pumpkin/src/net/java/pending.rs | 3 +- crates/pumpkin/src/net/java/play/attack.rs | 14 +- .../src/net/java/play/bundle_item_selected.rs | 9 +- crates/pumpkin/src/net/java/play/chat_ack.rs | 12 +- .../pumpkin/src/net/java/play/chat_message.rs | 18 +- .../src/net/java/play/client_command.rs | 63 ++-- .../src/net/java/play/client_information.rs | 18 +- .../src/net/java/play/close_container.rs | 7 +- .../src/net/java/play/command_suggestion.rs | 6 +- .../src/net/java/play/confirm_teleport.rs | 13 +- crates/pumpkin/src/net/java/play/interact.rs | 22 +- crates/pumpkin/src/net/java/play/mod.rs | 2 +- .../pumpkin/src/net/java/play/move_vehicle.rs | 4 +- crates/pumpkin/src/net/java/play/pick_item.rs | 24 +- .../pumpkin/src/net/java/play/ping_request.rs | 5 +- .../pumpkin/src/net/java/play/place_recipe.rs | 8 +- .../src/net/java/play/player_abilities.rs | 6 +- .../src/net/java/play/player_action.rs | 53 ++- .../src/net/java/play/player_command.rs | 15 +- .../pumpkin/src/net/java/play/player_input.rs | 24 +- .../src/net/java/play/player_position.rs | 37 +- .../src/net/java/play/player_rotation.rs | 7 +- .../java/play/recipe_book_change_settings.rs | 6 +- .../net/java/play/recipe_book_seen_recipe.rs | 6 +- .../net/java/play/resource_pack_response.rs | 11 +- .../src/net/java/play/seen_advancement.rs | 2 +- .../pumpkin/src/net/java/play/select_trade.rs | 4 +- .../pumpkin/src/net/java/play/set_beacon.rs | 7 +- .../src/net/java/play/set_command_block.rs | 9 +- .../src/net/java/play/set_creative_slot.rs | 5 +- .../src/net/java/play/set_held_item.rs | 16 +- .../src/net/java/play/spectate_entity.rs | 12 +- crates/pumpkin/src/net/java/play/swing_arm.rs | 10 +- crates/pumpkin/src/net/java/play/tag_query.rs | 14 +- .../src/net/java/play/teleport_to_entity.rs | 8 +- .../pumpkin/src/net/java/play/update_sign.rs | 6 +- crates/pumpkin/src/net/java/play/use_item.rs | 27 +- .../pumpkin/src/net/java/play/use_item_on.rs | 31 +- crates/pumpkin/src/net/java/status.rs | 2 +- .../wasm/wasm_host/wit/v0_1/boss_bar.rs | 2 - .../loader/wasm/wasm_host/wit/v0_1/entity.rs | 8 +- .../wasm/wasm_host/wit/v0_1/events/world.rs | 18 +- .../loader/wasm/wasm_host/wit/v0_1/mob.rs | 57 +++- .../loader/wasm/wasm_host/wit/v0_1/player.rs | 8 +- crates/pumpkin/src/plugin/mod.rs | 94 ++++-- crates/pumpkin/src/server/mod.rs | 77 +++-- crates/pumpkin/src/world/bossbar.rs | 1 - crates/pumpkin/src/world/chunker.rs | 61 ++-- crates/pumpkin/src/world/custom_bossbar.rs | 2 - crates/pumpkin/src/world/mod.rs | 18 +- crates/pumpkin/src/world/portal/mod.rs | 1 - crates/pumpkin/src/world/raid.rs | 14 +- 166 files changed, 1681 insertions(+), 1520 deletions(-) create mode 100644 crates/pumpkin/src/entity/ai/goal/move_towards_target.rs diff --git a/Cargo.lock b/Cargo.lock index 53caf7733..3b1250a35 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1668,12 +1668,13 @@ checksum = "d45db016d36b838f563236e9193d0ee6ce38f3f68b6c94e914b4929c96bbb890" [[package]] name = "flate2" -version = "1.1.9" +version = "1.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c" +checksum = "6e634e2e0ebac1ee034020da1ca582e17ffe4e0f5e985823721e168928136dcb" dependencies = [ "crc32fast", - "miniz_oxide", + "miniz_oxide 0.9.1", + "zlib-rs", ] [[package]] @@ -2591,6 +2592,16 @@ dependencies = [ "simd-adler32", ] +[[package]] +name = "miniz_oxide" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b63fbc4a50860e98e7b2aa7804ded1db5cbc3aff9193adaff57a6931bf7c4b4c" +dependencies = [ + "adler2", + "simd-adler32", +] + [[package]] name = "mio" version = "1.2.2" @@ -3082,7 +3093,7 @@ dependencies = [ "crc32fast", "fdeflate", "flate2", - "miniz_oxide", + "miniz_oxide 0.8.9", ] [[package]] @@ -5172,9 +5183,9 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.25.0" +version = "1.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f053576934f05a761a402421fbbe3d425d9366f75f978806a037b3ca481abecc" +checksum = "b5772d71c9be8a8a6ac2117d949c5b224c1b72241bb611d9a3012edcf8af7812" dependencies = [ "getrandom 0.4.3", "js-sys", @@ -6342,6 +6353,12 @@ dependencies = [ "syn 3.0.4", ] +[[package]] +name = "zlib-rs" +version = "0.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34b31d188d9d685a4f9c7b46d6e36631b07058d2cfe190267adce54dc230bf12" + [[package]] name = "zmij" version = "1.0.23" diff --git a/Cargo.toml b/Cargo.toml index 1ce0b495e..7c803e971 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -135,7 +135,7 @@ futures = { version = "0.3", default-features = false, features = ["std"] } rayon = { version = "1.12", default-features = false } crossbeam = { version = "0.8", default-features = false, features = ["std"] } -uuid = { version = "1.25", default-features = false, features = ["serde", "v3", "v4", "std"] } +uuid = { version = "1.26", default-features = false, features = ["serde", "v3", "v4", "std"] } serde = { version = "1.0", default-features = false, features = ["derive", "std"] } serde_repr = { version = "0.1", default-features = false } serde_json = { version = "1.0", default-features = false, features = ["std"] } @@ -156,13 +156,13 @@ crossbeam-utils = { version = "0.8.22", default-features = false, features = ["s crypto-bigint = { version = "0.7.5", default-features = false } dashmap = { version = "6.2", default-features = false } ecdsa = { version = "0.17.0", default-features = false, features = ["std"] } -flate2 = { version = "1.1.9", default-features = false, features = ["rust_backend"] } +flate2 = { version = "1.1.10", default-features = false, features = ["rust_backend"] } heck = { version = "0.5", default-features = false } hmac = { version = "=0.13.0", default-features = false } indexmap = { version = "2.14", default-features = false, features = ["std"] } itertools = { version = "0.15.0", default-features = false, features = ["use_std"] } libloading = { version = "0.9", default-features = false, features = ["std"] } -lru = { version = "0.18.2", default-features = false } +lru = { version = "0.18.3", default-features = false } lz4-java-wrc = { version = "0.2.0", default-features = false, features = ["lz4_flex"] } md5 = { version = "0.8", default-features = false } num-bigint = { version = "0.5", default-features = false, features = ["std"] } diff --git a/crates/pumpkin-inventory/src/enchanting/enchanting_screen_handler.rs b/crates/pumpkin-inventory/src/enchanting/enchanting_screen_handler.rs index 73f29dc65..5ef9f1730 100644 --- a/crates/pumpkin-inventory/src/enchanting/enchanting_screen_handler.rs +++ b/crates/pumpkin-inventory/src/enchanting/enchanting_screen_handler.rs @@ -114,7 +114,7 @@ impl EnchantingTableScreenHandler { let mut random = LegacyRand::from_seed(self.enchantment_seed as u64); for i in 0..3 { - let level = self.calculate_level_requirement(&mut random, i, enchantability); + let level = self.calculate_level_requirement(&mut random, i); self.level_requirements[i] = level; } @@ -124,7 +124,6 @@ impl EnchantingTableScreenHandler { let enchantments = Self::get_enchantment_list( &mut random, &item, - i, self.level_requirements[i], ); if enchantments.is_empty() { @@ -161,12 +160,7 @@ impl EnchantingTableScreenHandler { self.send_property_updates(); } - fn calculate_level_requirement( - &self, - random: &mut LegacyRand, - slot: usize, - _enchantability: i32, - ) -> i32 { + fn calculate_level_requirement(&self, random: &mut LegacyRand, slot: usize) -> i32 { let b = self.bookshelf_count; let level = random.next_bounded_i32(8) + 1 + (b >> 1) + random.next_bounded_i32(b + 1); @@ -185,7 +179,6 @@ impl EnchantingTableScreenHandler { fn get_enchantment_list( random: &mut LegacyRand, item: &ItemStack, - _slot: usize, level: i32, ) -> Vec<(&'static Enchantment, i32)> { let enchantability = item @@ -363,8 +356,7 @@ impl ScreenHandler for EnchantingTableScreenHandler { } let mut random = self.create_enchantment_random(id as usize); - let mut enchantments = - Self::get_enchantment_list(&mut random, &item_stack, id as usize, level_req); + let mut enchantments = Self::get_enchantment_list(&mut random, &item_stack, level_req); if enchantments.is_empty() { return false; diff --git a/crates/pumpkin-inventory/src/screen_handler.rs b/crates/pumpkin-inventory/src/screen_handler.rs index e43542e52..cd96f97bf 100644 --- a/crates/pumpkin-inventory/src/screen_handler.rs +++ b/crates/pumpkin-inventory/src/screen_handler.rs @@ -561,7 +561,7 @@ pub trait ScreenHandler: Send + Sync { .previous_cursor_stack .set_received_stack(cursor_stack.clone()); if let Some(sync_handler) = behaviour.sync_handler.as_ref() { - sync_handler.update_cursor_stack(behaviour, &cursor_stack); + sync_handler.update_cursor_stack(&cursor_stack); } } } diff --git a/crates/pumpkin-inventory/src/sync_handler.rs b/crates/pumpkin-inventory/src/sync_handler.rs index 0a33c80cf..de279a2b9 100644 --- a/crates/pumpkin-inventory/src/sync_handler.rs +++ b/crates/pumpkin-inventory/src/sync_handler.rs @@ -162,9 +162,8 @@ impl SyncHandler { /// Sent when the player's held (cursor) item changes. /// /// # Arguments - /// - `screen_handler` - The screen handler /// - `stack` - The new cursor item - pub fn update_cursor_stack(&self, _screen_handler: &ScreenHandlerBehaviour, stack: &ItemStack) { + pub fn update_cursor_stack(&self, stack: &ItemStack) { if let Some(player) = self .player .lock() diff --git a/crates/pumpkin-macros/src/lib.rs b/crates/pumpkin-macros/src/lib.rs index a4eb04a89..51e8d0369 100644 --- a/crates/pumpkin-macros/src/lib.rs +++ b/crates/pumpkin-macros/src/lib.rs @@ -184,6 +184,88 @@ pub fn send_cancellable(input: TokenStream) -> TokenStream { expanded.into() } +#[proc_macro] +pub fn send_cancellable_blocking(input: TokenStream) -> TokenStream { + let input = parse_macro_input!(input as SynBlock); + + let mut stmts_iter = input.stmts.into_iter(); + + let Some(Stmt::Expr(server_stmt, _)) = stmts_iter.next() else { + abort_call_site!("expected server expression as first statement") + }; + + let Some(Stmt::Expr(event_stmt, _)) = stmts_iter.next() else { + abort_call_site!("expected event expression as second statement") + }; + + let event_expr = if let Expr::Reference(syn::ExprReference { + expr, + mutability: Some(_), + .. + }) = event_stmt + { + *expr + } else { + event_stmt + }; + + let mut after_block = None; + let mut cancelled_block = None; + + for stmt in stmts_iter { + if let Stmt::Expr(Expr::Block(b), _) = stmt + && let Some(ref label) = b.label + { + if label.name.ident == "after" { + after_block = Some(b.block); + } else if label.name.ident == "cancelled" { + cancelled_block = Some(b.block); + } + } + } + + let execution = match (after_block, cancelled_block) { + (Some(after), Some(cancelled)) => quote! { + if !is_cancelled { + #after + } else { + #cancelled + } + }, + (Some(after), None) => quote! { + if !is_cancelled { + #after + } + }, + (None, Some(cancelled)) => quote! { + if is_cancelled { + #cancelled + } + }, + (None, None) => quote! {}, + }; + + let expanded = quote! { + { + let mut event = #event_expr; + let server_ref: &std::sync::Arc = { + use std::borrow::Borrow; + (#server_stmt).borrow() + }; + server_ref.plugin_manager.fire_blocking(server_ref, &mut event); + + let is_cancelled = { + use crate::plugin::Cancellable; + event.cancelled() + }; + + #execution + } + }; + + expanded.into() +} + /// Attaches a fixed packet ID to a struct implementing `Packet`. /// /// # Arguments diff --git a/crates/pumpkin-protocol/fuzz/Cargo.lock b/crates/pumpkin-protocol/fuzz/Cargo.lock index ddc32a5e7..e678e5d03 100644 --- a/crates/pumpkin-protocol/fuzz/Cargo.lock +++ b/crates/pumpkin-protocol/fuzz/Cargo.lock @@ -293,19 +293,6 @@ version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" -[[package]] -name = "crossfire" -version = "3.1.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "111ce8f7abfbac38b4bc4f32a3a2dda1a8034b873c90c7899f302cc9dbbc05ec" -dependencies = [ - "crossbeam-utils", - "futures-core", - "parking_lot", - "pointers", - "smallvec", -] - [[package]] name = "crypto-bigint" version = "0.7.5" @@ -521,7 +508,6 @@ checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d" dependencies = [ "futures-channel", "futures-core", - "futures-executor", "futures-io", "futures-sink", "futures-task", @@ -544,17 +530,6 @@ version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" -[[package]] -name = "futures-executor" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d" -dependencies = [ - "futures-core", - "futures-task", - "futures-util", -] - [[package]] name = "futures-io" version = "0.3.32" @@ -1033,12 +1008,6 @@ dependencies = [ "spki", ] -[[package]] -name = "pointers" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dcdc93847ad24990939cce6e1804361e903efcb5f99daa5abd87943a9d6d7ba" - [[package]] name = "prettyplease" version = "0.2.37" @@ -1229,7 +1198,6 @@ dependencies = [ "bitflags", "bytes", "crossbeam", - "crossfire", "dashmap", "flate2", "futures", diff --git a/crates/pumpkin-protocol/fuzz/fuzz_targets/decoder_bedrock.rs b/crates/pumpkin-protocol/fuzz/fuzz_targets/decoder_bedrock.rs index 6500ccdf5..4317c7880 100644 --- a/crates/pumpkin-protocol/fuzz/fuzz_targets/decoder_bedrock.rs +++ b/crates/pumpkin-protocol/fuzz/fuzz_targets/decoder_bedrock.rs @@ -3,10 +3,10 @@ use libfuzzer_sys::fuzz_target; use pumpkin_protocol::bedrock::packet_decoder::BedrockBatchDecoder; use pumpkin_protocol::bedrock::server::{ SAnimate, SBlockPickRequest, SClientCacheBlobStatus, SClientCacheStatus, SCommandRequest, - SContainerClose, SEmote, SEmoteList, SInteraction, SInventoryTransaction, SItemStackRequest, + SContainerClose, SEmote, SEmoteList, SInteract, SInventoryTransaction, SItemStackRequest, SLoadingScreen, SLogin, SMobEquipment, SModalFormResponse, SPacketViolationWarning, SPlayerAction, SPlayerAuthInput, SPlayerHotbar, SRequestAbility, SRequestChunkRadius, - SRequestNetworkSettings, SResourcePackResponse, SRespawn, SSetLocalPlayerAsInitialized, + SRequestNetworkSettings, SResourcePackClientResponse, SRespawn, SSetLocalPlayerAsInitialized, SSetPlayerInventoryOptions, SText, }; use pumpkin_protocol::serial::{PacketRead, PacketReadSlice}; @@ -46,7 +46,7 @@ fn fuzz_serverbound_packets(payload: &[u8]) { SContainerClose, SEmote, SEmoteList, - SInteraction, + SInteract, SInventoryTransaction, SItemStackRequest, SLoadingScreen, @@ -60,7 +60,7 @@ fn fuzz_serverbound_packets(payload: &[u8]) { SRequestAbility, SRequestChunkRadius, SRequestNetworkSettings, - SResourcePackResponse, + SResourcePackClientResponse, SRespawn, SSetLocalPlayerAsInitialized, SSetPlayerInventoryOptions, diff --git a/crates/pumpkin-protocol/fuzz/fuzz_targets/encoder_bedrock.rs b/crates/pumpkin-protocol/fuzz/fuzz_targets/encoder_bedrock.rs index 46e98e2a6..22a61c0af 100644 --- a/crates/pumpkin-protocol/fuzz/fuzz_targets/encoder_bedrock.rs +++ b/crates/pumpkin-protocol/fuzz/fuzz_targets/encoder_bedrock.rs @@ -23,7 +23,7 @@ fuzz_target!(|data: &[u8]| { 2 => SubClient::SubClient1, _ => SubClient::SubClietn2, }; - let use_compression = data[6] % 2 == 0; + let use_compression = data[6].is_multiple_of(2); let packet_payload = &data[7..]; let mut encoder = BedrockBatchEncoder::new(); diff --git a/crates/pumpkin-protocol/fuzz/fuzz_targets/encoder_java.rs b/crates/pumpkin-protocol/fuzz/fuzz_targets/encoder_java.rs index 62ae13447..d91a5ea2e 100644 --- a/crates/pumpkin-protocol/fuzz/fuzz_targets/encoder_java.rs +++ b/crates/pumpkin-protocol/fuzz/fuzz_targets/encoder_java.rs @@ -20,8 +20,8 @@ fuzz_target!(|data: &[u8]| { let compression_threshold = data[0] as usize; let compression_level = (data[1] % 10) as u32; let encryption_key: [u8; 16] = data[2..18].try_into().unwrap(); - let use_compression = data[18] % 2 == 0; - let use_encryption = data[19] % 2 == 0; + let use_compression = data[18].is_multiple_of(2); + let use_encryption = data[19].is_multiple_of(2); let packet_data = &data[20..]; let rt = Runtime::new().unwrap(); diff --git a/crates/pumpkin-util/src/math/pool.rs b/crates/pumpkin-util/src/math/pool.rs index 16d4292a7..4f7dd3da7 100644 --- a/crates/pumpkin-util/src/math/pool.rs +++ b/crates/pumpkin-util/src/math/pool.rs @@ -27,7 +27,7 @@ impl Pool { let mut index = random.next_bounded_i32(total_weight); if total_weight < 64 { - return Some(FlattenedContent::get(index, distribution, total_weight)); + return Some(FlattenedContent::get(index, distribution)); } // WrappedContent @@ -60,11 +60,10 @@ impl FlattenedContent { /// # Arguments /// * `index` – The target index to select. /// * `entries` – The weighted entries to flatten. - /// * `total_weight` – The total weight of all entries. /// /// # Returns /// The element corresponding to the given index. - pub fn get(index: i32, entries: &[Weighted], _total_weight: i32) -> &E { + pub fn get(index: i32, entries: &[Weighted]) -> &E { let mut cur_index = 0; for entry in entries { diff --git a/crates/pumpkin-world/benches/chunk.rs b/crates/pumpkin-world/benches/chunk.rs index 44b13e7ee..8e26abb79 100644 --- a/crates/pumpkin-world/benches/chunk.rs +++ b/crates/pumpkin-world/benches/chunk.rs @@ -1,57 +1,30 @@ #![allow(clippy::unwrap_used, clippy::expect_used, clippy::panic)] use criterion::{Criterion, criterion_group, criterion_main}; -use pumpkin_data::{ - chunk_gen_settings::GenerationSettings, dimension::Dimension, - noise_router::OVERWORLD_BASE_NOISE_ROUTER, -}; use pumpkin_world::{ - GlobalRandomConfig, ProtoNoiseRouters, bench_create_and_populate_biome, - bench_create_and_populate_noise, bench_create_and_populate_noise_with_surface, - generation::proto_chunk::TerrainCache, + GlobalRandomConfig, bench_create_and_populate_biome, bench_create_and_populate_noise, + bench_create_and_populate_noise_with_surface, }; fn bench_terrain_gen(c: &mut Criterion) { let seed = 0; let random_config = GlobalRandomConfig::new(seed, false); - let base_router = ProtoNoiseRouters::generate(&OVERWORLD_BASE_NOISE_ROUTER, &random_config); - let surface_config = GenerationSettings::from_dimension(&Dimension::OVERWORLD); - let terrain_cache = TerrainCache::from_random(&random_config); - let default_state = surface_config.default_block; c.bench_function("overworld biome", |b| { b.iter(|| { - bench_create_and_populate_biome( - &base_router, - &random_config, - surface_config, - &terrain_cache, - default_state, - ); + bench_create_and_populate_biome(&random_config); }); }); c.bench_function("overworld noise", |b| { b.iter(|| { - bench_create_and_populate_noise( - &base_router, - &random_config, - surface_config, - &terrain_cache, - default_state, - ); + bench_create_and_populate_noise(&random_config); }); }); c.bench_function("overworld surface", |b| { b.iter(|| { - bench_create_and_populate_noise_with_surface( - &base_router, - &random_config, - surface_config, - &terrain_cache, - default_state, - ); + bench_create_and_populate_noise_with_surface(&random_config); }); }); } diff --git a/crates/pumpkin-world/benches/chunk_gen.rs b/crates/pumpkin-world/benches/chunk_gen.rs index 4b36a5d93..84684c850 100644 --- a/crates/pumpkin-world/benches/chunk_gen.rs +++ b/crates/pumpkin-world/benches/chunk_gen.rs @@ -102,15 +102,12 @@ fn setup_cache( } fn bench_full_chunk_generation(c: &mut Criterion) { - let dimension = Dimension::OVERWORLD; let world_gen = make_world_gen(); let block_registry = Arc::new(BlockRegistry); c.bench_function("full_chunk_generation", |b| { b.iter(|| { black_box(generate_single_chunk( - &dimension, - 0, &world_gen, block_registry.as_ref(), black_box(0), diff --git a/crates/pumpkin-world/benches/chunk_gen_concurrent.rs b/crates/pumpkin-world/benches/chunk_gen_concurrent.rs index 2f1d119c5..6b152164d 100644 --- a/crates/pumpkin-world/benches/chunk_gen_concurrent.rs +++ b/crates/pumpkin-world/benches/chunk_gen_concurrent.rs @@ -96,8 +96,6 @@ fn bench_concurrent_chunk_generation(c: &mut Criterion) { let br = br.clone(); s.spawn(move |_| { black_box(generate_single_chunk( - &Dimension::OVERWORLD, - 0, &wg, br.as_ref(), cx, diff --git a/crates/pumpkin-world/benches/chunk_io.rs b/crates/pumpkin-world/benches/chunk_io.rs index 1ad5c8a38..321c4f637 100644 --- a/crates/pumpkin-world/benches/chunk_io.rs +++ b/crates/pumpkin-world/benches/chunk_io.rs @@ -54,22 +54,8 @@ impl WorldPortalExt for BlockRegistry { fn bench_chunk_deserialization(c: &mut Criterion) { let dimension = Dimension::OVERWORLD; - let world_gen = get_world_gen( - Seed(42), - dimension.clone(), - false, - Vec::new(), - String::new(), - ); - let chunk = generate_single_chunk( - &dimension, - 0, - &world_gen, - &BlockRegistry, - 0, - 0, - StagedChunkEnum::Full, - ); + let world_gen = get_world_gen(Seed(42), dimension, false, Vec::new(), String::new()); + let chunk = generate_single_chunk(&world_gen, &BlockRegistry, 0, 0, StagedChunkEnum::Full); let Chunk::Level(chunk) = chunk else { panic!("full generation must return a level chunk"); }; diff --git a/crates/pumpkin-world/src/chunk/format/anvil.rs b/crates/pumpkin-world/src/chunk/format/anvil.rs index b792e66ae..cd78d7a98 100644 --- a/crates/pumpkin-world/src/chunk/format/anvil.rs +++ b/crates/pumpkin-world/src/chunk/format/anvil.rs @@ -777,24 +777,37 @@ impl ChunkSerializer for AnvilChunkFile< chunks: Vec>, stream: tokio::sync::mpsc::Sender>, ) { - // Don't par iter here so we can prevent backpressure with the await in the async - // runtime - for chunk in chunks { - let index = Self::get_chunk_index(chunk.x, chunk.y); - let is_ok = match &self.chunks_data[index] { - None => stream.send(LoadedData::Missing(chunk)).await.is_ok(), - Some(chunk_metadata) => { - let result = match chunk_metadata.serialized_data.to_chunk(chunk) { - Ok(chunk_res) => LoadedData::Loaded(chunk_res), - Err(err) => LoadedData::Error((chunk, err)), - }; + let chunk_items: Vec<(Vector2, Option)> = chunks + .into_iter() + .map(|chunk| { + let index = Self::get_chunk_index(chunk.x, chunk.y); + let data = self.chunks_data[index] + .as_ref() + .map(|chunk_metadata| chunk_metadata.serialized_data.clone()); + (chunk, data) + }) + .collect(); - stream.send(result).await.is_ok() - } - }; + let (tx, mut rx) = tokio::sync::mpsc::channel(chunk_items.len().max(1)); - if !is_ok { - // Stream is closed. Stop unneeded work and IO + rayon::spawn(move || { + use rayon::prelude::*; + chunk_items + .into_par_iter() + .for_each(|(chunk, serialized_data)| { + let result = serialized_data.map_or_else( + || LoadedData::Missing(chunk), + |data| match data.to_chunk(chunk) { + Ok(chunk_res) => LoadedData::Loaded(chunk_res), + Err(err) => LoadedData::Error((chunk, err)), + }, + ); + let _ = tx.blocking_send(result); + }); + }); + + while let Some(item) = rx.recv().await { + if stream.send(item).await.is_err() { return; } } diff --git a/crates/pumpkin-world/src/chunk/format/linear.rs b/crates/pumpkin-world/src/chunk/format/linear.rs index 8136ab3ce..53a902fc7 100644 --- a/crates/pumpkin-world/src/chunk/format/linear.rs +++ b/crates/pumpkin-world/src/chunk/format/linear.rs @@ -586,22 +586,33 @@ impl ChunkSerializer for LinearV2File chunks: Vec>, stream: tokio::sync::mpsc::Sender>, ) { - for chunk in chunks { - let index = Self::get_chunk_index(chunk.x, chunk.y); + let chunk_items: Vec<(Vector2, Option)> = chunks + .into_iter() + .map(|chunk| { + let index = Self::get_chunk_index(chunk.x, chunk.y); + let data = self.chunks_data[index].clone(); + (chunk, data) + }) + .collect(); - let is_ok = match &self.chunks_data[index] { - None => stream.send(LoadedData::Missing(chunk)).await.is_ok(), - Some(data) => { - let result = match S::from_bytes(data, chunk) { + let (tx, mut rx) = tokio::sync::mpsc::channel(chunk_items.len().max(1)); + + rayon::spawn(move || { + use rayon::prelude::*; + chunk_items.into_par_iter().for_each(|(chunk, data)| { + let result = data.map_or_else( + || LoadedData::Missing(chunk), + |data| match S::from_bytes(&data, chunk) { Ok(c) => LoadedData::Loaded(c), Err(err) => LoadedData::Error((chunk, err)), - }; - stream.send(result).await.is_ok() - } - }; + }, + ); + let _ = tx.blocking_send(result); + }); + }); - if !is_ok { - // Receiver dropped — stop early to avoid unnecessary work. + while let Some(item) = rx.recv().await { + if stream.send(item).await.is_err() { return; } } diff --git a/crates/pumpkin-world/src/chunk/format/pump.rs b/crates/pumpkin-world/src/chunk/format/pump.rs index e3e8d8fd9..2f290b80b 100644 --- a/crates/pumpkin-world/src/chunk/format/pump.rs +++ b/crates/pumpkin-world/src/chunk/format/pump.rs @@ -122,33 +122,49 @@ where chunks: Vec>, stream: tokio::sync::mpsc::Sender>, ) { - for pos in chunks { - let rel_x = pos.x.rem_euclid(32); - let rel_z = pos.y.rem_euclid(32); - let index = (rel_x + rel_z * 32) as usize; + let chunk_items: Vec<(Vector2, Option>)> = chunks + .into_iter() + .map(|pos| { + let rel_x = pos.x.rem_euclid(32); + let rel_z = pos.y.rem_euclid(32); + let index = (rel_x + rel_z * 32) as usize; + let data = self.data.chunks.get(&index.to_string()).cloned(); + (pos, data) + }) + .collect(); - if let Some(chunk_bytes) = self.data.chunks.get(&index.to_string()) { - let res = (|| { - let mut decoder = StreamingDecoder::new(&chunk_bytes[..]).map_err(|e| { - ChunkReadingError::IoError(std::io::Error::other(e.to_string())) - })?; - let mut decompressed = Vec::new(); - std::io::Read::read_to_end(&mut decoder, &mut decompressed) - .map_err(ChunkReadingError::IoError)?; - let bytes = Bytes::from(decompressed); - D::from_bytes(&bytes, pos) - })(); + let (tx, mut rx) = tokio::sync::mpsc::channel(chunk_items.len().max(1)); - let data_res = match res { - Ok(data) => LoadedData::Loaded(data), - Err(e) => LoadedData::Error((pos, e)), - }; + rayon::spawn(move || { + use rayon::prelude::*; + chunk_items.into_par_iter().for_each(|(pos, chunk_bytes)| { + let data_res = chunk_bytes.map_or_else( + || LoadedData::Missing(pos), + |chunk_bytes| { + let res = (|| { + let mut decoder = + StreamingDecoder::new(&chunk_bytes[..]).map_err(|e| { + ChunkReadingError::IoError(std::io::Error::other(e.to_string())) + })?; + let mut decompressed = Vec::new(); + std::io::Read::read_to_end(&mut decoder, &mut decompressed) + .map_err(ChunkReadingError::IoError)?; + let bytes = Bytes::from(decompressed); + D::from_bytes(&bytes, pos) + })(); + match res { + Ok(data) => LoadedData::Loaded(data), + Err(e) => LoadedData::Error((pos, e)), + } + }, + ); + let _ = tx.blocking_send(data_res); + }); + }); - if stream.send(data_res).await.is_err() { - return; - } - } else { - let _ = stream.send(LoadedData::Missing(pos)).await; + while let Some(item) = rx.recv().await { + if stream.send(item).await.is_err() { + return; } } } diff --git a/crates/pumpkin-world/src/chunk_system/generation.rs b/crates/pumpkin-world/src/chunk_system/generation.rs index c449b6a12..9c0ea36f5 100644 --- a/crates/pumpkin-world/src/chunk_system/generation.rs +++ b/crates/pumpkin-world/src/chunk_system/generation.rs @@ -1,5 +1,3 @@ -use pumpkin_data::dimension::Dimension; - use crate::ProtoChunk; use crate::generation::generator::WorldGenerator; use crate::world::WorldPortalExt; @@ -8,8 +6,6 @@ use pumpkin_config::lighting::LightingEngineConfig; use super::{Cache, Chunk, StagedChunkEnum}; pub fn generate_single_chunk( - dimension: &Dimension, - biome_mixer_seed: i64, generator: &WorldGenerator, block_registry: &dyn WorldPortalExt, chunk_x: i32, @@ -17,8 +13,6 @@ pub fn generate_single_chunk( target_stage: StagedChunkEnum, ) -> Chunk { generate_single_chunk_with_radius( - dimension, - biome_mixer_seed, generator, block_registry, chunk_x, @@ -28,10 +22,7 @@ pub fn generate_single_chunk( ) } -#[expect(clippy::too_many_arguments)] pub fn generate_single_chunk_with_radius( - _dimension: &Dimension, - _biome_mixer_seed: i64, generator: &WorldGenerator, block_registry: &dyn WorldPortalExt, chunk_x: i32, @@ -98,7 +89,6 @@ pub fn generate_single_chunk_with_radius( #[cfg(test)] mod tests { - use crate::biome::hash_seed; use crate::chunk::ChunkHeightmapType; use crate::chunk_system::Chunk; use crate::chunk_system::{ @@ -167,11 +157,8 @@ mod tests { let block_registry = Arc::new(BlockRegistry); let world_gen = get_world_gen(seed, dimension.clone(), false, Vec::new(), String::new()); - let biome_mixer_seed = hash_seed(world_gen.seed()); let chunk = generate_single_chunk( - &dimension, - biome_mixer_seed, &world_gen, block_registry.as_ref(), 0, @@ -201,12 +188,9 @@ mod tests { let dimension = Dimension::OVERWORLD; let seed = Seed(42); let block_registry = Arc::new(BlockRegistry); - let world_gen = get_world_gen(seed, dimension.clone(), false, Vec::new(), String::new()); - let biome_mixer_seed = hash_seed(world_gen.seed()); + let world_gen = get_world_gen(seed, dimension, false, Vec::new(), String::new()); let chunk = generate_single_chunk( - &dimension, - biome_mixer_seed, &world_gen, block_registry.as_ref(), 0, @@ -239,12 +223,9 @@ mod tests { let dimension = Dimension::OVERWORLD; let seed = Seed(1_782_124_772_053_846_960); let block_registry = Arc::new(BlockRegistry); - let world_gen = get_world_gen(seed, dimension.clone(), false, Vec::new(), String::new()); - let biome_mixer_seed = hash_seed(world_gen.seed()); + let world_gen = get_world_gen(seed, dimension, false, Vec::new(), String::new()); let chunk = generate_single_chunk( - &dimension, - biome_mixer_seed, &world_gen, block_registry.as_ref(), 31, @@ -292,12 +273,9 @@ mod tests { let dimension = Dimension::OVERWORLD; let seed = Seed(1_782_124_772_053_846_960); let block_registry = Arc::new(BlockRegistry); - let world_gen = get_world_gen(seed, dimension.clone(), false, Vec::new(), String::new()); - let biome_mixer_seed = hash_seed(world_gen.seed()); + let world_gen = get_world_gen(seed, dimension, false, Vec::new(), String::new()); let chunk = generate_single_chunk_with_radius( - &dimension, - biome_mixer_seed, &world_gen, block_registry.as_ref(), 73, @@ -345,11 +323,8 @@ mod tests { let dimension = Dimension::THE_END; let seed = Seed(12_345); let block_registry = Arc::new(BlockRegistry); - let world_gen = get_world_gen(seed, dimension.clone(), false, Vec::new(), String::new()); - let biome_mixer_seed = hash_seed(world_gen.seed()); + let world_gen = get_world_gen(seed, dimension, false, Vec::new(), String::new()); let chunk = generate_single_chunk_with_radius( - &dimension, - biome_mixer_seed, &world_gen, block_registry.as_ref(), -306, @@ -398,12 +373,9 @@ mod tests { let dimension = Dimension::OVERWORLD; let seed = Seed(1_782_124_772_053_846_960); let block_registry = Arc::new(BlockRegistry); - let world_gen = get_world_gen(seed, dimension.clone(), false, Vec::new(), String::new()); - let biome_mixer_seed = hash_seed(world_gen.seed()); + let world_gen = get_world_gen(seed, dimension, false, Vec::new(), String::new()); let chunk = generate_single_chunk( - &dimension, - biome_mixer_seed, &world_gen, block_registry.as_ref(), 73, @@ -420,8 +392,6 @@ mod tests { } let cage_chunk = generate_single_chunk( - &dimension, - biome_mixer_seed, &world_gen, block_registry.as_ref(), 73, diff --git a/crates/pumpkin-world/src/chunk_system/schedule.rs b/crates/pumpkin-world/src/chunk_system/schedule.rs index 63c16737a..4616f032b 100644 --- a/crates/pumpkin-world/src/chunk_system/schedule.rs +++ b/crates/pumpkin-world/src/chunk_system/schedule.rs @@ -12,7 +12,6 @@ use crate::chunk::io::Dirtiable; use crate::level::{Level, SyncChunk}; use dashmap::DashMap; use pumpkin_config::lighting::LightingEngineConfig; -use pumpkin_data::chunk_gen_settings::GenerationSettings; use pumpkin_util::math::vector2::Vector2; use slotmap::Key; use std::cmp::{Ordering, max}; @@ -76,6 +75,7 @@ pub struct GenerationSchedule { listener: Arc, lighting_config: LightingEngineConfig, last_unload: std::time::Instant, + generation_pool: Arc, } impl GenerationSchedule { @@ -113,8 +113,16 @@ impl GenerationSchedule { io_lock.clone(), )); - let max_in_flight = - (thread::available_parallelism().map_or(1, std::num::NonZero::get) * 4) as u16; + let cpus = thread::available_parallelism().map_or(1, std::num::NonZero::get); + let gen_threads = (cpus / 2).clamp(2, 16); + let generation_pool = Arc::new( + rayon::ThreadPoolBuilder::new() + .num_threads(gen_threads) + .thread_name(|i| format!("ChunkGen-{i}")) + .build() + .expect("Failed to build Chunk Generation ThreadPool"), + ); + let max_in_flight = (gen_threads * 2) as u16; let level_sched = level; let lighting_config = level_sched.lighting_config; @@ -142,6 +150,7 @@ impl GenerationSchedule { chunk_map: HashMap::default(), lighting_config, last_unload: std::time::Instant::now(), + generation_pool, }; scheduler.work(&level_sched); }) @@ -752,8 +761,10 @@ impl GenerationSchedule { chunks.push((pos, Chunk::Level(chunk))); } } - Chunk::Proto(chunk) => { - chunks.push((pos, Chunk::Proto(chunk))); + Chunk::Proto(_) => { + // ProtoChunks are in-memory intermediate generation stages + // (e.g. temporary border dependencies). Do not convert and save + // incomplete chunks to disk during runtime unloads. } } } @@ -1416,12 +1427,10 @@ impl GenerationSchedule { let stage = node.stage; let send_chunk = self.send_chunk.clone(); let level = level.clone(); - let settings = - GenerationSettings::from_dimension(level.world_gen.load().dimension()); - rayon::spawn(move || { + self.generation_pool.spawn(move || { let result = crate::chunk_system::worker_logic::run_generation( - pos, cache, stage, &level, settings, + pos, cache, stage, &level, ); let _ = send_chunk.send((pos, result)); }); diff --git a/crates/pumpkin-world/src/chunk_system/worker_logic.rs b/crates/pumpkin-world/src/chunk_system/worker_logic.rs index 55f37e47b..636b22834 100644 --- a/crates/pumpkin-world/src/chunk_system/worker_logic.rs +++ b/crates/pumpkin-world/src/chunk_system/worker_logic.rs @@ -8,7 +8,6 @@ use crate::chunk::io::{FileIO, LoadedData}; use crate::level::Level; use pumpkin_config::lighting::LightingEngineConfig; use pumpkin_data::chunk::ChunkStatus; -use pumpkin_data::chunk_gen_settings::GenerationSettings; use std::collections::hash_map::Entry; use std::sync::Arc; use std::sync::atomic::Ordering::Relaxed; @@ -55,19 +54,11 @@ fn needs_relighting(chunk: &crate::chunk::ChunkData, config: LightingEngineConfi !has_complex_light } -async fn load_proto_chunk(chunk: &Arc, level: &Level) -> ProtoChunk { - let (tx, rx) = tokio::sync::oneshot::channel(); - let world_gen = level.world_gen.load(); - let chunk_clone = chunk.clone(); - rayon::spawn(move || { - let p = ProtoChunk::from_chunk_data(&chunk_clone, &world_gen); - let _ = tx.send(p); - }); - rx.await - .unwrap_or_else(|_| ProtoChunk::from_chunk_data(chunk, &level.world_gen.load())) +fn load_proto_chunk(chunk: &crate::chunk::ChunkData, level: &Level) -> ProtoChunk { + ProtoChunk::from_chunk_data(chunk, &level.world_gen.load()) } -async fn process_loaded_chunk(chunk: Arc, level: &Level) -> Chunk { +fn process_loaded_chunk(chunk: Arc, level: &Level) -> Chunk { let pos = ChunkPos::new(chunk.x, chunk.z); if chunk.status == ChunkStatus::Full { let needs_relight = needs_relighting(&chunk, level.lighting_config); @@ -76,7 +67,7 @@ async fn process_loaded_chunk(chunk: Arc, level: &Level "Chunk {pos:?} has uniform lighting, downgrading to Features stage for relighting" ); - let mut proto = load_proto_chunk(&chunk, level).await; + let mut proto = load_proto_chunk(&chunk, level); // Clear all lighting data let section_count = proto.light.sky_light.len(); @@ -92,7 +83,7 @@ async fn process_loaded_chunk(chunk: Arc, level: &Level Chunk::Level(chunk) } } else { - let proto = load_proto_chunk(&chunk, level).await; + let proto = load_proto_chunk(&chunk, level); Chunk::Proto(Box::new(proto)) } } @@ -150,7 +141,7 @@ pub async fn io_read_work( match data { Loaded(chunk) => { let pos = ChunkPos::new(chunk.x, chunk.z); - let processed = process_loaded_chunk(chunk, &level).await; + let processed = process_loaded_chunk(chunk, &level); if send.send((pos, RecvChunk::IO(processed))).is_err() { break; } @@ -212,31 +203,31 @@ pub async fn io_write_work( error!("Failed to save chunks: {:?}", e); } - for i in positions { + { let mut data = lock .0 .lock() .unwrap_or_else(std::sync::PoisonError::into_inner); - match data.entry(i) { - Entry::Occupied(mut entry) => { - let rc = entry.get_mut(); - if *rc == 1 { - entry.remove(); - drop(data); - lock.1.notify_waiters(); - } else { - *rc -= 1; + for i in positions { + match data.entry(i) { + Entry::Occupied(mut entry) => { + let rc = entry.get_mut(); + if *rc <= 1 { + entry.remove(); + } else { + *rc -= 1; + } + } + Entry::Vacant(_) => { + warn!( + "io_write: attempted to release missing lock entry for {:?}", + i + ); } - } - Entry::Vacant(_) => { - warn!( - "io_write: attempted to release missing lock entry for {:?}", - i - ); - // continue without panicking to avoid crashing on shutdown races } } } + lock.1.notify_waiters(); } } @@ -245,7 +236,6 @@ pub fn run_generation( mut cache: Cache, stage: StagedChunkEnum, level: &Level, - _settings: &GenerationSettings, ) -> RecvChunk { let portal = level.world_portal.load_full(); let Some(portal_ref) = portal.as_deref() else { diff --git a/crates/pumpkin-world/src/generation/blender/blending_data.rs b/crates/pumpkin-world/src/generation/blender/blending_data.rs index d80dbedf3..1bc85e852 100644 --- a/crates/pumpkin-world/src/generation/blender/blending_data.rs +++ b/crates/pumpkin-world/src/generation/blender/blending_data.rs @@ -15,7 +15,7 @@ pub struct BlendingData { impl BlendingData { #[must_use] - pub fn get_height(&self, cell_x: i32, _cell_y: i32, cell_z: i32) -> f64 { + pub fn get_height(&self, cell_x: i32, cell_z: i32) -> f64 { if !(0..16).contains(&cell_x) || !(0..16).contains(&cell_z) { return f64::MAX; } @@ -81,7 +81,7 @@ impl BlendingData { } } - pub fn iterate_biomes(&self, quart_x: i32, _quart_y: i32, quart_z: i32, mut consumer: F) + pub fn iterate_biomes(&self, quart_x: i32, quart_z: i32, mut consumer: F) where F: FnMut(i32, i32, &'static Biome), { diff --git a/crates/pumpkin-world/src/generation/blender/mod.rs b/crates/pumpkin-world/src/generation/blender/mod.rs index 736ad0af9..01d660ae2 100644 --- a/crates/pumpkin-world/src/generation/blender/mod.rs +++ b/crates/pumpkin-world/src/generation/blender/mod.rs @@ -90,12 +90,8 @@ impl Blender { let cell_x = biome_coords::from_block(block_x); let cell_z = biome_coords::from_block(block_z); - let fixed_height = self.get_blending_data_value( - cell_x, - 0, - cell_z, - blending_data::BlendingData::get_height, - ); + let fixed_height = + self.get_blending_data_value(cell_x, 0, cell_z, |data, x, _, z| data.get_height(x, z)); if fixed_height != f64::MAX { return BlendingOutput { @@ -275,7 +271,6 @@ impl Blender { pub fn blend_biome( &self, quart_x: i32, - quart_y: i32, quart_z: i32, shift_noise: &DoublePerlinNoiseSampler, ) -> Option<&'static Biome> { @@ -288,7 +283,6 @@ impl Blender { blending_data.iterate_biomes( biome_coords::from_chunk(chunk_x), - quart_y, biome_coords::from_chunk(chunk_z), |test_cell_x, test_cell_z, biome| { let dx = (quart_x - test_cell_x) as f64; @@ -324,7 +318,7 @@ pub struct BlenderBiomeSupplier<'a> { impl BiomeSupplier for BlenderBiomeSupplier<'_> { fn biome(&self, x: i32, y: i32, z: i32, sampler: &mut MultiNoiseSampler<'_>) -> &'static Biome { self.blender - .blend_biome(x, y, z, &self.shift_noise) + .blend_biome(x, z, &self.shift_noise) .unwrap_or_else(|| self.base.biome(x, y, z, sampler)) } } diff --git a/crates/pumpkin-world/src/generation/feature/configured_features.rs b/crates/pumpkin-world/src/generation/feature/configured_features.rs index 338d6fc92..cbd9b9bbc 100644 --- a/crates/pumpkin-world/src/generation/feature/configured_features.rs +++ b/crates/pumpkin-world/src/generation/feature/configured_features.rs @@ -228,15 +228,9 @@ impl ConfiguredFeature { random, pos, ), - Self::CoralClaw(_feature) => CoralClawFeature::generate( - chunk, - block_registry, - min_y, - height, - feature_name, - random, - pos, - ), + Self::CoralClaw(_feature) => { + CoralClawFeature::generate(chunk, block_registry, random, pos) + } Self::EndPlatform(_feature) => EndPlatformFeature::generate( chunk, block_registry, @@ -299,15 +293,7 @@ impl ConfiguredFeature { random, pos, ), - Self::Tree(feature) => feature.generate( - block_registry, - chunk, - min_y, - height, - feature_name, - random, - pos, - ), + Self::Tree(feature) => feature.generate(block_registry, chunk, random, pos), Self::RandomSelector(feature) => feature.generate( chunk, block_registry, @@ -436,7 +422,7 @@ impl ConfiguredFeature { Self::DeltaFeature(_feature) => { DeltaFeatureFeature::generate(chunk, min_y, height, feature_name, random, pos) } - Self::DripstoneCluster(feature) => feature.generate(chunk, random, pos), + Self::DripstoneCluster(feature) => feature.generate(chunk, pos), Self::LargeDripstone(feature) => feature.generate(chunk, random, pos), Self::EndGateway(_feature) => { EndGatewayFeature::generate(chunk, min_y, height, feature_name, random, pos) diff --git a/crates/pumpkin-world/src/generation/feature/features/coral/coral_claw.rs b/crates/pumpkin-world/src/generation/feature/features/coral/coral_claw.rs index 3602e6a06..281e43580 100644 --- a/crates/pumpkin-world/src/generation/feature/features/coral/coral_claw.rs +++ b/crates/pumpkin-world/src/generation/feature/features/coral/coral_claw.rs @@ -10,13 +10,9 @@ use super::CoralFeature; pub struct CoralClawFeature; impl CoralClawFeature { - #[allow(clippy::too_many_arguments)] pub fn generate( chunk: &mut T, block_registry: &dyn WorldPortalExt, - _min_y: i8, - _height: u16, - _feature: pumpkin_data::placed_feature::PlacedFeature, // This placed feature random: &mut RandomGenerator, pos: BlockPos, ) -> bool { diff --git a/crates/pumpkin-world/src/generation/feature/features/drip_stone/cluster.rs b/crates/pumpkin-world/src/generation/feature/features/drip_stone/cluster.rs index 57d371d00..2e32b3fa3 100644 --- a/crates/pumpkin-world/src/generation/feature/features/drip_stone/cluster.rs +++ b/crates/pumpkin-world/src/generation/feature/features/drip_stone/cluster.rs @@ -1,5 +1,5 @@ use pumpkin_data::Block; -use pumpkin_util::{math::position::BlockPos, random::RandomGenerator}; +use pumpkin_util::math::position::BlockPos; use crate::generation::proto_chunk::GenerationCache; @@ -7,12 +7,7 @@ pub struct DripstoneClusterFeature; impl DripstoneClusterFeature { #[allow(clippy::unused_self)] - pub fn generate( - &self, - chunk: &mut T, - _random: &mut RandomGenerator, - pos: BlockPos, - ) -> bool { + pub fn generate(&self, chunk: &mut T, pos: BlockPos) -> bool { let mut generated = false; let radius = 3; diff --git a/crates/pumpkin-world/src/generation/feature/features/tree/foliage/acacia.rs b/crates/pumpkin-world/src/generation/feature/features/tree/foliage/acacia.rs index 5cc712fad..fbe7aae83 100644 --- a/crates/pumpkin-world/src/generation/feature/features/tree/foliage/acacia.rs +++ b/crates/pumpkin-world/src/generation/feature/features/tree/foliage/acacia.rs @@ -56,7 +56,7 @@ impl AcaciaFoliagePlacer { foliage_positions } - pub const fn get_random_height(_random: &mut RandomGenerator) -> i32 { + pub const fn get_random_height() -> i32 { 0 } } diff --git a/crates/pumpkin-world/src/generation/feature/features/tree/foliage/blob.rs b/crates/pumpkin-world/src/generation/feature/features/tree/foliage/blob.rs index e3307da45..9d3f2262d 100644 --- a/crates/pumpkin-world/src/generation/feature/features/tree/foliage/blob.rs +++ b/crates/pumpkin-world/src/generation/feature/features/tree/foliage/blob.rs @@ -42,7 +42,7 @@ impl BlobFoliagePlacer { foliage_positions } - pub const fn get_random_height(&self, _random: &mut RandomGenerator) -> i32 { + pub const fn get_random_height(&self) -> i32 { self.height } } diff --git a/crates/pumpkin-world/src/generation/feature/features/tree/foliage/bush.rs b/crates/pumpkin-world/src/generation/feature/features/tree/foliage/bush.rs index 06a6e781f..850ed8c65 100644 --- a/crates/pumpkin-world/src/generation/feature/features/tree/foliage/bush.rs +++ b/crates/pumpkin-world/src/generation/feature/features/tree/foliage/bush.rs @@ -42,7 +42,7 @@ impl BushFoliagePlacer { foliage_positions } - pub const fn get_random_height(&self, _random: &mut RandomGenerator) -> i32 { + pub const fn get_random_height(&self) -> i32 { self.height } } diff --git a/crates/pumpkin-world/src/generation/feature/features/tree/foliage/dark_oak.rs b/crates/pumpkin-world/src/generation/feature/features/tree/foliage/dark_oak.rs index d6ebef9d4..12b0b51c9 100644 --- a/crates/pumpkin-world/src/generation/feature/features/tree/foliage/dark_oak.rs +++ b/crates/pumpkin-world/src/generation/feature/features/tree/foliage/dark_oak.rs @@ -88,7 +88,7 @@ impl DarkOakFoliagePlacer { foliage_positions } - pub const fn get_random_height(_random: &mut RandomGenerator) -> i32 { + pub const fn get_random_height() -> i32 { 4 } } diff --git a/crates/pumpkin-world/src/generation/feature/features/tree/foliage/fancy.rs b/crates/pumpkin-world/src/generation/feature/features/tree/foliage/fancy.rs index 0c78b76c6..2ba0fdff2 100644 --- a/crates/pumpkin-world/src/generation/feature/features/tree/foliage/fancy.rs +++ b/crates/pumpkin-world/src/generation/feature/features/tree/foliage/fancy.rs @@ -41,7 +41,7 @@ impl LargeOakFoliagePlacer { foliage_positions } - pub const fn get_random_height(&self, _random: &mut RandomGenerator) -> i32 { + pub const fn get_random_height(&self) -> i32 { self.height } } diff --git a/crates/pumpkin-world/src/generation/feature/features/tree/foliage/jungle.rs b/crates/pumpkin-world/src/generation/feature/features/tree/foliage/jungle.rs index f9e606cc3..fb42c5578 100644 --- a/crates/pumpkin-world/src/generation/feature/features/tree/foliage/jungle.rs +++ b/crates/pumpkin-world/src/generation/feature/features/tree/foliage/jungle.rs @@ -46,11 +46,7 @@ impl JungleFoliagePlacer { } foliage_positions } - pub const fn get_random_height( - &self, - _random: &mut RandomGenerator, - _trunk_height: i32, - ) -> i32 { + pub const fn get_random_height(&self) -> i32 { self.height } } diff --git a/crates/pumpkin-world/src/generation/feature/features/tree/foliage/mega_pine.rs b/crates/pumpkin-world/src/generation/feature/features/tree/foliage/mega_pine.rs index 0b871cbfb..8da1eecd2 100644 --- a/crates/pumpkin-world/src/generation/feature/features/tree/foliage/mega_pine.rs +++ b/crates/pumpkin-world/src/generation/feature/features/tree/foliage/mega_pine.rs @@ -52,7 +52,7 @@ impl MegaPineFoliagePlacer { } foliage_positions } - pub fn get_random_height(&self, random: &mut RandomGenerator, _trunk_height: i32) -> i32 { + pub fn get_random_height(&self, random: &mut RandomGenerator) -> i32 { self.crown_height.get(random) } } diff --git a/crates/pumpkin-world/src/generation/feature/features/tree/foliage/mod.rs b/crates/pumpkin-world/src/generation/feature/features/tree/foliage/mod.rs index 89b61a095..b8e58b008 100644 --- a/crates/pumpkin-world/src/generation/feature/features/tree/foliage/mod.rs +++ b/crates/pumpkin-world/src/generation/feature/features/tree/foliage/mod.rs @@ -380,18 +380,16 @@ impl FoliageType { pub fn get_random_height(&self, random: &mut RandomGenerator, trunk_height: i32) -> i32 { match self { - Self::Blob(blob) => blob.get_random_height(random), + Self::Blob(blob) => blob.get_random_height(), Self::Spruce(spruce) => spruce.get_random_height(random, trunk_height), - Self::Pine(pine) => pine.get_random_height(random, trunk_height), - Self::Acacia(_acacia) => AcaciaFoliagePlacer::get_random_height(random), - Self::Bush(bush) => bush.get_random_height(random), - Self::Fancy(fancy) => fancy.get_random_height(random), - Self::Jungle(jungle) => jungle.get_random_height(random, trunk_height), - Self::MegaPine(mega_pine) => mega_pine.get_random_height(random, trunk_height), - Self::DarkOak(_dark_oak) => DarkOakFoliagePlacer::get_random_height(random), - Self::RandomSpread(random_spread) => { - random_spread.get_random_height(random, trunk_height) - } + Self::Pine(pine) => pine.get_random_height(random), + Self::Acacia(_acacia) => AcaciaFoliagePlacer::get_random_height(), + Self::Bush(bush) => bush.get_random_height(), + Self::Fancy(fancy) => fancy.get_random_height(), + Self::Jungle(jungle) => jungle.get_random_height(), + Self::MegaPine(mega_pine) => mega_pine.get_random_height(random), + Self::DarkOak(_dark_oak) => DarkOakFoliagePlacer::get_random_height(), + Self::RandomSpread(random_spread) => random_spread.get_random_height(random), Self::Cherry(cherry) => cherry.get_random_height(random), } } diff --git a/crates/pumpkin-world/src/generation/feature/features/tree/foliage/pine.rs b/crates/pumpkin-world/src/generation/feature/features/tree/foliage/pine.rs index 7641af8d5..da35b5cdb 100644 --- a/crates/pumpkin-world/src/generation/feature/features/tree/foliage/pine.rs +++ b/crates/pumpkin-world/src/generation/feature/features/tree/foliage/pine.rs @@ -55,7 +55,7 @@ impl PineFoliagePlacer { placer.radius.get(random) + random.next_bounded_i32((base_height + 1).max(1)) } - pub fn get_random_height(&self, random: &mut RandomGenerator, _trunk_height: i32) -> i32 { + pub fn get_random_height(&self, random: &mut RandomGenerator) -> i32 { self.height.get(random) } } diff --git a/crates/pumpkin-world/src/generation/feature/features/tree/foliage/random_spread.rs b/crates/pumpkin-world/src/generation/feature/features/tree/foliage/random_spread.rs index 319cd640f..8c6ef08fc 100644 --- a/crates/pumpkin-world/src/generation/feature/features/tree/foliage/random_spread.rs +++ b/crates/pumpkin-world/src/generation/feature/features/tree/foliage/random_spread.rs @@ -38,8 +38,7 @@ impl RandomSpreadFoliagePlacer { } foliage_positions } - // TODO: getRandomRadius - pub fn get_random_height(&self, random: &mut RandomGenerator, _trunk_height: i32) -> i32 { + pub fn get_random_height(&self, random: &mut RandomGenerator) -> i32 { self.foliage_height.get(random) } } diff --git a/crates/pumpkin-world/src/generation/feature/features/tree/mod.rs b/crates/pumpkin-world/src/generation/feature/features/tree/mod.rs index 29cea523e..a9459912c 100644 --- a/crates/pumpkin-world/src/generation/feature/features/tree/mod.rs +++ b/crates/pumpkin-world/src/generation/feature/features/tree/mod.rs @@ -37,26 +37,15 @@ pub struct TreeNode { } impl TreeFeature { - #[expect(clippy::too_many_arguments)] pub fn generate( &self, block_registry: &dyn WorldPortalExt, chunk: &mut T, - min_y: i8, - height: u16, - feature_name: pumpkin_data::placed_feature::PlacedFeature, // This placed feature random: &mut RandomGenerator, pos: BlockPos, ) -> bool { - let (log_positions, root_positions, foliage_positions) = self.generate_main( - block_registry, - chunk, - min_y, - height, - feature_name, - random, - pos, - ); + let (log_positions, root_positions, foliage_positions) = + self.generate_main(block_registry, chunk, random, pos); if log_positions.is_empty() && foliage_positions.is_empty() { return false; @@ -205,14 +194,10 @@ impl TreeFeature { state.is_air() || id.has_tag(tag::Block::MINECRAFT_REPLACEABLE_BY_TREES) } - #[expect(clippy::too_many_arguments)] fn generate_main( &self, block_registry: &dyn WorldPortalExt, chunk: &mut T, - _min_y: i8, - _height: u16, - _feature_name: pumpkin_data::placed_feature::PlacedFeature, // This placed feature random: &mut RandomGenerator, pos: BlockPos, ) -> (Vec, Vec, Vec) { diff --git a/crates/pumpkin-world/src/lib.rs b/crates/pumpkin-world/src/lib.rs index 944aa9825..e1957af5b 100644 --- a/crates/pumpkin-world/src/lib.rs +++ b/crates/pumpkin-world/src/lib.rs @@ -1,9 +1,7 @@ #![allow(clippy::unreachable)] #![cfg_attr(test, allow(clippy::unwrap_used, clippy::expect_used, clippy::panic))] -use pumpkin_data::{ - Block, BlockState, chunk_gen_settings::GenerationSettings, dimension::Dimension, -}; +use pumpkin_data::{Block, dimension::Dimension}; use pumpkin_util::math::vector2::Vector2; pub mod biome; @@ -52,16 +50,9 @@ use crate::generation::{ biome_coords, noise::{CHUNK_DIM, ChunkNoiseGenerator, aquifer_sampler::FluidLevel}, positions::chunk_pos, - proto_chunk::TerrainCache, }; -pub fn bench_create_and_populate_noise( - _base_router: &ProtoNoiseRouters, - random_config: &GlobalRandomConfig, - _settings: &GenerationSettings, - _terrain_cache: &TerrainCache, - _default_block: &'static BlockState, -) { +pub fn bench_create_and_populate_noise(random_config: &GlobalRandomConfig) { use crate::generation::generator::{GeneratorInit, VanillaGenerator, WorldGenerator}; use crate::generation::noise::router::surface_height_sampler::{ SurfaceHeightEstimateSampler, SurfaceHeightSamplerBuilderOptions, @@ -137,13 +128,7 @@ pub fn bench_create_and_populate_noise( ); } -pub fn bench_create_and_populate_biome( - _base_router: &ProtoNoiseRouters, - random_config: &GlobalRandomConfig, - _settings: &GenerationSettings, - _terrain_cache: &TerrainCache, - _default_block: &'static BlockState, -) { +pub fn bench_create_and_populate_biome(random_config: &GlobalRandomConfig) { use crate::generation::generator::{GeneratorInit, VanillaGenerator, WorldGenerator}; use crate::generation::noise::router::multi_noise_sampler::{ MultiNoiseSampler, MultiNoiseSamplerBuilderOptions, @@ -179,13 +164,7 @@ pub fn bench_create_and_populate_biome( chunk.populate_biomes(generator, &mut multi_noise_sampler); } -pub fn bench_create_and_populate_noise_with_surface( - _base_router: &ProtoNoiseRouters, - random_config: &GlobalRandomConfig, - _settings: &GenerationSettings, - _terrain_cache: &TerrainCache, - _default_block: &'static BlockState, -) { +pub fn bench_create_and_populate_noise_with_surface(random_config: &GlobalRandomConfig) { use crate::generation::generator::{GeneratorInit, VanillaGenerator, WorldGenerator}; use crate::generation::noise::router::{ multi_noise_sampler::{MultiNoiseSampler, MultiNoiseSamplerBuilderOptions}, diff --git a/crates/pumpkin/src/block/blocks/end_portal.rs b/crates/pumpkin/src/block/blocks/end_portal.rs index efb92e73c..2538a470d 100644 --- a/crates/pumpkin/src/block/blocks/end_portal.rs +++ b/crates/pumpkin/src/block/blocks/end_portal.rs @@ -28,7 +28,7 @@ impl BlockBehaviour for EndPortalBlock { ); args.entity .get_entity() - .try_use_portal(0, target_world, *args.position); + .try_use_portal(target_world, *args.position); } fn placed(&self, args: PlacedArgs<'_>) { diff --git a/crates/pumpkin/src/block/blocks/fire/fire.rs b/crates/pumpkin/src/block/blocks/fire/fire.rs index a2fd74d90..89e3b0e3b 100644 --- a/crates/pumpkin/src/block/blocks/fire/fire.rs +++ b/crates/pumpkin/src/block/blocks/fire/fire.rs @@ -115,10 +115,12 @@ impl FireBlock { total_burn_chance } - const fn is_near_rain(_world: &World, _pos: &BlockPos) -> bool { - // TODO: Implement proper rain checking when weather is implemented - // For now, return false to allow fire to work - false + fn is_near_rain(world: &World, pos: &BlockPos) -> bool { + world.is_raining_at(pos) + || world.is_raining_at(&pos.west()) + || world.is_raining_at(&pos.east()) + || world.is_raining_at(&pos.north()) + || world.is_raining_at(&pos.south()) } // Get burn odds for a block, used in try_spreading_fire diff --git a/crates/pumpkin/src/block/blocks/nether_portal.rs b/crates/pumpkin/src/block/blocks/nether_portal.rs index c76d1f2b9..735d23b19 100644 --- a/crates/pumpkin/src/block/blocks/nether_portal.rs +++ b/crates/pumpkin/src/block/blocks/nether_portal.rs @@ -139,11 +139,9 @@ impl BlockBehaviour for NetherPortalBlock { args.position, target_world.dimension.minecraft_name ); - let portal_delay = Self::get_portal_time(args.world, args.entity); - args.entity .get_entity() - .try_use_portal(portal_delay, target_world, *args.position); + .try_use_portal(target_world, *args.position); } fn on_state_replaced(&self, args: OnStateReplacedArgs<'_>) { diff --git a/crates/pumpkin/src/block/blocks/plant/mushroom_plant.rs b/crates/pumpkin/src/block/blocks/plant/mushroom_plant.rs index 1ea0134c8..70c948863 100644 --- a/crates/pumpkin/src/block/blocks/plant/mushroom_plant.rs +++ b/crates/pumpkin/src/block/blocks/plant/mushroom_plant.rs @@ -52,12 +52,7 @@ impl MushroomPlantBlock { is_dark_enough && Self::may_place_on(block_accessor.get_block_state(&below_pos)) } - pub fn grow_mushroom( - world: &Arc, - pos: &BlockPos, - block: &Block, - _state_id: BlockStateId, - ) -> bool { + pub fn grow_mushroom(world: &Arc, pos: &BlockPos, block: &Block) -> bool { let species = if block == &Block::BROWN_MUSHROOM { TreeType::BrownMushroom } else if block == &Block::RED_MUSHROOM { @@ -291,7 +286,7 @@ impl BlockBehaviour for MushroomPlantBlock { } fn perform_bonemeal(&self, args: BonemealArgs<'_>) { - Self::grow_mushroom(args.world, args.position, args.block, args.state_id); + Self::grow_mushroom(args.world, args.position, args.block); } } diff --git a/crates/pumpkin/src/block/blocks/vine.rs b/crates/pumpkin/src/block/blocks/vine.rs index b111f2442..1b073988f 100644 --- a/crates/pumpkin/src/block/blocks/vine.rs +++ b/crates/pumpkin/src/block/blocks/vine.rs @@ -71,7 +71,6 @@ pub fn get_updated_state( mut props: VineLikeProperties, world: &dyn BlockAccessor, pos: &BlockPos, - _block: &Block, ) -> VineLikeProperties { let above_pos = pos.up(); if props.up { @@ -324,7 +323,6 @@ impl BlockBehaviour for VineBlock { VineLikeProperties::from_state_id(args.state_id, args.block), args.world, args.position, - args.block, ); if count_faces(&updated_props) == 0 { Block::AIR.default_state.id diff --git a/crates/pumpkin/src/block/fluid/lava.rs b/crates/pumpkin/src/block/fluid/lava.rs index 9067b6d90..559ad703f 100644 --- a/crates/pumpkin/src/block/fluid/lava.rs +++ b/crates/pumpkin/src/block/fluid/lava.rs @@ -88,7 +88,7 @@ impl FlowingLava { world.set_block_state(pos, fire_state_id, BlockFlags::NOTIFY_ALL); } - fn receive_neighbor_fluids(world: &Arc, _fluid: &Fluid, block_pos: &BlockPos) -> bool { + fn receive_neighbor_fluids(world: &Arc, block_pos: &BlockPos) -> bool { // Logic to determine if we should replace the fluid with any of (cobble, obsidian, stone, etc.) let below_is_soul_soil = world .get_block(&block_pos.offset(BlockDirection::Down.to_offset())) @@ -152,7 +152,7 @@ impl FluidBehaviour for FlowingLava { old_state_id: BlockStateId, _notify: bool, ) { - if old_state_id != state_id && Self::receive_neighbor_fluids(world, fluid, block_pos) { + if old_state_id != state_id && Self::receive_neighbor_fluids(world, block_pos) { let flow_speed = self.get_flow_speed(world); world.schedule_fluid_tick(fluid, *block_pos, flow_speed, TickPriority::Normal); } @@ -169,7 +169,7 @@ impl FluidBehaviour for FlowingLava { block_pos: &BlockPos, _notify: bool, ) { - if Self::receive_neighbor_fluids(world, fluid, block_pos) { + if Self::receive_neighbor_fluids(world, block_pos) { let flow_speed = self.get_flow_speed(world); world.schedule_fluid_tick(fluid, *block_pos, flow_speed, TickPriority::Normal); } diff --git a/crates/pumpkin/src/block/registry.rs b/crates/pumpkin/src/block/registry.rs index c5d95fa38..27e1588af 100644 --- a/crates/pumpkin/src/block/registry.rs +++ b/crates/pumpkin/src/block/registry.rs @@ -541,7 +541,7 @@ impl BlockRegistry { } #[expect(clippy::too_many_lines)] - pub async fn place_block( + pub fn place_block( &self, player: &Arc, placed_block: &'static Block, @@ -697,11 +697,7 @@ impl BlockRegistry { }; server .plugin_manager - .fire::( - server, - &mut can_build_event, - ) - .await; + .fire_blocking(server, &mut can_build_event); if can_build_event.cancelled || !can_build_event.buildable { return Ok(None); } @@ -713,10 +709,7 @@ impl BlockRegistry { final_block_pos, true, ); - server - .plugin_manager - .fire::(server, &mut event) - .await; + server.plugin_manager.fire_blocking(server, &mut event); if event.cancelled { return Ok(None); } @@ -1204,13 +1197,7 @@ impl BlockRegistry { state_id } - pub fn update_neighbors( - &self, - world: &Arc, - position: &BlockPos, - _block: &Block, - flags: BlockFlags, - ) { + pub fn update_neighbors(&self, world: &Arc, position: &BlockPos, flags: BlockFlags) { for direction in BlockDirection::abstract_block_update_order() { let pos = position.offset(direction.to_offset()); diff --git a/crates/pumpkin/src/command/commands/mod.rs b/crates/pumpkin/src/command/commands/mod.rs index 9537db4ab..4768e53f1 100644 --- a/crates/pumpkin/src/command/commands/mod.rs +++ b/crates/pumpkin/src/command/commands/mod.rs @@ -1,6 +1,6 @@ use crate::command::node::dispatcher::CommandDispatcher; use crate::command::tree::Command; -use pumpkin_config::{BasicConfiguration, CommandsConfig}; +use pumpkin_config::CommandsConfig; use pumpkin_util::{ PermissionLvl, permission::{Permission, PermissionDefault, PermissionManager, PermissionRegistry}, @@ -94,7 +94,6 @@ mod worldborder; #[must_use] pub fn default_dispatcher( permission_manager: &PermissionManager, - _basic_config: &BasicConfiguration, commands_config: &CommandsConfig, ) -> CommandDispatcher { let mut dispatcher = crate::command::dispatcher::CommandDispatcher::default(); @@ -666,7 +665,7 @@ fn register_level_3_permissions(registry: &PermissionRegistry) { #[cfg(test)] mod override_tests { - use pumpkin_config::{BasicConfiguration, CommandOverride, CommandsConfig}; + use pumpkin_config::{CommandOverride, CommandsConfig}; use pumpkin_util::PermissionLvl; use pumpkin_util::permission::{PermissionDefault, PermissionManager}; @@ -694,14 +693,13 @@ mod override_tests { #[test] fn disabling_a_command_removes_and_hides_it() { - let basic = BasicConfiguration::default(); let mut commands = CommandsConfig::default(); // `gamemode` lives on the legacy dispatcher; disabling it should remove // it there and flag it on the wrapper. disabled(&mut commands, "gamemode"); let manager = PermissionManager::new(); - let dispatcher = default_dispatcher(&manager, &basic, &commands); + let dispatcher = default_dispatcher(&manager, &commands); assert!(dispatcher.is_disabled("gamemode")); assert!( @@ -716,14 +714,13 @@ mod override_tests { #[test] fn disabling_an_alias_turns_off_the_whole_command() { - let basic = BasicConfiguration::default(); let mut commands = CommandsConfig::default(); // `tp` is an alias of `teleport`; disabling it should take the whole // command down, including the primary name. disabled(&mut commands, "tp"); let manager = PermissionManager::new(); - let dispatcher = default_dispatcher(&manager, &basic, &commands); + let dispatcher = default_dispatcher(&manager, &commands); assert!(dispatcher.is_disabled("tp")); assert!(dispatcher.is_disabled("teleport")); @@ -733,13 +730,12 @@ mod override_tests { #[test] fn disabling_a_node_command_also_disables_its_aliases() { - let basic = BasicConfiguration::default(); let mut commands = CommandsConfig::default(); // `help` is a node-based command with the aliases `h` and `?`. disabled(&mut commands, "help"); let manager = PermissionManager::new(); - let dispatcher = default_dispatcher(&manager, &basic, &commands); + let dispatcher = default_dispatcher(&manager, &commands); assert!(dispatcher.is_disabled("help")); assert!(dispatcher.is_disabled("h")); @@ -748,14 +744,13 @@ mod override_tests { #[test] fn override_for_unknown_command_is_ignored() { - let basic = BasicConfiguration::default(); let mut commands = CommandsConfig::default(); // A command name that does not exist (usually a typo in the config) // should be ignored, not silently swallow a real command or panic. disabled(&mut commands, "notacommand"); let manager = PermissionManager::new(); - let dispatcher = default_dispatcher(&manager, &basic, &commands); + let dispatcher = default_dispatcher(&manager, &commands); assert!(!dispatcher.is_disabled("notacommand")); assert!(dispatcher.fallback_dispatcher.get_tree("gamemode").is_ok()); @@ -763,25 +758,23 @@ mod override_tests { #[test] fn override_is_case_insensitive() { - let basic = BasicConfiguration::default(); let mut commands = CommandsConfig::default(); disabled(&mut commands, "GameMode"); let manager = PermissionManager::new(); - let dispatcher = default_dispatcher(&manager, &basic, &commands); + let dispatcher = default_dispatcher(&manager, &commands); assert!(dispatcher.is_disabled("gamemode")); } #[test] fn permission_level_override_rewrites_the_registry_default() { - let basic = BasicConfiguration::default(); let mut commands = CommandsConfig::default(); // `gamemode` is normally level 2; bump it to owner-only. permission(&mut commands, "gamemode", PermissionLvl::Four); let manager = PermissionManager::new(); - let _dispatcher = default_dispatcher(&manager, &basic, &commands); + let _dispatcher = default_dispatcher(&manager, &commands); let permission = manager .get_permission("minecraft:command.gamemode") @@ -794,7 +787,6 @@ mod override_tests { #[test] fn permission_override_resolves_node_command_by_convention() { - let basic = BasicConfiguration::default(); let mut commands = CommandsConfig::default(); // `kill` is a node-based command whose permission node is not recorded in // the legacy dispatcher, so the override must fall back to the @@ -802,7 +794,7 @@ mod override_tests { permission(&mut commands, "kill", PermissionLvl::Four); let manager = PermissionManager::new(); - let _dispatcher = default_dispatcher(&manager, &basic, &commands); + let _dispatcher = default_dispatcher(&manager, &commands); let permission = manager .get_permission("minecraft:command.kill") @@ -815,12 +807,11 @@ mod override_tests { #[test] fn permission_level_zero_allows_everyone() { - let basic = BasicConfiguration::default(); let mut commands = CommandsConfig::default(); permission(&mut commands, "gamemode", PermissionLvl::Zero); let manager = PermissionManager::new(); - let _dispatcher = default_dispatcher(&manager, &basic, &commands); + let _dispatcher = default_dispatcher(&manager, &commands); let permission = manager .get_permission("minecraft:command.gamemode") diff --git a/crates/pumpkin/src/command/commands/raid.rs b/crates/pumpkin/src/command/commands/raid.rs index 23be0ab18..76d544ae1 100644 --- a/crates/pumpkin/src/command/commands/raid.rs +++ b/crates/pumpkin/src/command/commands/raid.rs @@ -60,7 +60,7 @@ impl CommandExecutor for StartExecutor { } else { 1 }; - let raid_id = raids.create_or_extend_raid(&player, pos, &world); + let raid_id = raids.create_or_extend_raid(pos, &world); if let Some(id) = raid_id && let Some(raid) = raids.get_mut(id) { diff --git a/crates/pumpkin/src/command/dispatcher.rs b/crates/pumpkin/src/command/dispatcher.rs index 5ec132741..32b493732 100644 --- a/crates/pumpkin/src/command/dispatcher.rs +++ b/crates/pumpkin/src/command/dispatcher.rs @@ -793,7 +793,6 @@ impl CommandDispatcher { #[cfg(test)] mod test { - use pumpkin_config::BasicConfiguration; use pumpkin_data::translation; use pumpkin_util::permission::PermissionManager; use pumpkin_util::text::TextContent; @@ -817,22 +816,18 @@ mod test { #[test] fn dynamic_command() { - let config = BasicConfiguration::default(); let commands_config = pumpkin_config::CommandsConfig::default(); let manager = PermissionManager::new(); - let mut dispatcher = - default_dispatcher(&manager, &config, &commands_config).fallback_dispatcher; + let mut dispatcher = default_dispatcher(&manager, &commands_config).fallback_dispatcher; let tree = CommandTree::new(["test"], "test_desc"); dispatcher.register(tree, "minecraft:test"); } #[test] fn pumpkin_command_aliases() { - let config = BasicConfiguration::default(); let commands_config = pumpkin_config::CommandsConfig::default(); let manager = PermissionManager::new(); - let dispatcher = - default_dispatcher(&manager, &config, &commands_config).fallback_dispatcher; + let dispatcher = default_dispatcher(&manager, &commands_config).fallback_dispatcher; let pumpkin_tree = dispatcher.get_tree("pumpkin").unwrap(); let version_tree = dispatcher.get_tree("version").unwrap(); diff --git a/crates/pumpkin/src/entity/ai/goal/follow_owner.rs b/crates/pumpkin/src/entity/ai/goal/follow_owner.rs index 3928a4002..00cfa93b4 100644 --- a/crates/pumpkin/src/entity/ai/goal/follow_owner.rs +++ b/crates/pumpkin/src/entity/ai/goal/follow_owner.rs @@ -117,7 +117,7 @@ impl FollowOwnerGoal { ), None, None, - world.clone(), + &world, ); let mut navigator = mob diff --git a/crates/pumpkin/src/entity/ai/goal/mod.rs b/crates/pumpkin/src/entity/ai/goal/mod.rs index c47deffaf..a6327afe7 100644 --- a/crates/pumpkin/src/entity/ai/goal/mod.rs +++ b/crates/pumpkin/src/entity/ai/goal/mod.rs @@ -22,6 +22,7 @@ pub mod look_around; pub mod look_at_entity; pub mod melee_attack; pub mod move_to_target_pos; +pub mod move_towards_target; pub mod offer_flower; pub mod open_door; pub mod owner_hurt_by_target; @@ -89,7 +90,7 @@ pub trait Goal: Send + Sync { } } -#[derive(Clone, Copy, Default)] +#[derive(Clone, Copy, Default, PartialEq, Eq, Debug)] // We actually only use the first 4 bits ;) pub struct Controls(u8); diff --git a/crates/pumpkin/src/entity/ai/goal/move_to_target_pos.rs b/crates/pumpkin/src/entity/ai/goal/move_to_target_pos.rs index aa3c30616..d29b6ce1f 100644 --- a/crates/pumpkin/src/entity/ai/goal/move_to_target_pos.rs +++ b/crates/pumpkin/src/entity/ai/goal/move_to_target_pos.rs @@ -8,10 +8,11 @@ use pumpkin_util::math::vector3::Vector3; use rand::RngExt; use std::sync::Arc; -const MIN_WAITING_TIME: i32 = 1200; -const MAX_TRYING_TIME: i32 = 1200; -const MIN_INTERVAL: i32 = 200; +const GIVE_UP_TICKS: i32 = 1200; +const STAY_TICKS: i32 = 1200; +const INTERVAL_TICKS: i32 = 200; +/// Mirrors vanilla Minecraft's `net.minecraft.world.entity.ai.goal.MoveToBlockGoal`. pub struct MoveToTargetPosGoal { goal_control: Controls, pub move_to_target_pos: ParentHandle, @@ -55,7 +56,7 @@ impl MoveToTargetPosGoal { } pub fn get_interval(mob: &dyn Mob) -> i32 { - to_goal_ticks(MIN_INTERVAL + mob.get_random().random_range(0..MIN_INTERVAL)) + to_goal_ticks(INTERVAL_TICKS + mob.get_random().random_range(0..INTERVAL_TICKS)) } pub fn find_target_pos(&mut self, mob: &dyn Mob) -> bool { @@ -104,16 +105,38 @@ impl MoveToTargetPosGoal { false } - fn get_target_pos(&self) -> BlockPos { + #[must_use] + pub fn get_target_pos(&self) -> BlockPos { self.target_pos.up() } - const fn should_reset_path(&self) -> bool { + #[must_use] + pub const fn should_reset_path(&self) -> bool { self.trying_time % 40 == 0 } - fn start_moving_to_target(_mob: &dyn Mob) { - // TODO: implement when navigation is implemented + #[must_use] + pub const fn is_reached_target(&self) -> bool { + self.reached + } + + pub fn move_mob_to_block(&self, mob: &dyn Mob) { + let target = self.get_target_pos(); + let mut navigator = mob + .get_mob_entity() + .navigator + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + + navigator.set_progress(NavigatorGoal { + current_progress: mob.get_entity().pos.load(), + destination: Vector3::new( + target.0.x as f64 + 0.5, + target.0.y as f64, + target.0.z as f64 + 0.5, + ), + speed: self.speed, + }); } } @@ -141,29 +164,34 @@ impl Goal for MoveToTargetPosGoal { let can_target = self .move_to_target_pos .get() - .is_some_and(|x| x.is_target_pos(world, self.target_pos)); + .is_some_and(|move_to_target_pos| { + move_to_target_pos.is_target_pos(world, self.target_pos) + }); self.trying_time >= -self.safe_waiting_time - && self.trying_time <= MAX_TRYING_TIME + && self.trying_time <= GIVE_UP_TICKS && can_target } fn start(&mut self, mob: &dyn Mob) { - Self::start_moving_to_target(mob); + self.move_mob_to_block(mob); self.trying_time = 0; - let random = mob.get_random().random_range(0..MIN_WAITING_TIME); - self.safe_waiting_time = - mob.get_random().random_range(random..MIN_WAITING_TIME) + MIN_WAITING_TIME; + let bound = mob.get_random().random_range(0..STAY_TICKS) + STAY_TICKS; + self.safe_waiting_time = mob.get_random().random_range(0..bound) + STAY_TICKS; } fn tick(&mut self, mob: &dyn Mob) { - let block_pos = self.get_target_pos(); - let block_pos: Vector3 = block_pos.to_f64(); + let target = self.get_target_pos(); + let target_center = Vector3::new( + target.0.x as f64 + 0.5, + target.0.y as f64 + 0.5, + target.0.z as f64 + 0.5, + ); let Some(move_to_target_pos) = self.move_to_target_pos.get() else { return; }; let desired_distance = move_to_target_pos.get_desired_distance_to_target(); - if block_pos.squared_distance_to_vec(&mob.get_entity().pos.load()) + if target_center.squared_distance_to_vec(&mob.get_entity().pos.load()) < desired_distance * desired_distance { self.reached = true; @@ -180,7 +208,11 @@ impl Goal for MoveToTargetPosGoal { navigator.set_progress(NavigatorGoal { current_progress: mob.get_entity().pos.load(), - destination: Vector3::new(block_pos.x + 0.5, block_pos.y, block_pos.z + 0.5), + destination: Vector3::new( + target.0.x as f64 + 0.5, + target.0.y as f64, + target.0.z as f64 + 0.5, + ), speed: self.speed, }); } diff --git a/crates/pumpkin/src/entity/ai/goal/move_towards_target.rs b/crates/pumpkin/src/entity/ai/goal/move_towards_target.rs new file mode 100644 index 000000000..557fde256 --- /dev/null +++ b/crates/pumpkin/src/entity/ai/goal/move_towards_target.rs @@ -0,0 +1,208 @@ +use std::sync::Arc; + +use super::{Controls, Goal}; +use crate::entity::{EntityBase, ai::pathfinder::NavigatorGoal, mob::Mob}; +use pumpkin_util::math::{position::BlockPos, vector3::Vector3}; +use rand::RngExt; + +const HORIZONTAL_RANGE: f64 = 16.0; +const VERTICAL_RANGE: i32 = 7; +const TARGET_ATTEMPTS: usize = 10; + +/// Mirrors vanilla Minecraft's `MoveTowardsTargetGoal`. +/// +/// Moves the mob towards its current attack target within a specified maximum distance. +pub struct MoveTowardsTargetGoal { + goal_control: Controls, + speed: f64, + within: f32, + target: Option>, + wanted_pos: Option>, +} + +impl MoveTowardsTargetGoal { + #[must_use] + pub const fn new(speed: f64, within: f32) -> Self { + Self { + goal_control: Controls::MOVE, + speed, + within, + target: None, + wanted_pos: None, + } + } + + /// Mirrors vanilla's `DefaultRandomPos.getPosTowards(mob, 16, 7, target.position(), Math.PI / 2)`. + fn find_pos_towards(mob: &dyn Mob, target_pos: &Vector3) -> Option> { + let entity = &mob.get_mob_entity().living_entity.entity; + let mob_pos = entity.pos.load(); + let world = entity.world.load(); + + let dir_x = target_pos.x - mob_pos.x; + let dir_z = target_pos.z - mob_pos.z; + + let mut rng = mob.get_random(); + let (dir_x, dir_z) = if dir_x == 0.0 && dir_z == 0.0 { + (rng.random_range(-1.0..1.0), rng.random_range(-1.0..1.0)) + } else { + (dir_x, dir_z) + }; + let base_angle = dir_z.atan2(dir_x) - std::f64::consts::FRAC_PI_2; + + for _ in 0..TARGET_ATTEMPTS { + let angle = + base_angle + (2.0 * rng.random_range(0.0..1.0) - 1.0) * std::f64::consts::FRAC_PI_2; + let t = rng.random_range(0.0..1.0f64).sqrt(); + let dist = t * HORIZONTAL_RANGE * std::f64::consts::SQRT_2; + let dx = -dist * angle.sin(); + let dz = dist * angle.cos(); + + if dx.abs() > HORIZONTAL_RANGE || dz.abs() > HORIZONTAL_RANGE { + continue; + } + + let dy = rng.random_range(-VERTICAL_RANGE..=VERTICAL_RANGE); + + let candidate = BlockPos::new( + (mob_pos.x + dx) as i32, + (mob_pos.y + dy as f64) as i32, + (mob_pos.z + dz) as i32, + ); + + let block_at = world.get_block_state(&candidate); + let block_below = world.get_block_state(&BlockPos::new( + candidate.0.x, + candidate.0.y - 1, + candidate.0.z, + )); + + if block_at.is_solid() || !block_below.is_solid() { + continue; + } + + return Some(Vector3::new( + candidate.0.x as f64 + 0.5, + candidate.0.y as f64, + candidate.0.z as f64 + 0.5, + )); + } + + None + } +} + +impl Goal for MoveTowardsTargetGoal { + fn can_start(&mut self, mob: &dyn Mob) -> bool { + let target = mob.get_mob_entity().get_target(); + let Some(target) = target else { + self.target = None; + return false; + }; + + if !target.get_entity().is_alive() { + self.target = None; + return false; + } + + let mob_pos = mob.get_entity().pos.load(); + let target_pos = target.get_entity().pos.load(); + let dist_sq = mob_pos.squared_distance_to_vec(&target_pos); + let within_sq = f64::from(self.within) * f64::from(self.within); + + if dist_sq > within_sq { + self.target = None; + return false; + } + + let pos = Self::find_pos_towards(mob, &target_pos); + let Some(pos) = pos else { + self.target = None; + return false; + }; + + self.wanted_pos = Some(pos); + self.target = Some(target); + true + } + + fn should_continue(&self, mob: &dyn Mob) -> bool { + let Some(target) = &self.target else { + return false; + }; + + if !target.get_entity().is_alive() { + return false; + } + + let navigator = mob + .get_mob_entity() + .navigator + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + if navigator.is_idle() { + return false; + } + + let mob_pos = mob.get_entity().pos.load(); + let target_pos = target.get_entity().pos.load(); + let dist_sq = mob_pos.squared_distance_to_vec(&target_pos); + let within_sq = f64::from(self.within) * f64::from(self.within); + + dist_sq < within_sq + } + + fn start(&mut self, mob: &dyn Mob) { + if let Some(wanted_pos) = self.wanted_pos { + let mob_pos = mob.get_entity().pos.load(); + let mut navigator = mob + .get_mob_entity() + .navigator + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + navigator.set_progress(NavigatorGoal::new(mob_pos, wanted_pos, self.speed)); + } + } + + fn stop(&mut self, _mob: &dyn Mob) { + self.target = None; + self.wanted_pos = None; + } + + fn controls(&self) -> Controls { + self.goal_control + } +} + +#[cfg(test)] +#[allow(clippy::unimplemented)] +mod tests { + use super::MoveTowardsTargetGoal; + use crate::entity::ai::goal::{Controls, Goal}; + + #[test] + fn initial_controls_and_state() { + let mut goal = MoveTowardsTargetGoal::new(1.0, 16.0); + assert_eq!(goal.controls(), Controls::MOVE); + assert!(goal.target.is_none()); + assert!(goal.wanted_pos.is_none()); + + goal.wanted_pos = Some(pumpkin_util::math::vector3::Vector3::new(1.0, 2.0, 3.0)); + assert!(goal.wanted_pos.is_some()); + goal.stop(&MockMob); + assert!(goal.target.is_none()); + assert!(goal.wanted_pos.is_none()); + } + + struct MockMob; + impl crate::entity::mob::Mob for MockMob { + fn get_mob_entity(&self) -> &crate::entity::mob::MobEntity { + unimplemented!() + } + fn mob_write_nbt(&self, _nbt: &mut pumpkin_nbt::compound::NbtCompound) { + unimplemented!() + } + fn mob_read_nbt(&self, _nbt: &pumpkin_nbt::compound::NbtCompound) { + unimplemented!() + } + } +} diff --git a/crates/pumpkin/src/entity/ai/goal/track_target.rs b/crates/pumpkin/src/entity/ai/goal/track_target.rs index 995de6868..3d4dd4e4f 100644 --- a/crates/pumpkin/src/entity/ai/goal/track_target.rs +++ b/crates/pumpkin/src/entity/ai/goal/track_target.rs @@ -47,7 +47,7 @@ impl TrackTargetGoal { self } - fn can_navigate_to_entity(&self, mob: &dyn Mob, _target: &LivingEntity) -> bool { + fn can_navigate_to_entity(&self, mob: &dyn Mob) -> bool { let cooldown = to_goal_ticks(10 + mob.get_random().random_range(0..5)); self.check_can_navigate_cooldown .store(cooldown, Ordering::Relaxed); @@ -95,7 +95,7 @@ impl TrackTargetGoal { } if self.can_navigate_flag.load(Ordering::Relaxed) == UNSET { - let can_reach = self.can_navigate_to_entity(mob, target); + let can_reach = self.can_navigate_to_entity(mob); self.can_navigate_flag.store( if can_reach { CAN_TRACK } else { CANNOT_TRACK }, Ordering::Relaxed, diff --git a/crates/pumpkin/src/entity/ai/pathfinder/walk_node_evaluator.rs b/crates/pumpkin/src/entity/ai/pathfinder/walk_node_evaluator.rs index e52ba791e..96ab4e157 100644 --- a/crates/pumpkin/src/entity/ai/pathfinder/walk_node_evaluator.rs +++ b/crates/pumpkin/src/entity/ai/pathfinder/walk_node_evaluator.rs @@ -94,8 +94,6 @@ impl WalkNodeEvaluator { pos: Vector3, max_y_step: i32, last_feet_y: f64, - facing: (i32, i32), - current_path_type: PathType, ) -> Option { let feet_y = self.get_floor_level(pos); if feet_y - last_feet_y > self.get_mob_jump_height() { @@ -124,13 +122,7 @@ impl WalkNodeEvaluator { && path_type != PathType::Trapdoor && path_type != PathType::PowderSnow { - let jump_node = self.get_jump_on_top_node( - pos, - max_y_step, - last_feet_y, - facing, - current_path_type, - ); + let jump_node = self.get_jump_on_top_node(pos, max_y_step, last_feet_y); if jump_node.is_some() { node = jump_node; } @@ -157,8 +149,6 @@ impl WalkNodeEvaluator { pos: Vector3, max_y_step: i32, last_feet_y: f64, - _facing: (i32, i32), - _current_path_type: PathType, ) -> Option { for dy in 1..=max_y_step { let step_pos = Vector3::new(pos.x, pos.y + dy, pos.z); @@ -419,13 +409,7 @@ impl NodeEvaluator for WalkNodeEvaluator { for (i, &(dx, dz)) in DIRECTIONS.iter().enumerate() { let neighbor_pos = current.pos.0.add_raw(dx, 0, dz); - let neighbor_opt = self.find_accepted_node( - neighbor_pos, - max_y_step, - floor_level, - (dx, dz), - current.path_type, - ); + let neighbor_opt = self.find_accepted_node(neighbor_pos, max_y_step, floor_level); if let Some(neighbor) = neighbor_opt { self.reusable_neighbors[i] = Some(neighbor); @@ -452,13 +436,7 @@ impl NodeEvaluator for WalkNodeEvaluator { ) { let diagonal_pos = current.pos.0.add_raw(dx, 0, dz); - let diagonal_opt = self.find_accepted_node( - diagonal_pos, - max_y_step, - floor_level, - (dx, dz), - current.path_type, - ); + let diagonal_opt = self.find_accepted_node(diagonal_pos, max_y_step, floor_level); if let Some(diagonal) = diagonal_opt && Self::is_diagonal_node_valid(Some(&diagonal)) diff --git a/crates/pumpkin/src/entity/boss/ender_dragon.rs b/crates/pumpkin/src/entity/boss/ender_dragon.rs index ce3d725ab..61010939f 100644 --- a/crates/pumpkin/src/entity/boss/ender_dragon.rs +++ b/crates/pumpkin/src/entity/boss/ender_dragon.rs @@ -434,7 +434,6 @@ impl EnderDragonEntity { target: Vector3, fly_speed: f32, turn_speed: f32, - _y_scale: f64, ) { let xdd = target.x - pos.x; let mut ydd = target.y - pos.y; @@ -818,13 +817,7 @@ impl EnderDragonEntity { .unwrap_or_else(std::sync::PoisonError::into_inner); if let Some(target) = target_location { let pos = self.mob_entity.living_entity.entity.pos.load(); - self.steer_toward( - pos, - target, - phase.get_fly_speed(), - phase.get_turn_speed(), - 0.5, - ); + self.steer_toward(pos, target, phase.get_fly_speed(), phase.get_turn_speed()); } self.mob_entity.living_entity.entity.send_pos_rot(); diff --git a/crates/pumpkin/src/entity/combat.rs b/crates/pumpkin/src/entity/combat.rs index f0da220f0..e717c6dc9 100644 --- a/crates/pumpkin/src/entity/combat.rs +++ b/crates/pumpkin/src/entity/combat.rs @@ -102,7 +102,7 @@ pub fn spawn_sweep_particle(attacker_entity: &Entity, world: &World, pos: &Vecto ); } -pub async fn player_attack_sound(pos: &Vector3, world: &World, attack_type: AttackType) { +pub fn player_attack_sound(pos: &Vector3, world: &World, attack_type: AttackType) { match attack_type { AttackType::Knockback => { world.play_sound( diff --git a/crates/pumpkin/src/entity/effect/raid_omen.rs b/crates/pumpkin/src/entity/effect/raid_omen.rs index 3c6bb10ff..40e31d458 100644 --- a/crates/pumpkin/src/entity/effect/raid_omen.rs +++ b/crates/pumpkin/src/entity/effect/raid_omen.rs @@ -21,7 +21,7 @@ impl MobEffect for RaidOmenMobEffect { .raids .lock() .unwrap_or_else(std::sync::PoisonError::into_inner); - raids.create_or_extend_raid(player, raid_pos, &world); + raids.create_or_extend_raid(raid_pos, &world); player.clear_raid_omen_position(); } } diff --git a/crates/pumpkin/src/entity/experience_orb.rs b/crates/pumpkin/src/entity/experience_orb.rs index b6cadffdf..50ae0d90a 100644 --- a/crates/pumpkin/src/entity/experience_orb.rs +++ b/crates/pumpkin/src/entity/experience_orb.rs @@ -90,7 +90,7 @@ impl EntityBase for ExperienceOrbEntity { entity.move_entity(caller, velo); - entity.tick_block_collisions(caller, server); + entity.tick_block_collisions(caller); let age = self.orb_age.fetch_add(1, Ordering::Relaxed); if age >= 6000 { diff --git a/crates/pumpkin/src/entity/falling.rs b/crates/pumpkin/src/entity/falling.rs index 03c01b8db..c6d2fd0b2 100644 --- a/crates/pumpkin/src/entity/falling.rs +++ b/crates/pumpkin/src/entity/falling.rs @@ -46,7 +46,7 @@ impl FallingEntity { } impl EntityBase for FallingEntity { - fn tick(&self, caller: &dyn EntityBase, server: &Server) { + fn tick(&self, caller: &dyn EntityBase, _server: &Server) { let entity = &self.entity; let mut velo = entity.velocity.load(); velo.y -= self.get_gravity(); @@ -54,7 +54,7 @@ impl EntityBase for FallingEntity { entity.velocity.store(velo); entity.move_entity(caller, velo); - entity.tick_block_collisions(caller, server); + entity.tick_block_collisions(caller); if entity.on_ground.load(Ordering::Relaxed) { entity.velocity.store(velo.multiply(0.7, -0.5, 0.7)); entity.world.load().set_block_state( diff --git a/crates/pumpkin/src/entity/item.rs b/crates/pumpkin/src/entity/item.rs index 7aa64657b..36a1f4625 100644 --- a/crates/pumpkin/src/entity/item.rs +++ b/crates/pumpkin/src/entity/item.rs @@ -341,16 +341,11 @@ impl ItemEntity { Some(tick_move) } - fn move_and_apply_friction( - &self, - caller: &dyn EntityBase, - server: &Server, - move_velo: Vector3, - ) { + fn move_and_apply_friction(&self, caller: &dyn EntityBase, move_velo: Vector3) { let entity = &self.entity; entity.move_entity(caller, move_velo); - entity.tick_block_collisions(caller, server); + entity.tick_block_collisions(caller); let mut friction = 0.98; let on_ground = entity.on_ground.load(Ordering::SeqCst); @@ -443,7 +438,7 @@ impl ItemEntity { } impl EntityBase for ItemEntity { - fn tick(&self, caller: &dyn EntityBase, server: &Server) { + fn tick(&self, caller: &dyn EntityBase, _server: &Server) { let entity = &self.entity; self.decrement_pickup_delay(); @@ -461,7 +456,7 @@ impl EntityBase for ItemEntity { }; if tick_move { - self.move_and_apply_friction(caller, server, move_velo); + self.move_and_apply_friction(caller, move_velo); } if self.process_age_and_merge() { diff --git a/crates/pumpkin/src/entity/living.rs b/crates/pumpkin/src/entity/living.rs index bc95f055c..9a9fc54e6 100644 --- a/crates/pumpkin/src/entity/living.rs +++ b/crates/pumpkin/src/entity/living.rs @@ -986,7 +986,7 @@ impl LivingEntity { world.broadcast_editioned(&je_packet, &be_packet); } - fn tick_movement(&self, server: &Server, caller: &dyn EntityBase) { + fn tick_movement(&self, caller: &dyn EntityBase) { if self.jumping_cooldown.load(Relaxed) != 0 { self.jumping_cooldown.fetch_sub(1, Relaxed); } @@ -1060,7 +1060,7 @@ impl LivingEntity { self.travel_in_air(caller); } - let suffocating = self.entity.tick_block_collisions(caller, server); + let suffocating = self.entity.tick_block_collisions(caller); if suffocating { caller.damage(caller, 1.0, DamageType::IN_WALL); @@ -2644,7 +2644,7 @@ impl EntityBase for LivingEntity { let is_alive = !self.dead.load(Relaxed) && self.health.load() > 0.0; let in_death_animation = self.health.load() <= 0.0 && self.death_time.load(Relaxed) < 20; if is_alive || (in_death_animation && self.entity.entity_type != &EntityType::PLAYER) { - self.tick_movement(server, caller); + self.tick_movement(caller); // Vanilla-like order: freeze logic runs after movement/collisions. self.entity.tick_frozen(caller); } diff --git a/crates/pumpkin/src/entity/mob/equipment.rs b/crates/pumpkin/src/entity/mob/equipment.rs index 361cd024a..5cb7508eb 100644 --- a/crates/pumpkin/src/entity/mob/equipment.rs +++ b/crates/pumpkin/src/entity/mob/equipment.rs @@ -868,7 +868,7 @@ fn select_vanilla_armor(difficulty: &RegionalDifficulty) -> Vec<(EquipmentSlot, first = false; pieces.push(( slot.clone(), - create_equipment_item(tier[i], difficulty), + create_equipment_item(tier[i]), DEFAULT_EQUIPMENT_DROP_CHANCE, )); } @@ -878,7 +878,7 @@ fn select_vanilla_armor(difficulty: &RegionalDifficulty) -> Vec<(EquipmentSlot, /// Creates a fresh, full-durability `ItemStack` for mob equipment. /// Vanilla mobs always spawn with equipment at full durability. #[must_use] -fn create_equipment_item(item: &'static Item, _difficulty: &RegionalDifficulty) -> ItemStack { +fn create_equipment_item(item: &'static Item) -> ItemStack { ItemStack::new(1, item) } @@ -897,7 +897,7 @@ fn equip_mob_from_def( // ── Weapon ── match def.weapon { WeaponConfig::Always(item) => { - let mut stack = create_equipment_item(item, difficulty); + let mut stack = create_equipment_item(item); if def.enchanted && difficulty.should_happen(WEAPON_ENCHANT_CHANCE) { apply_vanilla_enchantments( &mut stack, @@ -913,7 +913,7 @@ fn equip_mob_from_def( } WeaponConfig::AlwaysWeighted(items) => { let item = weighted_select_item(items); - let mut stack = create_equipment_item(item, difficulty); + let mut stack = create_equipment_item(item); if def.enchanted && difficulty.should_happen(WEAPON_ENCHANT_CHANCE) { apply_vanilla_enchantments( &mut stack, @@ -939,7 +939,7 @@ fn equip_mob_from_def( }; if rand::random::() < chance { let item = weighted_select_item(items); - let mut stack = create_equipment_item(item, difficulty); + let mut stack = create_equipment_item(item); if def.enchanted && difficulty.should_happen(WEAPON_ENCHANT_CHANCE) { apply_vanilla_enchantments( &mut stack, @@ -977,7 +977,7 @@ fn equip_mob_from_def( ArmorConfig::CustomPerSlot(entries) => { for entry in entries { if rand::random::() < entry.chance { - let mut stack = create_equipment_item(entry.item, difficulty); + let mut stack = create_equipment_item(entry.item); if def.enchanted && difficulty.should_happen(ARMOR_ENCHANT_CHANCE) { apply_vanilla_enchantments( &mut stack, diff --git a/crates/pumpkin/src/entity/mob/shulker.rs b/crates/pumpkin/src/entity/mob/shulker.rs index f46dd2d9a..0df7f513d 100644 --- a/crates/pumpkin/src/entity/mob/shulker.rs +++ b/crates/pumpkin/src/entity/mob/shulker.rs @@ -325,7 +325,7 @@ impl ShulkerEntity { false } - pub fn on_shulker_damage(&self, _damage_type: DamageType) { + pub fn on_shulker_damage(&self) { let living = &self.mob_entity.living_entity; let health = living.health.load(); let max = living.get_max_health(); @@ -386,8 +386,8 @@ impl Mob for ShulkerEntity { } } - fn on_damage(&self, damage_type: DamageType, _source: Option<&dyn EntityBase>) { - self.on_shulker_damage(damage_type); + fn on_damage(&self, _damage_type: DamageType, _source: Option<&dyn EntityBase>) { + self.on_shulker_damage(); } /// When closed, block arrows entirely. diff --git a/crates/pumpkin/src/entity/mod.rs b/crates/pumpkin/src/entity/mod.rs index 59575bfbc..7bc127f32 100644 --- a/crates/pumpkin/src/entity/mod.rs +++ b/crates/pumpkin/src/entity/mod.rs @@ -216,7 +216,7 @@ pub trait EntityBase: Send + Sync + std::any::Any { pitch: Option, world: Arc, ) { - self.get_entity().teleport(position, yaw, pitch, world); + self.get_entity().teleport(position, yaw, pitch, &world); } fn is_pushed_by_fluids(&self) -> bool { @@ -1502,7 +1502,7 @@ impl Entity { } #[expect(dead_code)] - fn tick_block_underneath(_caller: &dyn EntityBase) { + const fn tick_block_underneath() { // let world = self.world.read(); // let (pos, block, state) = self.get_block_with_y_offset(0.2); @@ -1565,7 +1565,7 @@ impl Entity { */ } - pub fn tick_block_collisions(&self, caller: &dyn EntityBase, _server: &Server) -> bool { + pub fn tick_block_collisions(&self, caller: &dyn EntityBase) -> bool { if !self.is_affected_by_blocks() { return false; } @@ -2313,7 +2313,6 @@ impl Entity { let world_clone = self.world.load_full(); let portal_type = portal_processor.portal_type; let dest_world_opt = portal_processor.destination_world.clone(); - let entry_pos = portal_processor.entry_position; let src_portal = portal_processor.source_portal.clone(); let entity_id = self.entity_id; let yaw = self.yaw.load(); @@ -2326,7 +2325,6 @@ impl Entity { &world_clone, dest_world_opt, entity_arc.as_ref(), - entry_pos, src_portal.as_ref(), ); @@ -2395,7 +2393,7 @@ impl Entity { } } - pub fn try_use_portal(&self, _portal_delay: u32, portal_world: Arc, pos: BlockPos) { + pub fn try_use_portal(&self, portal_world: Arc, pos: BlockPos) { let mut portal_event = crate::plugin::api::events::entity::entity_portal::EntityPortalEvent::new( self.entity_id, @@ -3109,7 +3107,7 @@ impl Entity { position: Vector3, yaw: Option, pitch: Option, - _world: Arc, + world: &World, ) { // Update server-side position and bounding box self.set_pos(position); @@ -3132,7 +3130,7 @@ impl Entity { .store((pitch * 256.0 / 360.0).rem_euclid(256.0) as u8, Relaxed); } let chunk_pos = self.chunk_pos.load(); - self.world.load().broadcast_to_chunk( + world.broadcast_to_chunk( chunk_pos, &CEntityPositionSync::new( self.entity_id.into(), @@ -3429,16 +3427,16 @@ impl Entity { self.remove_passenger_on_disconnect(passenger_id); } - pub async fn remove_passenger(&self, passenger_id: i32) { - self.remove_passenger_internal(passenger_id, true).await; + pub fn remove_passenger(&self, passenger_id: i32) { + self.remove_passenger_internal(passenger_id, true); } - pub async fn remove_passenger_before_teleport(&self, passenger_id: i32) { - self.remove_passenger_internal(passenger_id, false).await; + pub fn remove_passenger_before_teleport(&self, passenger_id: i32) { + self.remove_passenger_internal(passenger_id, false); } #[allow(clippy::too_many_lines)] - async fn remove_passenger_internal(&self, passenger_id: i32, reposition: bool) { + fn remove_passenger_internal(&self, passenger_id: i32, reposition: bool) { let mut dismount_event = crate::plugin::api::events::entity::entity_dismount::EntityDismountEvent::new( passenger_id, @@ -3452,9 +3450,10 @@ impl Entity { if let Some(server) = self.world.load().server.upgrade() { server .plugin_manager - .fire(&server, &mut dismount_event) - .await; - server.plugin_manager.fire(&server, &mut vehicle_exit).await; + .fire_blocking(&server, &mut dismount_event); + server + .plugin_manager + .fire_blocking(&server, &mut vehicle_exit); } if dismount_event.cancelled || vehicle_exit.cancelled { return; @@ -3521,13 +3520,7 @@ impl Entity { let world = self.world.load(); let passengers_packet = CSetPassengers::new(VarInt(self.entity_id), &passenger_ids); if let Some(player) = passenger.get_player() { - if reposition { - player.send_client_packet(&passengers_packet).await; - } else if let ClientPlatform::Java(client) = player.client.as_ref() - && let Ok(data) = client.serialize_packet(&passengers_packet) - { - client.send_packet_now(data).await; - } + player.try_send_client_packet(&passengers_packet); world.broadcast_to_chunk_except( chunk_pos, &[player.get_entity().entity_uuid], @@ -3729,22 +3722,20 @@ impl Entity { // the same packet queue as CSetPassengers, preserving send order. // Vanilla uses DELTA | ROT flags: position absolute, delta/rotation relative. // With rotation relative and yaw/pitch=0, the client preserves its current look. - player - .send_client_packet(&CPlayerPosition::new( - id.into(), - dismount_pos, - Vector3::new(0.0, 0.0, 0.0), - 0.0, - 0.0, - vec![ - PositionFlag::DeltaX, - PositionFlag::DeltaY, - PositionFlag::DeltaZ, - PositionFlag::YRot, - PositionFlag::XRot, - ], - )) - .await; + player.try_send_client_packet(&CPlayerPosition::new( + id.into(), + dismount_pos, + Vector3::new(0.0, 0.0, 0.0), + 0.0, + 0.0, + vec![ + PositionFlag::DeltaX, + PositionFlag::DeltaY, + PositionFlag::DeltaZ, + PositionFlag::YRot, + PositionFlag::XRot, + ], + )); } // Vanilla: setSneaking(false) after dismount via sneak input diff --git a/crates/pumpkin/src/entity/passive/iron_golem.rs b/crates/pumpkin/src/entity/passive/iron_golem.rs index e5aed283e..f3307197c 100644 --- a/crates/pumpkin/src/entity/passive/iron_golem.rs +++ b/crates/pumpkin/src/entity/passive/iron_golem.rs @@ -16,7 +16,8 @@ use crate::entity::{ ai::goal::{ active_target::ActiveTargetGoal, look_around::RandomLookAroundGoal, look_at_entity::LookAtEntityGoal, melee_attack::MeleeAttackGoal, - offer_flower::OfferFlowerGoal, revenge::RevengeGoal, wander_around::WanderAroundGoal, + move_towards_target::MoveTowardsTargetGoal, offer_flower::OfferFlowerGoal, + revenge::RevengeGoal, wander_around::WanderAroundGoal, }, mob::{Mob, MobEntity}, player::Player, @@ -60,17 +61,18 @@ impl IronGolemEntity { .unwrap_or_else(std::sync::PoisonError::into_inner); goal_selector.add_goal(1, Box::new(MeleeAttackGoal::new(1.0, true))); + goal_selector.add_goal(2, Box::new(MoveTowardsTargetGoal::new(0.9, 32.0))); + goal_selector.add_goal(4, Box::new(WanderAroundGoal::new(0.6))); goal_selector.add_goal(5, Box::new(OfferFlowerGoal::new())); - goal_selector.add_goal(6, Box::new(WanderAroundGoal::new(0.6))); goal_selector.add_goal( 7, LookAtEntityGoal::with_default(mob_weak, &EntityType::PLAYER, 6.0), ); goal_selector.add_goal(8, Box::new(RandomLookAroundGoal::default())); - target_selector.add_goal(1, Box::new(RevengeGoal::new(true))); + target_selector.add_goal(2, Box::new(RevengeGoal::new(true))); target_selector.add_goal( - 2, + 3, ActiveTargetGoal::with_default(&mob_arc.mob_entity, &EntityType::PLAYER, false), ); target_selector.add_goal( diff --git a/crates/pumpkin/src/entity/player.rs b/crates/pumpkin/src/entity/player.rs index e476a2755..6bd68c691 100644 --- a/crates/pumpkin/src/entity/player.rs +++ b/crates/pumpkin/src/entity/player.rs @@ -16,9 +16,11 @@ use advancement::PlayerAdvancement; use arc_swap::ArcSwap; use crossbeam::atomic::AtomicCell; use crossbeam::channel::Receiver; +use crossbeam::queue::SegQueue; use pumpkin_data::dimension::Dimension; use pumpkin_inventory::merchant::merchant_screen_handler::MerchantScreenHandler; use pumpkin_inventory::player::ender_chest_inventory::EnderChestInventory; +use pumpkin_protocol::RawPacket; use pumpkin_protocol::bedrock::client::play_status::CPlayStatus; use pumpkin_protocol::bedrock::client::set_time::CSetTime; use pumpkin_protocol::bedrock::client::update_abilities::{Ability, CUpdateAbilities}; @@ -80,8 +82,8 @@ impl JavaPlayer<'_> { self.send_packet(&packet).await; } - pub async fn send_stats(&self) { - self.0.send_stats().await; + pub fn send_stats(&self) { + self.0.send_stats(); } pub fn set_scoreboard(&self, scoreboard: Option) { @@ -258,15 +260,15 @@ use pumpkin_protocol::codec::var_int::VarInt; use pumpkin_protocol::codec::var_long::VarLong; use pumpkin_protocol::codec::var_ulong::VarULong; use pumpkin_protocol::java::client::play::{ - Animation, CActionBar, CAwardStats, CChangeDifficulty, CCloseContainer, CCombatDeath, - CCustomPayload, CDisguisedChatMessage, CEntityAnimation, CEntityPositionSync, CGameEvent, - CItemCooldown, CMapItemData, COpenScreen, CParticle, CPlayerAbilities, CPlayerInfoUpdate, - CPlayerPosition, CPlayerSpawnPosition, CRespawn, CSetCamera, CSetContainerContent, - CSetContainerProperty, CSetContainerSlot, CSetCursorItem, CSetExperience, CSetHealth, - CSetPlayerInventory, CSetSelectedSlot, CSoundEffect, CStopSound, CSubtitle, CSystemChatMessage, - CTabList, CTitleAnimation, CTitleText, CUnloadChunk, CUpdateMobEffect, CUpdateTime, GameEvent, - MapIcon, MapPatch, Metadata, PlayerAction, PlayerInfoFlags, PlayerSpawnData, PreviousMessage, - Statistic, + Animation, CAcknowledgeBlockChange, CActionBar, CAwardStats, CChangeDifficulty, + CCloseContainer, CCombatDeath, CCustomPayload, CDisguisedChatMessage, CEntityAnimation, + CEntityPositionSync, CGameEvent, CItemCooldown, CMapItemData, COpenScreen, CParticle, + CPlayerAbilities, CPlayerInfoUpdate, CPlayerPosition, CPlayerSpawnPosition, CRespawn, + CSetCamera, CSetContainerContent, CSetContainerProperty, CSetContainerSlot, CSetCursorItem, + CSetExperience, CSetHealth, CSetPlayerInventory, CSetSelectedSlot, CSoundEffect, CStopSound, + CSubtitle, CSystemChatMessage, CTabList, CTitleAnimation, CTitleText, CUnloadChunk, + CUpdateMobEffect, CUpdateTime, GameEvent, MapIcon, MapPatch, Metadata, PlayerAction, + PlayerInfoFlags, PlayerSpawnData, PreviousMessage, Statistic, }; use pumpkin_protocol::java::server::play::{ SClickSlot, SContainerButtonClick, SRenameItem, SlotActionType, @@ -827,6 +829,8 @@ pub struct Player { pub seen_credits: AtomicBool, pub score: AtomicI32, pub spawn_extra_particles_on_fall: AtomicBool, + /// Inbound packets waiting to be processed during player tick. + pub inbound_packets: SegQueue, } use base64::prelude::*; @@ -1116,6 +1120,7 @@ impl Player { seen_credits: AtomicBool::new(false), score: AtomicI32::new(0), spawn_extra_particles_on_fall: AtomicBool::new(false), + inbound_packets: SegQueue::new(), } } @@ -1123,13 +1128,12 @@ impl Player { /// /// Note: Tab list header and footer formatting is a Java Edition-specific protocol feature /// and is safely ignored on Bedrock Edition clients. - pub async fn set_tab_list(&self, tab_list: impl Into) { + pub fn set_tab_list(&self, tab_list: impl Into) { let list = tab_list.into(); - self.set_tab_list_header_footer(list.header, list.footer) - .await; + self.set_tab_list_header_footer(&list.header, &list.footer); } - pub async fn set_tab_list_header_footer(&self, header: TextComponent, footer: TextComponent) { + pub fn set_tab_list_header_footer(&self, header: &TextComponent, footer: &TextComponent) { *self .tab_list_header .lock() @@ -1138,8 +1142,7 @@ impl Player { .tab_list_footer .lock() .unwrap_or_else(std::sync::PoisonError::into_inner) = footer.clone(); - self.send_client_packet(&CTabList::new(&header, &footer)) - .await; + self.try_send_client_packet(&CTabList::new(header, footer)); } pub fn start_cooldown(&self, group: String, duration: i32) { @@ -1394,7 +1397,7 @@ impl Player { } #[expect(clippy::too_many_lines)] - pub async fn attack(&self, victim: Arc) { + pub fn attack(&self, victim: &Arc) { let world = self.world(); let Some(server) = world.server.upgrade() else { return; @@ -1522,7 +1525,7 @@ impl Player { } if !victim.damage_with_context( - &*victim, + victim.as_ref(), damage as f32, if is_mace_smash { DamageType::MACE_SMASH @@ -1567,7 +1570,7 @@ impl Player { ); } - player_attack_sound(&pos, &world, attack_type).await; + player_attack_sound(&pos, &world, attack_type); self.living_entity.last_attacking_id.store( victim_entity.entity_id, @@ -1947,9 +1950,7 @@ impl Player { // Try positions around the bed based on facing direction // Vanilla tries multiple offset patterns; we use a simplified version - if let Some(spawn_pos) = - Self::find_bed_spawn_position(&world, pos, facing, respawn_point.yaw) - { + if let Some(spawn_pos) = Self::find_bed_spawn_position(&world, pos, facing) { return Some(CalculatedRespawnPoint { position: spawn_pos, yaw: respawn_point.yaw, @@ -2002,7 +2003,6 @@ impl Player { world: &Arc, bed_pos: &BlockPos, facing: HorizontalFacing, - _spawn_angle: f32, ) -> Option> { // Get offsets based on bed facing direction (vanilla-like order) let offsets = Self::get_bed_spawn_offsets(facing); @@ -2427,8 +2427,77 @@ impl Player { self.try_send_client_packet(&packet); } + pub fn process_inbound_packets(&self) { + const MAX_PACKETS_PER_TICK: usize = 64; + + let Some(player_arc) = self.world().get_player_by_uuid(self.gameprofile.id) else { + return; + }; + let Some(server_arc) = self.world().server.upgrade() else { + return; + }; + + let mut count = 0; + + while let Some(packet) = self.inbound_packets.pop() { + if self.client.closed() { + break; + } + + match self.client.as_ref() { + ClientPlatform::Java(client) => { + if let Err(e) = client.handle_play_packet(&player_arc, &server_arc, &packet) { + if e.is_kick() { + if let Some(kick_reason) = e.client_kick_reason() { + client.try_kick(&TextComponent::text(kick_reason)); + } else { + client.try_kick(&TextComponent::text(format!( + "Error while handling incoming packet {e}" + ))); + } + } + tracing::error!( + "Failed to handle play packet id {} (payload {} bytes): {}", + packet.id, + packet.payload.len(), + e + ); + } + + let seq = client.packet_sequence.swap(-1, Ordering::Relaxed); + if seq != -1 { + client.try_send_packet(&CAcknowledgeBlockChange::new(seq.into())); + } + } + ClientPlatform::Bedrock(client) => { + let mut event = crate::plugin::server::packet::PacketReceivedEvent::new( + player_arc.clone(), + packet.id, + packet.payload.clone(), + ); + server_arc + .plugin_manager + .fire_blocking(&server_arc, &mut event); + if !event.cancelled + && let Err(err) = + client.handle_play_packet(&player_arc, &server_arc, &packet) + { + tracing::error!("Failed to handle Bedrock play packet: {err}"); + } + } + } + + count += 1; + if count >= MAX_PACKETS_PER_TICK { + break; + } + } + } + #[expect(clippy::too_many_lines)] pub fn tick<'a>(&'a self, server: &'a Server) { + self.process_inbound_packets(); + if self.is_spectator() { self.living_entity .entity @@ -2451,9 +2520,7 @@ impl Player { if player_pos != target_pos { self.living_entity.entity.set_pos(target_pos); if let Some(p) = self.world().get_player_by_uuid(self.gameprofile.id) { - server.spawn_task(async move { - crate::world::chunker::update_position(&p).await; - }); + crate::world::chunker::update_position(&p); } } } else { @@ -2525,7 +2592,7 @@ impl Player { && !chunk_of_chunks.is_empty() { let client = self.client.clone(); - server.spawn_task(async move { + self.spawn_task(async move { client.send_chunks(&chunk_of_chunks).await; }); if let ClientPlatform::Bedrock(bedrock_client) = self.client.as_ref() @@ -2952,7 +3019,7 @@ impl Player { } } - pub async fn send_stats(&self) { + pub fn send_stats(&self) { if let ClientPlatform::Java(java) = self.client.as_ref() { let packet_stats: Vec = { let stats_guard = self @@ -2974,7 +3041,7 @@ impl Player { stats: &packet_stats, }; if let Ok(data) = java.serialize_packet(&packet) { - java.enqueue_packet(data).await; + java.try_enqueue_packet(data); } } } @@ -3343,11 +3410,10 @@ impl Player { sb.get_entity_team(&self.gameprofile.name).cloned() } - pub async fn set_compass_target(&self, pos: pumpkin_util::math::position::BlockPos) { + pub fn set_compass_target(&self, pos: pumpkin_util::math::position::BlockPos) { use pumpkin_protocol::java::client::play::CPlayerSpawnPosition; self.compass_target.store(Some(pos)); - self.send_client_packet(&CPlayerSpawnPosition::new(pos, 0.0, 0.0, String::new())) - .await; + self.try_send_client_packet(&CPlayerSpawnPosition::new(pos, 0.0, 0.0, String::new())); } pub fn get_compass_target(&self) -> Option { @@ -3636,7 +3702,7 @@ impl Player { self.send_health(); - new_world.send_world_info(&player, position, yaw, pitch).await; + new_world.send_world_info(&player, position, yaw, pitch); } }} } @@ -4025,14 +4091,16 @@ impl Player { self.hunger_manager.level.load() } - pub async fn set_food_level(&self, food_level: u8) { + pub fn set_food_level(&self, food_level: u8) { let mut food_event = crate::plugin::api::events::entity::food_level_change::FoodLevelChangeEvent::new( self.living_entity.entity.entity_id, food_level, ); if let Some(server) = self.world().server.upgrade() { - server.plugin_manager.fire(&server, &mut food_event).await; + server + .plugin_manager + .fire_blocking(&server, &mut food_event); } if food_event.cancelled { return; @@ -4518,7 +4586,7 @@ impl Player { self.world().spawn_entity(item_entity); } - pub async fn drop_held_item(&self, drop_stack: bool) { + pub fn drop_held_item(&self, drop_stack: bool) { let mut item_stack = self.inventory().held_item(); if item_stack.is_empty() { @@ -4537,7 +4605,7 @@ impl Player { dropped_stack.item.registry_key.to_string(), dropped_stack.item_count, ); - server.plugin_manager.fire(&server, &mut event).await; + server.plugin_manager.fire_blocking(&server, &mut event); if event.cancelled { return; } @@ -4564,12 +4632,12 @@ impl Player { } } - pub async fn swap_item(&self) { + pub fn swap_item(&self) { if let Some(server) = self.world().server.upgrade() && let Some(player_arc) = self.world().get_player_by_uuid(self.gameprofile.id) { let mut event = crate::plugin::api::events::player::player_swap_hands::PlayerSwapHandItemsEvent::new(player_arc); - server.plugin_manager.fire(&server, &mut event).await; + server.plugin_manager.fire_blocking(&server, &mut event); if event.cancelled { return; } @@ -5096,7 +5164,7 @@ impl Player { screen_handler.update_sync_handler(self.screen_handler_sync_handler.clone()); } - pub async fn on_rename_item(self: &Arc, packet: SRenameItem<'_>) { + pub fn on_rename_item(self: &Arc, packet: &SRenameItem<'_>) { self.update_last_action_time(); let mut prepare_event = @@ -5108,8 +5176,7 @@ impl Player { if let Some(server) = self.world().server.upgrade() { server .plugin_manager - .fire(&server, &mut prepare_event) - .await; + .fire_blocking(&server, &mut prepare_event); } let screen_handler_arc = self @@ -5284,7 +5351,7 @@ impl Player { } #[allow(clippy::too_many_lines)] - pub async fn on_slot_click(self: &Arc, packet: SClickSlot, server: &Arc) { + pub fn on_slot_click(self: &Arc, packet: SClickSlot, server: &Arc) { self.update_last_action_time(); let ( @@ -5433,7 +5500,7 @@ impl Player { SlotActionType::PickupAll => ClickType::DoubleClick, }; - send_cancellable! {{ + send_cancellable_blocking! {{ server; InventoryClickEvent::new( self, @@ -5459,8 +5526,7 @@ impl Player { if let Some(server) = self.world().server.upgrade() { server .plugin_manager - .fire(&server, &mut interact_event) - .await; + .fire_blocking(&server, &mut interact_event); } if interact_event.cancelled { cancel_screen(); @@ -5482,8 +5548,12 @@ impl Player { stack.item.registry_key.to_string(), ); if let Some(server) = self.world().server.upgrade() { - server.plugin_manager.fire(&server, &mut craft_event).await; - server.plugin_manager.fire(&server, &mut prep_craft).await; + server + .plugin_manager + .fire_blocking(&server, &mut craft_event); + server + .plugin_manager + .fire_blocking(&server, &mut prep_craft); } if craft_event.cancelled || prep_craft.cancelled { cancel_screen(); @@ -5507,8 +5577,12 @@ impl Player { Some(stack.item.registry_key.to_string()), ); if let Some(server) = self.world().server.upgrade() { - server.plugin_manager.fire(&server, &mut smith_event).await; - server.plugin_manager.fire(&server, &mut prep_smith).await; + server + .plugin_manager + .fire_blocking(&server, &mut smith_event); + server + .plugin_manager + .fire_blocking(&server, &mut prep_smith); } if smith_event.cancelled { cancel_screen(); @@ -5534,8 +5608,7 @@ impl Player { if let Some(server) = self.world().server.upgrade() { server .plugin_manager - .fire(&server, &mut extract_event) - .await; + .fire_blocking(&server, &mut extract_event); } } @@ -5550,8 +5623,7 @@ impl Player { if let Some(server) = self.world().server.upgrade() { server .plugin_manager - .fire(&server, &mut prep_grindstone) - .await; + .fire_blocking(&server, &mut prep_grindstone); } } @@ -5562,7 +5634,9 @@ impl Player { if stack.is_empty() { None } else { Some(stack.item.registry_key.to_string()) }, ); if let Some(server) = self.world().server.upgrade() { - server.plugin_manager.fire(&server, &mut prep_result).await; + server + .plugin_manager + .fire_blocking(&server, &mut prep_result); } } @@ -5572,7 +5646,9 @@ impl Player { self.clone(), ); if let Some(server) = self.world().server.upgrade() { - server.plugin_manager.fire(&server, &mut drag_event).await; + server + .plugin_manager + .fire_blocking(&server, &mut drag_event); } if drag_event.cancelled { cancel_screen(); diff --git a/crates/pumpkin/src/entity/projectile/egg.rs b/crates/pumpkin/src/entity/projectile/egg.rs index 80c559dce..c41dc49ac 100644 --- a/crates/pumpkin/src/entity/projectile/egg.rs +++ b/crates/pumpkin/src/entity/projectile/egg.rs @@ -80,8 +80,8 @@ impl EntityBase for EggEntity { ); } - fn tick(&self, caller: &dyn EntityBase, server: &Server) { - self.thrown.process_tick(caller, server); + fn tick(&self, caller: &dyn EntityBase, _server: &Server) { + self.thrown.process_tick(caller); } fn get_entity(&self) -> &Entity { diff --git a/crates/pumpkin/src/entity/projectile/ender_pearl.rs b/crates/pumpkin/src/entity/projectile/ender_pearl.rs index bbccd2c26..063e0ee56 100644 --- a/crates/pumpkin/src/entity/projectile/ender_pearl.rs +++ b/crates/pumpkin/src/entity/projectile/ender_pearl.rs @@ -46,8 +46,8 @@ impl EnderPearlEntity { } impl EntityBase for EnderPearlEntity { - fn tick(&self, caller: &dyn EntityBase, server: &Server) { - self.thrown.process_tick(caller, server); + fn tick(&self, caller: &dyn EntityBase, _server: &Server) { + self.thrown.process_tick(caller); } fn get_entity(&self) -> &Entity { diff --git a/crates/pumpkin/src/entity/projectile/fireball.rs b/crates/pumpkin/src/entity/projectile/fireball.rs index 865ac9687..6bb3f7449 100644 --- a/crates/pumpkin/src/entity/projectile/fireball.rs +++ b/crates/pumpkin/src/entity/projectile/fireball.rs @@ -13,7 +13,6 @@ use crate::{ entity::{ Entity, EntityBase, projectile::{ProjectileHit, ThrownItemEntity}, - projectile_deflection::ProjectileDeflectionType, }, server::Server, }; @@ -145,7 +144,7 @@ impl FireballEntity { self.explosion_power.store(power, Ordering::Relaxed); } - pub fn on_deflection(&self, _deflection: &ProjectileDeflectionType, by_attack: bool) { + pub fn on_deflection(&self, by_attack: bool) { if by_attack { self.set_acceleration_power(INITIAL_ACCELERATION_POWER); } else { @@ -206,7 +205,7 @@ impl EntityBase for FireballEntity { ); } - fn tick(&self, caller: &dyn EntityBase, server: &Server) { + fn tick(&self, caller: &dyn EntityBase, _server: &Server) { let entity = self.get_entity(); let mut velocity = entity.velocity.load(); @@ -227,7 +226,7 @@ impl EntityBase for FireballEntity { entity.velocity.store(velocity); } - self.thrown.process_tick(caller, server); + self.thrown.process_tick(caller); } fn get_entity(&self) -> &Entity { diff --git a/crates/pumpkin/src/entity/projectile/firework_rocket.rs b/crates/pumpkin/src/entity/projectile/firework_rocket.rs index c2da6d3f1..4a3dfc9fb 100644 --- a/crates/pumpkin/src/entity/projectile/firework_rocket.rs +++ b/crates/pumpkin/src/entity/projectile/firework_rocket.rs @@ -91,8 +91,8 @@ impl FireworkRocketEntity { } impl EntityBase for FireworkRocketEntity { - fn tick(&self, caller: &dyn EntityBase, server: &Server) { - self.entity.process_tick(caller, server); + fn tick(&self, caller: &dyn EntityBase, _server: &Server) { + self.entity.process_tick(caller); let entity = self.get_entity(); let world = entity.world.load(); diff --git a/crates/pumpkin/src/entity/projectile/fishing_bobber.rs b/crates/pumpkin/src/entity/projectile/fishing_bobber.rs index 0babe3118..5c3a3b296 100644 --- a/crates/pumpkin/src/entity/projectile/fishing_bobber.rs +++ b/crates/pumpkin/src/entity/projectile/fishing_bobber.rs @@ -91,7 +91,7 @@ impl FishingBobberEntity { } #[expect(clippy::too_many_lines)] - pub fn process_tick(&self, caller: &dyn EntityBase, _server: &Server) { + pub fn process_tick(&self, caller: &dyn EntityBase) { let entity = self.get_entity(); let world = entity.world.load(); @@ -233,7 +233,7 @@ impl EntityBase for FishingBobberEntity { self.has_hit.store(true, Ordering::Relaxed); } - fn tick(&self, caller: &dyn EntityBase, server: &Server) { - self.process_tick(caller, server); + fn tick(&self, caller: &dyn EntityBase, _server: &Server) { + self.process_tick(caller); } } diff --git a/crates/pumpkin/src/entity/projectile/lingering_potion.rs b/crates/pumpkin/src/entity/projectile/lingering_potion.rs index 47c3192f6..69d8eaf98 100644 --- a/crates/pumpkin/src/entity/projectile/lingering_potion.rs +++ b/crates/pumpkin/src/entity/projectile/lingering_potion.rs @@ -83,8 +83,8 @@ impl EntityBase for LingeringPotionEntity { ); } - fn tick(&self, caller: &dyn EntityBase, server: &Server) { - self.thrown.process_tick(caller, server); + fn tick(&self, caller: &dyn EntityBase, _server: &Server) { + self.thrown.process_tick(caller); } fn get_entity(&self) -> &Entity { diff --git a/crates/pumpkin/src/entity/projectile/llama_spit.rs b/crates/pumpkin/src/entity/projectile/llama_spit.rs index 5006367f7..802c9dd3f 100644 --- a/crates/pumpkin/src/entity/projectile/llama_spit.rs +++ b/crates/pumpkin/src/entity/projectile/llama_spit.rs @@ -56,12 +56,12 @@ impl LlamaSpitEntity { } impl EntityBase for LlamaSpitEntity { - fn tick(&self, caller: &dyn EntityBase, server: &Server) { + fn tick(&self, caller: &dyn EntityBase, _server: &Server) { if self.get_entity().touching_water.load(Ordering::Relaxed) { self.get_entity().remove(); return; } - self.thrown.process_tick(caller, server); + self.thrown.process_tick(caller); } fn get_entity(&self) -> &Entity { diff --git a/crates/pumpkin/src/entity/projectile/mod.rs b/crates/pumpkin/src/entity/projectile/mod.rs index 02349c836..eee5e52c6 100644 --- a/crates/pumpkin/src/entity/projectile/mod.rs +++ b/crates/pumpkin/src/entity/projectile/mod.rs @@ -1,5 +1,4 @@ use super::{Entity, EntityBase, living::LivingEntity}; -use crate::server::Server; use pumpkin_data::BlockDirection; use pumpkin_data::entity::EntityType; use pumpkin_protocol::java::client::play::CEntityVelocity; @@ -68,15 +67,7 @@ impl ThrownItemEntity { } } - pub fn set_velocity_from( - &self, - _shooter: &Entity, - pitch: f32, - yaw: f32, - roll: f32, - speed: f32, - divergence: f32, - ) { + pub fn set_velocity_from(&self, pitch: f32, yaw: f32, roll: f32, speed: f32, divergence: f32) { let yaw_rad = yaw.to_radians(); let pitch_rad = pitch.to_radians(); let roll_rad = (pitch + roll).to_radians(); @@ -118,7 +109,7 @@ impl ThrownItemEntity { impl ThrownItemEntity { /// Process a tick for projectile movement and collisions - pub fn process_tick(&self, caller: &dyn EntityBase, _server: &Server) { + pub fn process_tick(&self, caller: &dyn EntityBase) { let entity = self.get_entity(); let world = entity.world.load(); diff --git a/crates/pumpkin/src/entity/projectile/small_fireball.rs b/crates/pumpkin/src/entity/projectile/small_fireball.rs index bc9e203dd..3648579e4 100644 --- a/crates/pumpkin/src/entity/projectile/small_fireball.rs +++ b/crates/pumpkin/src/entity/projectile/small_fireball.rs @@ -36,8 +36,8 @@ impl SmallFireballEntity { } impl EntityBase for SmallFireballEntity { - fn tick(&self, caller: &dyn EntityBase, server: &Server) { - self.thrown.process_tick(caller, server); + fn tick(&self, caller: &dyn EntityBase, _server: &Server) { + self.thrown.process_tick(caller); } fn get_entity(&self) -> &Entity { diff --git a/crates/pumpkin/src/entity/projectile/snowball.rs b/crates/pumpkin/src/entity/projectile/snowball.rs index c96ba6128..abc0909d5 100644 --- a/crates/pumpkin/src/entity/projectile/snowball.rs +++ b/crates/pumpkin/src/entity/projectile/snowball.rs @@ -41,8 +41,8 @@ impl SnowballEntity { } impl EntityBase for SnowballEntity { - fn tick(&self, caller: &dyn EntityBase, server: &Server) { - self.thrown.process_tick(caller, server); + fn tick(&self, caller: &dyn EntityBase, _server: &Server) { + self.thrown.process_tick(caller); } fn get_entity(&self) -> &Entity { diff --git a/crates/pumpkin/src/entity/projectile/splash_potion.rs b/crates/pumpkin/src/entity/projectile/splash_potion.rs index 4ec65fc6d..ebba0ef3b 100644 --- a/crates/pumpkin/src/entity/projectile/splash_potion.rs +++ b/crates/pumpkin/src/entity/projectile/splash_potion.rs @@ -118,8 +118,8 @@ impl EntityBase for SplashPotionEntity { ); } - fn tick(&self, caller: &dyn EntityBase, server: &Server) { - self.thrown.process_tick(caller, server); + fn tick(&self, caller: &dyn EntityBase, _server: &Server) { + self.thrown.process_tick(caller); } fn get_entity(&self) -> &Entity { diff --git a/crates/pumpkin/src/entity/projectile/wind_charge.rs b/crates/pumpkin/src/entity/projectile/wind_charge.rs index 8b25b099c..c963e99e9 100644 --- a/crates/pumpkin/src/entity/projectile/wind_charge.rs +++ b/crates/pumpkin/src/entity/projectile/wind_charge.rs @@ -105,7 +105,6 @@ impl WindChargeEntity { &mut self, deflection: &ProjectileDeflectionType, deflector: Option<&dyn EntityBase>, - _from_attack: bool, ) -> bool { if let Some(cooldown) = self.deflect_cooldown() && cooldown.load(Ordering::Relaxed) > 0 @@ -126,8 +125,8 @@ impl WindChargeEntity { } impl EntityBase for WindChargeEntity { - fn tick(&self, caller: &dyn EntityBase, server: &Server) { - self.thrown_item_entity.process_tick(caller, server); + fn tick(&self, caller: &dyn EntityBase, _server: &Server) { + self.thrown_item_entity.process_tick(caller); if let Some(cooldown) = self.deflect_cooldown() { let cooldown_ticks = cooldown.load(Ordering::Relaxed); diff --git a/crates/pumpkin/src/entity/projectile/wither_skull.rs b/crates/pumpkin/src/entity/projectile/wither_skull.rs index c6e5cb6b1..08cd7a7af 100644 --- a/crates/pumpkin/src/entity/projectile/wither_skull.rs +++ b/crates/pumpkin/src/entity/projectile/wither_skull.rs @@ -101,8 +101,8 @@ impl EntityBase for WitherSkullEntity { ); } - fn tick(&self, caller: &dyn EntityBase, server: &Server) { - self.thrown.process_tick(caller, server); + fn tick(&self, caller: &dyn EntityBase, _server: &Server) { + self.thrown.process_tick(caller); } fn get_entity(&self) -> &Entity { diff --git a/crates/pumpkin/src/entity/tnt.rs b/crates/pumpkin/src/entity/tnt.rs index a928e4af6..332752135 100644 --- a/crates/pumpkin/src/entity/tnt.rs +++ b/crates/pumpkin/src/entity/tnt.rs @@ -29,14 +29,14 @@ impl TNTEntity { } impl EntityBase for TNTEntity { - fn tick(&self, caller: &dyn EntityBase, server: &Server) { + fn tick(&self, caller: &dyn EntityBase, _server: &Server) { let entity = &self.entity; let mut velo = entity.velocity.load(); velo.y -= self.get_gravity(); entity.move_entity(caller, velo); - entity.tick_block_collisions(caller, server); + entity.tick_block_collisions(caller); // Read back what actually happened instead of reusing the pre-move // value: `move_entity` clamps on collision, and an explosion may have diff --git a/crates/pumpkin/src/item/items/egg.rs b/crates/pumpkin/src/item/items/egg.rs index d3bb27ee1..fd60d3751 100644 --- a/crates/pumpkin/src/item/items/egg.rs +++ b/crates/pumpkin/src/item/items/egg.rs @@ -40,8 +40,7 @@ impl ItemBehaviour for EggItem { egg.set_item_stack(item_stack); let (yaw, pitch) = player.rotation(); - egg.thrown - .set_velocity_from(player.get_entity(), pitch, yaw, 0.0, POWER, 1.0); + egg.thrown.set_velocity_from(pitch, yaw, 0.0, POWER, 1.0); world.spawn_entity(Arc::new(egg)); // Consume item diff --git a/crates/pumpkin/src/item/items/ender_pearl.rs b/crates/pumpkin/src/item/items/ender_pearl.rs index 130ae383a..cd1eca857 100644 --- a/crates/pumpkin/src/item/items/ender_pearl.rs +++ b/crates/pumpkin/src/item/items/ender_pearl.rs @@ -41,7 +41,7 @@ impl ItemBehaviour for EnderPearlItem { let (yaw, pitch) = player.rotation(); pearl .thrown - .set_velocity_from(player.get_entity(), pitch, yaw, ROLL, POWER, DIVERGENCE); + .set_velocity_from(pitch, yaw, ROLL, POWER, DIVERGENCE); world.spawn_entity(Arc::new(pearl)); // Consume item diff --git a/crates/pumpkin/src/item/items/potions.rs b/crates/pumpkin/src/item/items/potions.rs index e07a32484..75f484424 100644 --- a/crates/pumpkin/src/item/items/potions.rs +++ b/crates/pumpkin/src/item/items/potions.rs @@ -75,9 +75,7 @@ impl ItemBehaviour for SplashPotionItem { splash.set_item_stack(stack); let (yaw, pitch) = player.rotation(); - splash - .thrown - .set_velocity_from(player.get_entity(), pitch, yaw, 0.0, POWER, 1.0); + splash.thrown.set_velocity_from(pitch, yaw, 0.0, POWER, 1.0); world.spawn_entity(Arc::new(splash)); @@ -129,8 +127,7 @@ impl ItemBehaviour for LingeringPotionItem { ling.set_item_stack(stack); let (yaw, pitch) = player.rotation(); - ling.thrown - .set_velocity_from(player.get_entity(), pitch, yaw, 0.0, POWER, 1.0); + ling.thrown.set_velocity_from(pitch, yaw, 0.0, POWER, 1.0); world.spawn_entity(Arc::new(ling)); diff --git a/crates/pumpkin/src/item/items/snowball.rs b/crates/pumpkin/src/item/items/snowball.rs index 3a8d2ae4e..83094d229 100644 --- a/crates/pumpkin/src/item/items/snowball.rs +++ b/crates/pumpkin/src/item/items/snowball.rs @@ -33,7 +33,7 @@ impl ItemBehaviour for SnowBallItem { let (yaw, pitch) = player.rotation(); snowball .thrown - .set_velocity_from(player.get_entity(), pitch, yaw, 0.0, POWER, 1.0); + .set_velocity_from(pitch, yaw, 0.0, POWER, 1.0); world.spawn_entity(Arc::new(snowball)); // Consume item diff --git a/crates/pumpkin/src/item/items/wind_charge.rs b/crates/pumpkin/src/item/items/wind_charge.rs index 96b54b2f5..702f42604 100644 --- a/crates/pumpkin/src/item/items/wind_charge.rs +++ b/crates/pumpkin/src/item/items/wind_charge.rs @@ -37,7 +37,7 @@ impl ItemBehaviour for WindChargeItem { let wind_charge = ThrownItemEntity::new(entity, player.get_entity(), WIND_CHARGE_GRAVITY); let (yaw, pitch) = player.rotation(); - wind_charge.set_velocity_from(player.get_entity(), pitch, yaw, 0.0, POWER, 1.0); + wind_charge.set_velocity_from(pitch, yaw, 0.0, POWER, 1.0); // TODO: player.incrementStat(Stats.USED) // TODO: Implement that the projectile will explode on impact diff --git a/crates/pumpkin/src/lib.rs b/crates/pumpkin/src/lib.rs index 3a5cb3b97..b607d7a3f 100644 --- a/crates/pumpkin/src/lib.rs +++ b/crates/pumpkin/src/lib.rs @@ -565,26 +565,25 @@ impl PumpkinServer { java_client.start_outgoing_packet_task(); if let Some((player, world)) = server_clone - .add_player(Arc::new(ClientPlatform::Java(java_client)), profile, Some(config)) - .await - { + .add_player(Arc::new(ClientPlatform::Java(java_client)), profile, Some(config)) + { - if let ClientPlatform::Java(client) = player.client.as_ref() { - client.set_player(player.clone()); - } - world - .spawn_java_player(&server_clone.basic_config, &player, &server_clone) - .await; + if let ClientPlatform::Java(client) = player.client.as_ref() { + client.set_player(player.clone()); + } + world + .spawn_java_player(&server_clone.basic_config, &player, &server_clone) + .await; - if let ClientPlatform::Java(client) = player.client.as_ref() { - client.progress_player_packets(&player, &server_clone).await; + if let ClientPlatform::Java(client) = player.client.as_ref() { + client.progress_player_packets(&player, &server_clone).await; - // Close when done - client.close(); - client.await_tasks().await; - } - player.remove().await; - server_clone.remove_player(&player).await; + // Close when done + client.close(); + client.await_tasks().await; + } + player.remove().await; + server_clone.remove_player(&player); if let Err(e) = server_clone .player_data_storage .handle_player_leave(&player) @@ -672,20 +671,17 @@ impl PumpkinServer { client.await_tasks().await; } PacketHandlerResult::ReadyToPlay(profile, config) => { - if let Some((player, _world)) = server - .add_player( - Arc::new(ClientPlatform::Bedrock(client.clone())), - profile, - Some(config), - ) - .await - { + if let Some((player, _world)) = server.add_player( + Arc::new(ClientPlatform::Bedrock(client.clone())), + profile, + Some(config), + ) { client.set_player(player.clone()); - client.progress_player_packets(&player, &server).await; + client.progress_player_packets(&player).await; client.close().await; client.await_tasks().await; player.remove().await; - server.remove_player(&player).await; + server.remove_player(&player); if let Err(error) = server.player_data_storage.handle_player_leave(&player) { error!("Failed to save player data on disconnect: {error}"); diff --git a/crates/pumpkin/src/net/bedrock/login/login.rs b/crates/pumpkin/src/net/bedrock/login/login.rs index 652224495..a457c4030 100644 --- a/crates/pumpkin/src/net/bedrock/login/login.rs +++ b/crates/pumpkin/src/net/bedrock/login/login.rs @@ -21,7 +21,7 @@ impl BedrockClient { let player_data = if server.advanced_config.networking.bedrock.online_mode { match auth_payload.authentication_type { AuthenticationType::Full => { - verify_oidc_token_path(server, &auth_payload.token, false)? + verify_oidc_token_path(server, &auth_payload.token, false).await? } AuthenticationType::SelfSigned => { if server @@ -34,7 +34,7 @@ impl BedrockClient { return Err(LoginError::SelfSignedNotAllowed); } - verify_oidc_token_path(server, &auth_payload.token, true)? + verify_oidc_token_path(server, &auth_payload.token, true).await? } AuthenticationType::Guest => { return Err(LoginError::GuestUnimplemented); diff --git a/crates/pumpkin/src/net/bedrock/login/mod.rs b/crates/pumpkin/src/net/bedrock/login/mod.rs index 1951158c1..b01f71b75 100644 --- a/crates/pumpkin/src/net/bedrock/login/mod.rs +++ b/crates/pumpkin/src/net/bedrock/login/mod.rs @@ -60,27 +60,40 @@ struct AuthPayload { token: String, } -/// Verifies OIDC tokens for Bedrock 1.26.10+ clients. -fn verify_oidc_token_path( +/// Verifies OIDC tokens for Bedrock 1.26.10+ clients on Rayon thread pool. +async fn verify_oidc_token_path( server: &Server, token: &str, self_signed: bool, ) -> Result { + let token = token.to_string(); + let (tx, rx) = tokio::sync::oneshot::channel(); if self_signed { - pumpkin_util::jwt::verify_oidc_token_self_signed(token) - .map_err(LoginError::ChainValidationFailed) + rayon::spawn(move || { + let res = pumpkin_util::jwt::verify_oidc_token_self_signed(&token) + .map_err(LoginError::ChainValidationFailed); + let _ = tx.send(res); + }); } else { let (issuer, jwks) = server .bedrock_oidc_keys .get() + .cloned() .ok_or(LoginError::ChainValidationFailed( AuthError::PublicKeyBuild("OIDC keys not initialized".into()), ))?; - pumpkin_util::jwt::verify_oidc_token(token, issuer, jwks) - .map_err(LoginError::ChainValidationFailed) + rayon::spawn(move || { + let res = pumpkin_util::jwt::verify_oidc_token(&token, &issuer, &jwks) + .map_err(LoginError::ChainValidationFailed); + let _ = tx.send(res); + }); } + + rx.await.map_err(|_| { + LoginError::ChainValidationFailed(AuthError::PublicKeyBuild("Task cancelled".into())) + })? } #[allow(clippy::module_inception)] diff --git a/crates/pumpkin/src/net/bedrock/mod.rs b/crates/pumpkin/src/net/bedrock/mod.rs index 6f73e4388..5ea3e41ac 100644 --- a/crates/pumpkin/src/net/bedrock/mod.rs +++ b/crates/pumpkin/src/net/bedrock/mod.rs @@ -117,7 +117,7 @@ pub struct BedrockClient { pub next_form_id: AtomicU32, pub inventory_opened: AtomicBool, pub client_cache_supported: AtomicBool, - pub blob_cache: Mutex>>, + pub blob_cache: std::sync::Mutex>>, /// An notifier that is triggered when this client is closed. close_token: CancellationToken, last_seen: Arc>, @@ -157,7 +157,7 @@ impl BedrockClient { next_form_id: AtomicU32::new(0), inventory_opened: AtomicBool::new(false), client_cache_supported: AtomicBool::new(false), - blob_cache: Mutex::new(HashMap::new()), + blob_cache: std::sync::Mutex::new(HashMap::new()), close_token: CancellationToken::new(), last_seen: Arc::new(AtomicCell::new(std::time::Instant::now())), incoming_game_packet_send: incoming_send, @@ -371,7 +371,10 @@ impl BedrockClient { }; if !new_blobs.is_empty() { - let mut cache = self.blob_cache.lock().await; + let mut cache = self + .blob_cache + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); for (hash, payload) in new_blobs { cache.insert(hash, payload); } @@ -501,6 +504,13 @@ impl BedrockClient { } } + pub fn try_enqueue_client_packet(&self, packet: &P) { + match self.serialize_packet(packet) { + Ok(data) => self.try_enqueue_packet(data), + Err(err) => error!("Failed to serialize Bedrock packet: {err}"), + } + } + pub async fn send_game_packet(&self, packet_data: Bytes) { let (tx, rx) = oneshot::channel(); if let Err(err) = self @@ -577,17 +587,13 @@ impl BedrockClient { return Err(Error::other("Packet rate limit exceeded")); } - self.handle_game_packet(server, game_packet).await?; + self.handle_game_packet(game_packet).await?; } Ok(()) } - async fn handle_game_packet( - &self, - _server: &Arc, - packet: RawPacket, - ) -> Result<(), Error> { + async fn handle_game_packet(&self, packet: RawPacket) -> Result<(), Error> { if let Err(err) = self.incoming_game_packet_send.send(packet).await { debug!("Failed to send game packet to session task: {err}"); } @@ -642,34 +648,18 @@ impl BedrockClient { PacketHandlerResult::Stop } - pub async fn progress_player_packets( - self: &Arc, - player: &Arc, - server: &Arc, - ) { + pub async fn progress_player_packets(self: &Arc, player: &Arc) { while let Some(packet) = self.get_packet().await { - let mut event = crate::plugin::server::packet::PacketReceivedEvent::new( - player.clone(), - packet.id, - packet.payload.clone(), - ); - server.plugin_manager.fire(server, &mut event).await; - if event.cancelled { - continue; - } - - if let Err(err) = self.handle_play_packet(player, server, packet).await { - error!("Failed to handle Bedrock play packet: {err}"); - } + player.inbound_packets.push(packet); } } #[allow(clippy::too_many_lines)] - pub async fn handle_play_packet( - &self, + pub fn handle_play_packet( + self: &Arc, player: &Arc, server: &Arc, - packet: RawPacket, + packet: &RawPacket, ) -> Result<(), Error> { let payload = &packet.payload[..]; let reader = &mut &payload[..]; @@ -680,42 +670,58 @@ impl BedrockClient { .store(packet.is_cache_supported, Ordering::Relaxed); } SClientCacheBlobStatus::PACKET_ID => { - self.handle_client_cache_blob_status(SClientCacheBlobStatus::read(reader)?) - .await; + let packet = SClientCacheBlobStatus::read(reader)?; + self.handle_client_cache_blob_status(packet); } SResourcePackClientResponse::PACKET_ID => { - self.handle_resource_pack_response(SResourcePackClientResponse::read(reader)?, server) - .await; + let packet = SResourcePackClientResponse::read(reader)?; + let client = self.clone(); + let server_c = server.clone(); + server.spawn_task(async move { + client.handle_resource_pack_response(packet, &server_c).await; + }); } SPlayerAuthInput::PACKET_ID => { - self.handle_player_auth_input(player, SPlayerAuthInput::read(reader)?, server) - .await; + let packet = SPlayerAuthInput::read(reader)?; + self.handle_player_auth_input(player, packet, server); } SRequestChunkRadius::PACKET_ID => { - self.handle_request_chunk_radius(player, SRequestChunkRadius::read(reader)?) - .await; + let packet = SRequestChunkRadius::read(reader)?; + self.handle_request_chunk_radius(player, &packet); } SInventoryTransaction::PACKET_ID => { - self.handle_inventory_action(player, SInventoryTransaction::read(reader)?).await; + let packet = SInventoryTransaction::read(reader)?; + self.handle_inventory_action(player, packet); } pumpkin_protocol::bedrock::server::item_stack_request::SItemStackRequest::PACKET_ID => { - self.handle_item_stack_request(player, pumpkin_protocol::bedrock::server::item_stack_request::SItemStackRequest::read(reader)?).await; + let packet = pumpkin_protocol::bedrock::server::item_stack_request::SItemStackRequest::read(reader)?; + self.handle_item_stack_request(player, packet); } SInteract::PACKET_ID => { - self.handle_interaction(player, SInteract::read(reader)?, server) - .await; + let packet = SInteract::read(reader)?; + self.handle_interaction(&packet); } SContainerClose::PACKET_ID => { - self.handle_container_close(player, SContainerClose::read(reader)?) - .await; + let packet = SContainerClose::read(reader)?; + self.handle_container_close(player, &packet); } SText::PACKET_ID => { - self.handle_chat_message(server, player, SText::read_slice(reader)?) - .await; + let text = SText::read(reader)?; + let client = self.clone(); + let player_c = player.clone(); + let server_c = server.clone(); + player.spawn_task(async move { + client.handle_chat_message(&server_c, &player_c, text).await; + }); } SCommandRequest::PACKET_ID => { - self.handle_chat_command(player, server, SCommandRequest::read_slice(reader)?) - .await; + let req = SCommandRequest::read(reader)?; + let client = self.clone(); + let player_c = player.clone(); + let server_c = server.clone(); + player.spawn_task(async move { + client.handle_chat_command(&player_c, &server_c, req).await; + }); } SSetLocalPlayerAsInitialized::PACKET_ID => { self.handle_set_local_player_as_initialized( @@ -728,47 +734,44 @@ impl BedrockClient { // Ignore for now } SPlayerAction::PACKET_ID => { - self.handle_player_action(player, server, SPlayerAction::read(reader)?) - .await; + let packet = SPlayerAction::read(reader)?; + self.handle_player_action(player, server, packet); } SRespawn::PACKET_ID => { - self.handle_respawn(player, SRespawn::read(reader)?).await; + let packet = SRespawn::read(reader)?; + self.handle_respawn(player, &packet); } SAnimate::PACKET_ID => { - self.handle_animate(player, server, &SAnimate::read(reader)?); + self.handle_animate(player, &SAnimate::read(reader)?); } SActorEvent::PACKET_ID => { self.handle_actor_event(player, &SActorEvent::read(reader)?); } SEmote::PACKET_ID => { - self.handle_emote(player, server, SEmote::read_slice(reader)?); + self.handle_emote(player, SEmote::read_slice(reader)?); } SEmoteList::PACKET_ID => { - self.handle_emote_list(player, server, &SEmoteList::read(reader)?); + self.handle_emote_list(player, &SEmoteList::read(reader)?); } pumpkin_protocol::bedrock::server::modal_form_response::SModalFormResponse::PACKET_ID => { - self.handle_modal_form_response( - player, - server, - pumpkin_protocol::bedrock::server::modal_form_response::SModalFormResponse::read_slice( - reader, - )?, - ) - .await; + let form_resp = pumpkin_protocol::bedrock::server::modal_form_response::SModalFormResponse::read( + reader, + )?; + self.handle_modal_form_response(player, server, form_resp); } SLoadingScreen::PACKET_ID => { // Ignore for now } SBlockPickRequest::PACKET_ID => { - self.handle_block_pick_request(player, SBlockPickRequest::read(reader)?) - .await; + let packet = SBlockPickRequest::read(reader)?; + self.handle_block_pick_request(player, &packet); } SRequestAbility::PACKET_ID => { self.handle_request_ability(player, &SRequestAbility::read(reader)?); } SMobEquipment::PACKET_ID => { - self.handle_mob_equipment(server, player, SMobEquipment::read(reader)?) - .await; + let packet = SMobEquipment::read(reader)?; + self.handle_mob_equipment(server, player, &packet); } SPacketViolationWarning::PACKET_ID => { let warning = SPacketViolationWarning::read(reader)?; @@ -787,12 +790,15 @@ impl BedrockClient { Ok(()) } - pub async fn handle_client_cache_blob_status(&self, packet: SClientCacheBlobStatus) { + pub fn handle_client_cache_blob_status(&self, packet: SClientCacheBlobStatus) { if packet.miss_hashes.is_empty() { return; } let missing_blobs = { - let cache = self.blob_cache.lock().await; + let cache = self + .blob_cache + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); let mut missing_blobs = Vec::with_capacity(packet.miss_hashes.len()); for hash in packet.miss_hashes { if let Some(payload) = cache.get(&hash) { @@ -807,8 +813,7 @@ impl BedrockClient { missing_blobs }; if !missing_blobs.is_empty() { - self.send_packet(&CClientCacheMissResponse { missing_blobs }) - .await; + self.try_enqueue_client_packet(&CClientCacheMissResponse { missing_blobs }); } } diff --git a/crates/pumpkin/src/net/bedrock/nethernet.rs b/crates/pumpkin/src/net/bedrock/nethernet.rs index 7df908d19..8057b844f 100644 --- a/crates/pumpkin/src/net/bedrock/nethernet.rs +++ b/crates/pumpkin/src/net/bedrock/nethernet.rs @@ -256,6 +256,7 @@ async fn negotiate_direct( Box::pin(negotiate_inner(state, address, offer, None, advertised_ip)).await } +#[expect(clippy::too_many_lines)] async fn negotiate_inner( state: &EndpointState, address: SocketAddr, @@ -266,11 +267,17 @@ async fn negotiate_inner( let signaling = if candidates.is_some() { "LAN" } else { "HTTP" }; let direct_ip = candidates.is_none(); trace!(%address, signaling, "Starting NetherNet negotiation"); - let (offer, client_public_key) = authenticate_client_offer( - offer, - state.require_client_identity, - state.oidc_verifier.as_ref().and_then(|c| c.get()), - )?; + let (offer, client_public_key) = { + let offer = offer.to_string(); + let require_identity = state.require_client_identity; + let oidc_verifier = state.oidc_verifier.as_ref().and_then(|c| c.get()).cloned(); + let (tx, rx) = tokio::sync::oneshot::channel(); + rayon::spawn(move || { + let res = authenticate_client_offer(&offer, require_identity, oidc_verifier.as_ref()); + let _ = tx.send(res); + }); + rx.await.map_err(|_| "Task cancelled".to_string())?? + }; trace!( %address, signaling, @@ -357,7 +364,17 @@ async fn negotiate_inner( "Gathered NetherNet ICE candidates" ); trace!(%address, signaling, "Completed NetherNet negotiation"); - Ok((add_server_identity(&answer, &state.identity_key)?, session)) + + let answer = { + let identity_key = state.identity_key.clone(); + let (tx, rx) = tokio::sync::oneshot::channel(); + rayon::spawn(move || { + let res = add_server_identity(&answer, &identity_key); + let _ = tx.send(res); + }); + rx.await.map_err(|_| "Task cancelled".to_string())?? + }; + Ok((answer, session)) } async fn build_peer( diff --git a/crates/pumpkin/src/net/bedrock/nethernet/discovery.rs b/crates/pumpkin/src/net/bedrock/nethernet/discovery.rs index b54e608de..c167ae7bb 100644 --- a/crates/pumpkin/src/net/bedrock/nethernet/discovery.rs +++ b/crates/pumpkin/src/net/bedrock/nethernet/discovery.rs @@ -103,7 +103,7 @@ impl NetherNetDiscovery { let players = server .get_status() .lock() - .await + .unwrap_or_else(std::sync::PoisonError::into_inner) .status_response .players .as_ref() diff --git a/crates/pumpkin/src/net/bedrock/play/animate.rs b/crates/pumpkin/src/net/bedrock/play/animate.rs index 52c8df198..d2dbd9e67 100644 --- a/crates/pumpkin/src/net/bedrock/play/animate.rs +++ b/crates/pumpkin/src/net/bedrock/play/animate.rs @@ -2,7 +2,7 @@ use super::*; impl BedrockClient { - pub fn handle_animate(&self, player: &Arc, _server: &Server, packet: &SAnimate) { + pub fn handle_animate(&self, player: &Arc, packet: &SAnimate) { if !player.has_client_loaded() { return; } diff --git a/crates/pumpkin/src/net/bedrock/play/block_pick_request.rs b/crates/pumpkin/src/net/bedrock/play/block_pick_request.rs index 15bbedce8..3e13ea3fb 100644 --- a/crates/pumpkin/src/net/bedrock/play/block_pick_request.rs +++ b/crates/pumpkin/src/net/bedrock/play/block_pick_request.rs @@ -3,7 +3,7 @@ use super::*; impl BedrockClient { #[allow(clippy::too_many_lines)] - pub async fn handle_block_pick_request(&self, player: &Arc, packet: SBlockPickRequest) { + pub fn handle_block_pick_request(&self, player: &Arc, packet: &SBlockPickRequest) { if !player.can_interact_with_block_at(&packet.position, 1.0) { return; } @@ -60,17 +60,14 @@ impl BedrockClient { player.inventory.set_selected_slot(target_hotbar_slot as u8); // Send hotbar updates - player - .client - .enqueue_packet_editioned( - &CSetSelectedSlot::new(player.inventory.get_selected_slot() as i8), - &CPlayerHotbar { - selected_slot: VarUInt(player.inventory.get_selected_slot() as u32), - container_id: 0, - should_select_slot: true, - }, - ) - .await; + player.client.try_enqueue_packet_editioned( + &CSetSelectedSlot::new(player.inventory.get_selected_slot() as i8), + &CPlayerHotbar { + selected_slot: VarUInt(player.inventory.get_selected_slot() as u32), + container_id: 0, + should_select_slot: true, + }, + ); // Send screen handler / Java inventory updates player @@ -93,7 +90,7 @@ impl BedrockClient { .iter() .map(NetworkItemStackDescriptor::from) .collect(); - self.enqueue_client_packet(&CInventoryContent { + self.try_enqueue_client_packet(&CInventoryContent { container_id: VarUInt(0), slots, full_container_name: FullContainerName { @@ -101,7 +98,6 @@ impl BedrockClient { dynamic_id: None, }, storage_item: NetworkItemStackDescriptor::default(), - }) - .await; + }); } } diff --git a/crates/pumpkin/src/net/bedrock/play/container_close.rs b/crates/pumpkin/src/net/bedrock/play/container_close.rs index 102e7be11..d3296e0c4 100644 --- a/crates/pumpkin/src/net/bedrock/play/container_close.rs +++ b/crates/pumpkin/src/net/bedrock/play/container_close.rs @@ -2,21 +2,20 @@ use super::*; impl BedrockClient { - pub async fn handle_container_close(&self, player: &Arc, packet: SContainerClose) { + pub fn handle_container_close(&self, player: &Arc, packet: &SContainerClose) { if packet.container_id == 0 || packet.container_id == 0xff { self.inventory_opened.store(false, Ordering::Relaxed); } player.on_handled_screen_closed(); - self.enqueue_client_packet(&SContainerClose { + self.try_enqueue_client_packet(&SContainerClose { container_id: packet.container_id, container_type: packet.container_type, server_initiated_close: false, - }) - .await; + }); // Sync the cursor (make it empty) to Bedrock client - self.enqueue_client_packet(&CInventoryContent { + self.try_enqueue_client_packet(&CInventoryContent { container_id: VarUInt(59), // Cursor container ID slots: vec![NetworkItemStackDescriptor::default()], full_container_name: FullContainerName { @@ -24,8 +23,7 @@ impl BedrockClient { dynamic_id: None, }, storage_item: NetworkItemStackDescriptor::default(), - }) - .await; + }); // Sync the inventory content to Bedrock client let slots = player @@ -36,7 +34,7 @@ impl BedrockClient { .iter() .map(NetworkItemStackDescriptor::from) .collect(); - self.enqueue_client_packet(&CInventoryContent { + self.try_enqueue_client_packet(&CInventoryContent { container_id: VarUInt(0), // player inventory slots, full_container_name: FullContainerName { @@ -44,7 +42,6 @@ impl BedrockClient { dynamic_id: None, }, storage_item: NetworkItemStackDescriptor::default(), - }) - .await; + }); } } diff --git a/crates/pumpkin/src/net/bedrock/play/emote.rs b/crates/pumpkin/src/net/bedrock/play/emote.rs index ebe4f7f40..188e61b4c 100644 --- a/crates/pumpkin/src/net/bedrock/play/emote.rs +++ b/crates/pumpkin/src/net/bedrock/play/emote.rs @@ -2,7 +2,7 @@ use super::*; impl BedrockClient { - pub fn handle_emote(&self, player: &Arc, _server: &Server, packet: SEmote<'_>) { + pub fn handle_emote(&self, player: &Arc, packet: SEmote<'_>) { if !player.has_client_loaded() { return; } diff --git a/crates/pumpkin/src/net/bedrock/play/emote_list.rs b/crates/pumpkin/src/net/bedrock/play/emote_list.rs index b304567b5..aeacfb8f3 100644 --- a/crates/pumpkin/src/net/bedrock/play/emote_list.rs +++ b/crates/pumpkin/src/net/bedrock/play/emote_list.rs @@ -2,7 +2,7 @@ use super::*; impl BedrockClient { - pub fn handle_emote_list(&self, player: &Arc, _server: &Server, packet: &SEmoteList) { + pub fn handle_emote_list(&self, player: &Arc, packet: &SEmoteList) { tracing::info!( "handle_emote_list: player={} packet={:?}", player.gameprofile.name, diff --git a/crates/pumpkin/src/net/bedrock/play/interaction.rs b/crates/pumpkin/src/net/bedrock/play/interaction.rs index 746b8b9e1..90dfc9975 100644 --- a/crates/pumpkin/src/net/bedrock/play/interaction.rs +++ b/crates/pumpkin/src/net/bedrock/play/interaction.rs @@ -2,24 +2,18 @@ use super::*; impl BedrockClient { - pub async fn handle_interaction( - &self, - _player: &Arc, - packet: SInteract, - _server: &Arc, - ) { + pub fn handle_interaction(&self, packet: &SInteract) { if matches!(packet.action, Action::OpenInventory) { if self.inventory_opened.load(Ordering::Relaxed) { return; } self.inventory_opened.store(true, Ordering::Relaxed); - self.enqueue_client_packet(&CContainerOpen { + self.try_enqueue_client_packet(&CContainerOpen { container_id: 0, container_type: 0xff, position: BlockPos::ZERO, target_entity_id: VarLong(-1), - }) - .await; + }); } } } diff --git a/crates/pumpkin/src/net/bedrock/play/inventory_action.rs b/crates/pumpkin/src/net/bedrock/play/inventory_action.rs index dfa83e91e..eb12c435f 100644 --- a/crates/pumpkin/src/net/bedrock/play/inventory_action.rs +++ b/crates/pumpkin/src/net/bedrock/play/inventory_action.rs @@ -3,11 +3,7 @@ use super::*; impl BedrockClient { #[allow(clippy::too_many_lines, clippy::collapsible_if, clippy::unreachable)] - pub async fn handle_inventory_action( - &self, - player: &Arc, - packet: SInventoryTransaction, - ) { + pub fn handle_inventory_action(&self, player: &Arc, packet: SInventoryTransaction) { tracing::debug!("handle_inventory_action: packet={:?}", packet); let mut inventory_updated = false; let mut updates = Vec::new(); @@ -129,7 +125,7 @@ impl BedrockClient { .iter() .map(NetworkItemStackDescriptor::from) .collect(); - self.enqueue_client_packet(&CInventoryContent { + self.try_enqueue_client_packet(&CInventoryContent { container_id: VarUInt(0), slots, full_container_name: FullContainerName { @@ -137,8 +133,7 @@ impl BedrockClient { dynamic_id: None, }, storage_item: NetworkItemStackDescriptor::default(), - }) - .await; + }); } match packet.transaction_data { @@ -173,10 +168,10 @@ impl BedrockClient { let client_stack = descriptor_to_stack(&data.item_in_hand); let mut held_item = player.inventory().held_item(); - if !client_stack.is_empty() { - if held_item.is_empty() || held_item.item.id != client_stack.item.id { - held_item = client_stack.clone(); - } + if !client_stack.is_empty() + && (held_item.is_empty() || held_item.item.id != client_stack.item.id) + { + held_item = client_stack; } let result = server.block_registry.use_with_item( @@ -236,18 +231,14 @@ impl BedrockClient { sequence: VarInt(0), }; - if let Ok(Some(_)) = server - .block_registry - .place_block( - player, - placed_block, - &server, - &dummy_use_item_on, - data.block_position, - face, - ) - .await - { + if let Ok(Some(_)) = server.block_registry.place_block( + player, + placed_block, + &server, + &dummy_use_item_on, + data.block_position, + face, + ) { if player.gamemode.load() != GameMode::Creative { stack.decrement(1); } @@ -263,7 +254,7 @@ impl BedrockClient { if !client_stack.is_empty() && (held.is_empty() || held.item.id != client_stack.item.id) { - held = client_stack.clone(); + held = client_stack; player.inventory.set_held_item(held.clone()); } @@ -352,7 +343,7 @@ impl BedrockClient { } } - send_cancellable! {{ + send_cancellable_blocking! {{ &server; event; 'after: { @@ -385,7 +376,7 @@ impl BedrockClient { 1 => { let world = player.world(); if let Some(target) = world.get_entity_by_id(target_runtime_id) { - player.attack(target).await; + player.attack(&target); } } _ => { @@ -450,14 +441,13 @@ impl BedrockClient { } } - self.enqueue_client_packet(&CItemStackResponse { + self.try_enqueue_client_packet(&CItemStackResponse { responses: vec![ItemStackResponseInfo { result, client_request_id: packet.legacy_request_id, containers: container_infos, }], - }) - .await; + }); } } } diff --git a/crates/pumpkin/src/net/bedrock/play/item_stack_request.rs b/crates/pumpkin/src/net/bedrock/play/item_stack_request.rs index acef30981..28bc689c3 100644 --- a/crates/pumpkin/src/net/bedrock/play/item_stack_request.rs +++ b/crates/pumpkin/src/net/bedrock/play/item_stack_request.rs @@ -3,7 +3,7 @@ use super::*; impl BedrockClient { #[allow(clippy::too_many_lines)] - pub async fn handle_item_stack_request( + pub fn handle_item_stack_request( &self, player: &Arc, packet: pumpkin_protocol::bedrock::server::item_stack_request::SItemStackRequest, @@ -520,8 +520,7 @@ impl BedrockClient { }; // Send Bedrock specific responses and updates - self.enqueue_client_packet(&CItemStackResponse { responses }) - .await; + self.try_enqueue_client_packet(&CItemStackResponse { responses }); if inventory_updated { let slots = player @@ -532,7 +531,7 @@ impl BedrockClient { .iter() .map(NetworkItemStackDescriptor::from) .collect(); - self.enqueue_client_packet(&CInventoryContent { + self.try_enqueue_client_packet(&CInventoryContent { container_id: VarUInt(0), slots, full_container_name: FullContainerName { @@ -540,8 +539,7 @@ impl BedrockClient { dynamic_id: None, }, storage_item: NetworkItemStackDescriptor::default(), - }) - .await; + }); } } } diff --git a/crates/pumpkin/src/net/bedrock/play/mob_equipment.rs b/crates/pumpkin/src/net/bedrock/play/mob_equipment.rs index 8d13f3419..8e3d7bca1 100644 --- a/crates/pumpkin/src/net/bedrock/play/mob_equipment.rs +++ b/crates/pumpkin/src/net/bedrock/play/mob_equipment.rs @@ -2,11 +2,11 @@ use super::*; impl BedrockClient { - pub async fn handle_mob_equipment( + pub fn handle_mob_equipment( &self, - _server: &Arc, + server: &Arc, player: &Arc, - packet: SMobEquipment, + packet: &SMobEquipment, ) { player.update_last_action_time(); let slot = packet.selected_slot; @@ -14,23 +14,15 @@ impl BedrockClient { return; } let previous_slot = player.inventory.get_selected_slot(); - let server = player.world().server.upgrade(); - if let Some(server) = server { - let mut event = PlayerItemHeldEvent::new(player.clone(), previous_slot, slot); - server.plugin_manager.fire(&server, &mut event).await; - let is_cancelled = { - use crate::plugin::Cancellable; - event.cancelled() - }; - if is_cancelled { - self.enqueue_client_packet(&CPlayerHotbar { - selected_slot: VarUInt(previous_slot as u32), - container_id: 0, - should_select_slot: true, - }) - .await; - return; - } + let mut event = PlayerItemHeldEvent::new(player.clone(), previous_slot, slot); + server.plugin_manager.fire_blocking(server, &mut event); + if event.cancelled { + self.try_enqueue_client_packet(&CPlayerHotbar { + selected_slot: VarUInt(previous_slot as u32), + container_id: 0, + should_select_slot: true, + }); + return; } let inv = player.inventory(); diff --git a/crates/pumpkin/src/net/bedrock/play/modal_form_response.rs b/crates/pumpkin/src/net/bedrock/play/modal_form_response.rs index 3ac98f3d2..6be7f313f 100644 --- a/crates/pumpkin/src/net/bedrock/play/modal_form_response.rs +++ b/crates/pumpkin/src/net/bedrock/play/modal_form_response.rs @@ -2,7 +2,7 @@ use super::*; impl BedrockClient { - pub async fn handle_modal_form_response( + pub fn handle_modal_form_response( &self, player: &Arc, server: &Arc, @@ -17,6 +17,6 @@ impl BedrockClient { .filter(|data| data != "null") .map(std::borrow::Cow::into_owned), ); - server.plugin_manager.fire(server, &mut event).await; + server.plugin_manager.fire_blocking(server, &mut event); } } diff --git a/crates/pumpkin/src/net/bedrock/play/player_action.rs b/crates/pumpkin/src/net/bedrock/play/player_action.rs index 6ea773434..45b50c0b1 100644 --- a/crates/pumpkin/src/net/bedrock/play/player_action.rs +++ b/crates/pumpkin/src/net/bedrock/play/player_action.rs @@ -4,7 +4,7 @@ use super::*; impl BedrockClient { #[expect(clippy::match_same_arms)] #[expect(clippy::too_many_lines)] - pub async fn handle_player_action( + pub fn handle_player_action( &self, player: &Arc, server: &Server, @@ -169,8 +169,10 @@ impl BedrockClient { } } else { let runtime_id = pumpkin_data::BlockState::to_be_network_id(state.id); - self.enqueue_client_packet(&CUpdateBlock::new(location, runtime_id as u32)) - .await; + self.try_enqueue_client_packet(&CUpdateBlock::new( + location, + runtime_id as u32, + )); if matches!(action, PlayerAction::StopDestroyBlock) { player.stop_mining(); } else { @@ -198,13 +200,16 @@ impl BedrockClient { player.stop_mining(); } PlayerAction::DropItem => { - player.drop_held_item(false).await; + player.drop_held_item(false); } PlayerAction::Respawn if player.living_entity.dead.load(Ordering::Relaxed) || player.living_entity.health.load() <= 0.0 => { - player.world().respawn_player(player, false).await; + let player_c = player.clone(); + player.spawn_task(async move { + player_c.world().respawn_player(&player_c, false).await; + }); } // TODO _ => {} diff --git a/crates/pumpkin/src/net/bedrock/play/player_auth_input.rs b/crates/pumpkin/src/net/bedrock/play/player_auth_input.rs index 2a1a17f85..5af0c7fc1 100644 --- a/crates/pumpkin/src/net/bedrock/play/player_auth_input.rs +++ b/crates/pumpkin/src/net/bedrock/play/player_auth_input.rs @@ -4,7 +4,7 @@ use pumpkin_data::entity::EntityPose; impl BedrockClient { #[expect(clippy::too_many_lines)] - pub async fn handle_player_auth_input( + pub fn handle_player_auth_input( &self, player: &Arc, packet: SPlayerAuthInput, @@ -144,7 +144,7 @@ impl BedrockClient { } if pos_changed { - chunker::update_position(player).await; + chunker::update_position(player); player.progress_motion(delta); } } @@ -178,7 +178,7 @@ impl BedrockClient { .flying }; if !flying { - send_cancellable! {{ + send_cancellable_blocking! {{ server; PlayerToggleFlightEvent::new(player.clone(), true); 'after: { @@ -202,7 +202,7 @@ impl BedrockClient { .flying }; if flying { - send_cancellable! {{ + send_cancellable_blocking! {{ server; PlayerToggleFlightEvent::new(player.clone(), false); 'after: { @@ -219,9 +219,8 @@ impl BedrockClient { } if let Some(block_actions) = packet.block_actions { - for action in block_actions { - self.handle_player_block_action(player, server, action) - .await; + for action in &block_actions { + self.handle_player_block_action(player, server, action); } } } diff --git a/crates/pumpkin/src/net/bedrock/play/player_block_action.rs b/crates/pumpkin/src/net/bedrock/play/player_block_action.rs index 328c78fd7..b3c749be9 100644 --- a/crates/pumpkin/src/net/bedrock/play/player_block_action.rs +++ b/crates/pumpkin/src/net/bedrock/play/player_block_action.rs @@ -2,11 +2,11 @@ use super::*; impl BedrockClient { - pub async fn handle_player_block_action( + pub fn handle_player_block_action( &self, player: &Arc, server: &Server, - packet: pumpkin_protocol::bedrock::server::player_auth_input::PlayerBlockAction, + packet: &pumpkin_protocol::bedrock::server::player_auth_input::PlayerBlockAction, ) { use pumpkin_protocol::bedrock::server::player_action::PlayerActionType as PlayerAction; let Ok(action) = PlayerAction::try_from(packet.action.0) else { @@ -22,7 +22,6 @@ impl BedrockClient { result_pos: BlockPos::ZERO, face: packet.face, }, - ) - .await; + ); } } diff --git a/crates/pumpkin/src/net/bedrock/play/request_chunk_radius.rs b/crates/pumpkin/src/net/bedrock/play/request_chunk_radius.rs index 476c37a9c..32952231c 100644 --- a/crates/pumpkin/src/net/bedrock/play/request_chunk_radius.rs +++ b/crates/pumpkin/src/net/bedrock/play/request_chunk_radius.rs @@ -2,18 +2,13 @@ use super::*; impl BedrockClient { - pub async fn handle_request_chunk_radius( - &self, - player: &Arc, - packet: SRequestChunkRadius, - ) { + pub fn handle_request_chunk_radius(&self, player: &Arc, packet: &SRequestChunkRadius) { let chunk_radius = packet.chunk_radius; if chunk_radius.0 < 1 { - self.kick( + self.try_kick( DisconnectReason::Kicked, "Cannot have zero or negative view distance!".to_string(), - ) - .await; + ); return; } let Some(server) = player.world().server.upgrade() else { @@ -25,10 +20,9 @@ impl BedrockClient { NonZero::::from(server.advanced_config.networking.bedrock.view_distance).get(), ); - self.enqueue_client_packet(&CChunkRadiusUpdated { + self.try_enqueue_client_packet(&CChunkRadiusUpdated { chunk_radius: VarInt(view_distance), - }) - .await; + }); let old_view_distance = { let current_config = player.config.load(); @@ -46,6 +40,6 @@ impl BedrockClient { "Player {} updated their render distance: {} -> {}.", player.gameprofile.name, old_view_distance, view_distance ); - chunker::update_position(player).await; + chunker::update_position(player); } } diff --git a/crates/pumpkin/src/net/bedrock/play/respawn.rs b/crates/pumpkin/src/net/bedrock/play/respawn.rs index 6e65399f2..dc1d89f4b 100644 --- a/crates/pumpkin/src/net/bedrock/play/respawn.rs +++ b/crates/pumpkin/src/net/bedrock/play/respawn.rs @@ -4,7 +4,7 @@ use pumpkin_protocol::bedrock::server::RespawnState; use super::*; impl BedrockClient { - pub async fn handle_respawn(&self, player: &Arc, packet: SRespawn) { + pub fn handle_respawn(&self, player: &Arc, packet: &SRespawn) { if packet.state != RespawnState::ClientReadyToSpawn || (!player.living_entity.dead.load(Ordering::Relaxed) && player.living_entity.health.load() > 0.0) @@ -14,7 +14,7 @@ impl BedrockClient { let entity = player.get_entity(); let position = entity.pos.load(); - self.enqueue_client_packet(&SRespawn { + self.try_enqueue_client_packet(&SRespawn { position: pumpkin_util::math::vector3::Vector3::new( position.x as f32, position.y as f32 + entity.entity_type.eye_height, @@ -22,7 +22,6 @@ impl BedrockClient { ), state: RespawnState::ReadyToSpawn, player_runtime_id: VarULong(player.entity_id() as u64), - }) - .await; + }); } } diff --git a/crates/pumpkin/src/net/bedrock/status.rs b/crates/pumpkin/src/net/bedrock/status.rs index 973343391..ef443d3dc 100644 --- a/crates/pumpkin/src/net/bedrock/status.rs +++ b/crates/pumpkin/src/net/bedrock/status.rs @@ -210,7 +210,7 @@ pub async fn handle_packet( let players = server .get_status() .lock() - .await + .unwrap_or_else(std::sync::PoisonError::into_inner) .status_response .players .as_ref() diff --git a/crates/pumpkin/src/net/java/config/known_packs.rs b/crates/pumpkin/src/net/java/config/known_packs.rs index 5f2ac435d..93195c0b4 100644 --- a/crates/pumpkin/src/net/java/config/known_packs.rs +++ b/crates/pumpkin/src/net/java/config/known_packs.rs @@ -2,11 +2,7 @@ use super::*; impl JavaClient { - pub async fn handle_known_packs( - &self, - _config_acknowledged: SKnownPacks<'_>, - server: &Server, - ) -> Option { + pub async fn handle_known_packs(&self, server: &Server) -> Option { debug!("Handling known packs"); // let mut tags_to_send = Vec::new(); let version = self.version.load(); diff --git a/crates/pumpkin/src/net/java/config/mod.rs b/crates/pumpkin/src/net/java/config/mod.rs index 0c9fe69a8..6167e3ab5 100644 --- a/crates/pumpkin/src/net/java/config/mod.rs +++ b/crates/pumpkin/src/net/java/config/mod.rs @@ -16,7 +16,7 @@ use pumpkin_protocol::{ client::config::{CFeatureFlags, CFinishConfig, CKnownPacks, CRegistryData, CUpdateTags}, server::config::{ ResourcePackResponseResult, SClientInformationConfig, SConfigCookieResponse, - SConfigResourcePack, SKeepAlive, SKnownPacks, SPluginMessage, + SConfigResourcePack, SKeepAlive, SPluginMessage, }, }, }; diff --git a/crates/pumpkin/src/net/java/login/encryption_response.rs b/crates/pumpkin/src/net/java/login/encryption_response.rs index af4e1856c..18fbae44b 100644 --- a/crates/pumpkin/src/net/java/login/encryption_response.rs +++ b/crates/pumpkin/src/net/java/login/encryption_response.rs @@ -66,10 +66,7 @@ impl PendingConnection { }; if server.advanced_config.networking.java.online_mode { - match self - .authenticate(server, &shared_secret, &profile_name) - .await - { + match self.authenticate(server, &shared_secret, &profile_name) { Ok(new_profile) => self.gameprofile = Some(new_profile), Err(error) => { self.kick(match error { @@ -171,13 +168,13 @@ impl PendingConnection { Some(PacketHandlerResult::ReadyToPlay(profile.clone(), config)) } - async fn authenticate( + fn authenticate( &self, server: &Server, shared_secret: &[u8], username: &str, ) -> Result { - let hash = server.digest_secret(shared_secret).await; + let hash = server.digest_secret(shared_secret); let ip = self.address.ip(); let profile = authentication::authenticate( username, diff --git a/crates/pumpkin/src/net/java/login/known_packs.rs b/crates/pumpkin/src/net/java/login/known_packs.rs index 801a4d015..09c57893a 100644 --- a/crates/pumpkin/src/net/java/login/known_packs.rs +++ b/crates/pumpkin/src/net/java/login/known_packs.rs @@ -2,7 +2,7 @@ use super::*; impl PendingConnection { - pub async fn handle_known_packs(&mut self, _packet: SKnownPacks<'_>, _server: &Server) { + pub async fn handle_known_packs(&mut self) { let version = self.version.load(); if version.supports_configuration_state() { self.send_packet_now(&CFeatureFlags::new(&["minecraft:vanilla".to_string()])) diff --git a/crates/pumpkin/src/net/java/login/login_acknowledged.rs b/crates/pumpkin/src/net/java/login/login_acknowledged.rs index 0f6bab235..725d42c47 100644 --- a/crates/pumpkin/src/net/java/login/login_acknowledged.rs +++ b/crates/pumpkin/src/net/java/login/login_acknowledged.rs @@ -99,13 +99,7 @@ impl PendingConnection { } else if self.version.load() >= JavaMinecraftVersion::V_1_20_5 { self.send_known_packs().await; } else { - self.handle_known_packs( - SKnownPacks { - known_packs: Vec::new(), - }, - server, - ) - .await; + self.handle_known_packs().await; } debug!("login acknowledged"); None diff --git a/crates/pumpkin/src/net/java/login/mod.rs b/crates/pumpkin/src/net/java/login/mod.rs index 011ac601b..b974b3cb9 100644 --- a/crates/pumpkin/src/net/java/login/mod.rs +++ b/crates/pumpkin/src/net/java/login/mod.rs @@ -9,7 +9,6 @@ use pumpkin_protocol::{ }, login::{CLoginSuccess, CSetCompression}, }, - java::server::config::SKnownPacks, java::server::login::{ SEncryptionResponse, SLoginCookieResponse, SLoginPluginResponse, SLoginStart, }, diff --git a/crates/pumpkin/src/net/java/mod.rs b/crates/pumpkin/src/net/java/mod.rs index ad4f2e6ce..3c7e3348a 100644 --- a/crates/pumpkin/src/net/java/mod.rs +++ b/crates/pumpkin/src/net/java/mod.rs @@ -1,6 +1,5 @@ use pumpkin_protocol::java::client::play::{ - CAcknowledgeBlockChange, CChunkBatchEnd, CChunkBatchStart, CChunkData, CLightUpdate, - CPlayDisconnect, + CChunkBatchEnd, CChunkBatchStart, CChunkData, CLightUpdate, CPlayDisconnect, }; use pumpkin_world::level::SyncChunk; use std::net::SocketAddr; @@ -65,7 +64,9 @@ use arc_swap::ArcSwap; use pending::PendingConnection; use crate::entity::player::Player; -use crate::net::{GameProfile, PacketHandlerResult, PacketRateLimiter, PlayerConfig}; +use crate::net::{ + ClientPlatform, GameProfile, PacketHandlerResult, PacketRateLimiter, PlayerConfig, +}; use crate::plugin::api::events::world::chunk_send::ChunkSend; use crate::plugin::player::player_custom_payload::PlayerCustomPayloadEvent; use crate::{error::PumpkinError, server::Server}; @@ -189,7 +190,6 @@ impl JavaClient { self.player.store(Arc::new(Some(player))); } - #[expect(clippy::too_many_lines)] pub async fn progress_player_packets(&self, player: &Arc, server: &Arc) { let Some(mut network_reader) = self .network_reader @@ -251,6 +251,10 @@ impl JavaClient { self.enqueue_client_packet(&packet).await; } + () = self.close_token.cancelled() => { + break; + } + // INCOMING PACKETS packet_opt = self.get_packet_with_reader(&mut network_reader) => { let Some(packet) = packet_opt else { @@ -278,36 +282,7 @@ impl JavaClient { break; } - match self.handle_play_packet(player, server, &packet).await { - Ok(()) => {} - Err(e) => { - if e.is_kick() { - if let Some(kick_reason) = e.client_kick_reason() { - self.kick(TextComponent::text(kick_reason)).await; - } else { - self.kick(TextComponent::text(format!( - "Error while handling incoming packet {e}" - ))) - .await; - } - } - error!( - "Failed to handle play packet id {} (payload {} bytes): {}", - packet.id, - packet.payload.len(), - e - ); - } - } - - // ServerGamePacketListenerImpl acknowledges the sequence at the end of the - // packet that carried it. Until we do, the client keeps predicting the block - // it interacted with and drops our updates for that position - let seq = self.packet_sequence.swap(-1, Ordering::Relaxed); - if seq != -1 { - self.send_packet(&CAcknowledgeBlockChange::new(seq.into())) - .await; - } + player.inbound_packets.push(packet); } } } @@ -613,6 +588,12 @@ impl JavaClient { Self::serialize_packet_for_version(packet, self.version.load()) } + pub fn try_send_packet(&self, packet: &P) { + if let Ok(data) = self.serialize_packet(packet) { + self.try_enqueue_packet(data); + } + } + pub async fn send_packet(&self, packet: &P) { if let Ok(data) = self.serialize_packet(packet) { self.send_packet_now(data).await; @@ -747,7 +728,7 @@ impl JavaClient { } #[expect(clippy::too_many_lines)] - pub async fn handle_play_packet( + pub fn handle_play_packet( &self, player: &Arc, server: &Arc, @@ -760,7 +741,7 @@ impl JavaClient { packet.id, packet.payload.clone(), ); - server.plugin_manager.fire(server, &mut event).await; + server.plugin_manager.fire_blocking(server, &mut event); if event.cancelled { return Ok(()); } @@ -770,9 +751,8 @@ impl JavaClient { id if id == SConfirmTeleport::to_id(version) => { self.handle_confirm_teleport( player, - SConfirmTeleport::read(&mut payload, &version)?, - ) - .await; + &SConfirmTeleport::read(&mut payload, &version)?, + ); } id if id == SChangeGameMode::to_id(version) => { self.handle_change_game_mode( @@ -782,84 +762,108 @@ impl JavaClient { } id if id == SChatAck::to_id(version) => { let packet = SChatAck::read(&mut payload, &version)?; - self.handle_chat_ack(player, &packet).await; + self.handle_chat_ack(player, &packet); } id if id == SChatCommand::to_id(version) => { - self.handle_chat_command( - player, - server, - &(SChatCommand::read(&mut payload, &version)?), - ) - .await; + let packet = SChatCommand::read(&mut payload, &version)?; + let cmd = packet.command.to_string(); + let client_platform = player.client.clone(); + let player_c = player.clone(); + let server_c = server.clone(); + server.spawn_task(async move { + if let ClientPlatform::Java(client) = client_platform.as_ref() { + let packet = SChatCommand { command: &cmd }; + client + .handle_chat_command(&player_c, &server_c, &packet) + .await; + } + }); } id if id == SChatCommandSigned::to_id(version) => { let signed = SChatCommandSigned::read(&mut payload, &version)?; - self.handle_chat_command( - player, - server, - &SChatCommand { - command: signed.command, - }, - ) - .await; + let cmd = signed.command.to_string(); + let client_platform = player.client.clone(); + let player_c = player.clone(); + let server_c = server.clone(); + server.spawn_task(async move { + if let ClientPlatform::Java(client) = client_platform.as_ref() { + let packet = SChatCommand { command: &cmd }; + client + .handle_chat_command(&player_c, &server_c, &packet) + .await; + } + }); } id if id == SChatMessage::to_id(version) => { - self.handle_chat_message( - server, - player, - SChatMessage::read(&mut payload, &version)?, - ) - .await; + let packet = SChatMessage::read(&mut payload, &version)?; + let msg = packet.message.to_string(); + let signature = packet.signature.map(<[u8]>::to_vec); + let ack = packet.acknowledged.to_vec(); + let ts = packet.timestamp; + let salt = packet.salt; + let count = packet.message_count; + let checksum = packet.checksum; + let client_platform = player.client.clone(); + let player_c = player.clone(); + let server_c = server.clone(); + server.spawn_task(async move { + if let ClientPlatform::Java(client) = client_platform.as_ref() { + let packet = SChatMessage { + message: &msg, + timestamp: ts, + salt, + signature: signature.as_deref(), + message_count: count, + acknowledged: &ack, + checksum, + }; + client + .handle_chat_message(&server_c, &player_c, packet) + .await; + } + }); } id if id == SClientInformationPlay::to_id(version) => { self.handle_client_information( server, player, - SClientInformationPlay::read(&mut payload, &version)?, - ) - .await; + &SClientInformationPlay::read(&mut payload, &version)?, + ); } id if id == SClientCommand::to_id(version) => { - self.handle_client_status(player, SClientCommand::read(&mut payload, &version)?) - .await; + self.handle_client_status(player, &SClientCommand::read(&mut payload, &version)?); } id if id == SPlayerInput::to_id(version) => { self.handle_player_input( player, - SPlayerInput::read(&mut payload, &version)?, + &SPlayerInput::read(&mut payload, &version)?, server, - ) - .await; + ); } id if id == SMoveVehicle::to_id(version) => { - self.handle_move_vehicle(player, SMoveVehicle::read(&mut payload, &version)?) - .await; + self.handle_move_vehicle(player, &SMoveVehicle::read(&mut payload, &version)?); } id if id == SPaddleBoat::to_id(version) => { self.handle_paddle_boat(player, &SPaddleBoat::read(&mut payload, &version)?); } id if id == SInteract::to_id(version) => { - self.handle_interact(player, SInteract::read(&mut payload, &version)?, server) - .await; + self.handle_interact(player, &SInteract::read(&mut payload, &version)?, server); } id if id == SBundleItemSelected::to_id(version) => { self.handle_bundle_item_selected( player, - SBundleItemSelected::read(&mut payload, &version)?, - ) - .await; + &SBundleItemSelected::read(&mut payload, &version)?, + ); } id if id == SAttack::to_id(version) => { - self.handle_attack(player, SAttack::read(&mut payload, &version)?, server) - .await; + self.handle_attack(player, &SAttack::read(&mut payload, &version)?, server); } id if id == STeleportToEntity::to_id(version) => { self.handle_teleport_to_entity( player, - STeleportToEntity::read(&mut payload, &version)?, + &STeleportToEntity::read(&mut payload, &version)?, server, - ) - .await; + ); } id if id == pumpkin_protocol::java::server::play::SKeepAlive::to_id(version) => { self.handle_keep_alive( @@ -898,21 +902,18 @@ impl JavaClient { self.handle_position( player, server, - SPlayerPosition::read(&mut payload, &version)?, - ) - .await; + &SPlayerPosition::read(&mut payload, &version)?, + ); } id if id == SPlayerPositionRotation::to_id(version) => { self.handle_position_rotation( player, server, - SPlayerPositionRotation::read(&mut payload, &version)?, - ) - .await; + &SPlayerPositionRotation::read(&mut payload, &version)?, + ); } id if id == SPlayerRotation::to_id(version) => { - self.handle_rotation(player, SPlayerRotation::read(&mut payload, &version)?) - .await; + self.handle_rotation(player, &SPlayerRotation::read(&mut payload, &version)?); } id if id == SSetPlayerGround::to_id(version) => { self.handle_player_ground(player, &SSetPlayerGround::read(&mut payload, &version)?); @@ -920,44 +921,39 @@ impl JavaClient { id if id == SPickItemFromBlock::to_id(version) => { self.handle_pick_item_from_block( player, - SPickItemFromBlock::read(&mut payload, &version)?, - ) - .await; + &SPickItemFromBlock::read(&mut payload, &version)?, + ); } id if id == pumpkin_protocol::java::server::play::SPickItemFromEntity::to_id(version) => { self.handle_pick_item_from_entity( player, - pumpkin_protocol::java::server::play::SPickItemFromEntity::read( + &pumpkin_protocol::java::server::play::SPickItemFromEntity::read( &mut payload, &version, )?, - ) - .await; + ); } id if id == SPlayerAbilities::to_id(version) => { self.handle_player_abilities( player, - SPlayerAbilities::read(&mut payload, &version)?, + &SPlayerAbilities::read(&mut payload, &version)?, server, - ) - .await; + ); } id if id == SPlayerAction::to_id(version) => { self.handle_player_action( player, - SPlayerAction::read(&mut payload, &version)?, + &SPlayerAction::read(&mut payload, &version)?, server, - ) - .await; + ); } id if id == SSetCommandBlock::to_id(version) => { self.handle_set_command_block( player, - SSetCommandBlock::read(&mut payload, &version)?, - ) - .await; + &SSetCommandBlock::read(&mut payload, &version)?, + ); } id if id == SSetJigsawBlock::to_id(version) => { self.handle_set_jigsaw_block( @@ -974,22 +970,18 @@ impl JavaClient { id if id == SPlayerCommand::to_id(version) => { self.handle_player_command( player, - SPlayerCommand::read(&mut payload, &version)?, + &SPlayerCommand::read(&mut payload, &version)?, server, - ) - .await; + ); } id if id == SPlayerLoaded::to_id(version) => { Self::handle_player_loaded(player); } id if id == SPlayPingRequest::to_id(version) => { - self.handle_play_ping_request(SPlayPingRequest::read(&mut payload, &version)?) - .await; + self.handle_play_ping_request(&SPlayPingRequest::read(&mut payload, &version)?); } id if id == SClickSlot::to_id(version) => { - player - .on_slot_click(SClickSlot::read(&mut payload, &version)?, server) - .await; + player.on_slot_click(SClickSlot::read(&mut payload, &version)?, server); } id if id == SContainerButtonClick::to_id(version) => { player.on_container_button_click(&SContainerButtonClick::read( @@ -1001,64 +993,63 @@ impl JavaClient { self.handle_set_held_item( server, player, - SSetHeldItem::read(&mut payload, &version)?, - ) - .await; + &SSetHeldItem::read(&mut payload, &version)?, + ); } id if id == SSetCreativeSlot::to_id(version) => { self.handle_set_creative_slot( player, SSetCreativeSlot::read(&mut payload, &version)?, - ) - .await?; + )?; } id if id == SSwingArm::to_id(version) => { - self.handle_swing_arm(server, player, SSwingArm::read(&mut payload, &version)?) - .await; + self.handle_swing_arm(server, player, &SSwingArm::read(&mut payload, &version)?); } id if id == SUpdateSign::to_id(version) => { - self.handle_sign_update(player, SUpdateSign::read(&mut payload, &version)?) - .await; + self.handle_sign_update(player, &SUpdateSign::read(&mut payload, &version)?); } id if id == SEditBook::to_id(version) => { self.handle_edit_book(player, &SEditBook::read(&mut payload, &version)?); } id if id == SUseItemOn::to_id(version) => { - self.handle_use_item_on(player, SUseItemOn::read(&mut payload, &version)?, server) - .await?; + self.handle_use_item_on( + player, + &SUseItemOn::read(&mut payload, &version)?, + server, + )?; } id if id == SUseItem::to_id(version) => { - self.handle_use_item(player, &SUseItem::read(&mut payload, &version)?, server) - .await; + self.handle_use_item(player, &SUseItem::read(&mut payload, &version)?, server); } id if id == SCommandSuggestion::to_id(version) => { self.handle_command_suggestion( player, - SCommandSuggestion::read(&mut payload, &version)?, + &SCommandSuggestion::read(&mut payload, &version)?, server, - ) - .await; + ); } id if id == SPCookieResponse::to_id(version) => { self.handle_cookie_response(&SPCookieResponse::read(&mut payload, &version)?); } id if id == SCloseContainer::to_id(version) => { - self.handle_close_container( - player, - server, - SCloseContainer::read(&mut payload, &version)?, - ); + let _ = SCloseContainer::read(&mut payload, &version)?; + self.handle_close_container(player); } id if id == SChunkBatch::to_id(version) => { self.handle_chunk_batch(player, &SChunkBatch::read(&mut payload, &version)?); } id if id == SPlayerSession::to_id(version) => { - self.handle_chat_session_update( - player, - server, - SPlayerSession::read(&mut payload, &version)?, - ) - .await; + let session = SPlayerSession::read(&mut payload, &version)?; + let client_platform = player.client.clone(); + let player_c = player.clone(); + let server_c = server.clone(); + server.spawn_task(async move { + if let ClientPlatform::Java(client) = client_platform.as_ref() { + client + .handle_chat_session_update(&player_c, &server_c, session) + .await; + } + }); } id if id == SCustomPayload::to_id(version) => { let payload = SCustomPayload::read(&mut payload, &version)?; @@ -1067,32 +1058,28 @@ impl JavaClient { payload.channel.to_string(), Bytes::copy_from_slice(payload.data), ); - server.plugin_manager.fire(server, &mut event).await; + server.plugin_manager.fire_blocking(server, &mut event); } id if id == SRecipeBookChangeSettings::to_id(version) => { self.handle_recipe_book_change_settings( server, player, - SRecipeBookChangeSettings::read(&mut payload, &version)?, - ) - .await; + &SRecipeBookChangeSettings::read(&mut payload, &version)?, + ); } id if id == SRecipeBookSeenRecipe::to_id(version) => { self.handle_recipe_book_seen_recipe( server, player, - SRecipeBookSeenRecipe::read(&mut payload, &version)?, - ) - .await; + &SRecipeBookSeenRecipe::read(&mut payload, &version)?, + ); } id if id == SRenameItem::to_id(version) => { - player - .on_rename_item(SRenameItem::read(&mut payload, &version)?) - .await; + player.on_rename_item(&SRenameItem::read(&mut payload, &version)?); } id if id == SPlaceRecipe::to_id(version) => { let packet = SPlaceRecipe::read(&mut payload, &version)?; - self.handle_place_recipe(server, player, packet).await; + self.handle_place_recipe(server, player, &packet); } id if id == pumpkin_protocol::java::server::play::SCustomClickAction::to_id(version) => @@ -1106,25 +1093,23 @@ impl JavaClient { packet.action_id.to_string(), packet.payload.map(Bytes::copy_from_slice), ); - server.plugin_manager.fire(server, &mut event).await; + server.plugin_manager.fire_blocking(server, &mut event); } id if id == SSelectTrade::to_id(version) => { - self.handle_select_trade(player, SSelectTrade::read(&mut payload, &version)?) - .await; + self.handle_select_trade(player, &SSelectTrade::read(&mut payload, &version)?); } id if id == SSeenAdvancement::to_id(version) => { self.handle_seen_advancement( player, - SSeenAdvancement::read(&mut payload, &version)?, + &SSeenAdvancement::read(&mut payload, &version)?, ); } id if id == SPlayResourcePack::to_id(version) => { self.handle_play_resource_pack_response( server, player, - SPlayResourcePack::read(&mut payload, &version)?, - ) - .await; + &SPlayResourcePack::read(&mut payload, &version)?, + ); } id if id == SPlayPong::to_id(version) => { self.handle_play_pong(player, &SPlayPong::read(&mut payload, &version)?); @@ -1144,8 +1129,7 @@ impl JavaClient { ); } id if id == SSetBeacon::to_id(version) => { - self.handle_set_beacon(player, &SSetBeacon::read(&mut payload, &version)?) - .await; + self.handle_set_beacon(player, &SSetBeacon::read(&mut payload, &version)?); } id if id == SContainerSlotStateChanged::to_id(version) => { self.handle_container_slot_state_changed( @@ -1157,9 +1141,8 @@ impl JavaClient { self.handle_spectate_entity( player, server, - SSpectateEntity::read(&mut payload, &version)?, - ) - .await; + &SSpectateEntity::read(&mut payload, &version)?, + ); } id if id == SSetCommandMinecart::to_id(version) => { self.handle_set_command_minecart( @@ -1179,16 +1162,14 @@ impl JavaClient { id if id == SBlockEntityTagQuery::to_id(version) => { self.handle_block_entity_tag_query( player, - SBlockEntityTagQuery::read(&mut payload, &version)?, - ) - .await; + &SBlockEntityTagQuery::read(&mut payload, &version)?, + ); } id if id == SEntityTagQuery::to_id(version) => { self.handle_entity_tag_query( player, - SEntityTagQuery::read(&mut payload, &version)?, - ) - .await; + &SEntityTagQuery::read(&mut payload, &version)?, + ); } id if id == SConfigurationAcknowledged::to_id(version) => { self.handle_configuration_acknowledged(player); diff --git a/crates/pumpkin/src/net/java/pending.rs b/crates/pumpkin/src/net/java/pending.rs index cc8681c35..7bb64b37b 100644 --- a/crates/pumpkin/src/net/java/pending.rs +++ b/crates/pumpkin/src/net/java/pending.rs @@ -418,8 +418,7 @@ impl PendingConnection { } } id if id == SKnownPacks::to_id(version) => { - self.handle_known_packs(SKnownPacks::read(&mut payload, &version)?, server) - .await; + self.handle_known_packs().await; Ok(None) } id if id == SConfigResourcePack::to_id(version) => { diff --git a/crates/pumpkin/src/net/java/play/attack.rs b/crates/pumpkin/src/net/java/play/attack.rs index 88f84fbc7..e69e75c32 100644 --- a/crates/pumpkin/src/net/java/play/attack.rs +++ b/crates/pumpkin/src/net/java/play/attack.rs @@ -2,7 +2,7 @@ use super::*; impl JavaClient { - pub async fn handle_attack(&self, player: &Arc, attack: SAttack, server: &Arc) { + pub fn handle_attack(&self, player: &Arc, attack: &SAttack, server: &Arc) { if !player.has_client_loaded() { return; } @@ -17,12 +17,11 @@ impl JavaClient { } if entity_id.0 == player.entity_id() { - self.kick(TextComponent::translate_cross( + self.try_kick(&TextComponent::translate_cross( translation::java::MULTIPLAYER_DISCONNECT_INVALID_ENTITY_ATTACKED, translation::java::MULTIPLAYER_DISCONNECT_INVALID_ENTITY_ATTACKED, [], - )) - .await; + )); return; } @@ -32,12 +31,11 @@ impl JavaClient { .map(|p| Arc::clone(p) as Arc) .or_else(|| world.get_entity_by_id(entity_id.0)); let Some(target) = target else { - self.kick(TextComponent::translate_cross( + self.try_kick(&TextComponent::translate_cross( translation::java::MULTIPLAYER_DISCONNECT_INVALID_ENTITY_ATTACKED, translation::java::MULTIPLAYER_DISCONNECT_INVALID_ENTITY_ATTACKED, [], - )) - .await; + )); return; }; if let Some(player_victim) = &player_target { @@ -53,6 +51,6 @@ impl JavaClient { return; } } - player.attack(target).await; + player.attack(&target); } } diff --git a/crates/pumpkin/src/net/java/play/bundle_item_selected.rs b/crates/pumpkin/src/net/java/play/bundle_item_selected.rs index 7e1a62b87..854d77d56 100644 --- a/crates/pumpkin/src/net/java/play/bundle_item_selected.rs +++ b/crates/pumpkin/src/net/java/play/bundle_item_selected.rs @@ -2,11 +2,7 @@ use super::*; impl JavaClient { - pub async fn handle_bundle_item_selected( - &self, - player: &Arc, - packet: SBundleItemSelected, - ) { + pub fn handle_bundle_item_selected(&self, player: &Arc, packet: &SBundleItemSelected) { if !player.has_client_loaded() { return; } @@ -14,8 +10,7 @@ impl JavaClient { let selected_item_index = packet.selected_item_index.0; if selected_item_index < 0 && selected_item_index != -1 { - self.kick(TextComponent::text("Invalid selected item index")) - .await; + self.try_kick(&TextComponent::text("Invalid selected item index")); return; } diff --git a/crates/pumpkin/src/net/java/play/chat_ack.rs b/crates/pumpkin/src/net/java/play/chat_ack.rs index ffb7f3bc2..6b533e391 100644 --- a/crates/pumpkin/src/net/java/play/chat_ack.rs +++ b/crates/pumpkin/src/net/java/play/chat_ack.rs @@ -3,19 +3,18 @@ use super::*; use pumpkin_protocol::java::server::play::SChatAck; impl JavaClient { - pub async fn handle_chat_ack(&self, player: &Arc, packet: &SChatAck) { + pub fn handle_chat_ack(&self, player: &Arc, packet: &SChatAck) { let offset = packet.offset.0; if offset < 0 { warn!( "Failed to validate message acknowledgement offset from {}: negative offset {}", player.gameprofile.name, offset ); - self.kick(TextComponent::translate_cross( + self.try_kick(&TextComponent::translate_cross( translation::java::MULTIPLAYER_DISCONNECT_CHAT_VALIDATION_FAILED, translation::java::MULTIPLAYER_DISCONNECT_CHAT_VALIDATION_FAILED, [], - )) - .await; + )); return; } @@ -35,12 +34,11 @@ impl JavaClient { "Failed to validate message acknowledgement offset from {}: {}", player.gameprofile.name, err ); - self.kick(TextComponent::translate_cross( + self.try_kick(&TextComponent::translate_cross( translation::java::MULTIPLAYER_DISCONNECT_CHAT_VALIDATION_FAILED, translation::java::MULTIPLAYER_DISCONNECT_CHAT_VALIDATION_FAILED, [], - )) - .await; + )); return; } diff --git a/crates/pumpkin/src/net/java/play/chat_message.rs b/crates/pumpkin/src/net/java/play/chat_message.rs index a55ed81e2..c2b6c103b 100644 --- a/crates/pumpkin/src/net/java/play/chat_message.rs +++ b/crates/pumpkin/src/net/java/play/chat_message.rs @@ -192,7 +192,7 @@ impl JavaClient { return; } - if let Err(err) = self.validate_chat_session(player, server, &session) { + if let Err(err) = self.validate_chat_session(player, server, &session).await { log_at_level!( err.severity(), "{} (uuid {}) {}", @@ -234,7 +234,7 @@ impl JavaClient { } /// Runs vanilla checks for a valid player session - pub fn validate_chat_session( + pub async fn validate_chat_session( &self, player: &Player, server: &Server, @@ -257,13 +257,17 @@ impl JavaClient { signable.extend_from_slice(&session.expires_at.to_be_bytes()); signable.extend_from_slice(&session.public_key); - let public_keys_guard = server.mojang_public_keys.load(); + let public_keys = server.mojang_public_keys.load_full(); - // Verify signature with RSA-SHA1 - let is_valid = public_keys_guard.iter().any(|key| { - let verifying_key = VerifyingKey::::new(key.clone()); - verifying_key.verify(&signable, &key_signature).is_ok() + let (tx, rx) = tokio::sync::oneshot::channel(); + rayon::spawn(move || { + let is_valid = public_keys.iter().any(|key| { + let verifying_key = VerifyingKey::::new(key.clone()); + verifying_key.verify(&signable, &key_signature).is_ok() + }); + let _ = tx.send(is_valid); }); + let is_valid = rx.await.unwrap_or(false); // Verify that the signable is valid for any one of Mojang's public keys if !is_valid { diff --git a/crates/pumpkin/src/net/java/play/client_command.rs b/crates/pumpkin/src/net/java/play/client_command.rs index e95baee38..6c323785d 100644 --- a/crates/pumpkin/src/net/java/play/client_command.rs +++ b/crates/pumpkin/src/net/java/play/client_command.rs @@ -4,7 +4,7 @@ use pumpkin_data::game_rules::GameRule; use pumpkin_protocol::java::client::play::CGameRuleValues; impl JavaClient { - pub async fn handle_client_status(&self, player: &Arc, client_status: SClientCommand) { + pub fn handle_client_status(&self, player: &Arc, client_status: &SClientCommand) { player.update_last_action_time(); match client_status.action_id.0 { SClientCommand::PERFORM_RESPAWN => { @@ -12,44 +12,53 @@ impl JavaClient { if player.living_entity.health.load() > 0.0 { return; } - player.world().clone().respawn_player(player, false).await; - - { - let screen_handler = player - .current_screen_handler - .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner); - let mut screen_handler = screen_handler - .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner); - screen_handler.sync_state(); + let player_c = player.clone(); + let Some(server) = player.world().server.upgrade() else { + return; }; + server.spawn_task(async move { + player_c + .world() + .clone() + .respawn_player(&player_c, false) + .await; - // Restore abilities based on gamemode after respawn - { - let mut abilities = player - .abilities - .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner); - abilities.set_for_gamemode(player.gamemode.load()); - }; - player.send_abilities_update(); + { + let screen_handler = player_c + .current_screen_handler + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + let mut screen_handler = screen_handler + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + screen_handler.sync_state(); + }; + + // Restore abilities based on gamemode after respawn + { + let mut abilities = player_c + .abilities + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + abilities.set_for_gamemode(player_c.gamemode.load()); + }; + player_c.send_abilities_update(); + }); } SClientCommand::REQUEST_STATS => { // Request stats - player.send_stats().await; + player.send_stats(); } SClientCommand::REQUEST_GAMERULE_VALUES => { - self.send_game_rule_values(player).await; + self.send_game_rule_values(player); } _ => { - self.kick(TextComponent::text("Invalid client status")) - .await; + self.try_kick(&TextComponent::text("Invalid client status")); } } } - pub async fn send_game_rule_values(&self, player: &Player) { + pub fn send_game_rule_values(&self, player: &Player) { if player.permission_lvl.load() < PermissionLvl::Two { warn!( "Player {} tried to request game rule values without required permissions", @@ -73,6 +82,6 @@ impl JavaClient { .map(|(k, v)| (k.as_str(), v.as_str())) .collect(); - self.send_packet(&CGameRuleValues::new(&rules_ref)).await; + self.try_send_packet(&CGameRuleValues::new(&rules_ref)); } } diff --git a/crates/pumpkin/src/net/java/play/client_information.rs b/crates/pumpkin/src/net/java/play/client_information.rs index adb3e4af2..4c2692ae7 100644 --- a/crates/pumpkin/src/net/java/play/client_information.rs +++ b/crates/pumpkin/src/net/java/play/client_information.rs @@ -2,21 +2,20 @@ use super::*; impl JavaClient { - pub async fn handle_client_information( + pub fn handle_client_information( &self, server: &Arc, player: &Arc, - client_information: SClientInformationPlay<'_>, + client_information: &SClientInformationPlay<'_>, ) { if let (Ok(main_hand), Ok(chat_mode)) = ( Hand::try_from(client_information.main_hand.0), ChatMode::try_from(client_information.chat_mode.0), ) { if client_information.view_distance <= 0 { - self.kick(TextComponent::text( + self.try_kick(&TextComponent::text( "Cannot have zero or negative view distance!", - )) - .await; + )); return; } @@ -72,12 +71,12 @@ impl JavaClient { }; if update_watched { - chunker::update_position(player).await; + chunker::update_position(player); } if main_hand_changed { let mut event = PlayerChangedMainHandEvent::new(player.clone(), main_hand); - server.plugin_manager.fire(server, &mut event).await; + server.plugin_manager.fire_blocking(server, &mut event); } if locale_changed { @@ -86,7 +85,7 @@ impl JavaClient { new_locale: client_information.locale.to_string(), cancelled: false, }; - server.plugin_manager.fire(server, &mut event).await; + server.plugin_manager.fire_blocking(server, &mut event); } if update_settings { @@ -97,8 +96,7 @@ impl JavaClient { player.send_client_information(); } } else { - self.kick(TextComponent::text("Invalid hand or chat type")) - .await; + self.try_kick(&TextComponent::text("Invalid hand or chat type")); } } } diff --git a/crates/pumpkin/src/net/java/play/close_container.rs b/crates/pumpkin/src/net/java/play/close_container.rs index 617576e5e..f70765c84 100644 --- a/crates/pumpkin/src/net/java/play/close_container.rs +++ b/crates/pumpkin/src/net/java/play/close_container.rs @@ -2,12 +2,7 @@ use super::*; impl JavaClient { - pub fn handle_close_container( - &self, - player: &Arc, - _server: &Server, - _packet: SCloseContainer, - ) { + pub fn handle_close_container(&self, player: &Arc) { player.on_handled_screen_closed(); } } diff --git a/crates/pumpkin/src/net/java/play/command_suggestion.rs b/crates/pumpkin/src/net/java/play/command_suggestion.rs index 393368ad9..2e31b4d58 100644 --- a/crates/pumpkin/src/net/java/play/command_suggestion.rs +++ b/crates/pumpkin/src/net/java/play/command_suggestion.rs @@ -2,10 +2,10 @@ use super::*; impl JavaClient { - pub async fn handle_command_suggestion( + pub fn handle_command_suggestion( &self, player: &Arc, - packet: SCommandSuggestion<'_>, + packet: &SCommandSuggestion<'_>, server: &Arc, ) { let Some(cmd) = &packet.command.get(1..) else { @@ -29,6 +29,6 @@ impl JavaClient { suggestions.into(), ); - self.enqueue_client_packet(&response).await; + player.try_send_client_packet(&response); } } diff --git a/crates/pumpkin/src/net/java/play/confirm_teleport.rs b/crates/pumpkin/src/net/java/play/confirm_teleport.rs index f60c0b7bc..0b9e19f32 100644 --- a/crates/pumpkin/src/net/java/play/confirm_teleport.rs +++ b/crates/pumpkin/src/net/java/play/confirm_teleport.rs @@ -2,11 +2,7 @@ use super::*; impl JavaClient { - pub async fn handle_confirm_teleport( - &self, - player: &Player, - confirm_teleport: SConfirmTeleport, - ) { + pub fn handle_confirm_teleport(&self, player: &Player, confirm_teleport: &SConfirmTeleport) { enum TeleportResult { Success, WrongId, @@ -36,13 +32,12 @@ impl JavaClient { match result { TeleportResult::Success => {} TeleportResult::WrongId => { - self.kick(TextComponent::text("Wrong teleport id")).await; + self.try_kick(&TextComponent::text("Wrong teleport id")); } TeleportResult::NotTeleporting => { - self.kick(TextComponent::text( + self.try_kick(&TextComponent::text( "Send Teleport confirm, but we did not teleport", - )) - .await; + )); } } } diff --git a/crates/pumpkin/src/net/java/play/interact.rs b/crates/pumpkin/src/net/java/play/interact.rs index 5202677d1..6f714a938 100644 --- a/crates/pumpkin/src/net/java/play/interact.rs +++ b/crates/pumpkin/src/net/java/play/interact.rs @@ -3,10 +3,10 @@ use super::*; impl JavaClient { #[expect(clippy::too_many_lines)] - pub async fn handle_interact( + pub fn handle_interact( &self, player: &Arc, - interact: SInteract, + interact: &SInteract, server: &Arc, ) { if !player.has_client_loaded() { @@ -21,7 +21,7 @@ impl JavaClient { player_entity.set_sneaking(sneaking); } let Ok(action) = ActionType::try_from(interact.r#type.0) else { - self.kick(TextComponent::text("Invalid action type")).await; + self.try_kick(&TextComponent::text("Invalid action type")); return; }; @@ -36,10 +36,10 @@ impl JavaClient { if let Some(target) = target { if player.gamemode.load() == GameMode::Spectator { player.camera_target_id.store(Some(entity_id.0)); - player.send_client_packet(&CSetCamera::new(entity_id)).await; + player.try_send_client_packet(&CSetCamera::new(entity_id)); return; } - send_cancellable! {{ + send_cancellable_blocking! {{ server; PlayerInteractEntityEvent::new( player, @@ -58,8 +58,7 @@ impl JavaClient { } if entity_id.0 == player.entity_id() { - self.kick(TextComponent::translate_cross(translation::java::MULTIPLAYER_DISCONNECT_INVALID_ENTITY_ATTACKED, translation::java::MULTIPLAYER_DISCONNECT_INVALID_ENTITY_ATTACKED, [],)) - .await; + self.try_kick(&TextComponent::translate_cross(translation::java::MULTIPLAYER_DISCONNECT_INVALID_ENTITY_ATTACKED, translation::java::MULTIPLAYER_DISCONNECT_INVALID_ENTITY_ATTACKED, [])); return; } @@ -80,7 +79,7 @@ impl JavaClient { return; } } - player.attack(event.target).await; + player.attack(&event.target); } ActionType::Interact | ActionType::InteractAt => { if event.action == ActionType::InteractAt @@ -94,7 +93,7 @@ impl JavaClient { pos.z, u8::from(interact.hand.map_or(0, |h| h.0) != 0), ); - server.plugin_manager.fire(server, &mut at_event).await; + server.plugin_manager.fire_blocking(server, &mut at_event); if at_event.cancelled { return; } @@ -120,7 +119,7 @@ impl JavaClient { }} } else { // Entity not found - send_cancellable! {{ + send_cancellable_blocking! {{ server; PlayerInteractUnknownEntityEvent::new(player, entity_id.0, action); @@ -131,8 +130,7 @@ impl JavaClient { player.entity_id(), event.entity_id ); - self.kick(TextComponent::translate_cross(translation::java::MULTIPLAYER_DISCONNECT_INVALID_ENTITY_ATTACKED, translation::java::MULTIPLAYER_DISCONNECT_INVALID_ENTITY_ATTACKED, [],)) - .await; + self.try_kick(&TextComponent::translate_cross(translation::java::MULTIPLAYER_DISCONNECT_INVALID_ENTITY_ATTACKED, translation::java::MULTIPLAYER_DISCONNECT_INVALID_ENTITY_ATTACKED, [])); } } }} diff --git a/crates/pumpkin/src/net/java/play/mod.rs b/crates/pumpkin/src/net/java/play/mod.rs index 235718fb8..bbf9bb68a 100644 --- a/crates/pumpkin/src/net/java/play/mod.rs +++ b/crates/pumpkin/src/net/java/play/mod.rs @@ -65,7 +65,7 @@ use pumpkin_protocol::java::client::play::{ use pumpkin_protocol::java::server::play::{ Action, ActionType, CommandBlockMode, FLAG_ON_GROUND, SAttack, SBundleItemSelected, SChangeGameMode, SChatCommand, SChatMessage, SChunkBatch, SClientCommand, - SClientInformationPlay, SCloseContainer, SCommandSuggestion, SConfirmTeleport, + SClientInformationPlay, SCommandSuggestion, SConfirmTeleport, SCookieResponse as SPCookieResponse, SEditBook, SInteract, SJigsawGenerate, SKeepAlive, SMoveVehicle, SPaddleBoat, SPickItemFromBlock, SPickItemFromEntity, SPlaceRecipe, SPlayPingRequest, SPlayerAbilities, SPlayerAction, SPlayerCommand, SPlayerInput, diff --git a/crates/pumpkin/src/net/java/play/move_vehicle.rs b/crates/pumpkin/src/net/java/play/move_vehicle.rs index 2a8bb8fb2..945c884db 100644 --- a/crates/pumpkin/src/net/java/play/move_vehicle.rs +++ b/crates/pumpkin/src/net/java/play/move_vehicle.rs @@ -2,7 +2,7 @@ use super::*; impl JavaClient { - pub async fn handle_move_vehicle(&self, player: &Arc, packet: SMoveVehicle) { + pub fn handle_move_vehicle(&self, player: &Arc, packet: &SMoveVehicle) { let entity = player.get_entity(); let pos = Vector3::new(packet.x, packet.y, packet.z); let vehicle = entity @@ -16,6 +16,6 @@ impl JavaClient { vehicle_entity.set_rotation(packet.yaw, packet.pitch); } entity.set_pos(pos); - chunker::update_position(player).await; + chunker::update_position(player); } } diff --git a/crates/pumpkin/src/net/java/play/pick_item.rs b/crates/pumpkin/src/net/java/play/pick_item.rs index 76791ca1e..d8739e967 100644 --- a/crates/pumpkin/src/net/java/play/pick_item.rs +++ b/crates/pumpkin/src/net/java/play/pick_item.rs @@ -2,10 +2,10 @@ use super::*; impl JavaClient { - pub async fn handle_pick_item_from_block( + pub fn handle_pick_item_from_block( &self, player: &Arc, - pick_item: SPickItemFromBlock, + pick_item: &SPickItemFromBlock, ) { if !player.can_interact_with_block_at(&pick_item.pos, 1.0) { return; @@ -38,11 +38,9 @@ impl JavaClient { player.inventory.swap_stack_with_hotbar(stack); } - player - .send_client_packet(&CSetSelectedSlot::new( - player.inventory.get_selected_slot() as i8 - )) - .await; + player.try_send_client_packet(&CSetSelectedSlot::new( + player.inventory.get_selected_slot() as i8 + )); player .player_screen_handler .lock() @@ -50,10 +48,10 @@ impl JavaClient { .send_content_updates(); } - pub async fn handle_pick_item_from_entity( + pub fn handle_pick_item_from_entity( &self, player: &Arc, - pick_item: SPickItemFromEntity, + pick_item: &SPickItemFromEntity, ) { use pumpkin_data::entity::{entity_from_egg, spawn_egg_ids}; @@ -99,11 +97,9 @@ impl JavaClient { player.inventory.swap_stack_with_hotbar(stack); } - player - .send_client_packet(&CSetSelectedSlot::new( - player.inventory.get_selected_slot() as i8 - )) - .await; + player.try_send_client_packet(&CSetSelectedSlot::new( + player.inventory.get_selected_slot() as i8, + )); player .player_screen_handler .lock() diff --git a/crates/pumpkin/src/net/java/play/ping_request.rs b/crates/pumpkin/src/net/java/play/ping_request.rs index ada98ff15..64e651e69 100644 --- a/crates/pumpkin/src/net/java/play/ping_request.rs +++ b/crates/pumpkin/src/net/java/play/ping_request.rs @@ -2,8 +2,7 @@ use super::*; impl JavaClient { - pub async fn handle_play_ping_request(&self, request: SPlayPingRequest) { - self.enqueue_client_packet(&CPingResponse::new(request.payload)) - .await; + pub fn handle_play_ping_request(&self, request: &SPlayPingRequest) { + self.try_send_packet(&CPingResponse::new(request.payload)); } } diff --git a/crates/pumpkin/src/net/java/play/place_recipe.rs b/crates/pumpkin/src/net/java/play/place_recipe.rs index 8e2a70ab2..effa5f513 100644 --- a/crates/pumpkin/src/net/java/play/place_recipe.rs +++ b/crates/pumpkin/src/net/java/play/place_recipe.rs @@ -3,11 +3,11 @@ use super::*; impl JavaClient { #[allow(clippy::too_many_lines)] - pub async fn handle_place_recipe( + pub fn handle_place_recipe( &self, server: &Arc, player: &Arc, - packet: SPlaceRecipe, + packet: &SPlaceRecipe, ) { use crate::net::java::recipe_helper::{ GenericIngredient, compute_biggest_craftable, take_n_ingredient, @@ -25,7 +25,9 @@ impl JavaClient { format!("display_{}", packet.recipe_display_id.0), use_max, ); - server.plugin_manager.fire(server, &mut click_event).await; + server + .plugin_manager + .fire_blocking(server, &mut click_event); if click_event.cancelled { return; } diff --git a/crates/pumpkin/src/net/java/play/player_abilities.rs b/crates/pumpkin/src/net/java/play/player_abilities.rs index a0c45dbf5..54d1c78ce 100644 --- a/crates/pumpkin/src/net/java/play/player_abilities.rs +++ b/crates/pumpkin/src/net/java/play/player_abilities.rs @@ -2,10 +2,10 @@ use super::*; impl JavaClient { - pub async fn handle_player_abilities( + pub fn handle_player_abilities( &self, player: &Arc, - player_abilities: SPlayerAbilities, + player_abilities: &SPlayerAbilities, server: &Arc, ) { let (flying, allow_flying) = { @@ -19,7 +19,7 @@ impl JavaClient { // Set the flying ability let new_flying = player_abilities.is_flying() && allow_flying; if flying != new_flying { - send_cancellable! {{ + send_cancellable_blocking! {{ server; PlayerToggleFlightEvent::new(player.clone(), new_flying); 'after: { diff --git a/crates/pumpkin/src/net/java/play/player_action.rs b/crates/pumpkin/src/net/java/play/player_action.rs index 8adee03e4..cad79b06f 100644 --- a/crates/pumpkin/src/net/java/play/player_action.rs +++ b/crates/pumpkin/src/net/java/play/player_action.rs @@ -3,10 +3,10 @@ use super::*; impl JavaClient { #[expect(clippy::too_many_lines)] - pub async fn handle_player_action( + pub fn handle_player_action( &self, player: &Arc, - player_action: SPlayerAction, + player_action: &SPlayerAction, server: &Server, ) { if !player.has_client_loaded() { @@ -21,7 +21,7 @@ impl JavaClient { "Player {0} tried to interact with block out of reach at {1}", player.gameprofile.name, player_action.position ); - self.update_sequence(player, player_action.sequence.0); + self.update_sequence(player_action.sequence.0); return; } let position = player_action.position; @@ -39,10 +39,9 @@ impl JavaClient { ); server_arc .plugin_manager - .fire(&server_arc, &mut event) - .await; + .fire_blocking(&server_arc, &mut event); if event.cancelled { - self.update_sequence(player, player_action.sequence.0); + self.update_sequence(player_action.sequence.0); return; } } @@ -63,12 +62,11 @@ impl JavaClient { let inventory = player.inventory(); let held = inventory.held_item(); if !server.item_registry.can_mine(held.item, player) { - self.enqueue_client_packet(&CBlockUpdate::new( + player.try_send_client_packet(&CBlockUpdate::new( position, VarInt(i32::from(state.id.as_u16())), - )) - .await; - self.update_sequence(player, player_action.sequence.0); + )); + self.update_sequence(player_action.sequence.0); return; } @@ -86,8 +84,8 @@ impl JavaClient { .block_registry .broken(&world, block, player, &position, server, state); } - self.sync_block_state_to_client(&world, position).await; - self.update_sequence(player, player_action.sequence.0); + self.sync_block_state_to_client(&world, position); + self.update_sequence(player_action.sequence.0); return; } player.start_mining_time.store( @@ -127,7 +125,7 @@ impl JavaClient { 1, ); } - self.sync_block_state_to_client(&world, position).await; + self.sync_block_state_to_client(&world, position); } else { player.mining.store(true, Ordering::Relaxed); *player @@ -151,7 +149,7 @@ impl JavaClient { .store(progress, Ordering::Relaxed); } } - self.update_sequence(player, player_action.sequence.0); + self.update_sequence(player_action.sequence.0); } Status::CancelledDigging => { if !player.can_interact_with_block_at(&player_action.position, 1.0) { @@ -159,7 +157,7 @@ impl JavaClient { "Player {0} tried to interact with block out of reach at {1}", player.gameprofile.name, player_action.position ); - self.update_sequence(player, player_action.sequence.0); + self.update_sequence(player_action.sequence.0); return; } player.mining.store(false, Ordering::Relaxed); @@ -169,7 +167,7 @@ impl JavaClient { player_action.position, BlockBreakingProgress::Stop, ); - self.update_sequence(player, player_action.sequence.0); + self.update_sequence(player_action.sequence.0); } Status::FinishedDigging => { // TODO: do validation @@ -179,7 +177,7 @@ impl JavaClient { "Player {0} tried to interact with block out of reach at {1}", player.gameprofile.name, player_action.position ); - self.update_sequence(player, player_action.sequence.0); + self.update_sequence(player_action.sequence.0); return; } @@ -221,15 +219,15 @@ impl JavaClient { ); } - self.sync_block_state_to_client(&world, location).await; + self.sync_block_state_to_client(&world, location); - self.update_sequence(player, player_action.sequence.0); + self.update_sequence(player_action.sequence.0); } Status::DropItem => { - player.drop_held_item(false).await; + player.drop_held_item(false); } Status::DropItemStack => { - player.drop_held_item(true).await; + player.drop_held_item(true); } Status::ReleaseItemInUse => { let item_in_use = player @@ -245,17 +243,17 @@ impl JavaClient { player.living_entity.clear_active_hand(); } Status::SwapItem => { - player.swap_item().await; + player.swap_item(); } Status::SpearJab => { debug!("todo"); } }, - Err(_) => self.kick(TextComponent::text("Invalid status")).await, + Err(_) => self.try_kick(&TextComponent::text("Invalid status")), } } - pub fn update_sequence(&self, _player: &Player, sequence: i32) { + pub fn update_sequence(&self, sequence: i32) { if sequence < 0 { error!("Expected packet sequence >= 0"); } @@ -265,12 +263,11 @@ impl JavaClient { ); } - async fn sync_block_state_to_client(&self, world: &World, position: BlockPos) { + fn sync_block_state_to_client(&self, world: &World, position: BlockPos) { let synced_state_id = world.get_block_state_id(&position); - self.send_packet(&CBlockUpdate::new( + self.try_send_packet(&CBlockUpdate::new( position, VarInt(i32::from(synced_state_id.as_u16())), - )) - .await; + )); } } diff --git a/crates/pumpkin/src/net/java/play/player_command.rs b/crates/pumpkin/src/net/java/play/player_command.rs index 094424f64..7ceccb74f 100644 --- a/crates/pumpkin/src/net/java/play/player_command.rs +++ b/crates/pumpkin/src/net/java/play/player_command.rs @@ -2,10 +2,10 @@ use super::*; impl JavaClient { - pub async fn handle_player_command( + pub fn handle_player_command( &self, player: &Arc, - command: SPlayerCommand, + command: &SPlayerCommand, server: &Arc, ) { if command.entity_id != player.entity_id().into() { @@ -20,7 +20,7 @@ impl JavaClient { match command.action { Action::StartSprinting => { if !entity.is_sprinting() { - send_cancellable! {{ + send_cancellable_blocking! {{ server; PlayerToggleSprintEvent::new(player.clone(), true); 'after: { @@ -31,7 +31,7 @@ impl JavaClient { } Action::StopSprinting => { if entity.is_sprinting() { - send_cancellable! {{ + send_cancellable_blocking! {{ server; PlayerToggleSprintEvent::new(player.clone(), false); 'after: { @@ -52,7 +52,7 @@ impl JavaClient { entity.entity_id, fall_flying, ); - server.plugin_manager.fire(server, &mut event).await; + server.plugin_manager.fire_blocking(server, &mut event); if !event.cancelled { entity.set_fall_flying(event.is_gliding); } @@ -62,12 +62,11 @@ impl JavaClient { Action::StartSneaking | Action::StopSneaking => { self.handle_player_input( player, - SPlayerInput { + &SPlayerInput { input: SPlayerInput::SNEAK, }, server, - ) - .await; + ); } } } diff --git a/crates/pumpkin/src/net/java/play/player_input.rs b/crates/pumpkin/src/net/java/play/player_input.rs index 357478985..c6330bc3b 100644 --- a/crates/pumpkin/src/net/java/play/player_input.rs +++ b/crates/pumpkin/src/net/java/play/player_input.rs @@ -2,10 +2,10 @@ use super::*; impl JavaClient { - pub async fn handle_player_input( + pub fn handle_player_input( &self, player: &Arc, - input: SPlayerInput, + input: &SPlayerInput, server: &Arc, ) { let mut input_event = @@ -13,7 +13,9 @@ impl JavaClient { player.clone(), format!("{:b}", input.input), ); - server.plugin_manager.fire(server, &mut input_event).await; + server + .plugin_manager + .fire_blocking(server, &mut input_event); if input_event.cancelled { return; } @@ -26,13 +28,11 @@ impl JavaClient { && player.camera_target_id.load().is_some() { player.camera_target_id.store(None); - player - .send_client_packet(&CSetCamera::new(player.entity_id().into())) - .await; + player.try_send_client_packet(&CSetCamera::new(player.entity_id().into())); } if player.get_entity().is_sneaking() != sneak { - send_cancellable! {{ + send_cancellable_blocking! {{ server; PlayerToggleSneakEvent::new(player.clone(), sneak); 'after: { @@ -45,10 +45,7 @@ impl JavaClient { .unwrap_or_else(std::sync::PoisonError::into_inner) .clone(); if let Some(vehicle) = vehicle { - vehicle - .get_entity() - .remove_passenger(player.entity_id()) - .await; + vehicle.get_entity().remove_passenger(player.entity_id()); } } } @@ -61,10 +58,7 @@ impl JavaClient { .unwrap_or_else(std::sync::PoisonError::into_inner) .clone(); if let Some(vehicle) = vehicle { - vehicle - .get_entity() - .remove_passenger(player.entity_id()) - .await; + vehicle.get_entity().remove_passenger(player.entity_id()); } } } diff --git a/crates/pumpkin/src/net/java/play/player_position.rs b/crates/pumpkin/src/net/java/play/player_position.rs index 517ef1af6..da0919e2f 100644 --- a/crates/pumpkin/src/net/java/play/player_position.rs +++ b/crates/pumpkin/src/net/java/play/player_position.rs @@ -43,11 +43,11 @@ impl JavaClient { } #[expect(clippy::too_many_lines)] - pub async fn handle_position( + pub fn handle_position( &self, player: &Arc, server: &Arc, - packet: SPlayerPosition, + packet: &SPlayerPosition, ) { if !player.has_client_loaded() { return; @@ -67,12 +67,11 @@ impl JavaClient { // y = feet Y let position = packet.position; if position.x.is_nan() || position.y.is_nan() || position.z.is_nan() { - self.kick(TextComponent::translate_cross( + self.try_kick(&TextComponent::translate_cross( translation::java::MULTIPLAYER_DISCONNECT_INVALID_PLAYER_MOVEMENT, translation::java::MULTIPLAYER_DISCONNECT_INVALID_PLAYER_MOVEMENT, [], - )) - .await; + )); return; } let position = Vector3::new( @@ -81,7 +80,7 @@ impl JavaClient { Self::clamp_horizontal(position.z), ); - send_cancellable! {{ + send_cancellable_blocking! {{ server; PlayerMoveEvent { player: player.clone(), @@ -157,7 +156,7 @@ impl JavaClient { player.gamemode.load() == GameMode::Creative, ); } - chunker::update_position(player).await; + chunker::update_position(player); let delta = Vector3::new( pos.x - last_pos.x, pos.y - last_pos.y, @@ -171,17 +170,17 @@ impl JavaClient { } 'cancelled: { - self.force_tp(player, player.get_entity().pos.load()).await; + self.force_tp(player, player.get_entity().pos.load()); } }} } #[expect(clippy::too_many_lines)] - pub async fn handle_position_rotation( + pub fn handle_position_rotation( &self, player: &Arc, server: &Arc, - packet: SPlayerPositionRotation, + packet: &SPlayerPositionRotation, ) { if !player.has_client_loaded() { return; @@ -206,12 +205,11 @@ impl JavaClient { || !packet.yaw.is_finite() || !packet.pitch.is_finite() { - self.kick(TextComponent::translate_cross( + self.try_kick(&TextComponent::translate_cross( translation::java::MULTIPLAYER_DISCONNECT_INVALID_PLAYER_MOVEMENT, translation::java::MULTIPLAYER_DISCONNECT_INVALID_PLAYER_MOVEMENT, [], - )) - .await; + )); return; } @@ -221,7 +219,7 @@ impl JavaClient { Self::clamp_horizontal(position.z), ); - send_cancellable! {{ + send_cancellable_blocking! {{ server; PlayerMoveEvent::new( player.clone(), @@ -314,7 +312,7 @@ impl JavaClient { player.gamemode.load() == GameMode::Creative, ); } - chunker::update_position(player).await; + chunker::update_position(player); let delta = Vector3::new( pos.x - last_pos.x, pos.y - last_pos.y, @@ -328,26 +326,25 @@ impl JavaClient { } 'cancelled: { - self.force_tp(player, position).await; + self.force_tp(player, position); } }} } - pub async fn force_tp(&self, player: &Arc, position: Vector3) { + pub fn force_tp(&self, player: &Arc, position: Vector3) { let teleport_id = player.teleport_id_count.fetch_add(1, Ordering::Relaxed) + 1; *player .awaiting_teleport .lock() .unwrap_or_else(std::sync::PoisonError::into_inner) = Some((teleport_id.into(), position)); - self.enqueue_client_packet(&CPlayerPosition::new( + player.try_send_client_packet(&CPlayerPosition::new( teleport_id.into(), player.get_entity().pos.load(), Vector3::new(0.0, 0.0, 0.0), player.get_entity().yaw.load(), player.get_entity().pitch.load(), Vec::new(), - )) - .await; + )); } } diff --git a/crates/pumpkin/src/net/java/play/player_rotation.rs b/crates/pumpkin/src/net/java/play/player_rotation.rs index 94b2fd423..edef85ee8 100644 --- a/crates/pumpkin/src/net/java/play/player_rotation.rs +++ b/crates/pumpkin/src/net/java/play/player_rotation.rs @@ -2,17 +2,16 @@ use super::*; impl JavaClient { - pub async fn handle_rotation(&self, player: &Player, rotation: SPlayerRotation) { + pub fn handle_rotation(&self, player: &Player, rotation: &SPlayerRotation) { if !player.has_client_loaded() { return; } if !rotation.yaw.is_finite() || !rotation.pitch.is_finite() { - self.kick(TextComponent::translate_cross( + self.try_kick(&TextComponent::translate_cross( translation::java::MULTIPLAYER_DISCONNECT_INVALID_PLAYER_MOVEMENT, translation::java::MULTIPLAYER_DISCONNECT_INVALID_PLAYER_MOVEMENT, [], - )) - .await; + )); return; } let entity = &player.get_entity(); diff --git a/crates/pumpkin/src/net/java/play/recipe_book_change_settings.rs b/crates/pumpkin/src/net/java/play/recipe_book_change_settings.rs index 3b2fd9f09..e990ebc88 100644 --- a/crates/pumpkin/src/net/java/play/recipe_book_change_settings.rs +++ b/crates/pumpkin/src/net/java/play/recipe_book_change_settings.rs @@ -2,11 +2,11 @@ use super::*; impl JavaClient { - pub async fn handle_recipe_book_change_settings( + pub fn handle_recipe_book_change_settings( &self, server: &Arc, player: &Arc, - packet: SRecipeBookChangeSettings, + packet: &SRecipeBookChangeSettings, ) { let mut event = crate::plugin::api::events::player::player_recipe_book_settings_change::PlayerRecipeBookSettingsChangeEvent::new( player.clone(), @@ -14,6 +14,6 @@ impl JavaClient { packet.is_open, packet.is_filtering, ); - server.plugin_manager.fire(server, &mut event).await; + server.plugin_manager.fire_blocking(server, &mut event); } } diff --git a/crates/pumpkin/src/net/java/play/recipe_book_seen_recipe.rs b/crates/pumpkin/src/net/java/play/recipe_book_seen_recipe.rs index e90c90aa5..aadb6be28 100644 --- a/crates/pumpkin/src/net/java/play/recipe_book_seen_recipe.rs +++ b/crates/pumpkin/src/net/java/play/recipe_book_seen_recipe.rs @@ -2,16 +2,16 @@ use super::*; impl JavaClient { - pub async fn handle_recipe_book_seen_recipe( + pub fn handle_recipe_book_seen_recipe( &self, server: &Arc, player: &Arc, - packet: SRecipeBookSeenRecipe, + packet: &SRecipeBookSeenRecipe, ) { let mut event = crate::plugin::api::events::player::player_recipe_discover::PlayerRecipeDiscoverEvent::new( player.clone(), format!("display_{}", packet.recipe_display_id.0), ); - server.plugin_manager.fire(server, &mut event).await; + server.plugin_manager.fire_blocking(server, &mut event); } } diff --git a/crates/pumpkin/src/net/java/play/resource_pack_response.rs b/crates/pumpkin/src/net/java/play/resource_pack_response.rs index 778aa1e6a..059e90d9e 100644 --- a/crates/pumpkin/src/net/java/play/resource_pack_response.rs +++ b/crates/pumpkin/src/net/java/play/resource_pack_response.rs @@ -6,11 +6,11 @@ use pumpkin_util::text::TextComponent; use crate::plugin::api::events::player::player_resource_pack_status::PlayerResourcePackStatusEvent; impl JavaClient { - pub async fn handle_play_resource_pack_response( + pub fn handle_play_resource_pack_response( &self, server: &Arc, player: &Arc, - packet: SPlayResourcePack, + packet: &SPlayResourcePack, ) { let result = packet.response_result(); debug!( @@ -23,16 +23,15 @@ impl JavaClient { packet.uuid.to_string(), format!("{result:?}"), ); - server.plugin_manager.fire(server, &mut event).await; + server.plugin_manager.fire_blocking(server, &mut event); if server.advanced_config.resource_pack.java.force && (result == PlayResourcePackResult::Declined || result == PlayResourcePackResult::DownloadFail) { - self.kick(TextComponent::text( + self.try_kick(&TextComponent::text( "You must accept the resource pack to play on this server.", - )) - .await; + )); } } } diff --git a/crates/pumpkin/src/net/java/play/seen_advancement.rs b/crates/pumpkin/src/net/java/play/seen_advancement.rs index a9763323a..2cd863631 100644 --- a/crates/pumpkin/src/net/java/play/seen_advancement.rs +++ b/crates/pumpkin/src/net/java/play/seen_advancement.rs @@ -2,7 +2,7 @@ use super::*; impl JavaClient { - pub fn handle_seen_advancement(&self, player: &Arc, packet: SSeenAdvancement) { + pub fn handle_seen_advancement(&self, player: &Arc, packet: &SSeenAdvancement) { if let SSeenAdvancement::OpenTab(tab) = packet { let advancement = Advancement::from_minecraft_name(&tab.to_string()); if advancement.is_some() { diff --git a/crates/pumpkin/src/net/java/play/select_trade.rs b/crates/pumpkin/src/net/java/play/select_trade.rs index 9c018558f..96564de06 100644 --- a/crates/pumpkin/src/net/java/play/select_trade.rs +++ b/crates/pumpkin/src/net/java/play/select_trade.rs @@ -2,13 +2,13 @@ use super::*; impl JavaClient { - pub async fn handle_select_trade(&self, player: &Arc, packet: SSelectTrade) { + pub fn handle_select_trade(&self, player: &Arc, packet: &SSelectTrade) { let mut event = crate::plugin::api::events::inventory::trade_select::TradeSelectEvent::new( player.clone(), packet.selected_slot.0 as u8, ); if let Some(server) = player.world().server.upgrade() { - server.plugin_manager.fire(&server, &mut event).await; + server.plugin_manager.fire_blocking(&server, &mut event); } if event.cancelled { return; diff --git a/crates/pumpkin/src/net/java/play/set_beacon.rs b/crates/pumpkin/src/net/java/play/set_beacon.rs index 599e32332..ed1cec793 100644 --- a/crates/pumpkin/src/net/java/play/set_beacon.rs +++ b/crates/pumpkin/src/net/java/play/set_beacon.rs @@ -6,7 +6,7 @@ use pumpkin_protocol::java::server::play::SSetBeacon; use pumpkin_world::inventory::Inventory; impl JavaClient { - pub async fn handle_set_beacon(&self, player: &Arc, packet: &SSetBeacon) { + pub fn handle_set_beacon(&self, player: &Arc, packet: &SSetBeacon) { let is_valid = { let screen_handler_lock = player .current_screen_handler @@ -81,11 +81,10 @@ impl JavaClient { "Player {} tried to set invalid beacon effects: primary {:?}, secondary {:?}", player.gameprofile.name, primary_id, secondary_id ); - self.kick(TextComponent::translate( + self.try_kick(&TextComponent::translate( "multiplayer.disconnect.generic", &[], - )) - .await; + )); } } } diff --git a/crates/pumpkin/src/net/java/play/set_command_block.rs b/crates/pumpkin/src/net/java/play/set_command_block.rs index a48e6daa9..99f9b718a 100644 --- a/crates/pumpkin/src/net/java/play/set_command_block.rs +++ b/crates/pumpkin/src/net/java/play/set_command_block.rs @@ -2,11 +2,7 @@ use super::*; impl JavaClient { - pub async fn handle_set_command_block( - &self, - player: &Arc, - command: SSetCommandBlock<'_>, - ) { + pub fn handle_set_command_block(&self, player: &Arc, command: &SSetCommandBlock<'_>) { if !player.is_creative() { return; } @@ -22,8 +18,7 @@ impl JavaClient { } let Ok(command_block_mode) = CommandBlockMode::try_from(command.mode) else { - self.kick(TextComponent::text("Invalid Command block mode")) - .await; + self.try_kick(&TextComponent::text("Invalid Command block mode")); return; }; diff --git a/crates/pumpkin/src/net/java/play/set_creative_slot.rs b/crates/pumpkin/src/net/java/play/set_creative_slot.rs index a8950a056..f257cddca 100644 --- a/crates/pumpkin/src/net/java/play/set_creative_slot.rs +++ b/crates/pumpkin/src/net/java/play/set_creative_slot.rs @@ -2,7 +2,7 @@ use super::*; impl JavaClient { - pub async fn handle_set_creative_slot( + pub fn handle_set_creative_slot( &self, player: &Arc, packet: SSetCreativeSlot, @@ -25,8 +25,7 @@ impl JavaClient { if let Some(server) = player.world().server.upgrade() { server .plugin_manager - .fire(&server, &mut creative_event) - .await; + .fire_blocking(&server, &mut creative_event); } if creative_event.cancelled { return Ok(()); diff --git a/crates/pumpkin/src/net/java/play/set_held_item.rs b/crates/pumpkin/src/net/java/play/set_held_item.rs index 3def8d707..4cdd984af 100644 --- a/crates/pumpkin/src/net/java/play/set_held_item.rs +++ b/crates/pumpkin/src/net/java/play/set_held_item.rs @@ -2,17 +2,11 @@ use super::*; impl JavaClient { - pub async fn handle_set_held_item( - &self, - server: &Arc, - - player: &Player, - held: SSetHeldItem, - ) { + pub fn handle_set_held_item(&self, server: &Arc, player: &Player, held: &SSetHeldItem) { player.update_last_action_time(); let slot = held.slot; if !(0..=8).contains(&slot) { - self.kick(TextComponent::text("Invalid held slot")).await; + self.try_kick(&TextComponent::text("Invalid held slot")); return; } let slot = slot as u8; @@ -21,11 +15,9 @@ impl JavaClient { return; }; let mut event = PlayerItemHeldEvent::new(player_arc, previous_slot, slot); - server.plugin_manager.fire(server, &mut event).await; + server.plugin_manager.fire_blocking(server, &mut event); if event.cancelled { - player - .send_client_packet(&CSetSelectedSlot::new(previous_slot as i8)) - .await; + player.try_send_client_packet(&CSetSelectedSlot::new(previous_slot as i8)); return; } diff --git a/crates/pumpkin/src/net/java/play/spectate_entity.rs b/crates/pumpkin/src/net/java/play/spectate_entity.rs index 5e90eebea..66e822805 100644 --- a/crates/pumpkin/src/net/java/play/spectate_entity.rs +++ b/crates/pumpkin/src/net/java/play/spectate_entity.rs @@ -4,11 +4,11 @@ use pumpkin_protocol::java::{client::play::CSetCamera, server::play::SSpectateEn use pumpkin_util::GameMode; impl JavaClient { - pub async fn handle_spectate_entity( + pub fn handle_spectate_entity( &self, player: &Arc, server: &Server, - packet: SSpectateEntity, + packet: &SSpectateEntity, ) { if !player.has_client_loaded() { return; @@ -27,9 +27,7 @@ impl JavaClient { let target_id = target.get_entity().entity_id; player.camera_target_id.store(Some(target_id)); - player - .send_client_packet(&CSetCamera::new(target_id.into())) - .await; + player.try_send_client_packet(&CSetCamera::new(target_id.into())); player.request_teleport(target_pos, target_yaw, target_pitch); } else if let Some(target_player) = server.get_player_by_uuid(packet.target) { @@ -39,9 +37,7 @@ impl JavaClient { let target_id = target_player.living_entity.entity.entity_id; player.camera_target_id.store(Some(target_id)); - player - .send_client_packet(&CSetCamera::new(target_id.into())) - .await; + player.try_send_client_packet(&CSetCamera::new(target_id.into())); player.request_teleport(target_pos, target_yaw, target_pitch); } diff --git a/crates/pumpkin/src/net/java/play/swing_arm.rs b/crates/pumpkin/src/net/java/play/swing_arm.rs index 0316b1079..c1154d9a9 100644 --- a/crates/pumpkin/src/net/java/play/swing_arm.rs +++ b/crates/pumpkin/src/net/java/play/swing_arm.rs @@ -2,15 +2,15 @@ use super::*; impl JavaClient { - pub async fn handle_swing_arm( + pub fn handle_swing_arm( &self, server: &Arc, player: &Arc, - swing_arm: SSwingArm, + swing_arm: &SSwingArm, ) { player.update_last_action_time(); let Ok(hand) = Hand::from_packet_id(swing_arm.hand.0) else { - self.kick(TextComponent::text("Invalid hand")).await; + self.try_kick(&TextComponent::text("Invalid hand")); return; }; @@ -21,7 +21,7 @@ impl JavaClient { Hand::Right => crate::plugin::api::events::player::player_animation::PlayerAnimationType::ArmSwingMain, }, ); - server.plugin_manager.fire(server, &mut anim_event).await; + server.plugin_manager.fire_blocking(server, &mut anim_event); if anim_event.cancelled { return; } @@ -49,7 +49,7 @@ impl JavaClient { PlayerInteractEvent::new(player, InteractAction::LeftClickAir, &Block::AIR, None) }; - send_cancellable! {{ + send_cancellable_blocking! {{ &server; event; 'after: { diff --git a/crates/pumpkin/src/net/java/play/tag_query.rs b/crates/pumpkin/src/net/java/play/tag_query.rs index 76d52ae2e..5f2e7c2db 100644 --- a/crates/pumpkin/src/net/java/play/tag_query.rs +++ b/crates/pumpkin/src/net/java/play/tag_query.rs @@ -7,11 +7,7 @@ use pumpkin_protocol::java::{ }; impl JavaClient { - pub async fn handle_block_entity_tag_query( - &self, - player: &Player, - packet: SBlockEntityTagQuery, - ) { + pub fn handle_block_entity_tag_query(&self, player: &Player, packet: &SBlockEntityTagQuery) { if player.permission_lvl.load() < PermissionLvl::Two { return; } @@ -22,11 +18,10 @@ impl JavaClient { } let nbt_bytes = Nbt::new(String::new(), compound).write_unnamed(); - self.send_packet(&CTagQueryResponse::new(packet.transaction_id, &nbt_bytes)) - .await; + self.try_send_packet(&CTagQueryResponse::new(packet.transaction_id, &nbt_bytes)); } - pub async fn handle_entity_tag_query(&self, player: &Player, packet: SEntityTagQuery) { + pub fn handle_entity_tag_query(&self, player: &Player, packet: &SEntityTagQuery) { if player.permission_lvl.load() < PermissionLvl::Two { return; } @@ -37,7 +32,6 @@ impl JavaClient { } let nbt_bytes = Nbt::new(String::new(), compound).write_unnamed(); - self.send_packet(&CTagQueryResponse::new(packet.transaction_id, &nbt_bytes)) - .await; + self.try_send_packet(&CTagQueryResponse::new(packet.transaction_id, &nbt_bytes)); } } diff --git a/crates/pumpkin/src/net/java/play/teleport_to_entity.rs b/crates/pumpkin/src/net/java/play/teleport_to_entity.rs index ae0088ebf..4b89b9819 100644 --- a/crates/pumpkin/src/net/java/play/teleport_to_entity.rs +++ b/crates/pumpkin/src/net/java/play/teleport_to_entity.rs @@ -2,10 +2,10 @@ use super::*; impl JavaClient { - pub async fn handle_teleport_to_entity( + pub fn handle_teleport_to_entity( &self, player: &Arc, - packet: STeleportToEntity, + packet: &STeleportToEntity, server: &Server, ) { if !player.has_client_loaded() { @@ -24,9 +24,7 @@ impl JavaClient { let target_id = target_player.living_entity.entity.entity_id; player.camera_target_id.store(Some(target_id)); - player - .send_client_packet(&CSetCamera::new(target_id.into())) - .await; + player.try_send_client_packet(&CSetCamera::new(target_id.into())); player.request_teleport(target_pos, target_yaw, target_pitch); } diff --git a/crates/pumpkin/src/net/java/play/update_sign.rs b/crates/pumpkin/src/net/java/play/update_sign.rs index 3a7507611..a14ae75ae 100644 --- a/crates/pumpkin/src/net/java/play/update_sign.rs +++ b/crates/pumpkin/src/net/java/play/update_sign.rs @@ -2,7 +2,7 @@ use super::*; impl JavaClient { - pub async fn handle_sign_update(&self, player: &Player, sign_data: SUpdateSign<'_>) { + pub fn handle_sign_update(&self, player: &Player, sign_data: &SUpdateSign<'_>) { let world = player.get_entity().world.load_full(); let Some(block_entity) = world.get_block_entity(&sign_data.location) else { return; @@ -25,10 +25,10 @@ impl JavaClient { let mut event = crate::plugin::api::events::block::sign_change::SignChangeEvent::new( player_arc, sign_data.location, - lines.clone(), + lines, ); if let Some(server) = world.server.upgrade() { - server.plugin_manager.fire(&server, &mut event).await; + server.plugin_manager.fire_blocking(&server, &mut event); } if event.cancelled { return; diff --git a/crates/pumpkin/src/net/java/play/use_item.rs b/crates/pumpkin/src/net/java/play/use_item.rs index 1f3c4350e..2cedbaad8 100644 --- a/crates/pumpkin/src/net/java/play/use_item.rs +++ b/crates/pumpkin/src/net/java/play/use_item.rs @@ -2,12 +2,7 @@ use super::*; impl JavaClient { - pub async fn handle_use_item( - &self, - player: &Arc, - use_item: &SUseItem, - server: &Arc, - ) { + pub fn handle_use_item(&self, player: &Arc, use_item: &SUseItem, server: &Arc) { if !player.has_client_loaded() { return; } @@ -15,10 +10,10 @@ impl JavaClient { let inventory = player.inventory(); let Ok(hand) = Hand::from_packet_id(use_item.hand.0) else { - self.kick(TextComponent::text("InvalidHand")).await; + self.try_kick(&TextComponent::text("InvalidHand")); return; }; - self.update_sequence(player, use_item.sequence.0); + self.update_sequence(use_item.sequence.0); let mut item_in_hand = inventory.get_stack_in_hand(hand); @@ -27,7 +22,9 @@ impl JavaClient { player.clone(), item_in_hand.item.registry_key.to_string(), ); - server.plugin_manager.fire(server, &mut consume_event).await; + server + .plugin_manager + .fire_blocking(server, &mut consume_event); if consume_event.cancelled { return; } @@ -60,14 +57,11 @@ impl JavaClient { let (item_for_use, stack_for_use) = (item_in_hand.item, item_in_hand.clone()); Self::prepare_hand_item_for_use(player, hand, &mut item_in_hand); - if !self - .should_continue_use_after_fish_event(server, player, hand, item_for_use) - .await - { + if !Self::should_continue_use_after_fish_event(server, player, hand, item_for_use) { return; } - send_cancellable! {{ + send_cancellable_blocking! {{ server; event; 'after: { @@ -147,8 +141,7 @@ impl JavaClient { } } - async fn should_continue_use_after_fish_event( - &self, + fn should_continue_use_after_fish_event( server: &Arc, player: &Arc, hand: Hand, @@ -168,7 +161,7 @@ impl JavaClient { hand, 0, ); - server.plugin_manager.fire(server, &mut fish_event).await; + server.plugin_manager.fire_blocking(server, &mut fish_event); !fish_event.cancelled } } diff --git a/crates/pumpkin/src/net/java/play/use_item_on.rs b/crates/pumpkin/src/net/java/play/use_item_on.rs index e7c92c5e3..53e44a494 100644 --- a/crates/pumpkin/src/net/java/play/use_item_on.rs +++ b/crates/pumpkin/src/net/java/play/use_item_on.rs @@ -3,17 +3,17 @@ use super::*; impl JavaClient { #[allow(clippy::too_many_lines)] - pub async fn handle_use_item_on( + pub fn handle_use_item_on( &self, player: &Arc, - use_item_on: SUseItemOn, + use_item_on: &SUseItemOn, server: &Arc, ) -> Result<(), BlockPlacingError> { if !player.has_client_loaded() { return Ok(()); } player.update_last_action_time(); - self.update_sequence(player, use_item_on.sequence.0); + self.update_sequence(use_item_on.sequence.0); let position = use_item_on.position; let cursor_pos = use_item_on.cursor_pos; @@ -59,16 +59,15 @@ impl JavaClient { Some(position), ); - send_cancellable! {{ + send_cancellable_blocking! {{ server; event; 'cancelled: { let state_id = world.get_block_state_id(&position); - self.enqueue_client_packet(&CBlockUpdate::new( + player.try_send_client_packet(&CBlockUpdate::new( position, VarInt(i32::from(state_id.as_u16())), - )) - .await; + )); return Ok(()); } }} @@ -125,9 +124,8 @@ impl JavaClient { // Check if the item is a block, because not every item can be placed :D let item_id = item.item.id; if let Some(block) = Block::from_item_id(item_id) { - should_try_decrement = self - .run_is_block_place(player, block, server, use_item_on, position, face) - .await?; + should_try_decrement = + Self::run_is_block_place(player, block, server, use_item_on, position, face)?; } if should_try_decrement { @@ -216,26 +214,23 @@ impl JavaClient { BlockActionResult::Pass } - async fn run_is_block_place( - &self, + fn run_is_block_place( player: &Arc, block: &'static Block, server: &Arc, - use_item_on: SUseItemOn, + use_item_on: &SUseItemOn, location: BlockPos, face: BlockDirection, ) -> Result { match server .block_registry - .place_block(player, block, server, &use_item_on, location, face) - .await + .place_block(player, block, server, use_item_on, location, face) { Ok(Some((final_block_pos, new_state))) => { - self.send_packet(&CBlockUpdate::new( + player.try_send_client_packet(&CBlockUpdate::new( final_block_pos, VarInt(i32::from(new_state.as_u16())), - )) - .await; + )); Ok(true) } Ok(None) => Ok(false), diff --git a/crates/pumpkin/src/net/java/status.rs b/crates/pumpkin/src/net/java/status.rs index 795e76621..2d661f280 100644 --- a/crates/pumpkin/src/net/java/status.rs +++ b/crates/pumpkin/src/net/java/status.rs @@ -19,7 +19,7 @@ impl PendingConnection { let status = server.get_status(); status .lock() - .await + .unwrap_or_else(std::sync::PoisonError::into_inner) .get_status_response(self.version.load().protocol_version()) }; diff --git a/crates/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1/boss_bar.rs b/crates/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1/boss_bar.rs index 90063c8e7..92b06ef42 100644 --- a/crates/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1/boss_bar.rs +++ b/crates/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1/boss_bar.rs @@ -214,7 +214,6 @@ impl boss_bar::HostBossBar for PluginHostState { &pbb.bossbar.uuid, pbb.bossbar.color, pbb.bossbar.division, - pbb.bossbar.flags, ); } } @@ -241,7 +240,6 @@ impl boss_bar::HostBossBar for PluginHostState { &pbb.bossbar.uuid, pbb.bossbar.color, pbb.bossbar.division, - pbb.bossbar.flags, ); } } diff --git a/crates/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1/entity.rs b/crates/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1/entity.rs index 98476dfc9..206ff1513 100644 --- a/crates/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1/entity.rs +++ b/crates/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1/entity.rs @@ -528,8 +528,7 @@ impl HostEntity for PluginHostState { .clone(); if let Some(v) = current_vehicle { v.get_entity() - .remove_passenger(entity_base.get_entity().entity_id) - .await; + .remove_passenger(entity_base.get_entity().entity_id); } if let Some(vehicle_res) = vehicle { @@ -584,8 +583,7 @@ impl HostEntity for PluginHostState { let passenger = entity_from_resource(self, &passenger)?; entity .get_entity() - .remove_passenger(passenger.get_entity().entity_id) - .await; + .remove_passenger(passenger.get_entity().entity_id); Ok(()) } @@ -600,7 +598,7 @@ impl HostEntity for PluginHostState { .map(|p| p.get_entity().entity_id) .collect(); for id in ids { - entity.get_entity().remove_passenger(id).await; + entity.get_entity().remove_passenger(id); } Ok(()) } diff --git a/crates/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1/events/world.rs b/crates/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1/events/world.rs index b9feac2a2..ac95d223c 100644 --- a/crates/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1/events/world.rs +++ b/crates/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1/events/world.rs @@ -60,11 +60,14 @@ impl ToFromWasmEvent for ChunkLoad { .add_world(self.world.clone()) .expect("failed to add world resource"); - let guard = self.chunk.blocking_read(); + let (chunk_x, chunk_z) = self + .chunk + .try_read() + .map_or((0, 0), |guard| (guard.x, guard.z)); Event::ChunkLoadEvent(ChunkLoadEventData { target_world, - chunk_x: guard.x, - chunk_z: guard.z, + chunk_x, + chunk_z, cancelled: self.cancelled, }) } @@ -110,11 +113,14 @@ impl ToFromWasmEvent for ChunkSave { .add_world(self.world.clone()) .expect("failed to add world resource"); - let guard = self.chunk.blocking_read(); + let (chunk_x, chunk_z) = self + .chunk + .try_read() + .map_or((0, 0), |guard| (guard.x, guard.z)); Event::ChunkSaveEvent(ChunkSaveEventData { target_world, - chunk_x: guard.x, - chunk_z: guard.z, + chunk_x, + chunk_z, cancelled: self.cancelled, }) } diff --git a/crates/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1/mob.rs b/crates/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1/mob.rs index c20a9d115..1b292b377 100644 --- a/crates/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1/mob.rs +++ b/crates/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1/mob.rs @@ -226,7 +226,7 @@ impl Goal for CustomWasmGoal { let goal_id = self.goal_id; let world = entity_arc.get_entity().world.load(); if let Some(server) = world.server.upgrade() { - server.spawn_task(async move { + let run = async move { let mut store = plugin.store.lock().await; match plugin.plugin_instance { PluginInstance::V0_1(ref plugin_inst) => { @@ -248,7 +248,12 @@ impl Goal for CustomWasmGoal { let server_rep = server_res.rep(); let entity_rep = entity_res.rep(); let _ = plugin_inst - .call_handle_ai_goal_start(&mut *store, goal_id, server_res, entity_res) + .call_handle_ai_goal_start( + &mut *store, + goal_id, + server_res, + entity_res, + ) .await; let _ = store .data_mut() @@ -264,7 +269,15 @@ impl Goal for CustomWasmGoal { ); } } - }); + }; + + if tokio::runtime::Handle::try_current().is_ok() { + tokio::task::block_in_place(|| { + server.runtime.block_on(run); + }); + } else { + server.runtime.block_on(run); + } } } } @@ -275,7 +288,7 @@ impl Goal for CustomWasmGoal { let goal_id = self.goal_id; let world = entity_arc.get_entity().world.load(); if let Some(server) = world.server.upgrade() { - server.spawn_task(async move { + let run = async move { let mut store = plugin.store.lock().await; match plugin.plugin_instance { PluginInstance::V0_1(ref plugin_inst) => { @@ -297,7 +310,12 @@ impl Goal for CustomWasmGoal { let server_rep = server_res.rep(); let entity_rep = entity_res.rep(); let _ = plugin_inst - .call_handle_ai_goal_tick(&mut *store, goal_id, server_res, entity_res) + .call_handle_ai_goal_tick( + &mut *store, + goal_id, + server_res, + entity_res, + ) .await; let _ = store .data_mut() @@ -313,7 +331,15 @@ impl Goal for CustomWasmGoal { ); } } - }); + }; + + if tokio::runtime::Handle::try_current().is_ok() { + tokio::task::block_in_place(|| { + server.runtime.block_on(run); + }); + } else { + server.runtime.block_on(run); + } } } } @@ -324,7 +350,7 @@ impl Goal for CustomWasmGoal { let goal_id = self.goal_id; let world = entity_arc.get_entity().world.load(); if let Some(server) = world.server.upgrade() { - server.spawn_task(async move { + let run = async move { let mut store = plugin.store.lock().await; match plugin.plugin_instance { PluginInstance::V0_1(ref plugin_inst) => { @@ -346,7 +372,12 @@ impl Goal for CustomWasmGoal { let server_rep = server_res.rep(); let entity_rep = entity_res.rep(); let _ = plugin_inst - .call_handle_ai_goal_stop(&mut *store, goal_id, server_res, entity_res) + .call_handle_ai_goal_stop( + &mut *store, + goal_id, + server_res, + entity_res, + ) .await; let _ = store .data_mut() @@ -362,7 +393,15 @@ impl Goal for CustomWasmGoal { ); } } - }); + }; + + if tokio::runtime::Handle::try_current().is_ok() { + tokio::task::block_in_place(|| { + server.runtime.block_on(run); + }); + } else { + server.runtime.block_on(run); + } } } } diff --git a/crates/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1/player.rs b/crates/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1/player.rs index a9b5010ab..9e05bac37 100644 --- a/crates/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1/player.rs +++ b/crates/pumpkin/src/plugin/loader/wasm/wasm_host/wit/v0_1/player.rs @@ -1691,7 +1691,7 @@ impl pumpkin::plugin::player::HostPlayer for PluginHostState { async fn send_stats(&mut self, player: Resource) -> wasmtime::Result<()> { let player = player_from_resource(self, &player)?; - player.send_stats().await; + player.send_stats(); Ok(()) } @@ -1843,7 +1843,7 @@ impl pumpkin::plugin::player::HostPlayer for PluginHostState { pos_vec.y as i32, pos_vec.z as i32, ); - player.set_compass_target(block_pos).await; + player.set_compass_target(block_pos); Ok(()) } @@ -2101,7 +2101,7 @@ impl pumpkin::plugin::player::HostPlayer for PluginHostState { let header = text_component_from_resource(self, &header); let footer = text_component_from_resource(self, &footer); let player = player_from_resource(self, &player)?; - player.set_tab_list_header_footer(header, footer).await; + player.set_tab_list_header_footer(&header, &footer); Ok(()) } @@ -2350,7 +2350,7 @@ impl pumpkin::plugin::player::HostPlayer for PluginHostState { level: u8, ) -> wasmtime::Result<()> { let player = player_from_resource(self, &player)?; - player.set_food_level(level).await; + player.set_food_level(level); Ok(()) } diff --git a/crates/pumpkin/src/plugin/mod.rs b/crates/pumpkin/src/plugin/mod.rs index 1426816d4..de368e9be 100644 --- a/crates/pumpkin/src/plugin/mod.rs +++ b/crates/pumpkin/src/plugin/mod.rs @@ -7,7 +7,7 @@ use std::{ collections::{HashMap, HashSet}, path::{Path, PathBuf}, pin::Pin, - sync::{Arc, atomic::AtomicBool}, + sync::{Arc, RwLock as SyncRwLock, atomic::AtomicBool}, thread::ThreadId, time::Duration, }; @@ -172,7 +172,7 @@ pub enum PluginState { /// Core plugin management system pub struct PluginManager { - plugins: RwLock>, + plugins: SyncRwLock>, loaders: RwLock>>, handlers: Arc>, unloaded_files: RwLock>, @@ -227,7 +227,7 @@ impl PluginManager { #[must_use] pub fn new(verify_plugin_signatures: bool) -> Self { Self { - plugins: RwLock::new(Vec::new()), + plugins: SyncRwLock::new(Vec::new()), loaders: RwLock::new(vec![ Arc::new(NativePluginLoader), Arc::new(WasmPluginLoader::new(verify_plugin_signatures)), @@ -246,7 +246,10 @@ impl PluginManager { /// Unload all loaded plugins pub async fn unload_all_plugins(&self) -> Result<(), ManagerError> { let plugin_names: Vec = { - let plugins = self.plugins.read().await; + let plugins = self + .plugins + .read() + .unwrap_or_else(std::sync::PoisonError::into_inner); plugins .iter() .filter(|p| p.is_active) @@ -318,7 +321,10 @@ impl PluginManager { // We need to find if this plugin is already loaded to unload it first let plugin_name = { - let plugins = manager.plugins.read().await; + let plugins = manager + .plugins + .read() + .unwrap_or_else(std::sync::PoisonError::into_inner); plugins .iter() .find(|p| p.path == path) @@ -562,7 +568,10 @@ impl PluginManager { }; let plugin_index = { - let mut plugins = self.plugins.write().await; + let mut plugins = self + .plugins + .write() + .unwrap_or_else(std::sync::PoisonError::into_inner); plugins.push(plugin); plugins.len() - 1 }; @@ -579,7 +588,10 @@ impl PluginManager { Ok(()) => { // Update plugin state to loaded { - let mut plugins = self_ref_clone.plugins.write().await; + let mut plugins = self_ref_clone + .plugins + .write() + .unwrap_or_else(std::sync::PoisonError::into_inner); if let Some(plugin) = plugins.get_mut(plugin_index) { plugin.instance = Some(instance); plugin.is_active = true; @@ -608,7 +620,10 @@ impl PluginManager { // Get the loader data before removing the plugin let loader_data: Option> = { - let mut plugins = self_ref_clone.plugins.write().await; + let mut plugins = self_ref_clone + .plugins + .write() + .unwrap_or_else(std::sync::PoisonError::into_inner); if let Some(plugin) = plugins.get_mut(plugin_index) { plugin.loader_data.take() } else { @@ -622,7 +637,10 @@ impl PluginManager { } { - let mut plugins = self_ref_clone.plugins.write().await; + let mut plugins = self_ref_clone + .plugins + .write() + .unwrap_or_else(std::sync::PoisonError::into_inner); if plugin_index < plugins.len() { plugins.remove(plugin_index); } @@ -1010,7 +1028,10 @@ impl PluginManager { /// Checks if plugin active #[must_use] pub fn is_plugin_active(&self, name: &str) -> bool { - let plugins = self.plugins.blocking_read(); + let plugins = self + .plugins + .read() + .unwrap_or_else(std::sync::PoisonError::into_inner); plugins .iter() .any(|p| p.metadata.name == name && p.is_active && p.instance.is_some()) @@ -1019,7 +1040,10 @@ impl PluginManager { /// Get list of active plugins #[must_use] pub fn active_plugins(&self) -> Vec { - let plugins = self.plugins.blocking_read(); + let plugins = self + .plugins + .read() + .unwrap_or_else(std::sync::PoisonError::into_inner); plugins .iter() .filter(|p| p.is_active && p.instance.is_some()) @@ -1030,29 +1054,34 @@ impl PluginManager { /// Checks if plugin loaded #[must_use] pub fn is_plugin_loaded(&self, name: &str) -> bool { - let plugins = self.plugins.blocking_read(); + let plugins = self + .plugins + .read() + .unwrap_or_else(std::sync::PoisonError::into_inner); plugins.iter().any(|p| p.metadata.name == name) } /// Get list of loaded plugins #[must_use] pub fn loaded_plugins(&self) -> Vec { - let plugins = self.plugins.blocking_read(); + let plugins = self + .plugins + .read() + .unwrap_or_else(std::sync::PoisonError::into_inner); plugins.iter().map(|p| p.metadata.clone()).collect() } /// Unload a plugin by name pub async fn unload_plugin(&self, name: &str) -> Result<(), ManagerError> { - let index = { - let plugins = self.plugins.read().await; - plugins + let mut plugin = { + let mut plugins = self + .plugins + .write() + .unwrap_or_else(std::sync::PoisonError::into_inner); + let index = plugins .iter() .position(|p| p.metadata.name == name) - .ok_or_else(|| ManagerError::PluginNotFound(name.to_string()))? - }; - - let mut plugin = { - let mut plugins = self.plugins.write().await; + .ok_or_else(|| ManagerError::PluginNotFound(name.to_string()))?; plugins.remove(index) }; @@ -1066,7 +1095,10 @@ impl PluginManager { } } else { plugin.is_active = false; - self.plugins.write().await.push(plugin); + self.plugins + .write() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .push(plugin); } // Remove from plugin states @@ -1212,12 +1244,18 @@ impl PluginManager { return Err(()); } - let plugins = self.plugins.read().await; - let target_plugin = &plugins - .iter() - .find(|p| p.metadata.name == recipient) - .ok_or(())?; - if let Some(instance) = &target_plugin.instance { + let instance = { + let plugins = self + .plugins + .read() + .unwrap_or_else(std::sync::PoisonError::into_inner); + let target_plugin = plugins + .iter() + .find(|p| p.metadata.name == recipient) + .ok_or(())?; + target_plugin.instance.clone() + }; + if let Some(instance) = instance { Ok(instance.on_ipc_message(sender, message).await) } else { Err(()) diff --git a/crates/pumpkin/src/server/mod.rs b/crates/pumpkin/src/server/mod.rs index 02f6151f1..3394393d1 100644 --- a/crates/pumpkin/src/server/mod.rs +++ b/crates/pumpkin/src/server/mod.rs @@ -49,7 +49,7 @@ use std::net::IpAddr; use std::sync::Arc; use std::sync::atomic::{AtomicBool, AtomicI32, AtomicI64, AtomicU32}; use std::{future::Future, sync::atomic::Ordering, time::Duration}; -use tokio::sync::{Mutex, OnceCell}; +use tokio::sync::OnceCell; use tokio::task::JoinHandle; use tokio_util::task::TaskTracker; @@ -91,7 +91,7 @@ pub struct Server { /// Cached Bedrock server private key (process-lifetime). Generated on first Bedrock login and reused. pub bedrock_private_key: OnceCell>, /// Manages server status information. - listing: Mutex, + listing: std::sync::Mutex, /// Saves server branding information. branding: CachedBranding, /// Saves and dispatches commands to appropriate handlers. @@ -162,7 +162,6 @@ impl Server { // First register the default commands. After that, plugins can put in their own. let command_dispatcher = ArcSwap::from_pointee(default_dispatcher( &permission_manager, - &basic_config, &advanced_config.commands, )); @@ -225,7 +224,7 @@ impl Server { let seed = level_info.world_gen_settings.seed; let level_info = Arc::new(ArcSwap::new(Arc::new(level_info))); - let listing = Mutex::new(CachedStatus::new( + let listing = std::sync::Mutex::new(CachedStatus::new( &basic_config, &advanced_config.networking.java.motd, advanced_config.networking.java.max_players, @@ -600,7 +599,7 @@ impl Server { /// # Note /// /// You still have to spawn the `Player` in a `World` to let them join and make them visible. - pub async fn add_player( + pub fn add_player( self: &Arc, client: Arc, profile: GameProfile, @@ -660,14 +659,12 @@ impl Server { advancements.player = Arc::downgrade(&player); }; - send_cancellable! {{ + send_cancellable_blocking! {{ self; &mut PlayerLoginEvent::new(player.clone(), TextComponent::text("You have been kicked from the server")); 'after: { player.screen_handler_sync_handler.store_player(player.clone()); - if world - .add_player(&player) - .is_ok() { + world.add_player(&player).is_ok().then(|| { { let mut user_cache = self .data @@ -681,14 +678,15 @@ impl Server { if let Some(config) = config { // TODO: Config so we can also just ignore this hehe if config.server_listing { - self.listing.lock().await.add_player(&player); + self.listing + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .add_player(&player); } } - Some((player, world.clone())) - } else { - None - } + (player, world) + }) } 'cancelled: { @@ -698,14 +696,17 @@ impl Server { }} } - pub async fn remove_player(&self, player: &Player) { + pub fn remove_player(&self, player: &Player) { player.increment_stat( pumpkin_data::statistic::StatisticCategory::Custom, pumpkin_data::statistic::CustomStatistic::LeaveGame as i32, 1, ); // TODO: Config if we want decrease online - self.listing.lock().await.remove_player(player); + self.listing + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .remove_player(player); } pub async fn shutdown(&self) { @@ -977,33 +978,49 @@ impl Server { self.branding.get_branding() } - pub const fn get_status(&self) -> &Mutex { + pub const fn get_status(&self) -> &std::sync::Mutex { &self.listing } + async fn get_or_init_key_store(&self) -> &Arc { + self.key_store + .get_or_init(|| async { + let (tx, rx) = tokio::sync::oneshot::channel(); + rayon::spawn(move || { + let _ = tx.send(Arc::new(KeyStore::new())); + }); + rx.await.unwrap_or_else(|_| Arc::new(KeyStore::new())) + }) + .await + } + pub async fn encryption_request<'a>( &'a self, verification_token: &'a [u8; 4], should_authenticate: bool, ) -> CEncryptionRequest<'a> { - self.key_store - .get_or_init(|| async { Arc::new(KeyStore::new()) }) - .await - .encryption_request("", verification_token, should_authenticate) + self.get_or_init_key_store().await.encryption_request( + "", + verification_token, + should_authenticate, + ) } pub async fn decrypt(&self, data: &[u8]) -> Result, EncryptionError> { - self.key_store - .get_or_init(|| async { Arc::new(KeyStore::new()) }) - .await - .decrypt(data) + let key_store = self.get_or_init_key_store().await.clone(); + let data = data.to_vec(); + let (tx, rx) = tokio::sync::oneshot::channel(); + rayon::spawn(move || { + let _ = tx.send(key_store.decrypt(&data)); + }); + rx.await.map_err(|_| EncryptionError::FailedDecrypt)? } - pub async fn digest_secret(&self, secret: &[u8]) -> String { - self.key_store - .get_or_init(|| async { Arc::new(KeyStore::new()) }) - .await - .get_digest(secret) + pub fn digest_secret(&self, secret: &[u8]) -> String { + self.key_store.get().map_or_else( + || KeyStore::new().get_digest(secret), + |key_store| key_store.get_digest(secret), + ) } /// Main server tick method. This now handles both player/network ticking (which always runs) diff --git a/crates/pumpkin/src/world/bossbar.rs b/crates/pumpkin/src/world/bossbar.rs index ae815f0fd..2b536d2d5 100644 --- a/crates/pumpkin/src/world/bossbar.rs +++ b/crates/pumpkin/src/world/bossbar.rs @@ -174,7 +174,6 @@ impl Player { uuid: &Uuid, color: BossbarColor, dividers: BossbarDivisions, - _flags: BossbarFlags, ) { let boss_action = BosseventAction::UpdateStyle { color: (color as u8).into(), diff --git a/crates/pumpkin/src/world/chunker.rs b/crates/pumpkin/src/world/chunker.rs index 7f77861dd..3e68ab1eb 100644 --- a/crates/pumpkin/src/world/chunker.rs +++ b/crates/pumpkin/src/world/chunker.rs @@ -40,7 +40,7 @@ pub fn is_within_view_distance( (target.x - center.x).abs().max((target.y - center.y).abs()) <= view_distance } -pub async fn update_position(player: &Arc) { +pub fn update_position(player: &Arc) { let entity = &player.get_entity(); let new_chunk_center = entity.chunk_pos.load(); let old_cylindrical = player.watched_section.load(); @@ -59,20 +59,18 @@ pub async fn update_position(player: &Arc) { match player.client.as_ref() { ClientPlatform::Java(java_client) => { - java_client - .send_packet(&CCenterChunk { - chunk_x: new_chunk_center.x.into(), - chunk_z: new_chunk_center.y.into(), - }) - .await; + java_client.try_send_packet(&CCenterChunk { + chunk_x: new_chunk_center.x.into(), + chunk_z: new_chunk_center.y.into(), + }); } ClientPlatform::Bedrock(bedrock_client) => { - bedrock_client - .send_packet(&CNetworkChunkPublisherUpdate::new( - player.get_entity().block_pos.load(), - u32::from(view_distance.get()) * 16, - )) - .await; + if let Ok(data) = bedrock_client.serialize_packet(&CNetworkChunkPublisherUpdate::new( + player.get_entity().block_pos.load(), + u32::from(view_distance.get()) * 16, + )) { + bedrock_client.try_enqueue_packet(data); + } } } let (loading_iter, unloading_iter) = @@ -101,26 +99,35 @@ pub async fn update_position(player: &Arc) { if let ClientPlatform::Java(client) = player.client.as_ref() { for chunk in &unloading_chunks { - client - .enqueue_client_packet(&CUnloadChunk::new(chunk.x, chunk.y)) - .await; + client.try_send_packet(&CUnloadChunk::new(chunk.x, chunk.y)); } } // Make sure the watched section and the chunk watcher updates are async atomic. We want to // ensure what we unload when the player disconnects is correct. - world - .level - .mark_chunks_as_newly_watched(&loading_chunks) - .await; - let chunks_to_clean = world - .level - .mark_chunks_as_not_watched(&unloading_chunks) - .await; + if !loading_chunks.is_empty() || !unloading_chunks.is_empty() { + let level = world.level.clone(); + let world_clone = world.clone(); + let loading_chunks_clone = loading_chunks.clone(); + let unloading_chunks_clone = unloading_chunks; - if !chunks_to_clean.is_empty() { - world.remove_entities_in_chunks(&chunks_to_clean).await; - world.level.clean_entity_chunks(&chunks_to_clean); + if let Some(server) = world.server.upgrade() { + server.spawn_task(async move { + level + .mark_chunks_as_newly_watched(&loading_chunks_clone) + .await; + let chunks_to_clean = level + .mark_chunks_as_not_watched(&unloading_chunks_clone) + .await; + + if !chunks_to_clean.is_empty() { + world_clone + .remove_entities_in_chunks(&chunks_to_clean) + .await; + world_clone.level.clean_entity_chunks(&chunks_to_clean); + } + }); + } } if !loading_chunks.is_empty() { diff --git a/crates/pumpkin/src/world/custom_bossbar.rs b/crates/pumpkin/src/world/custom_bossbar.rs index a897dc0a0..dd6badfad 100644 --- a/crates/pumpkin/src/world/custom_bossbar.rs +++ b/crates/pumpkin/src/world/custom_bossbar.rs @@ -363,7 +363,6 @@ impl CustomBossbars { &bossbar.bossbar_data.uuid, new_color, bossbar.bossbar_data.division, - bossbar.bossbar_data.flags, ); } } @@ -396,7 +395,6 @@ impl CustomBossbars { &bossbar.bossbar_data.uuid, bossbar.bossbar_data.color, new_style, - bossbar.bossbar_data.flags, ); } } diff --git a/crates/pumpkin/src/world/mod.rs b/crates/pumpkin/src/world/mod.rs index 7a459580b..b9322edfe 100644 --- a/crates/pumpkin/src/world/mod.rs +++ b/crates/pumpkin/src/world/mod.rs @@ -3188,7 +3188,7 @@ impl World { player.living_entity.entity.set_pos(position); player.living_entity.entity.set_rotation(yaw, pitch); player.living_entity.entity.last_pos.store(position); - chunker::update_position(player).await; + chunker::update_position(player); let center_chunk = player.living_entity.entity.chunk_pos.load(); let chunk = self @@ -3796,7 +3796,7 @@ impl World { ); } - pub async fn send_world_info( + pub fn send_world_info( &self, player: &Arc, position: Vector3, @@ -3815,9 +3815,7 @@ impl World { if let ClientPlatform::Java(client) = player.client.as_ref() && client.version.load() >= JavaMinecraftVersion::V_1_20_2 { - player - .send_client_packet(&CGameEvent::new(GameEvent::StartWaitingChunks, 0.0)) - .await; + player.try_send_client_packet(&CGameEvent::new(GameEvent::StartWaitingChunks, 0.0)); } let entity = &player.get_entity(); @@ -3840,7 +3838,7 @@ impl World { player.send_client_information(); - chunker::update_position(player).await; + chunker::update_position(player); // Update commands player.set_health(20.0); @@ -4220,9 +4218,7 @@ impl World { // TODO: difficulty, exp bar, status effect // Load chunks and send world info FIRST (before teleport packet) - target_world - .send_world_info(player, position, yaw, pitch) - .await; + target_world.send_world_info(player, position, yaw, pitch); // Ensure at least the center chunk is sent synchronously before teleport. if let crate::net::ClientPlatform::Java(java_client) = player.client.as_ref() { @@ -5003,7 +4999,7 @@ impl World { self: &Arc, position: &BlockPos, block_state_id: BlockStateId, - _flags: BlockFlags, + flags: BlockFlags, ) -> BlockStateId { let (chunk_coordinate, relative) = position.chunk_and_chunk_relative_position(); let replaced_block_state_id = self @@ -5023,7 +5019,7 @@ impl World { }) .unwrap_or(Block::AIR.default_state.id); - if replaced_block_state_id == block_state_id { + if !flags.contains(BlockFlags::FORCE_STATE) && replaced_block_state_id == block_state_id { return block_state_id; } diff --git a/crates/pumpkin/src/world/portal/mod.rs b/crates/pumpkin/src/world/portal/mod.rs index 7583142fe..648b12c5f 100644 --- a/crates/pumpkin/src/world/portal/mod.rs +++ b/crates/pumpkin/src/world/portal/mod.rs @@ -73,7 +73,6 @@ impl PortalType { current_level: &World, dest_world: Arc, caller: &dyn crate::entity::EntityBase, - _portal_entry_pos: BlockPos, source_portal: Option<&SourcePortalInfo>, ) -> Option { match self { diff --git a/crates/pumpkin/src/world/raid.rs b/crates/pumpkin/src/world/raid.rs index 818f295dd..22f4bb2c0 100644 --- a/crates/pumpkin/src/world/raid.rs +++ b/crates/pumpkin/src/world/raid.rs @@ -14,7 +14,6 @@ use pumpkin_util::text::TextComponent; use crate::entity::EntityBase; use crate::entity::mob::raider::create_ominous_banner; -use crate::entity::player::Player; use crate::entity::r#type::from_type; use crate::world::World; use crate::world::bossbar::{Bossbar, BossbarColor, BossbarDivisions}; @@ -260,7 +259,7 @@ impl Raid { self.players_in_raid.clear(); } - pub fn absorb_raid_omen(&mut self, _player: &Player) -> bool { + pub fn absorb_raid_omen(&mut self) -> bool { self.raid_omen_level = (self.raid_omen_level + 1).clamp(1, Self::MAX_RAID_OMEN_LEVEL); true } @@ -405,7 +404,7 @@ impl Raid { } #[must_use] - pub fn find_random_spawn_pos(&self, _world: &World, max_tries: usize) -> Option { + pub fn find_random_spawn_pos(&self, max_tries: usize) -> Option { let seconds_remaining = self.raid_cooldown_ticks / 20; let how_far = 0.22 * (seconds_remaining as f32) - 0.24; let start_angle = rand::random::() * std::f32::consts::PI * 2.0; @@ -559,7 +558,7 @@ impl Raid { } } else { if self.wave_spawn_pos.is_none() && self.raid_cooldown_ticks % 5 == 0 { - self.wave_spawn_pos = self.find_random_spawn_pos(world, 8); + self.wave_spawn_pos = self.find_random_spawn_pos(8); } if self.raid_cooldown_ticks == 300 || self.raid_cooldown_ticks % 20 == 0 { @@ -590,7 +589,7 @@ impl Raid { while self.should_spawn_group() { let spawn_pos = self .wave_spawn_pos - .or_else(|| self.find_random_spawn_pos(world, 20)) + .or_else(|| self.find_random_spawn_pos(20)) .unwrap_or(self.center); self.started = true; @@ -698,7 +697,6 @@ impl Raids { pub fn create_or_extend_raid( &mut self, - player: &Player, raid_position: BlockPos, world: &Arc, ) -> Option { @@ -714,14 +712,14 @@ impl Raids { if let Some(id) = existing_id { if let Some(raid) = self.raid_map.get_mut(&id) { - raid.absorb_raid_omen(player); + raid.absorb_raid_omen(); } Some(id) } else { self.next_id += 1; let id = self.next_id; let mut raid = Raid::new(id, raid_center_pos, world.level_info.load().difficulty); - raid.absorb_raid_omen(player); + raid.absorb_raid_omen(); self.raid_map.insert(id, raid); Some(id) }