From 8608ac0361c17c8daaada5c7bc341866b25c270c Mon Sep 17 00:00:00 2001 From: Alexander Medvedev Date: Wed, 2 Sep 2026 22:18:45 +0200 Subject: [PATCH] feat: add SynchedEntityData also some fixes --- crates/pumpkin-data/src/block_rotation.rs | 491 ++++++++++- crates/pumpkin-data/src/block_state.rs | 8 +- crates/pumpkin-data/src/blocks.rs | 52 +- crates/pumpkin-data/src/lib.rs | 2 +- .../src/codec/optional_int.rs | 1 + .../structure/structures/mansion/mod.rs | 128 +-- .../generation/structure/structures/mod.rs | 15 +- .../template/block_state_resolver.rs | 134 +-- .../structure/template/structure_template.rs | 49 +- .../pumpkin/src/command/commands/gamerule.rs | 11 + crates/pumpkin/src/entity/ageable.rs | 9 +- crates/pumpkin/src/entity/ai/goal/beg.rs | 12 +- .../pumpkin/src/entity/area_effect_cloud.rs | 66 +- crates/pumpkin/src/entity/boss/wither.rs | 50 +- crates/pumpkin/src/entity/breath.rs | 11 +- .../pumpkin/src/entity/decoration/display.rs | 496 ++++------- .../src/entity/decoration/end_crystal.rs | 19 +- .../src/entity/decoration/item_frame.rs | 45 +- crates/pumpkin/src/entity/falling.rs | 10 +- crates/pumpkin/src/entity/interaction.rs | 55 +- crates/pumpkin/src/entity/item.rs | 19 +- crates/pumpkin/src/entity/living.rs | 63 +- crates/pumpkin/src/entity/mob/bat.rs | 14 +- crates/pumpkin/src/entity/mob/blaze.rs | 12 +- crates/pumpkin/src/entity/mob/creaking.rs | 46 +- crates/pumpkin/src/entity/mob/creeper.rs | 48 +- crates/pumpkin/src/entity/mob/enderman.rs | 35 +- crates/pumpkin/src/entity/mob/evoker.rs | 10 +- crates/pumpkin/src/entity/mob/ghast.rs | 14 +- crates/pumpkin/src/entity/mob/hoglin.rs | 10 +- crates/pumpkin/src/entity/mob/mod.rs | 22 +- crates/pumpkin/src/entity/mob/piglin.rs | 57 +- crates/pumpkin/src/entity/mob/piglin_brute.rs | 19 +- crates/pumpkin/src/entity/mob/pillager.rs | 12 +- crates/pumpkin/src/entity/mob/raider.rs | 9 +- crates/pumpkin/src/entity/mob/shulker.rs | 27 +- crates/pumpkin/src/entity/mob/spider.rs | 12 +- crates/pumpkin/src/entity/mob/witch.rs | 12 +- .../pumpkin/src/entity/mob/zombie/zombie.rs | 12 +- crates/pumpkin/src/entity/mod.rs | 202 ++--- crates/pumpkin/src/entity/passive/allay.rs | 36 +- .../pumpkin/src/entity/passive/armadillo.rs | 27 +- crates/pumpkin/src/entity/passive/axolotl.rs | 63 +- crates/pumpkin/src/entity/passive/bee.rs | 27 +- crates/pumpkin/src/entity/passive/camel.rs | 43 +- crates/pumpkin/src/entity/passive/cat.rs | 132 +-- crates/pumpkin/src/entity/passive/chicken.rs | 17 +- .../src/entity/passive/copper_golem.rs | 38 +- crates/pumpkin/src/entity/passive/donkey.rs | 44 +- crates/pumpkin/src/entity/passive/fox.rs | 44 +- crates/pumpkin/src/entity/passive/frog.rs | 27 +- crates/pumpkin/src/entity/passive/goat.rs | 63 +- .../pumpkin/src/entity/passive/happy_ghast.rs | 46 +- crates/pumpkin/src/entity/passive/horse.rs | 45 +- .../pumpkin/src/entity/passive/iron_golem.rs | 17 +- crates/pumpkin/src/entity/passive/llama.rs | 80 +- .../pumpkin/src/entity/passive/mooshroom.rs | 27 +- crates/pumpkin/src/entity/passive/mule.rs | 44 +- crates/pumpkin/src/entity/passive/nautilus.rs | 21 +- crates/pumpkin/src/entity/passive/ocelot.rs | 26 +- crates/pumpkin/src/entity/passive/panda.rs | 90 +- .../pumpkin/src/entity/passive/polar_bear.rs | 27 +- crates/pumpkin/src/entity/passive/rabbit.rs | 27 +- crates/pumpkin/src/entity/passive/sheep.rs | 12 +- .../src/entity/passive/skeleton_horse.rs | 28 +- crates/pumpkin/src/entity/passive/sniffer.rs | 46 +- .../pumpkin/src/entity/passive/snow_golem.rs | 19 +- crates/pumpkin/src/entity/passive/strider.rs | 27 +- crates/pumpkin/src/entity/passive/tamable.rs | 28 +- .../src/entity/passive/trader_llama.rs | 80 +- crates/pumpkin/src/entity/passive/turtle.rs | 42 +- .../src/entity/passive/villager/mod.rs | 94 +- crates/pumpkin/src/entity/passive/wolf.rs | 54 +- .../src/entity/passive/zombie_horse.rs | 27 +- crates/pumpkin/src/entity/player.rs | 73 +- crates/pumpkin/src/entity/projectile/arrow.rs | 91 +- crates/pumpkin/src/entity/projectile/egg.rs | 10 +- .../src/entity/projectile/eye_of_ender.rs | 19 +- .../pumpkin/src/entity/projectile/fireball.rs | 19 +- .../src/entity/projectile/firework_rocket.rs | 11 +- .../src/entity/projectile/fishing_bobber.rs | 10 +- .../src/entity/projectile/lingering_potion.rs | 11 +- .../src/entity/projectile/splash_potion.rs | 11 +- .../src/entity/projectile/wither_skull.rs | 20 +- .../pumpkin/src/entity/synched_entity_data.rs | 221 +++++ crates/pumpkin/src/entity/tnt.rs | 21 +- crates/pumpkin/src/entity/vehicle/boat.rs | 15 +- .../src/entity/vehicle/minecart/furnace.rs | 10 +- crates/pumpkin/src/entity/vehicle/vehicle.rs | 29 +- crates/pumpkin/src/item/items/spawn_egg.rs | 9 +- crates/pumpkin/src/world/chunker.rs | 47 +- crates/pumpkin/src/world/entity_tracker.rs | 203 ++++- crates/pumpkin/src/world/mod.rs | 3 + crates/pumpkin/src/world/natural_spawner.rs | 809 ++++++++++-------- 94 files changed, 2576 insertions(+), 2853 deletions(-) create mode 100644 crates/pumpkin/src/entity/synched_entity_data.rs diff --git a/crates/pumpkin-data/src/block_rotation.rs b/crates/pumpkin-data/src/block_rotation.rs index 638aaeb94..3b7cc0d78 100644 --- a/crates/pumpkin-data/src/block_rotation.rs +++ b/crates/pumpkin-data/src/block_rotation.rs @@ -231,9 +231,9 @@ pub enum Mirror { /// No mirroring #[default] None, - /// Mirror along the X axis (left-right flip when looking north) + /// Mirror along the Z axis (inverting Z: north <-> south) LeftRight, - /// Mirror along the Z axis (front-back flip) + /// Mirror along the X axis (inverting X: east <-> west) FrontBack, } @@ -249,8 +249,8 @@ impl Mirror { pub const fn transform_pos(&self, pos: Vector3, size: Vector3) -> Vector3 { match self { Self::None => pos, - Self::LeftRight => Vector3::new(size.x - 1 - pos.x, pos.y, pos.z), - Self::FrontBack => Vector3::new(pos.x, pos.y, size.z - 1 - pos.z), + Self::LeftRight => Vector3::new(pos.x, pos.y, size.z - 1 - pos.z), + Self::FrontBack => Vector3::new(size.x - 1 - pos.x, pos.y, pos.z), } } @@ -266,19 +266,19 @@ impl Mirror { _ => leak_str(facing), }, Self::LeftRight => match facing { - "east" => "west", - "west" => "east", - "north" => "north", - "south" => "south", - _ => leak_str(facing), - }, - Self::FrontBack => match facing { "north" => "south", "south" => "north", "east" => "east", "west" => "west", _ => leak_str(facing), }, + Self::FrontBack => match facing { + "east" => "west", + "west" => "east", + "north" => "north", + "south" => "south", + _ => leak_str(facing), + }, } } @@ -287,8 +287,8 @@ impl Mirror { pub const fn mirror_block_rotation(&self, rotation: i32) -> i32 { match self { Self::None => rotation, - Self::LeftRight => (16 - rotation) % 16, - Self::FrontBack => (8 - rotation + 16) % 16, + Self::LeftRight => (8 - rotation + 16) % 16, + Self::FrontBack => (16 - rotation) % 16, } } @@ -298,17 +298,17 @@ impl Mirror { match self { Self::None => rotation, Self::LeftRight => match rotation { - Rotation::None => Rotation::None, - Rotation::Clockwise90 => Rotation::CounterClockwise90, - Rotation::Rotate180 => Rotation::Rotate180, - Rotation::CounterClockwise90 => Rotation::Clockwise90, - }, - Self::FrontBack => match rotation { Rotation::None => Rotation::Rotate180, Rotation::Clockwise90 => Rotation::Clockwise90, Rotation::Rotate180 => Rotation::None, Rotation::CounterClockwise90 => Rotation::CounterClockwise90, }, + Self::FrontBack => match rotation { + Rotation::None => Rotation::None, + Rotation::Clockwise90 => Rotation::CounterClockwise90, + Rotation::Rotate180 => Rotation::Rotate180, + Rotation::CounterClockwise90 => Rotation::Clockwise90, + }, } } @@ -383,8 +383,461 @@ impl Mirror { } } +/// Transforms rail shape based on rotation and mirror matching vanilla Minecraft BaseRailBlock. +#[must_use] +pub fn transform_rail_shape(shape: &str, rotation: Rotation, mirror: Mirror) -> String { + let mut shape = shape; + + match mirror { + Mirror::LeftRight => { + shape = match shape { + "ascending_north" => "ascending_south", + "ascending_south" => "ascending_north", + "south_east" => "north_east", + "south_west" => "north_west", + "north_west" => "south_west", + "north_east" => "south_east", + _ => shape, + }; + } + Mirror::FrontBack => { + shape = match shape { + "ascending_east" => "ascending_west", + "ascending_west" => "ascending_east", + "south_east" => "south_west", + "south_west" => "south_east", + "north_west" => "north_east", + "north_east" => "north_west", + _ => shape, + }; + } + Mirror::None => {} + } + + match rotation { + Rotation::Rotate180 => { + shape = match shape { + "ascending_east" => "ascending_west", + "ascending_west" => "ascending_east", + "ascending_north" => "ascending_south", + "ascending_south" => "ascending_north", + "south_east" => "north_west", + "south_west" => "north_east", + "north_west" => "south_east", + "north_east" => "south_west", + _ => shape, + }; + } + Rotation::CounterClockwise90 => { + shape = match shape { + "ascending_east" => "ascending_north", + "ascending_west" => "ascending_south", + "ascending_north" => "ascending_west", + "ascending_south" => "ascending_east", + "north_south" => "east_west", + "east_west" => "north_south", + "south_east" => "north_east", + "south_west" => "south_east", + "north_west" => "south_west", + "north_east" => "north_west", + _ => shape, + }; + } + Rotation::Clockwise90 => { + shape = match shape { + "ascending_east" => "ascending_south", + "ascending_west" => "ascending_north", + "ascending_north" => "ascending_east", + "ascending_south" => "ascending_west", + "north_south" => "east_west", + "east_west" => "north_south", + "south_east" => "south_west", + "south_west" => "north_west", + "north_west" => "north_east", + "north_east" => "south_east", + _ => shape, + }; + } + Rotation::None => {} + } + + shape.to_string() +} + +/// Transforms block state properties based on rotation and mirror, exactly matching vanilla Minecraft. +/// +/// Vanilla executes mirror first, then rotation. +#[must_use] +pub fn transform_block_properties, V: AsRef>( + block_name: &str, + properties: &[(K, V)], + rotation: Rotation, + mirror: Mirror, +) -> Vec<(String, String)> { + if rotation == Rotation::None && mirror == Mirror::None { + return properties + .iter() + .map(|(k, v)| (k.as_ref().to_string(), v.as_ref().to_string())) + .collect(); + } + + let is_stairs = block_name.ends_with("_stairs"); + let is_door = block_name.ends_with("_door") && !block_name.ends_with("trapdoor"); + let is_rail = block_name == "minecraft:rail" + || block_name == "rail" + || block_name == "minecraft:powered_rail" + || block_name == "powered_rail" + || block_name == "minecraft:detector_rail" + || block_name == "detector_rail" + || block_name == "minecraft:activator_rail" + || block_name == "activator_rail"; + + if is_stairs { + let mut facing_opt = None; + let mut shape_opt = None; + for (k, v) in properties { + if k.as_ref() == "facing" { + facing_opt = Some(v.as_ref()); + } else if k.as_ref() == "shape" { + shape_opt = Some(v.as_ref()); + } + } + + let (new_facing, new_shape) = if let Some(facing) = facing_opt { + let shape = shape_opt.unwrap_or("straight"); + let mut f = facing; + let mut s = shape; + + // Step 1: StairBlock.mirror + match mirror { + Mirror::LeftRight => { + if f == "north" || f == "south" { + f = match f { + "north" => "south", + "south" => "north", + _ => f, + }; + s = match s { + "straight" => "straight", + "outer_left" => "outer_right", + "inner_right" => "inner_left", + "inner_left" => "inner_right", + "outer_right" => "outer_left", + _ => s, + }; + } + } + Mirror::FrontBack => { + if f == "east" || f == "west" { + f = match f { + "east" => "west", + "west" => "east", + _ => f, + }; + s = match s { + "straight" => "straight", + "outer_left" => "outer_right", + "inner_right" => "inner_right", + "inner_left" => "inner_left", + "outer_right" => "outer_left", + _ => s, + }; + } + } + Mirror::None => {} + } + + // Step 2: StairBlock.rotate (only rotates facing) + f = rotation.rotate_facing(f); + + (Some(f.to_string()), Some(s.to_string())) + } else { + (None, None) + }; + + return properties + .iter() + .map(|(k, v)| { + let k_str = k.as_ref(); + if k_str == "facing" { + if let Some(ref nf) = new_facing { + return (k_str.to_string(), nf.clone()); + } + } else if k_str == "shape" { + if let Some(ref ns) = new_shape { + return (k_str.to_string(), ns.clone()); + } + } + (k_str.to_string(), v.as_ref().to_string()) + }) + .collect(); + } + + if is_door { + let mut facing_opt = None; + let mut hinge_opt = None; + for (k, v) in properties { + if k.as_ref() == "facing" { + facing_opt = Some(v.as_ref()); + } else if k.as_ref() == "hinge" { + hinge_opt = Some(v.as_ref()); + } + } + + let (new_facing, new_hinge) = if let Some(facing) = facing_opt { + let hinge = hinge_opt.unwrap_or("left"); + let mut f = facing; + let mut h = hinge; + + // Step 1: DoorBlock.mirror + if mirror != Mirror::None { + f = mirror.mirror_facing(f); + h = match h { + "left" => "right", + "right" => "left", + _ => h, + }; + } + + // Step 2: DoorBlock.rotate + f = rotation.rotate_facing(f); + + (Some(f.to_string()), Some(h.to_string())) + } else { + (None, None) + }; + + return properties + .iter() + .map(|(k, v)| { + let k_str = k.as_ref(); + if k_str == "facing" { + if let Some(ref nf) = new_facing { + return (k_str.to_string(), nf.clone()); + } + } else if k_str == "hinge" { + if let Some(ref nh) = new_hinge { + return (k_str.to_string(), nh.clone()); + } + } + (k_str.to_string(), v.as_ref().to_string()) + }) + .collect(); + } + + if is_rail { + return properties + .iter() + .map(|(k, v)| { + let k_str = k.as_ref(); + if k_str == "shape" { + let s = transform_rail_shape(v.as_ref(), rotation, mirror); + (k_str.to_string(), s) + } else { + (k_str.to_string(), v.as_ref().to_string()) + } + }) + .collect(); + } + + // General property transformation + properties + .iter() + .map(|(k, v)| { + let key = k.as_ref(); + let val = v.as_ref(); + + let transformed_key = match key { + "north" | "south" | "east" | "west" => rotation + .rotate_facing(mirror.mirror_facing(key)) + .to_string(), + _ => key.to_string(), + }; + + let transformed_val = match key { + "facing" => { + let mirrored = mirror.mirror_facing(val); + rotation.rotate_facing(mirrored).to_string() + } + "orientation" => { + let mut parts = val.split('_'); + if let (Some(front), Some(top)) = (parts.next(), parts.next()) { + let mirrored_front = mirror.mirror_facing(front); + let rotated_front = rotation.rotate_facing(mirrored_front); + let mirrored_top = mirror.mirror_facing(top); + let rotated_top = rotation.rotate_facing(mirrored_top); + format!("{rotated_front}_{rotated_top}") + } else { + val.to_string() + } + } + "axis" => rotation.rotate_axis(val).to_string(), + "rotation" => val.parse::().map_or_else( + |_| val.to_string(), + |rot_value| { + let mirrored = mirror.mirror_block_rotation(rot_value); + let rotated = rotation.rotate_block_rotation(mirrored); + rotated.to_string() + }, + ), + _ => val.to_string(), + }; + + (transformed_key, transformed_val) + }) + .collect() +} + /// Leaks a string to get a 'static str. /// This is used for non-standard property values that aren't covered by static strings. pub fn leak_str(s: &str) -> &'static str { s.to_string().leak() } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn stairs_mirror_left_right() { + // LeftRight inverts Z (North <-> South) + let props = [("facing", "north"), ("shape", "inner_left")]; + let transformed = transform_block_properties( + "minecraft:oak_stairs", + &props, + Rotation::None, + Mirror::LeftRight, + ); + let facing = transformed + .iter() + .find(|(k, _)| k == "facing") + .map(|(_, v)| v.as_str()); + let shape = transformed + .iter() + .find(|(k, _)| k == "shape") + .map(|(_, v)| v.as_str()); + assert_eq!(facing, Some("south")); + assert_eq!(shape, Some("inner_right")); + + // LeftRight does not change East/West facing stairs shape or facing + let props = [("facing", "east"), ("shape", "inner_left")]; + let transformed = transform_block_properties( + "minecraft:oak_stairs", + &props, + Rotation::None, + Mirror::LeftRight, + ); + let facing = transformed + .iter() + .find(|(k, _)| k == "facing") + .map(|(_, v)| v.as_str()); + let shape = transformed + .iter() + .find(|(k, _)| k == "shape") + .map(|(_, v)| v.as_str()); + assert_eq!(facing, Some("east")); + assert_eq!(shape, Some("inner_left")); + } + + #[test] + fn stairs_mirror_front_back() { + // FrontBack inverts X (East <-> West) + let props = [("facing", "east"), ("shape", "outer_left")]; + let transformed = transform_block_properties( + "minecraft:oak_stairs", + &props, + Rotation::None, + Mirror::FrontBack, + ); + let facing = transformed + .iter() + .find(|(k, _)| k == "facing") + .map(|(_, v)| v.as_str()); + let shape = transformed + .iter() + .find(|(k, _)| k == "shape") + .map(|(_, v)| v.as_str()); + assert_eq!(facing, Some("west")); + assert_eq!(shape, Some("outer_right")); + + // FrontBack does not change North/South facing stairs + let props = [("facing", "north"), ("shape", "outer_left")]; + let transformed = transform_block_properties( + "minecraft:oak_stairs", + &props, + Rotation::None, + Mirror::FrontBack, + ); + let facing = transformed + .iter() + .find(|(k, _)| k == "facing") + .map(|(_, v)| v.as_str()); + let shape = transformed + .iter() + .find(|(k, _)| k == "shape") + .map(|(_, v)| v.as_str()); + assert_eq!(facing, Some("north")); + assert_eq!(shape, Some("outer_left")); + } + + #[test] + fn stairs_mirror_and_rotate() { + // Mirror LeftRight then Rotate Clockwise90 + let props = [("facing", "north"), ("shape", "inner_left")]; + let transformed = transform_block_properties( + "minecraft:oak_stairs", + &props, + Rotation::Clockwise90, + Mirror::LeftRight, + ); + let facing = transformed + .iter() + .find(|(k, _)| k == "facing") + .map(|(_, v)| v.as_str()); + let shape = transformed + .iter() + .find(|(k, _)| k == "shape") + .map(|(_, v)| v.as_str()); + // north -> south (via LeftRight mirror) -> west (via Clockwise90 rotation) + assert_eq!(facing, Some("west")); + // inner_left -> inner_right (via LeftRight mirror, unchanged by rotation) + assert_eq!(shape, Some("inner_right")); + } + + #[test] + fn door_mirror_and_rotate() { + let props = [("facing", "east"), ("hinge", "left")]; + let transformed = transform_block_properties( + "minecraft:oak_door", + &props, + Rotation::None, + Mirror::FrontBack, + ); + let facing = transformed + .iter() + .find(|(k, _)| k == "facing") + .map(|(_, v)| v.as_str()); + let hinge = transformed + .iter() + .find(|(k, _)| k == "hinge") + .map(|(_, v)| v.as_str()); + assert_eq!(facing, Some("west")); + assert_eq!(hinge, Some("right")); + } + + #[test] + fn rail_mirror_and_rotate() { + let props = [("shape", "north_east")]; + let transformed = transform_block_properties( + "minecraft:rail", + &props, + Rotation::Clockwise90, + Mirror::LeftRight, + ); + let shape = transformed + .iter() + .find(|(k, _)| k == "shape") + .map(|(_, v)| v.as_str()); + assert_eq!(shape, Some("south_west")); + } +} diff --git a/crates/pumpkin-data/src/block_state.rs b/crates/pumpkin-data/src/block_state.rs index 7cfdc6a4c..ad1b26d34 100644 --- a/crates/pumpkin-data/src/block_state.rs +++ b/crates/pumpkin-data/src/block_state.rs @@ -214,12 +214,12 @@ impl BlockState { } #[must_use] - pub const fn rotate(&self, rotation: crate::block_rotation::Rotation) -> &'static Self { + pub fn rotate(&self, rotation: crate::block_rotation::Rotation) -> &'static Self { Block::from_state_id(self.id).rotate(self.id, rotation) } #[must_use] - pub const fn mirror(&self, mirror: crate::block_rotation::Mirror) -> &'static Self { + pub fn mirror(&self, mirror: crate::block_rotation::Mirror) -> &'static Self { Block::from_state_id(self.id).mirror(self.id, mirror) } } @@ -283,13 +283,13 @@ impl BlockStateId { #[inline] #[must_use] - pub const fn rotate(self, rotation: crate::block_rotation::Rotation) -> &'static BlockState { + pub fn rotate(self, rotation: crate::block_rotation::Rotation) -> &'static BlockState { Block::from_state_id(self).rotate(self, rotation) } #[inline] #[must_use] - pub const fn mirror(self, mirror: crate::block_rotation::Mirror) -> &'static BlockState { + pub fn mirror(self, mirror: crate::block_rotation::Mirror) -> &'static BlockState { Block::from_state_id(self).mirror(self, mirror) } } diff --git a/crates/pumpkin-data/src/blocks.rs b/crates/pumpkin-data/src/blocks.rs index e5ab4db75..ee00106aa 100644 --- a/crates/pumpkin-data/src/blocks.rs +++ b/crates/pumpkin-data/src/blocks.rs @@ -208,21 +208,61 @@ impl Block { } #[must_use] - pub const fn mirror( + pub fn mirror( &self, id: BlockStateId, - _mirror: crate::block_rotation::Mirror, + mirror: crate::block_rotation::Mirror, ) -> &'static BlockState { - BlockState::from_id(id) + if mirror == crate::block_rotation::Mirror::None || self.states.len() <= 1 { + return BlockState::from_id(id); + } + if let Some(props) = self.properties(id) { + let props_vec = props.to_props(); + let transformed = crate::block_rotation::transform_block_properties( + self.name, + &props_vec, + crate::block_rotation::Rotation::None, + mirror, + ); + let transformed_refs: Vec<(&str, &str)> = transformed + .iter() + .map(|(k, v)| (k.as_str(), v.as_str())) + .collect(); + let new_props = self.from_properties(&transformed_refs); + let new_state_id = new_props.to_state_id(self); + BlockState::from_id(new_state_id) + } else { + BlockState::from_id(id) + } } #[must_use] - pub const fn rotate( + pub fn rotate( &self, id: BlockStateId, - _rotation: crate::block_rotation::Rotation, + rotation: crate::block_rotation::Rotation, ) -> &'static BlockState { - BlockState::from_id(id) + if rotation == crate::block_rotation::Rotation::None || self.states.len() <= 1 { + return BlockState::from_id(id); + } + if let Some(props) = self.properties(id) { + let props_vec = props.to_props(); + let transformed = crate::block_rotation::transform_block_properties( + self.name, + &props_vec, + rotation, + crate::block_rotation::Mirror::None, + ); + let transformed_refs: Vec<(&str, &str)> = transformed + .iter() + .map(|(k, v)| (k.as_str(), v.as_str())) + .collect(); + let new_props = self.from_properties(&transformed_refs); + let new_state_id = new_props.to_state_id(self); + BlockState::from_id(new_state_id) + } else { + BlockState::from_id(id) + } } } diff --git a/crates/pumpkin-data/src/lib.rs b/crates/pumpkin-data/src/lib.rs index 6cb7f9305..c8320a188 100644 --- a/crates/pumpkin-data/src/lib.rs +++ b/crates/pumpkin-data/src/lib.rs @@ -403,7 +403,7 @@ mod blocks; #[cfg(feature = "block")] pub use block_direction::{BlockDirection, FacingExt, HorizontalFacingExt}; #[cfg(feature = "block")] -pub use block_rotation::{Mirror, Rotation}; +pub use block_rotation::{Mirror, Rotation, transform_block_properties, transform_rail_shape}; #[cfg(feature = "block")] pub use block_state::{BlockState, BlockStateId}; #[cfg(feature = "block")] diff --git a/crates/pumpkin-protocol/src/codec/optional_int.rs b/crates/pumpkin-protocol/src/codec/optional_int.rs index d0c63382b..548e3815f 100644 --- a/crates/pumpkin-protocol/src/codec/optional_int.rs +++ b/crates/pumpkin-protocol/src/codec/optional_int.rs @@ -1,3 +1,4 @@ +#[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct OptionalInt(pub Option); impl OptionalInt { diff --git a/crates/pumpkin-world/src/generation/structure/structures/mansion/mod.rs b/crates/pumpkin-world/src/generation/structure/structures/mansion/mod.rs index 5674a920f..e4cb49032 100644 --- a/crates/pumpkin-world/src/generation/structure/structures/mansion/mod.rs +++ b/crates/pumpkin-world/src/generation/structure/structures/mansion/mod.rs @@ -99,10 +99,8 @@ impl TemplateMirror { const fn state_mirror(self) -> Mirror { match self { Self::None => Mirror::None, - // Mojang names a Z reflection LEFT_RIGHT and an X reflection FRONT_BACK. - // Pumpkin names the axis being reflected, so the variants are exchanged. - Self::LeftRight => Mirror::FrontBack, - Self::FrontBack => Mirror::LeftRight, + Self::LeftRight => Mirror::LeftRight, + Self::FrontBack => Mirror::FrontBack, } } } @@ -298,14 +296,14 @@ impl MansionTemplatePiece { continue; } - let mut transformed = transformed_palette(palette, self.mirror, self.rotation); + let mut placed_entry = palette.clone(); if chunk.get_block_state(&position).to_block_id() == Block::WATER.id - && property(&transformed, "waterlogged").is_some() + && property(&placed_entry, "waterlogged").is_some() { - set_property(&mut transformed, "waterlogged", "true"); + set_property(&mut placed_entry, "waterlogged", "true"); } let Some(state) = BlockStateResolver::resolve( - &transformed, + &placed_entry, self.rotation, self.mirror.state_mirror(), ) else { @@ -510,48 +508,16 @@ fn transformed_palette( mirror: TemplateMirror, rotation: Rotation, ) -> PaletteEntry { - let mut palette = palette.clone(); - - if palette.name.ends_with("_stairs") { - let facing = property(&palette, "facing"); - let shape = property(&palette, "shape"); - let mirrored_shape = match (mirror, facing, shape) { - (TemplateMirror::LeftRight, Some("north" | "south"), Some("inner_left")) => { - Some("inner_right") - } - (TemplateMirror::LeftRight, Some("north" | "south"), Some("inner_right")) => { - Some("inner_left") - } - (TemplateMirror::LeftRight, Some("north" | "south"), Some("outer_left")) - | (TemplateMirror::FrontBack, Some("east" | "west"), Some("outer_left")) => { - Some("outer_right") - } - (TemplateMirror::LeftRight, Some("north" | "south"), Some("outer_right")) - | (TemplateMirror::FrontBack, Some("east" | "west"), Some("outer_right")) => { - Some("outer_left") - } - _ => None, - }; - if let Some(shape) = mirrored_shape { - set_property(&mut palette, "shape", shape); - } - } else if palette.name.ends_with("_door") && mirror != TemplateMirror::None { - let hinge = match property(&palette, "hinge") { - Some("left") => Some("right"), - Some("right") => Some("left"), - _ => None, - }; - if let Some(hinge) = hinge { - set_property(&mut palette, "hinge", hinge); - } - } else if palette.name == "minecraft:rail" - && let Some(shape) = property(&palette, "shape") - && let Some(shape) = transform_rail_shape(shape, mirror, rotation) - { - set_property(&mut palette, "shape", shape); + let properties = pumpkin_data::transform_block_properties( + &palette.name, + &palette.properties, + rotation, + mirror.state_mirror(), + ); + PaletteEntry { + name: palette.name.clone(), + properties, } - - palette } fn property<'a>(palette: &'a PaletteEntry, name: &str) -> Option<&'a str> { @@ -567,70 +533,6 @@ fn set_property(palette: &mut PaletteEntry, name: &str, value: &str) { } } -fn transform_rail_shape( - shape: &str, - mirror: TemplateMirror, - rotation: Rotation, -) -> Option<&'static str> { - let ascending = shape.strip_prefix("ascending_"); - if let Some(direction) = ascending.and_then(Direction::from_name) { - return Some(match transform_direction(direction, mirror, rotation) { - Direction::North => "ascending_north", - Direction::South => "ascending_south", - Direction::West => "ascending_west", - Direction::East => "ascending_east", - Direction::Up => return None, - }); - } - let (first, second) = match shape { - "north_south" => (Direction::North, Direction::South), - "east_west" => (Direction::East, Direction::West), - "north_east" => (Direction::North, Direction::East), - "north_west" => (Direction::North, Direction::West), - "south_east" => (Direction::South, Direction::East), - "south_west" => (Direction::South, Direction::West), - _ => return None, - }; - let first = transform_direction(first, mirror, rotation); - let second = transform_direction(second, mirror, rotation); - match (first, second) { - (Direction::North, Direction::South) | (Direction::South, Direction::North) => { - Some("north_south") - } - (Direction::East, Direction::West) | (Direction::West, Direction::East) => { - Some("east_west") - } - (Direction::North, Direction::East) | (Direction::East, Direction::North) => { - Some("north_east") - } - (Direction::North, Direction::West) | (Direction::West, Direction::North) => { - Some("north_west") - } - (Direction::South, Direction::East) | (Direction::East, Direction::South) => { - Some("south_east") - } - (Direction::South, Direction::West) | (Direction::West, Direction::South) => { - Some("south_west") - } - _ => None, - } -} - -fn transform_direction( - direction: Direction, - mirror: TemplateMirror, - rotation: Rotation, -) -> Direction { - let mirrored = match (mirror, direction) { - (TemplateMirror::LeftRight, Direction::North) => Direction::South, - (TemplateMirror::LeftRight, Direction::South) => Direction::North, - (TemplateMirror::FrontBack, Direction::East) => Direction::West, - (TemplateMirror::FrontBack, Direction::West) => Direction::East, - _ => direction, - }; - rotation.rotate(mirrored) -} - const fn transform( position: Vector3, mirror: TemplateMirror, diff --git a/crates/pumpkin-world/src/generation/structure/structures/mod.rs b/crates/pumpkin-world/src/generation/structure/structures/mod.rs index cab6acaec..c234ebcbf 100644 --- a/crates/pumpkin-world/src/generation/structure/structures/mod.rs +++ b/crates/pumpkin-world/src/generation/structure/structures/mod.rs @@ -442,13 +442,14 @@ impl StructurePiece { return; } - // // Apply Mirror and Rotation - // if self.mirror != BlockMirror::None { - // block = block.mirror(self.mirror); - // } - // if self.rotation != BlockRotation::None { - // block = block.rotate(self.rotation); - // } + // Apply Mirror and Rotation + let mut block = block; + if self.mirror != Mirror::None { + block = block.mirror(self.mirror); + } + if self.rotation != Rotation::None { + block = block.rotate(self.rotation); + } // World interaction world.set_block_state(block_pos.x, block_pos.y, block_pos.z, block); diff --git a/crates/pumpkin-world/src/generation/structure/template/block_state_resolver.rs b/crates/pumpkin-world/src/generation/structure/template/block_state_resolver.rs index dff6cf333..bb4821592 100644 --- a/crates/pumpkin-world/src/generation/structure/template/block_state_resolver.rs +++ b/crates/pumpkin-world/src/generation/structure/template/block_state_resolver.rs @@ -43,21 +43,13 @@ impl BlockStateResolver { return Some(block.default_state); } - // Transform properties for rotation/mirror - let transformed_props: Vec<(String, String)> = entry - .properties - .iter() - .map(|(key, value)| { - let transformed_key = match key.as_str() { - "north" | "south" | "east" | "west" => rotation - .rotate_facing(mirror.mirror_facing(key)) - .to_string(), - _ => key.clone(), - }; - let new_value = Self::transform_property(key, value, rotation, mirror); - (transformed_key, new_value) - }) - .collect(); + // Transform properties for rotation/mirror using the unified vanilla logic + let transformed_props = pumpkin_data::transform_block_properties( + &entry.name, + &entry.properties, + rotation, + mirror, + ); // Convert to the format expected by from_properties let props_slice = transformed_props @@ -77,56 +69,6 @@ impl BlockStateResolver { pub fn resolve_simple(entry: &PaletteEntry) -> Option<&'static BlockState> { Self::resolve(entry, Rotation::None, Mirror::None) } - - /// Transforms a property value based on rotation and mirror. - fn transform_property(key: &str, value: &str, rotation: Rotation, mirror: Mirror) -> String { - match key { - // Horizontal facing properties - "facing" => { - let mirrored = mirror.mirror_facing(value); - rotation.rotate_facing(mirrored).to_string() - } - - // Axis properties (for logs, pillars, etc.) - "axis" => rotation.rotate_axis(value).to_string(), - - // Block rotation (signs, banners - 0-15 value) - "rotation" => value.parse::().map_or_else( - |_| value.to_string(), - |rot_value| { - let mirrored = mirror.mirror_block_rotation(rot_value); - let rotated = rotation.rotate_block_rotation(mirrored); - Self::rotation_to_str(rotated).to_string() - }, - ), - - // Half properties don't need rotation (top/bottom stays the same) - // Shape, mode, and most other properties don't need transformation either - _ => value.to_string(), - } - } - - /// Converts a rotation value (0-15) to a static string. - const fn rotation_to_str(rotation: i32) -> &'static str { - match rotation % 16 { - 1 => "1", - 2 => "2", - 3 => "3", - 4 => "4", - 5 => "5", - 6 => "6", - 7 => "7", - 8 => "8", - 9 => "9", - 10 => "10", - 11 => "11", - 12 => "12", - 13 => "13", - 14 => "14", - 15 => "15", - _ => "0", - } - } } #[cfg(test)] @@ -180,4 +122,66 @@ mod tests { let state = BlockStateResolver::resolve_simple(&entry); assert!(state.is_none()); } + + #[test] + fn stairs_mirror_and_rotate_resolves_expected_state() { + let entry = PaletteEntry::with_properties( + "minecraft:oak_stairs".to_string(), + vec![ + ("facing".to_string(), "north".to_string()), + ("half".to_string(), "bottom".to_string()), + ("shape".to_string(), "inner_left".to_string()), + ("waterlogged".to_string(), "false".to_string()), + ], + ); + + // Mirror LeftRight (north -> south, inner_left -> inner_right) + // then rotate Clockwise90 (south -> west) + let resolved = + BlockStateResolver::resolve(&entry, Rotation::Clockwise90, Mirror::LeftRight).unwrap(); + + // Compare with explicitly resolved target state + let target_entry = PaletteEntry::with_properties( + "minecraft:oak_stairs".to_string(), + vec![ + ("facing".to_string(), "west".to_string()), + ("half".to_string(), "bottom".to_string()), + ("shape".to_string(), "inner_right".to_string()), + ("waterlogged".to_string(), "false".to_string()), + ], + ); + let expected = BlockStateResolver::resolve_simple(&target_entry).unwrap(); + assert_eq!(resolved.id, expected.id); + } + + #[test] + fn door_mirror_resolves_flipped_hinge() { + let entry = PaletteEntry::with_properties( + "minecraft:oak_door".to_string(), + vec![ + ("facing".to_string(), "east".to_string()), + ("half".to_string(), "lower".to_string()), + ("hinge".to_string(), "left".to_string()), + ("open".to_string(), "false".to_string()), + ("powered".to_string(), "false".to_string()), + ], + ); + + // Mirror FrontBack (east -> west, hinge left -> right) + let resolved = + BlockStateResolver::resolve(&entry, Rotation::None, Mirror::FrontBack).unwrap(); + + let target_entry = PaletteEntry::with_properties( + "minecraft:oak_door".to_string(), + vec![ + ("facing".to_string(), "west".to_string()), + ("half".to_string(), "lower".to_string()), + ("hinge".to_string(), "right".to_string()), + ("open".to_string(), "false".to_string()), + ("powered".to_string(), "false".to_string()), + ], + ); + let expected = BlockStateResolver::resolve_simple(&target_entry).unwrap(); + assert_eq!(resolved.id, expected.id); + } } diff --git a/crates/pumpkin-world/src/generation/structure/template/structure_template.rs b/crates/pumpkin-world/src/generation/structure/template/structure_template.rs index 0d58b7dbb..211c79e90 100644 --- a/crates/pumpkin-world/src/generation/structure/template/structure_template.rs +++ b/crates/pumpkin-world/src/generation/structure/template/structure_template.rs @@ -269,49 +269,12 @@ impl PaletteEntry { return self.clone(); } - let properties: Vec<(String, String)> = self - .properties - .iter() - .map(|(key, value)| { - let transformed_key = match key.as_str() { - "north" | "south" | "east" | "west" => rotation - .rotate_facing(mirror.mirror_facing(key)) - .to_string(), - _ => key.clone(), - }; - - let transformed_value = match key.as_str() { - "facing" => { - let mirrored = mirror.mirror_facing(value); - rotation.rotate_facing(mirrored).to_string() - } - "orientation" => { - let mut parts = value.split('_'); - if let (Some(front), Some(top)) = (parts.next(), parts.next()) { - let mirrored_front = mirror.mirror_facing(front); - let rotated_front = rotation.rotate_facing(mirrored_front); - let mirrored_top = mirror.mirror_facing(top); - let rotated_top = rotation.rotate_facing(mirrored_top); - format!("{rotated_front}_{rotated_top}") - } else { - value.clone() - } - } - "axis" => rotation.rotate_axis(value).to_string(), - "rotation" => value.parse::().map_or_else( - |_| value.clone(), - |rot_value| { - let mirrored = mirror.mirror_block_rotation(rot_value); - let rotated = rotation.rotate_block_rotation(mirrored); - rotated.to_string() - }, - ), - _ => value.clone(), - }; - - (transformed_key, transformed_value) - }) - .collect(); + let properties = pumpkin_data::transform_block_properties( + &self.name, + &self.properties, + rotation, + mirror, + ); Self { name: self.name.clone(), diff --git a/crates/pumpkin/src/command/commands/gamerule.rs b/crates/pumpkin/src/command/commands/gamerule.rs index 3f9953f53..899385b28 100644 --- a/crates/pumpkin/src/command/commands/gamerule.rs +++ b/crates/pumpkin/src/command/commands/gamerule.rs @@ -88,6 +88,17 @@ impl CommandExecutor for SetBoolExecutor { context.server().level_info.store(Arc::new(new_info)); + if self.0 == GameRule::SpectatorsGenerateChunks { + let server = context.server(); + for world in server.worlds.load().iter() { + for player in world.players.load().iter() { + if player.is_spectator() { + player.update_chunk_tickets_for_gamemode(); + } + } + } + } + let value_component = TextComponent::text(arg_value.to_string()); context.source.send_feedback( TextComponent::translate_cross( diff --git a/crates/pumpkin/src/entity/ageable.rs b/crates/pumpkin/src/entity/ageable.rs index b9aae948a..6ce5599c0 100644 --- a/crates/pumpkin/src/entity/ageable.rs +++ b/crates/pumpkin/src/entity/ageable.rs @@ -1,5 +1,4 @@ use pumpkin_data::tracked_data; -use pumpkin_protocol::java::client::play::Metadata; use std::sync::atomic::{AtomicBool, AtomicI32, Ordering::Relaxed}; use crate::entity::mob::Mob; @@ -51,13 +50,7 @@ pub trait AgeableMob: Mob { if (old_age < 0 && new_age >= 0) || (old_age >= 0 && new_age < 0) { let is_baby = new_age < 0; - entity.send_meta_data( - &[Metadata::new( - tracked_data::ageable_mob::DATA_BABY_ID, - is_baby, - )], - None, - ); + entity.set_synced_data(tracked_data::ageable_mob::DATA_BABY_ID, is_baby); } } diff --git a/crates/pumpkin/src/entity/ai/goal/beg.rs b/crates/pumpkin/src/entity/ai/goal/beg.rs index b62e69e50..f71c73697 100644 --- a/crates/pumpkin/src/entity/ai/goal/beg.rs +++ b/crates/pumpkin/src/entity/ai/goal/beg.rs @@ -4,7 +4,6 @@ use crate::entity::mob::Mob; use crate::entity::player::Player; use pumpkin_data::item::Item; use pumpkin_data::tag::{self, Taggable}; -use pumpkin_protocol::java::client::play::Metadata; use rand::RngExt; use std::sync::Arc; @@ -45,13 +44,10 @@ impl BegGoal { } fn set_is_interested(mob: &dyn Mob, value: bool) { - mob.get_mob_entity().living_entity.entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::wolf::INTERESTED_ID, - value, - )], - None, - ); + mob.get_mob_entity() + .living_entity + .entity + .set_synced_data(pumpkin_data::tracked_data::wolf::INTERESTED_ID, value); } } diff --git a/crates/pumpkin/src/entity/area_effect_cloud.rs b/crates/pumpkin/src/entity/area_effect_cloud.rs index c02ab9f6d..42ed1ddff 100644 --- a/crates/pumpkin/src/entity/area_effect_cloud.rs +++ b/crates/pumpkin/src/entity/area_effect_cloud.rs @@ -12,12 +12,13 @@ use pumpkin_util::math::vector3::Vector3; type EffectEntry = (&'static StatusEffect, i32, u8, bool, bool, bool); use pumpkin_data::item_stack::ItemStack; -struct ParticleMeta<'a> { +#[derive(Clone)] +struct ParticleMeta { particle_id: pumpkin_protocol::codec::var_int::VarInt, - data: &'a [u8], + data: Box<[u8]>, } -impl pumpkin_protocol::java::client::play::MetadataSerializer for ParticleMeta<'_> { +impl pumpkin_protocol::java::client::play::MetadataSerializer for ParticleMeta { fn write_metadata( &self, writer: &mut impl std::io::Write, @@ -25,7 +26,7 @@ impl pumpkin_protocol::java::client::play::MetadataSerializer for ParticleMeta<' ) -> Result<(), pumpkin_protocol::ser::WritingError> { use pumpkin_protocol::ser::NetworkWriteExt; writer.write_var_int(&self.particle_id)?; - writer.write_slice(self.data) + writer.write_slice(&self.data) } } @@ -179,24 +180,17 @@ impl EntityBase for AreaEffectCloudEntity { particle_id: pumpkin_protocol::codec::var_int::VarInt( pumpkin_data::particle::Particle::EntityEffect as i32, ), - data: &data_bytes, + data: Box::new(data_bytes), }; - // Send initial particle and radius - self.entity.send_meta_data( - &[pumpkin_protocol::java::client::play::Metadata::new( - pumpkin_data::tracked_data::area_effect_cloud::PARTICLE, - &meta, - )], - None, + self.entity.set_synced_data( + pumpkin_data::tracked_data::area_effect_cloud::PARTICLE, + meta, ); - self.entity.send_meta_data( - &[pumpkin_protocol::java::client::play::Metadata::new( - pumpkin_data::tracked_data::area_effect_cloud::RADIUS, - radius, - )], - None, + self.entity.set_synced_data( + pumpkin_data::tracked_data::area_effect_cloud::RADIUS, + radius, ); // Initial waiting flag @@ -205,12 +199,9 @@ impl EntityBase for AreaEffectCloudEntity { .lock() .unwrap_or_else(std::sync::PoisonError::into_inner); let is_waiting = 0 < wait_time; - self.entity.send_meta_data( - &[pumpkin_protocol::java::client::play::Metadata::new( - pumpkin_data::tracked_data::area_effect_cloud::WAITING, - is_waiting, - )], - None, + self.entity.set_synced_data( + pumpkin_data::tracked_data::area_effect_cloud::WAITING, + is_waiting, ); } @@ -247,12 +238,9 @@ impl EntityBase for AreaEffectCloudEntity { // When the waiting period ends, notify clients so they render full particles if age == wait_time && wait_time > 0 { - self.entity.send_meta_data( - &[pumpkin_protocol::java::client::play::Metadata::new( - pumpkin_data::tracked_data::area_effect_cloud::WAITING, - false, - )], - None, + self.entity.set_synced_data( + pumpkin_data::tracked_data::area_effect_cloud::WAITING, + false, ); } @@ -280,12 +268,9 @@ impl EntityBase for AreaEffectCloudEntity { // Send new radius drop(radius); - self.entity.send_meta_data( - &[pumpkin_protocol::java::client::play::Metadata::new( - pumpkin_data::tracked_data::area_effect_cloud::RADIUS, - current_radius, - )], - None, + self.entity.set_synced_data( + pumpkin_data::tracked_data::area_effect_cloud::RADIUS, + current_radius, ); } @@ -433,12 +418,9 @@ impl EntityBase for AreaEffectCloudEntity { drop(radius_lock); // Send updated radius to clients - self.entity.send_meta_data( - &[pumpkin_protocol::java::client::play::Metadata::new( - pumpkin_data::tracked_data::area_effect_cloud::RADIUS, - current_radius, - )], - None, + self.entity.set_synced_data( + pumpkin_data::tracked_data::area_effect_cloud::RADIUS, + current_radius, ); } diff --git a/crates/pumpkin/src/entity/boss/wither.rs b/crates/pumpkin/src/entity/boss/wither.rs index cdb3a7478..351c8fd14 100644 --- a/crates/pumpkin/src/entity/boss/wither.rs +++ b/crates/pumpkin/src/entity/boss/wither.rs @@ -15,7 +15,7 @@ use pumpkin_data::{ world::WorldEvent, }; use pumpkin_nbt::compound::NbtCompound; -use pumpkin_protocol::{codec::var_int::VarInt, java::client::play::Metadata}; +use pumpkin_protocol::codec::var_int::VarInt; use pumpkin_util::{ Difficulty, math::{position::BlockPos, vector3::Vector3}, @@ -106,13 +106,10 @@ impl WitherEntity { pub fn set_invulnerable_ticks(&self, ticks: i32) { self.invulnerable_ticks.store(ticks, Ordering::Relaxed); - self.mob_entity.living_entity.entity.send_meta_data( - &[Metadata::new( - tracked_data::wither::DATA_ID_INV, - VarInt(ticks), - )], - None, - ); + self.mob_entity + .living_entity + .entity + .set_synced_data(tracked_data::wither::DATA_ID_INV, VarInt(ticks)); } #[must_use] @@ -136,7 +133,7 @@ impl WitherEntity { self.mob_entity .living_entity .entity - .send_meta_data(&[Metadata::new(tracker_id, VarInt(entity_id))], None); + .set_synced_data(tracker_id, VarInt(entity_id)); } } } @@ -583,26 +580,21 @@ impl Mob for WitherEntity { fn mob_init_data_tracker(&self) { let entity = &self.mob_entity.living_entity.entity; - entity.send_meta_data( - &[ - Metadata::new( - tracked_data::wither::DATA_TARGET_A, - VarInt(self.get_alternative_target(0)), - ), - Metadata::new( - tracked_data::wither::DATA_TARGET_B, - VarInt(self.get_alternative_target(1)), - ), - Metadata::new( - tracked_data::wither::DATA_TARGET_C, - VarInt(self.get_alternative_target(2)), - ), - Metadata::new( - tracked_data::wither::DATA_ID_INV, - VarInt(self.get_invulnerable_ticks()), - ), - ], - None, + entity.set_synced_data( + tracked_data::wither::DATA_TARGET_A, + VarInt(self.get_alternative_target(0)), + ); + entity.set_synced_data( + tracked_data::wither::DATA_TARGET_B, + VarInt(self.get_alternative_target(1)), + ); + entity.set_synced_data( + tracked_data::wither::DATA_TARGET_C, + VarInt(self.get_alternative_target(2)), + ); + entity.set_synced_data( + tracked_data::wither::DATA_ID_INV, + VarInt(self.get_invulnerable_ticks()), ); } diff --git a/crates/pumpkin/src/entity/breath.rs b/crates/pumpkin/src/entity/breath.rs index 5a2d6fcd4..84eb19b55 100644 --- a/crates/pumpkin/src/entity/breath.rs +++ b/crates/pumpkin/src/entity/breath.rs @@ -5,7 +5,6 @@ use pumpkin_data::effect::StatusEffect; use pumpkin_data::tag; use pumpkin_data::tag::Taggable; use pumpkin_protocol::codec::var_int::VarInt; -use pumpkin_protocol::java::client::play::Metadata; use pumpkin_util::GameMode; use pumpkin_util::math::position::BlockPos; use std::sync::atomic::{AtomicI32, Ordering}; @@ -162,13 +161,11 @@ impl BreathManager { pumpkin_protocol::bedrock::client::set_actor_data::MetadataValue::Short(air as i16), ); - player.get_entity().send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::entity::DATA_AIR_SUPPLY_ID, - VarInt(air), - )], - Some(&bedrock_meta), + player.get_entity().set_synced_data( + pumpkin_data::tracked_data::entity::DATA_AIR_SUPPLY_ID, + VarInt(air), ); + player.get_entity().send_bedrock_actor_data(&bedrock_meta); } pub fn reset(&self, player: &Player) { diff --git a/crates/pumpkin/src/entity/decoration/display.rs b/crates/pumpkin/src/entity/decoration/display.rs index 151698061..1b8c6e861 100644 --- a/crates/pumpkin/src/entity/decoration/display.rs +++ b/crates/pumpkin/src/entity/decoration/display.rs @@ -7,7 +7,7 @@ use pumpkin_data::{damage::DamageType, item_stack::ItemStack}; use pumpkin_nbt::{compound::NbtCompound, tag::NbtTag}; use pumpkin_protocol::{ codec::{item_stack_seralizer::ItemStackSerializer, var_int::VarInt}, - java::client::play::{Metadata, MetadataSerializer}, + java::client::play::MetadataSerializer, ser::{NetworkWriteExt, WritingError}, }; use pumpkin_util::{math::vector3::Vector3, text::TextComponent}; @@ -97,12 +97,9 @@ impl DisplayEntity { pub fn set_interpolation_start_delta_ticks(&self, ticks: i32) { self.interpolation_start_delta_ticks .store(ticks, Ordering::Relaxed); - self.entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::display::START_INTERPOLATION, - VarInt(ticks), - )], - None, + self.entity.set_synced_data( + pumpkin_data::tracked_data::display::START_INTERPOLATION, + VarInt(ticks), ); } @@ -113,12 +110,9 @@ impl DisplayEntity { pub fn set_interpolation_duration(&self, duration: i32) { self.interpolation_duration .store(duration, Ordering::Relaxed); - self.entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::display::INTERPOLATION_DURATION, - VarInt(duration), - )], - None, + self.entity.set_synced_data( + pumpkin_data::tracked_data::display::INTERPOLATION_DURATION, + VarInt(duration), ); } @@ -128,12 +122,9 @@ impl DisplayEntity { pub fn set_teleport_duration(&self, duration: i32) { self.teleport_duration.store(duration, Ordering::Relaxed); - self.entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::display::TELEPORT_DURATION, - VarInt(duration), - )], - None, + self.entity.set_synced_data( + pumpkin_data::tracked_data::display::TELEPORT_DURATION, + VarInt(duration), ); } @@ -149,12 +140,9 @@ impl DisplayEntity { .translation .lock() .unwrap_or_else(std::sync::PoisonError::into_inner) = translation; - self.entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::display::TRANSLATION, - Vector3fSerializer(translation.x, translation.y, translation.z), - )], - None, + self.entity.set_synced_data( + pumpkin_data::tracked_data::display::TRANSLATION, + Vector3fSerializer(translation.x, translation.y, translation.z), ); } @@ -170,12 +158,9 @@ impl DisplayEntity { .scale .lock() .unwrap_or_else(std::sync::PoisonError::into_inner) = scale; - self.entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::display::SCALE, - Vector3fSerializer(scale.x, scale.y, scale.z), - )], - None, + self.entity.set_synced_data( + pumpkin_data::tracked_data::display::SCALE, + Vector3fSerializer(scale.x, scale.y, scale.z), ); } @@ -191,17 +176,14 @@ impl DisplayEntity { .left_rotation .lock() .unwrap_or_else(std::sync::PoisonError::into_inner) = left_rotation; - self.entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::display::LEFT_ROTATION, - QuaternionfSerializer( - left_rotation[0], - left_rotation[1], - left_rotation[2], - left_rotation[3], - ), - )], - None, + self.entity.set_synced_data( + pumpkin_data::tracked_data::display::LEFT_ROTATION, + QuaternionfSerializer( + left_rotation[0], + left_rotation[1], + left_rotation[2], + left_rotation[3], + ), ); } @@ -217,17 +199,14 @@ impl DisplayEntity { .right_rotation .lock() .unwrap_or_else(std::sync::PoisonError::into_inner) = right_rotation; - self.entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::display::RIGHT_ROTATION, - QuaternionfSerializer( - right_rotation[0], - right_rotation[1], - right_rotation[2], - right_rotation[3], - ), - )], - None, + self.entity.set_synced_data( + pumpkin_data::tracked_data::display::RIGHT_ROTATION, + QuaternionfSerializer( + right_rotation[0], + right_rotation[1], + right_rotation[2], + right_rotation[3], + ), ); } @@ -237,13 +216,8 @@ impl DisplayEntity { pub fn set_billboard(&self, billboard: u8) { self.billboard.store(billboard, Ordering::Relaxed); - self.entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::display::BILLBOARD, - billboard, - )], - None, - ); + self.entity + .set_synced_data(pumpkin_data::tracked_data::display::BILLBOARD, billboard); } pub fn get_brightness(&self) -> i32 { @@ -252,12 +226,9 @@ impl DisplayEntity { pub fn set_brightness(&self, brightness: i32) { self.brightness.store(brightness, Ordering::Relaxed); - self.entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::display::BRIGHTNESS, - VarInt(brightness), - )], - None, + self.entity.set_synced_data( + pumpkin_data::tracked_data::display::BRIGHTNESS, + VarInt(brightness), ); } @@ -273,13 +244,8 @@ impl DisplayEntity { .view_range .lock() .unwrap_or_else(std::sync::PoisonError::into_inner) = view_range; - self.entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::display::VIEW_RANGE, - view_range, - )], - None, - ); + self.entity + .set_synced_data(pumpkin_data::tracked_data::display::VIEW_RANGE, view_range); } pub fn get_shadow_radius(&self) -> f32 { @@ -294,12 +260,9 @@ impl DisplayEntity { .shadow_radius .lock() .unwrap_or_else(std::sync::PoisonError::into_inner) = shadow_radius; - self.entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::display::SHADOW_RADIUS, - shadow_radius, - )], - None, + self.entity.set_synced_data( + pumpkin_data::tracked_data::display::SHADOW_RADIUS, + shadow_radius, ); } @@ -315,12 +278,9 @@ impl DisplayEntity { .shadow_strength .lock() .unwrap_or_else(std::sync::PoisonError::into_inner) = shadow_strength; - self.entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::display::SHADOW_STRENGTH, - shadow_strength, - )], - None, + self.entity.set_synced_data( + pumpkin_data::tracked_data::display::SHADOW_STRENGTH, + shadow_strength, ); } @@ -336,13 +296,8 @@ impl DisplayEntity { .width .lock() .unwrap_or_else(std::sync::PoisonError::into_inner) = width; - self.entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::display::WIDTH, - width, - )], - None, - ); + self.entity + .set_synced_data(pumpkin_data::tracked_data::display::WIDTH, width); } pub fn get_display_height(&self) -> f32 { @@ -357,13 +312,8 @@ impl DisplayEntity { .height .lock() .unwrap_or_else(std::sync::PoisonError::into_inner) = height; - self.entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::display::HEIGHT, - height, - )], - None, - ); + self.entity + .set_synced_data(pumpkin_data::tracked_data::display::HEIGHT, height); } pub fn get_glow_color_override(&self) -> i32 { @@ -372,12 +322,9 @@ impl DisplayEntity { pub fn set_glow_color_override(&self, color: i32) { self.glow_color_override.store(color, Ordering::Relaxed); - self.entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::display::GLOW_COLOR_OVERRIDE, - VarInt(color), - )], - None, + self.entity.set_synced_data( + pumpkin_data::tracked_data::display::GLOW_COLOR_OVERRIDE, + VarInt(color), ); } @@ -420,120 +367,69 @@ impl DisplayEntity { .lock() .unwrap_or_else(std::sync::PoisonError::into_inner); - self.entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::display::START_INTERPOLATION, - VarInt(self.interpolation_start_delta_ticks.load(Ordering::Relaxed)), - )], - None, + self.entity.set_synced_data( + pumpkin_data::tracked_data::display::START_INTERPOLATION, + VarInt(self.interpolation_start_delta_ticks.load(Ordering::Relaxed)), ); - self.entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::display::INTERPOLATION_DURATION, - VarInt(self.interpolation_duration.load(Ordering::Relaxed)), - )], - None, + self.entity.set_synced_data( + pumpkin_data::tracked_data::display::INTERPOLATION_DURATION, + VarInt(self.interpolation_duration.load(Ordering::Relaxed)), ); - self.entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::display::TRANSLATION, - Vector3fSerializer(translation.x, translation.y, translation.z), - )], - None, + self.entity.set_synced_data( + pumpkin_data::tracked_data::display::TRANSLATION, + Vector3fSerializer(translation.x, translation.y, translation.z), ); - self.entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::display::SCALE, - Vector3fSerializer(scale.x, scale.y, scale.z), - )], - None, + self.entity.set_synced_data( + pumpkin_data::tracked_data::display::SCALE, + Vector3fSerializer(scale.x, scale.y, scale.z), ); - self.entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::display::LEFT_ROTATION, - QuaternionfSerializer( - left_rotation[0], - left_rotation[1], - left_rotation[2], - left_rotation[3], - ), - )], - None, + self.entity.set_synced_data( + pumpkin_data::tracked_data::display::LEFT_ROTATION, + QuaternionfSerializer( + left_rotation[0], + left_rotation[1], + left_rotation[2], + left_rotation[3], + ), ); - self.entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::display::RIGHT_ROTATION, - QuaternionfSerializer( - right_rotation[0], - right_rotation[1], - right_rotation[2], - right_rotation[3], - ), - )], - None, + self.entity.set_synced_data( + pumpkin_data::tracked_data::display::RIGHT_ROTATION, + QuaternionfSerializer( + right_rotation[0], + right_rotation[1], + right_rotation[2], + right_rotation[3], + ), ); - self.entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::display::BILLBOARD, - self.billboard.load(Ordering::Relaxed), - )], - None, + self.entity.set_synced_data( + pumpkin_data::tracked_data::display::BILLBOARD, + self.billboard.load(Ordering::Relaxed), ); - self.entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::display::BRIGHTNESS, - VarInt(self.brightness.load(Ordering::Relaxed)), - )], - None, + self.entity.set_synced_data( + pumpkin_data::tracked_data::display::BRIGHTNESS, + VarInt(self.brightness.load(Ordering::Relaxed)), ); - self.entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::display::VIEW_RANGE, - view_range, - )], - None, + self.entity + .set_synced_data(pumpkin_data::tracked_data::display::VIEW_RANGE, view_range); + self.entity.set_synced_data( + pumpkin_data::tracked_data::display::SHADOW_RADIUS, + shadow_radius, ); - self.entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::display::SHADOW_RADIUS, - shadow_radius, - )], - None, + self.entity.set_synced_data( + pumpkin_data::tracked_data::display::SHADOW_STRENGTH, + shadow_strength, ); - self.entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::display::SHADOW_STRENGTH, - shadow_strength, - )], - None, + self.entity + .set_synced_data(pumpkin_data::tracked_data::display::WIDTH, width); + self.entity + .set_synced_data(pumpkin_data::tracked_data::display::HEIGHT, height); + self.entity.set_synced_data( + pumpkin_data::tracked_data::display::TELEPORT_DURATION, + VarInt(self.teleport_duration.load(Ordering::Relaxed)), ); - self.entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::display::WIDTH, - width, - )], - None, - ); - self.entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::display::HEIGHT, - height, - )], - None, - ); - self.entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::display::TELEPORT_DURATION, - VarInt(self.teleport_duration.load(Ordering::Relaxed)), - )], - None, - ); - self.entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::display::GLOW_COLOR_OVERRIDE, - VarInt(self.glow_color_override.load(Ordering::Relaxed)), - )], - None, + self.entity.set_synced_data( + pumpkin_data::tracked_data::display::GLOW_COLOR_OVERRIDE, + VarInt(self.glow_color_override.load(Ordering::Relaxed)), ); } @@ -785,12 +681,9 @@ impl BlockDisplayEntity { pub fn set_block_state(&self, block_state: i32) { self.block_state.store(block_state, Ordering::Relaxed); - self.display.entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::block_display::BLOCK_STATE, - VarInt(block_state), - )], - None, + self.display.entity.set_synced_data( + pumpkin_data::tracked_data::block_display::BLOCK_STATE, + VarInt(block_state), ); } } @@ -812,12 +705,9 @@ impl EntityBase for BlockDisplayEntity { fn init_data_tracker(&self) { self.display.init_display_data_tracker(); - self.display.entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::block_display::BLOCK_STATE, - VarInt(self.block_state.load(Ordering::Relaxed)), - )], - None, + self.display.entity.set_synced_data( + pumpkin_data::tracked_data::block_display::BLOCK_STATE, + VarInt(self.block_state.load(Ordering::Relaxed)), ); } @@ -889,12 +779,9 @@ impl ItemDisplayEntity { .item_stack .lock() .unwrap_or_else(std::sync::PoisonError::into_inner) = item.clone(); - self.display.entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::item_display::ITEM, - ItemStackSerializer::from(item), - )], - None, + self.display.entity.set_synced_data( + pumpkin_data::tracked_data::item_display::ITEM, + ItemStackSerializer::from(item), ); } @@ -904,13 +791,9 @@ impl ItemDisplayEntity { pub fn set_item_display_mode(&self, mode: u8) { self.item_display.store(mode, Ordering::Relaxed); - self.display.entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::item_display::ITEM_DISPLAY, - mode, - )], - None, - ); + self.display + .entity + .set_synced_data(pumpkin_data::tracked_data::item_display::ITEM_DISPLAY, mode); } } @@ -953,24 +836,18 @@ impl EntityBase for ItemDisplayEntity { fn init_data_tracker(&self) { self.display.init_display_data_tracker(); - self.display.entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::item_display::ITEM, - ItemStackSerializer::from( - self.item_stack - .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner) - .clone(), - ), - )], - None, + self.display.entity.set_synced_data( + pumpkin_data::tracked_data::item_display::ITEM, + ItemStackSerializer::from( + self.item_stack + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .clone(), + ), ); - self.display.entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::item_display::ITEM_DISPLAY, - self.item_display.load(Ordering::Relaxed), - )], - None, + self.display.entity.set_synced_data( + pumpkin_data::tracked_data::item_display::ITEM_DISPLAY, + self.item_display.load(Ordering::Relaxed), ); } @@ -1048,13 +925,9 @@ impl TextDisplayEntity { .text .lock() .unwrap_or_else(std::sync::PoisonError::into_inner) = text.clone(); - self.display.entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::text_display::TEXT, - text, - )], - None, - ); + self.display + .entity + .set_synced_data(pumpkin_data::tracked_data::text_display::TEXT, text); } pub fn get_line_width(&self) -> i32 { @@ -1063,12 +936,9 @@ impl TextDisplayEntity { pub fn set_line_width(&self, width: i32) { self.line_width.store(width, Ordering::Relaxed); - self.display.entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::text_display::LINE_WIDTH, - VarInt(width), - )], - None, + self.display.entity.set_synced_data( + pumpkin_data::tracked_data::text_display::LINE_WIDTH, + VarInt(width), ); } @@ -1078,12 +948,9 @@ impl TextDisplayEntity { pub fn set_background_color(&self, color: i32) { self.background.store(color, Ordering::Relaxed); - self.display.entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::text_display::BACKGROUND, - VarInt(color), - )], - None, + self.display.entity.set_synced_data( + pumpkin_data::tracked_data::text_display::BACKGROUND, + VarInt(color), ); } @@ -1093,12 +960,9 @@ impl TextDisplayEntity { pub fn set_text_opacity(&self, opacity: i8) { self.text_opacity.store(opacity, Ordering::Relaxed); - self.display.entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::text_display::TEXT_OPACITY, - opacity as u8, - )], - None, + self.display.entity.set_synced_data( + pumpkin_data::tracked_data::text_display::TEXT_OPACITY, + opacity as u8, ); } @@ -1114,12 +978,9 @@ impl TextDisplayEntity { flags &= !1; } self.flags.store(flags, Ordering::Relaxed); - self.display.entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::text_display::TEXT_DISPLAY_FLAGS, - flags, - )], - None, + self.display.entity.set_synced_data( + pumpkin_data::tracked_data::text_display::TEXT_DISPLAY_FLAGS, + flags, ); } @@ -1135,12 +996,9 @@ impl TextDisplayEntity { flags &= !2; } self.flags.store(flags, Ordering::Relaxed); - self.display.entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::text_display::TEXT_DISPLAY_FLAGS, - flags, - )], - None, + self.display.entity.set_synced_data( + pumpkin_data::tracked_data::text_display::TEXT_DISPLAY_FLAGS, + flags, ); } @@ -1156,12 +1014,9 @@ impl TextDisplayEntity { flags &= !4; } self.flags.store(flags, Ordering::Relaxed); - self.display.entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::text_display::TEXT_DISPLAY_FLAGS, - flags, - )], - None, + self.display.entity.set_synced_data( + pumpkin_data::tracked_data::text_display::TEXT_DISPLAY_FLAGS, + flags, ); } @@ -1184,12 +1039,9 @@ impl TextDisplayEntity { flags |= 16; } self.flags.store(flags, Ordering::Relaxed); - self.display.entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::text_display::TEXT_DISPLAY_FLAGS, - flags, - )], - None, + self.display.entity.set_synced_data( + pumpkin_data::tracked_data::text_display::TEXT_DISPLAY_FLAGS, + flags, ); } } @@ -1273,40 +1125,24 @@ impl EntityBase for TextDisplayEntity { .lock() .unwrap_or_else(std::sync::PoisonError::into_inner) .clone(); - self.display.entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::text_display::TEXT, - text, - )], - None, + self.display + .entity + .set_synced_data(pumpkin_data::tracked_data::text_display::TEXT, text); + self.display.entity.set_synced_data( + pumpkin_data::tracked_data::text_display::LINE_WIDTH, + VarInt(self.line_width.load(Ordering::Relaxed)), ); - self.display.entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::text_display::LINE_WIDTH, - VarInt(self.line_width.load(Ordering::Relaxed)), - )], - None, + self.display.entity.set_synced_data( + pumpkin_data::tracked_data::text_display::BACKGROUND, + VarInt(self.background.load(Ordering::Relaxed)), ); - self.display.entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::text_display::BACKGROUND, - VarInt(self.background.load(Ordering::Relaxed)), - )], - None, + self.display.entity.set_synced_data( + pumpkin_data::tracked_data::text_display::TEXT_OPACITY, + self.text_opacity.load(Ordering::Relaxed) as u8, ); - self.display.entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::text_display::TEXT_OPACITY, - self.text_opacity.load(Ordering::Relaxed) as u8, - )], - None, - ); - self.display.entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::text_display::TEXT_DISPLAY_FLAGS, - self.flags.load(Ordering::Relaxed), - )], - None, + self.display.entity.set_synced_data( + pumpkin_data::tracked_data::text_display::TEXT_DISPLAY_FLAGS, + self.flags.load(Ordering::Relaxed), ); } diff --git a/crates/pumpkin/src/entity/decoration/end_crystal.rs b/crates/pumpkin/src/entity/decoration/end_crystal.rs index f050ae2bf..912d24b13 100644 --- a/crates/pumpkin/src/entity/decoration/end_crystal.rs +++ b/crates/pumpkin/src/entity/decoration/end_crystal.rs @@ -6,7 +6,6 @@ use pumpkin_data::{ damage::DamageType, tag::{self, Taggable}, }; -use pumpkin_protocol::java::client::play::Metadata; use pumpkin_util::math::{position::BlockPos, vector3::Vector3}; pub struct EndCrystalEntity { @@ -29,12 +28,9 @@ impl EndCrystalEntity { pub fn set_show_bottom(&self, show_bottom: bool) { self.show_bottom.store(show_bottom, Ordering::Relaxed); - self.entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::end_crystal::SHOW_BOTTOM, - show_bottom, - )], - None, + self.entity.set_synced_data( + pumpkin_data::tracked_data::end_crystal::SHOW_BOTTOM, + show_bottom, ); } @@ -47,12 +43,9 @@ impl EndCrystalEntity { .beam_target .lock() .unwrap_or_else(std::sync::PoisonError::into_inner) = beam_target; - self.entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::end_crystal::BEAM_TARGET, - beam_target, - )], - None, + self.entity.set_synced_data( + pumpkin_data::tracked_data::end_crystal::BEAM_TARGET, + beam_target, ); } diff --git a/crates/pumpkin/src/entity/decoration/item_frame.rs b/crates/pumpkin/src/entity/decoration/item_frame.rs index 314c9cddb..259c246e5 100644 --- a/crates/pumpkin/src/entity/decoration/item_frame.rs +++ b/crates/pumpkin/src/entity/decoration/item_frame.rs @@ -129,12 +129,9 @@ impl ItemFrameEntity { .lock() .unwrap_or_else(std::sync::PoisonError::into_inner) = item_stack; - self.entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::item_frame::ITEM, - &item_serializer, - )], - None, + self.entity.set_synced_data( + pumpkin_data::tracked_data::item_frame::ITEM, + item_serializer, ); if play_sound { @@ -156,13 +153,8 @@ impl ItemFrameEntity { let rot = rotation % 8; self.rotation.store(rot, Ordering::Relaxed); - self.entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::item_frame::ROTATION, - rot as i32, - )], - None, - ); + self.entity + .set_synced_data(pumpkin_data::tracked_data::item_frame::ROTATION, rot as i32); if update_neighbours { let world = self.entity.world.load(); @@ -247,12 +239,9 @@ impl ItemFrameEntity { .lock() .unwrap_or_else(std::sync::PoisonError::into_inner) = ItemStack::EMPTY.clone(); let item_serializer = ItemStackSerializer::from(ItemStack::EMPTY.clone()); - self.entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::item_frame::ITEM, - &item_serializer, - )], - None, + self.entity.set_synced_data( + pumpkin_data::tracked_data::item_frame::ITEM, + item_serializer, ); let is_creative_player = caused_by.is_some_and(|s| { @@ -343,20 +332,12 @@ impl EntityBase for ItemFrameEntity { ); let rotation = self.get_rotation() as i32; - self.entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::item_frame::ITEM, - &item_serializer, - )], - None, - ); - self.entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::item_frame::ROTATION, - rotation, - )], - None, + self.entity.set_synced_data( + pumpkin_data::tracked_data::item_frame::ITEM, + item_serializer, ); + self.entity + .set_synced_data(pumpkin_data::tracked_data::item_frame::ROTATION, rotation); } fn send_java_spawn_packet(&self, client: &crate::net::java::JavaClient) { diff --git a/crates/pumpkin/src/entity/falling.rs b/crates/pumpkin/src/entity/falling.rs index c6d2fd0b2..2bcbfcb5c 100644 --- a/crates/pumpkin/src/entity/falling.rs +++ b/crates/pumpkin/src/entity/falling.rs @@ -2,7 +2,6 @@ use pumpkin_data::Block; use pumpkin_data::BlockStateId; use pumpkin_data::damage::DamageType; use pumpkin_data::entity::EntityType; -use pumpkin_protocol::java::client::play::Metadata; use pumpkin_util::math::position::BlockPos; use pumpkin_world::world::BlockFlags; use std::sync::{Arc, atomic::Ordering}; @@ -74,12 +73,9 @@ impl EntityBase for FallingEntity { } fn init_data_tracker(&self) { - self.entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::falling_block::START_POS, - self.entity.block_pos.load(), - )], - None, + self.entity.set_synced_data( + pumpkin_data::tracked_data::falling_block::START_POS, + self.entity.block_pos.load(), ); } diff --git a/crates/pumpkin/src/entity/interaction.rs b/crates/pumpkin/src/entity/interaction.rs index c2d0a669b..c0b4f72c9 100644 --- a/crates/pumpkin/src/entity/interaction.rs +++ b/crates/pumpkin/src/entity/interaction.rs @@ -6,7 +6,6 @@ use uuid::Uuid; use pumpkin_data::{damage::DamageType, item_stack::ItemStack}; use pumpkin_nbt::{compound::NbtCompound, tag::NbtTag}; -use pumpkin_protocol::java::client::play::Metadata; use pumpkin_util::math::{ boundingbox::{BoundingBox, EntityDimensions}, vector3::Vector3, @@ -82,13 +81,8 @@ impl InteractionEntity { .lock() .unwrap_or_else(std::sync::PoisonError::into_inner) = width; self.update_dimensions(); - self.entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::interaction::WIDTH, - width, - )], - None, - ); + self.entity + .set_synced_data(pumpkin_data::tracked_data::interaction::WIDTH, width); } pub fn get_height(&self) -> f32 { @@ -104,13 +98,8 @@ impl InteractionEntity { .lock() .unwrap_or_else(std::sync::PoisonError::into_inner) = height; self.update_dimensions(); - self.entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::interaction::HEIGHT, - height, - )], - None, - ); + self.entity + .set_synced_data(pumpkin_data::tracked_data::interaction::HEIGHT, height); } pub fn get_response(&self) -> bool { @@ -119,13 +108,8 @@ impl InteractionEntity { pub fn set_response(&self, response: bool) { self.response.store(response, Ordering::Relaxed); - self.entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::interaction::RESPONSE, - response, - )], - None, - ); + self.entity + .set_synced_data(pumpkin_data::tracked_data::interaction::RESPONSE, response); } pub fn update_dimensions(&self) { @@ -249,27 +233,12 @@ impl EntityBase for InteractionEntity { .unwrap_or_else(std::sync::PoisonError::into_inner); let response = self.response.load(Ordering::Relaxed); - self.entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::interaction::WIDTH, - width, - )], - None, - ); - self.entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::interaction::HEIGHT, - height, - )], - None, - ); - self.entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::interaction::RESPONSE, - response, - )], - None, - ); + self.entity + .set_synced_data(pumpkin_data::tracked_data::interaction::WIDTH, width); + self.entity + .set_synced_data(pumpkin_data::tracked_data::interaction::HEIGHT, height); + self.entity + .set_synced_data(pumpkin_data::tracked_data::interaction::RESPONSE, response); } fn get_entity(&self) -> &Entity { diff --git a/crates/pumpkin/src/entity/item.rs b/crates/pumpkin/src/entity/item.rs index 36a1f4625..5a5fd3f68 100644 --- a/crates/pumpkin/src/entity/item.rs +++ b/crates/pumpkin/src/entity/item.rs @@ -465,17 +465,14 @@ impl EntityBase for ItemEntity { } fn init_data_tracker(&self) { - self.entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::item::ITEM, - &ItemStackSerializer::from( - self.item_stack - .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner) - .clone(), - ), - )], - None, + self.entity.set_synced_data( + pumpkin_data::tracked_data::item::ITEM, + ItemStackSerializer::from( + self.item_stack + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .clone(), + ), ); } diff --git a/crates/pumpkin/src/entity/living.rs b/crates/pumpkin/src/entity/living.rs index e1d17081c..87433e1ae 100644 --- a/crates/pumpkin/src/entity/living.rs +++ b/crates/pumpkin/src/entity/living.rs @@ -59,7 +59,7 @@ use pumpkin_protocol::java::client::play::{ }; use pumpkin_protocol::{ codec::item_stack_seralizer::ItemStackSerializer, - java::client::play::{CSetEquipment, Metadata, MetadataSerializer}, + java::client::play::{CSetEquipment, MetadataSerializer}, ser::{NetworkWriteExt, WritingError}, }; use pumpkin_util::math::boundingbox::BoundingBox; @@ -129,11 +129,13 @@ pub struct LivingEntity { pub attributes: RwLock>, } +#[derive(Clone)] struct EffectParticle { particle_id: VarInt, color: i32, } +#[derive(Clone)] struct EffectParticles(Vec); impl MetadataSerializer for EffectParticles { @@ -475,13 +477,11 @@ impl LivingEntity { meta }); - self.entity.send_meta_data( - &[Metadata::new( - tracked_data::living_entity::DATA_LIVING_ENTITY_FLAGS, - b, - )], - bedrock_meta.as_ref(), - ); + self.entity + .set_synced_data(tracked_data::living_entity::DATA_LIVING_ENTITY_FLAGS, b); + if let Some(bedrock_meta) = &bedrock_meta { + self.entity.send_bedrock_actor_data(bedrock_meta); + } } pub fn clear_active_hand(&self) { @@ -569,13 +569,8 @@ impl LivingEntity { let clamped = health.max(0.0).min(max_health); self.health.store(clamped); // tell everyone entities health changed - self.entity.send_meta_data( - &[Metadata::new( - tracked_data::living_entity::DATA_HEALTH_ID, - clamped, - )], - None, - ); + self.entity + .set_synced_data(tracked_data::living_entity::DATA_HEALTH_ID, clamped); } /// Returns the current maximum health for this entity @@ -623,13 +618,8 @@ impl LivingEntity { // Send absorption metadata for players (visual yellow hearts) if self.entity.entity_type == &EntityType::PLAYER { - self.entity.send_meta_data( - &[Metadata::new( - tracked_data::player::DATA_PLAYER_ABSORPTION_ID, - new_abs, - )], - None, - ); + self.entity + .set_synced_data(tracked_data::player::DATA_PLAYER_ABSORPTION_ID, new_abs); } } @@ -900,21 +890,11 @@ impl LivingEntity { .all(|effect| effect.ambient); drop(effects); - self.entity.send_meta_data( - &[Metadata::new( - tracked_data::living_entity::EFFECT_PARTICLES, - particles, - )], - None, - ); + self.entity + .set_synced_data(tracked_data::living_entity::EFFECT_PARTICLES, particles); if has_effects { - self.entity.send_meta_data( - &[Metadata::new( - tracked_data::living_entity::EFFECT_AMBIENCE_ID, - ambient, - )], - None, - ); + self.entity + .set_synced_data(tracked_data::living_entity::EFFECT_AMBIENCE_ID, ambient); } } @@ -2191,13 +2171,8 @@ impl LivingEntity { // Clear any absorption self.absorption.store(0.0); // Send health metadata - self.entity.send_meta_data( - &[Metadata::new( - tracked_data::living_entity::DATA_HEALTH_ID, - max_health, - )], - None, - ); + self.entity + .set_synced_data(tracked_data::living_entity::DATA_HEALTH_ID, max_health); self.reset_effects_and_attributes(); @@ -3489,7 +3464,7 @@ mod tests { show_icon: true, blend: false, }; - let metadata = Metadata::new( + let metadata = pumpkin_protocol::java::client::play::Metadata::new( tracked_data::living_entity::EFFECT_PARTICLES, EffectParticles(vec![EffectParticle::from_effect(&effect)]), ); diff --git a/crates/pumpkin/src/entity/mob/bat.rs b/crates/pumpkin/src/entity/mob/bat.rs index df95a0d81..b72f51828 100644 --- a/crates/pumpkin/src/entity/mob/bat.rs +++ b/crates/pumpkin/src/entity/mob/bat.rs @@ -7,7 +7,6 @@ use pumpkin_data::sound::{Sound, SoundCategory}; use pumpkin_data::tag::{self, Taggable}; use pumpkin_data::tracked_data; use pumpkin_nbt::compound::NbtCompound; -use pumpkin_protocol::java::client::play::Metadata; use pumpkin_util::math::position::BlockPos; use pumpkin_util::math::vector3::Vector3; use pumpkin_world::chunk::ChunkHeightmapType; @@ -71,10 +70,10 @@ impl BatEntity { pub fn set_roosting(&self, roosting: bool) { self.roosting.store(roosting, Relaxed); let flags: u8 = if roosting { ROOSTING_FLAG } else { 0 }; - self.mob_entity.living_entity.entity.send_meta_data( - &[Metadata::new(tracked_data::bat::DATA_ID_FLAGS, flags)], - None, - ); + self.mob_entity + .living_entity + .entity + .set_synced_data(tracked_data::bat::DATA_ID_FLAGS, flags); } fn tick_ambient_sound(&self, world: &World, pos: &Vector3) { @@ -211,10 +210,7 @@ impl Mob for BatEntity { fn mob_init_data_tracker(&self) { let entity = self.get_entity(); let flags: u8 = if self.is_roosting() { ROOSTING_FLAG } else { 0 }; - entity.send_meta_data( - &[Metadata::new(tracked_data::bat::DATA_ID_FLAGS, flags)], - None, - ); + entity.set_synced_data(tracked_data::bat::DATA_ID_FLAGS, flags); } fn mob_write_nbt(&self, nbt: &mut NbtCompound) { diff --git a/crates/pumpkin/src/entity/mob/blaze.rs b/crates/pumpkin/src/entity/mob/blaze.rs index 41a64537a..b41069e32 100644 --- a/crates/pumpkin/src/entity/mob/blaze.rs +++ b/crates/pumpkin/src/entity/mob/blaze.rs @@ -3,7 +3,6 @@ use std::sync::{Arc, Weak}; use pumpkin_data::damage::DamageType; use pumpkin_data::entity::EntityType; -use pumpkin_protocol::java::client::play::Metadata; use pumpkin_util::math::vector3::Vector3; use crate::entity::{ @@ -78,13 +77,10 @@ impl BlazeEntity { pub fn set_charged(&self, charged: bool) { self.is_charged.store(charged, Ordering::Relaxed); let flags = i8::from(charged); - self.entity.living_entity.entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::blaze::DATA_FLAGS_ID, - flags, - )], - None, - ); + self.entity + .living_entity + .entity + .set_synced_data(pumpkin_data::tracked_data::blaze::DATA_FLAGS_ID, flags); } } diff --git a/crates/pumpkin/src/entity/mob/creaking.rs b/crates/pumpkin/src/entity/mob/creaking.rs index 2eae1db2a..b9737b342 100644 --- a/crates/pumpkin/src/entity/mob/creaking.rs +++ b/crates/pumpkin/src/entity/mob/creaking.rs @@ -12,7 +12,7 @@ use pumpkin_data::item::Item; use pumpkin_data::particle::Particle; use pumpkin_data::sound::{Sound, SoundCategory}; use pumpkin_nbt::compound::NbtCompound; -use pumpkin_protocol::java::client::play::{CEntityStatus, Metadata}; +use pumpkin_protocol::java::client::play::CEntityStatus; use pumpkin_util::math::position::BlockPos; use pumpkin_util::math::vector3::Vector3; @@ -155,13 +155,10 @@ impl CreakingEntity { pub fn set_can_move(&self, can_move: bool) { self.can_move.store(can_move, Ordering::Relaxed); - self.mob_entity.living_entity.entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::creaking::CAN_MOVE, - can_move, - )], - None, - ); + self.mob_entity + .living_entity + .entity + .set_synced_data(pumpkin_data::tracked_data::creaking::CAN_MOVE, can_move); } pub fn is_active(&self) -> bool { @@ -170,13 +167,10 @@ impl CreakingEntity { pub fn set_is_active(&self, active: bool) { self.is_active.store(active, Ordering::Relaxed); - self.mob_entity.living_entity.entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::creaking::IS_ACTIVE, - active, - )], - None, - ); + self.mob_entity + .living_entity + .entity + .set_synced_data(pumpkin_data::tracked_data::creaking::IS_ACTIVE, active); } pub fn is_tearing_down(&self) -> bool { @@ -185,13 +179,10 @@ impl CreakingEntity { pub fn set_tearing_down(&self) { self.is_tearing_down.store(true, Ordering::Relaxed); - self.mob_entity.living_entity.entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::creaking::IS_TEARING_DOWN, - true, - )], - None, - ); + self.mob_entity + .living_entity + .entity + .set_synced_data(pumpkin_data::tracked_data::creaking::IS_TEARING_DOWN, true); } pub fn get_home_pos(&self) -> Option { @@ -210,13 +201,10 @@ impl CreakingEntity { .position_target_range .store(-1, Ordering::Relaxed); } - self.mob_entity.living_entity.entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::creaking::HOME_POS, - pos, - )], - None, - ); + self.mob_entity + .living_entity + .entity + .set_synced_data(pumpkin_data::tracked_data::creaking::HOME_POS, pos); } pub fn stop_in_place(&self) { diff --git a/crates/pumpkin/src/entity/mob/creeper.rs b/crates/pumpkin/src/entity/mob/creeper.rs index 90ff7f830..904db75f9 100644 --- a/crates/pumpkin/src/entity/mob/creeper.rs +++ b/crates/pumpkin/src/entity/mob/creeper.rs @@ -10,7 +10,7 @@ use pumpkin_data::{ sound::{Sound, SoundCategory}, }; use pumpkin_nbt::compound::NbtCompound; -use pumpkin_protocol::{codec::var_int::VarInt, java::client::play::Metadata}; +use pumpkin_protocol::codec::var_int::VarInt; use crate::entity::{ Entity, EntityBase, @@ -92,13 +92,10 @@ impl CreeperEntity { pub fn set_fuse_speed(&self, speed: i32) { self.fuse_speed.store(speed, Ordering::Relaxed); - self.mob_entity.living_entity.entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::creeper::FUSE_ID, - VarInt(speed), - )], - None, - ); + self.mob_entity + .living_entity + .entity + .set_synced_data(pumpkin_data::tracked_data::creeper::FUSE_ID, VarInt(speed)); } pub fn explode(&self) { @@ -162,13 +159,10 @@ impl Mob for CreeperEntity { lightning: &crate::entity::lightning::LightningBoltEntity, ) { self.charged.store(true, Ordering::Relaxed); - self.mob_entity.living_entity.entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::creeper::CHARGED, - true, - )], - None, - ); + self.mob_entity + .living_entity + .entity + .set_synced_data(pumpkin_data::tracked_data::creeper::CHARGED, true); self.mob_entity .living_entity .on_lightning_strike(caller, lightning); @@ -231,13 +225,7 @@ impl Mob for CreeperEntity { ); self.ignited.store(true, Ordering::Relaxed); - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::creeper::IS_IGNITED, - true, - )], - None, - ); + entity.set_synced_data(pumpkin_data::tracked_data::creeper::IS_IGNITED, true); if player.gamemode.load() != pumpkin_util::GameMode::Creative { // TODO: Handle DamageResult::Broken to broadcast item break and update player slot. @@ -256,13 +244,7 @@ impl CreeperEntity { pub fn set_charged(&self, charged: bool) { self.charged.store(charged, Ordering::Relaxed); let entity = &self.mob_entity.living_entity.entity; - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::creeper::CHARGED, - charged, - )], - None, - ); + entity.set_synced_data(pumpkin_data::tracked_data::creeper::CHARGED, charged); } pub fn is_ignited(&self) -> bool { @@ -272,13 +254,7 @@ impl CreeperEntity { pub fn set_ignited(&self, ignited: bool) { self.ignited.store(ignited, Ordering::Relaxed); let entity = &self.mob_entity.living_entity.entity; - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::creeper::IS_IGNITED, - ignited, - )], - None, - ); + entity.set_synced_data(pumpkin_data::tracked_data::creeper::IS_IGNITED, ignited); } pub fn get_fuse(&self) -> i32 { diff --git a/crates/pumpkin/src/entity/mob/enderman.rs b/crates/pumpkin/src/entity/mob/enderman.rs index 2370c21bd..f613658ce 100644 --- a/crates/pumpkin/src/entity/mob/enderman.rs +++ b/crates/pumpkin/src/entity/mob/enderman.rs @@ -19,7 +19,7 @@ use pumpkin_data::{ tag::Taggable, }; use pumpkin_nbt::compound::NbtCompound; -use pumpkin_protocol::{codec::var_int::VarInt, java::client::play::Metadata}; +use pumpkin_protocol::codec::var_int::VarInt; use pumpkin_util::math::{boundingbox::BoundingBox, position::BlockPos, vector3::Vector3}; use rand::RngExt; @@ -290,13 +290,10 @@ impl EndermanEntity { pub fn set_angry(&self, angry: bool) { self.angry.store(angry, Ordering::Relaxed); - self.mob_entity.living_entity.entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::enderman::CREEPY, - angry, - )], - None, - ); + self.mob_entity + .living_entity + .entity + .set_synced_data(pumpkin_data::tracked_data::enderman::CREEPY, angry); } pub fn is_angry(&self) -> bool { @@ -305,25 +302,19 @@ impl EndermanEntity { pub fn set_provoked(&self, provoked: bool) { self.provoked.store(provoked, Ordering::Relaxed); - self.mob_entity.living_entity.entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::enderman::STARED_AT, - provoked, - )], - None, - ); + self.mob_entity + .living_entity + .entity + .set_synced_data(pumpkin_data::tracked_data::enderman::STARED_AT, provoked); } pub fn set_carried_block(&self, block_state: Option) { self.carried_block.store(block_state); let value = block_state.map_or(VarInt(0), |id| VarInt(id.as_u16() as i32)); - self.mob_entity.living_entity.entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::enderman::CARRY_STATE, - value, - )], - None, - ); + self.mob_entity + .living_entity + .entity + .set_synced_data(pumpkin_data::tracked_data::enderman::CARRY_STATE, value); } pub fn get_carried_block(&self) -> Option { diff --git a/crates/pumpkin/src/entity/mob/evoker.rs b/crates/pumpkin/src/entity/mob/evoker.rs index 3c27da430..1f4bca083 100644 --- a/crates/pumpkin/src/entity/mob/evoker.rs +++ b/crates/pumpkin/src/entity/mob/evoker.rs @@ -6,7 +6,6 @@ use uuid::Uuid; use pumpkin_data::entity::EntityType; use pumpkin_data::sound::Sound; use pumpkin_nbt::compound::NbtCompound; -use pumpkin_protocol::java::client::play::Metadata; use pumpkin_util::math::vector3::Vector3; use crate::entity::{ @@ -129,12 +128,9 @@ impl EvokerEntity { pub fn set_is_casting_spell(&self, spell: IllagerSpell) { self.current_spell.store(spell as u8, Ordering::Relaxed); let entity = &self.mob_entity.living_entity.entity; - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::evoker::SPELL_CASTING_ID, - spell as u8 as i8, - )], - None, + entity.set_synced_data( + pumpkin_data::tracked_data::evoker::SPELL_CASTING_ID, + spell as u8 as i8, ); } diff --git a/crates/pumpkin/src/entity/mob/ghast.rs b/crates/pumpkin/src/entity/mob/ghast.rs index 03c822ac0..ff2a62b2a 100644 --- a/crates/pumpkin/src/entity/mob/ghast.rs +++ b/crates/pumpkin/src/entity/mob/ghast.rs @@ -6,7 +6,6 @@ use pumpkin_data::entity::EntityType; use pumpkin_data::sound::{Sound, SoundCategory}; use pumpkin_data::tracked_data; use pumpkin_nbt::compound::NbtCompound; -use pumpkin_protocol::java::client::play::Metadata; use pumpkin_util::math::position::BlockPos; use pumpkin_util::math::vector3::Vector3; use rand::RngExt; @@ -92,13 +91,7 @@ impl GhastEntity { pub fn set_charging(&self, charging: bool) { self.is_charging.store(charging, Ordering::Relaxed); let entity = &self.mob_entity.living_entity.entity; - entity.send_meta_data( - &[Metadata::new( - tracked_data::ghast::DATA_IS_CHARGING, - charging, - )], - None, - ); + entity.set_synced_data(tracked_data::ghast::DATA_IS_CHARGING, charging); } #[must_use] @@ -144,10 +137,7 @@ impl Mob for GhastEntity { fn mob_init_data_tracker(&self) { let entity = self.get_entity(); if self.is_charging() { - entity.send_meta_data( - &[Metadata::new(tracked_data::ghast::DATA_IS_CHARGING, true)], - None, - ); + entity.set_synced_data(tracked_data::ghast::DATA_IS_CHARGING, true); } } diff --git a/crates/pumpkin/src/entity/mob/hoglin.rs b/crates/pumpkin/src/entity/mob/hoglin.rs index fa87de6b6..5207233dd 100644 --- a/crates/pumpkin/src/entity/mob/hoglin.rs +++ b/crates/pumpkin/src/entity/mob/hoglin.rs @@ -6,7 +6,6 @@ use pumpkin_data::dimension::Dimension; use pumpkin_data::entity::EntityType; use pumpkin_data::sound::{Sound, SoundCategory}; use pumpkin_nbt::compound::NbtCompound; -use pumpkin_protocol::java::client::play::Metadata; use pumpkin_util::math::vector3::Vector3; use crate::entity::{ @@ -114,12 +113,9 @@ impl HoglinEntity { pub fn set_immune_to_zombification(&self, immune: bool) { self.immune_to_zombification .store(immune, Ordering::Relaxed); - self.mob_entity.living_entity.entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::hoglin::DATA_IMMUNE_TO_ZOMBIFICATION, - immune, - )], - None, + self.mob_entity.living_entity.entity.set_synced_data( + pumpkin_data::tracked_data::hoglin::DATA_IMMUNE_TO_ZOMBIFICATION, + immune, ); } diff --git a/crates/pumpkin/src/entity/mob/mod.rs b/crates/pumpkin/src/entity/mob/mod.rs index ccb8c3855..7c42a9af9 100644 --- a/crates/pumpkin/src/entity/mob/mod.rs +++ b/crates/pumpkin/src/entity/mob/mod.rs @@ -16,7 +16,7 @@ use pumpkin_data::tag::{self, Taggable}; use pumpkin_data::tracked_data; use pumpkin_data::{Block, BlockDirection}; use pumpkin_nbt::compound::NbtCompound; -use pumpkin_protocol::java::client::play::{CHeadRot, CUpdateEntityRot, Metadata}; +use pumpkin_protocol::java::client::play::{CHeadRot, CUpdateEntityRot}; use pumpkin_util::Difficulty; use pumpkin_util::math::boundingbox::BoundingBox; use pumpkin_util::math::position::BlockPos; @@ -330,10 +330,9 @@ impl MobEntity { if new_b != old_b { self.mob_flags.store(new_b, Ordering::Relaxed); - self.living_entity.entity.send_meta_data( - &[Metadata::new(tracked_data::mob::DATA_MOB_FLAGS_ID, new_b)], - None, - ); + self.living_entity + .entity + .set_synced_data(tracked_data::mob::DATA_MOB_FLAGS_ID, new_b); } } @@ -669,6 +668,14 @@ pub trait Mob: EntityBase + Send + Sync { rand::rng() } + fn requires_custom_persistence(&self) -> bool { + false + } + + fn remove_when_far_away(&self, _distance_sq: f64) -> bool { + true + } + fn get_max_look_yaw_change(&self) -> f32 { 10.0 } @@ -1052,10 +1059,7 @@ pub trait Mob: EntityBase + Send + Sync { let entity = self.get_entity(); let is_baby = entity.age.load(std::sync::atomic::Ordering::Relaxed) < 0; if is_baby { - entity.send_meta_data( - &[Metadata::new(tracked_data::ageable_mob::DATA_BABY_ID, true)], - None, - ); + entity.set_synced_data(tracked_data::ageable_mob::DATA_BABY_ID, true); } } diff --git a/crates/pumpkin/src/entity/mob/piglin.rs b/crates/pumpkin/src/entity/mob/piglin.rs index b366ce227..857858bae 100644 --- a/crates/pumpkin/src/entity/mob/piglin.rs +++ b/crates/pumpkin/src/entity/mob/piglin.rs @@ -13,7 +13,6 @@ use pumpkin_data::sound::{Sound, SoundCategory}; use pumpkin_data::tracked_data; use pumpkin_nbt::compound::NbtCompound; use pumpkin_nbt::tag::NbtTag; -use pumpkin_protocol::java::client::play::Metadata; use pumpkin_util::math::boundingbox::EntityDimensions; use pumpkin_util::math::position::BlockPos; @@ -171,13 +170,10 @@ impl PiglinEntity { pub fn set_immune_to_zombification(&self, immune: bool) { self.immune_to_zombification .store(immune, Ordering::Relaxed); - self.mob_entity.living_entity.entity.send_meta_data( - &[Metadata::new( - tracked_data::piglin::DATA_IMMUNE_TO_ZOMBIFICATION, - immune, - )], - None, - ); + self.mob_entity + .living_entity + .entity + .set_synced_data(tracked_data::piglin::DATA_IMMUNE_TO_ZOMBIFICATION, immune); } #[must_use] @@ -200,10 +196,7 @@ impl PiglinEntity { pub fn set_baby(&self, baby: bool) { self.is_baby.store(baby, Ordering::Relaxed); let entity = &self.mob_entity.living_entity.entity; - entity.send_meta_data( - &[Metadata::new(tracked_data::piglin::DATA_BABY_ID, baby)], - None, - ); + entity.set_synced_data(tracked_data::piglin::DATA_BABY_ID, baby); if baby { entity.entity_dimension.store(Self::BABY_DIMENSIONS); } else { @@ -219,13 +212,10 @@ impl PiglinEntity { pub fn set_charging_crossbow(&self, is_charging: bool) { self.is_charging_crossbow .store(is_charging, Ordering::Relaxed); - self.mob_entity.living_entity.entity.send_meta_data( - &[Metadata::new( - tracked_data::piglin::DATA_IS_CHARGING_CROSSBOW, - is_charging, - )], - None, - ); + self.mob_entity + .living_entity + .entity + .set_synced_data(tracked_data::piglin::DATA_IS_CHARGING_CROSSBOW, is_charging); } #[must_use] @@ -235,13 +225,10 @@ impl PiglinEntity { pub fn set_dancing(&self, is_dancing: bool) { self.is_dancing.store(is_dancing, Ordering::Relaxed); - self.mob_entity.living_entity.entity.send_meta_data( - &[Metadata::new( - tracked_data::piglin::DATA_IS_DANCING, - is_dancing, - )], - None, - ); + self.mob_entity + .living_entity + .entity + .set_synced_data(tracked_data::piglin::DATA_IS_DANCING, is_dancing); } #[must_use] @@ -537,27 +524,17 @@ impl Mob for PiglinEntity { fn mob_init_data_tracker(&self) { let entity = self.get_entity(); - let mut meta = Vec::new(); if self.is_immune_to_zombification() { - meta.push(Metadata::new( - tracked_data::piglin::DATA_IMMUNE_TO_ZOMBIFICATION, - true, - )); + entity.set_synced_data(tracked_data::piglin::DATA_IMMUNE_TO_ZOMBIFICATION, true); } if self.is_baby() { - meta.push(Metadata::new(tracked_data::piglin::DATA_BABY_ID, true)); + entity.set_synced_data(tracked_data::piglin::DATA_BABY_ID, true); } if self.is_charging_crossbow() { - meta.push(Metadata::new( - tracked_data::piglin::DATA_IS_CHARGING_CROSSBOW, - true, - )); + entity.set_synced_data(tracked_data::piglin::DATA_IS_CHARGING_CROSSBOW, true); } if self.is_dancing() { - meta.push(Metadata::new(tracked_data::piglin::DATA_IS_DANCING, true)); - } - if !meta.is_empty() { - entity.send_meta_data(&meta, None); + entity.set_synced_data(tracked_data::piglin::DATA_IS_DANCING, true); } } diff --git a/crates/pumpkin/src/entity/mob/piglin_brute.rs b/crates/pumpkin/src/entity/mob/piglin_brute.rs index 4ff19fc34..3464bd011 100644 --- a/crates/pumpkin/src/entity/mob/piglin_brute.rs +++ b/crates/pumpkin/src/entity/mob/piglin_brute.rs @@ -9,7 +9,6 @@ use pumpkin_data::entity::EntityType; use pumpkin_data::sound::{Sound, SoundCategory}; use pumpkin_data::tracked_data; use pumpkin_nbt::compound::NbtCompound; -use pumpkin_protocol::java::client::play::Metadata; use pumpkin_util::math::position::BlockPos; use crate::entity::{ @@ -98,12 +97,9 @@ impl PiglinBruteEntity { pub fn set_immune_to_zombification(&self, immune: bool) { self.immune_to_zombification .store(immune, Ordering::Relaxed); - self.mob_entity.living_entity.entity.send_meta_data( - &[Metadata::new( - tracked_data::piglin_brute::DATA_IMMUNE_TO_ZOMBIFICATION, - immune, - )], - None, + self.mob_entity.living_entity.entity.set_synced_data( + tracked_data::piglin_brute::DATA_IMMUNE_TO_ZOMBIFICATION, + immune, ); } @@ -185,12 +181,9 @@ impl Mob for PiglinBruteEntity { fn mob_init_data_tracker(&self) { let entity = self.get_entity(); if self.is_immune_to_zombification() { - entity.send_meta_data( - &[Metadata::new( - tracked_data::piglin_brute::DATA_IMMUNE_TO_ZOMBIFICATION, - true, - )], - None, + entity.set_synced_data( + tracked_data::piglin_brute::DATA_IMMUNE_TO_ZOMBIFICATION, + true, ); } } diff --git a/crates/pumpkin/src/entity/mob/pillager.rs b/crates/pumpkin/src/entity/mob/pillager.rs index 59c625407..12bf7beeb 100644 --- a/crates/pumpkin/src/entity/mob/pillager.rs +++ b/crates/pumpkin/src/entity/mob/pillager.rs @@ -9,7 +9,6 @@ use pumpkin_data::sound::Sound; use pumpkin_data::tracked_data; use pumpkin_nbt::compound::NbtCompound; use pumpkin_nbt::tag::NbtTag; -use pumpkin_protocol::java::client::play::Metadata; use crate::entity::{ Entity, EntityBase, @@ -105,13 +104,10 @@ impl PillagerEntity { pub fn set_charging_crossbow(&self, is_charging: bool) { self.is_charging_crossbow .store(is_charging, Ordering::Relaxed); - self.mob_entity.living_entity.entity.send_meta_data( - &[Metadata::new( - tracked_data::pillager::IS_CHARGING_CROSSBOW, - is_charging, - )], - None, - ); + self.mob_entity + .living_entity + .entity + .set_synced_data(tracked_data::pillager::IS_CHARGING_CROSSBOW, is_charging); } pub fn add_to_inventory(&self, item: ItemStack) -> Option { diff --git a/crates/pumpkin/src/entity/mob/raider.rs b/crates/pumpkin/src/entity/mob/raider.rs index 09279e4f4..b6454beaa 100644 --- a/crates/pumpkin/src/entity/mob/raider.rs +++ b/crates/pumpkin/src/entity/mob/raider.rs @@ -8,7 +8,6 @@ use pumpkin_data::item_stack::ItemStack; use pumpkin_data::sound::Sound; use pumpkin_data::tracked_data; use pumpkin_nbt::compound::NbtCompound; -use pumpkin_protocol::java::client::play::Metadata; use pumpkin_util::math::vector3::Vector3; use pumpkin_util::text::TextComponent; @@ -93,13 +92,7 @@ pub trait Raider: PatrollingMonster { .is_celebrating .store(celebrating, Ordering::Relaxed); let entity = &self.get_mob_entity().living_entity.entity; - entity.send_meta_data( - &[Metadata::new( - tracked_data::pillager::IS_CELEBRATING, - celebrating, - )], - None, - ); + entity.set_synced_data(tracked_data::pillager::IS_CELEBRATING, celebrating); } fn has_active_raid(&self) -> bool { diff --git a/crates/pumpkin/src/entity/mob/shulker.rs b/crates/pumpkin/src/entity/mob/shulker.rs index 5dd7678e1..5e0bbf55e 100644 --- a/crates/pumpkin/src/entity/mob/shulker.rs +++ b/crates/pumpkin/src/entity/mob/shulker.rs @@ -8,7 +8,7 @@ use pumpkin_data::entity::EntityType; use pumpkin_data::sound::{Sound, SoundCategory}; use pumpkin_nbt::compound::NbtCompound; use pumpkin_protocol::codec::var_int::VarInt; -use pumpkin_protocol::java::client::play::{CEntityPositionSync, Metadata}; +use pumpkin_protocol::java::client::play::CEntityPositionSync; use pumpkin_util::math::position::BlockPos; use pumpkin_util::math::vector3::Vector3; @@ -113,12 +113,9 @@ impl ShulkerEntity { pub fn set_attach_face(&self, face: BlockDirection) { self.attach_face.store(face as u8, Ordering::Relaxed); let entity = &self.mob_entity.living_entity.entity; - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::shulker::ATTACH_FACE_ID, - VarInt(face as i32), - )], - None, + entity.set_synced_data( + pumpkin_data::tracked_data::shulker::ATTACH_FACE_ID, + VarInt(face as i32), ); } @@ -131,13 +128,7 @@ impl ShulkerEntity { let val = color.unwrap_or(NO_COLOR); self.color.store(val, Ordering::Relaxed); let entity = &self.mob_entity.living_entity.entity; - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::shulker::COLOR, - val as i8, - )], - None, - ); + entity.set_synced_data(pumpkin_data::tracked_data::shulker::COLOR, val as i8); } pub fn get_raw_peek(&self) -> u8 { @@ -169,13 +160,7 @@ impl ShulkerEntity { self.peek_amount.store(amount, Ordering::Relaxed); - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::shulker::PEEK_ID, - amount, - )], - None, - ); + entity.set_synced_data(pumpkin_data::tracked_data::shulker::PEEK_ID, amount); } pub fn is_closed(&self) -> bool { diff --git a/crates/pumpkin/src/entity/mob/spider.rs b/crates/pumpkin/src/entity/mob/spider.rs index 06a2f5adf..825bf1335 100644 --- a/crates/pumpkin/src/entity/mob/spider.rs +++ b/crates/pumpkin/src/entity/mob/spider.rs @@ -2,7 +2,6 @@ use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{Arc, Weak}; use pumpkin_data::entity::EntityType; -use pumpkin_protocol::java::client::play::Metadata; use crate::entity::{ Entity, EntityBase, @@ -74,13 +73,10 @@ impl SpiderEntity { pub fn set_climbing(&self, climbing: bool) { if self.is_climbing.swap(climbing, Ordering::Relaxed) != climbing { let flags = i8::from(climbing); - self.mob_entity.living_entity.entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::spider::DATA_FLAGS_ID, - flags, - )], - None, - ); + self.mob_entity + .living_entity + .entity + .set_synced_data(pumpkin_data::tracked_data::spider::DATA_FLAGS_ID, flags); } } } diff --git a/crates/pumpkin/src/entity/mob/witch.rs b/crates/pumpkin/src/entity/mob/witch.rs index 71415bede..16fe9a8a7 100644 --- a/crates/pumpkin/src/entity/mob/witch.rs +++ b/crates/pumpkin/src/entity/mob/witch.rs @@ -10,7 +10,6 @@ use pumpkin_data::item_stack::ItemStack; use pumpkin_data::potion::Potion; use pumpkin_data::sound::{Sound, SoundCategory}; use pumpkin_data::tracked_data; -use pumpkin_protocol::java::client::play::Metadata; use crate::entity::{ Entity, EntityBase, @@ -118,13 +117,10 @@ impl WitchEntity { pub fn set_drinking_potion(&self, drinking: bool) { self.drinking_potion.store(drinking, Ordering::Relaxed); - self.mob_entity.living_entity.entity.send_meta_data( - &[Metadata::new( - tracked_data::witch::DATA_USING_ITEM, - drinking, - )], - None, - ); + self.mob_entity + .living_entity + .entity + .set_synced_data(tracked_data::witch::DATA_USING_ITEM, drinking); } #[must_use] diff --git a/crates/pumpkin/src/entity/mob/zombie/zombie.rs b/crates/pumpkin/src/entity/mob/zombie/zombie.rs index 721d59fe3..23373a463 100644 --- a/crates/pumpkin/src/entity/mob/zombie/zombie.rs +++ b/crates/pumpkin/src/entity/mob/zombie/zombie.rs @@ -86,12 +86,10 @@ impl ZombieEntity { .entity .age .store(age, std::sync::atomic::Ordering::Relaxed); - self.entity.mob_entity.living_entity.entity.send_meta_data( - &[pumpkin_protocol::java::client::play::Metadata::new( - pumpkin_data::tracked_data::zombie::BABY, - baby, - )], - None, - ); + self.entity + .mob_entity + .living_entity + .entity + .set_synced_data(pumpkin_data::tracked_data::zombie::BABY, baby); } } diff --git a/crates/pumpkin/src/entity/mod.rs b/crates/pumpkin/src/entity/mod.rs index 017f1b339..9646180eb 100644 --- a/crates/pumpkin/src/entity/mod.rs +++ b/crates/pumpkin/src/entity/mod.rs @@ -101,6 +101,7 @@ pub mod passive; pub mod player; pub mod projectile; pub mod projectile_deflection; +pub mod synched_entity_data; pub mod tnt; pub mod r#type; pub mod vehicle; @@ -205,10 +206,8 @@ pub trait EntityBase: Send + Sync + std::any::Any { if is_baby { let mut bedrock_meta = SyncedActorDataList::new(); bedrock_meta.set_flag(entity_data_key::FLAGS, entity_data_flag::BABY as u8, true); - entity.send_meta_data( - &[Metadata::new(tracked_data::ageable_mob::DATA_BABY_ID, true)], - Some(&bedrock_meta), - ); + entity.set_synced_data(tracked_data::ageable_mob::DATA_BABY_ID, true); + entity.send_bedrock_actor_data(&bedrock_meta); } } fn set_variant_name(&self, _name: &str) {} @@ -878,6 +877,7 @@ pub struct Entity { pub bedrock_flags_two: std::sync::atomic::AtomicI64, /// If true, the entity bypasses physics, collisions, and block effects (e.g. spectator, markers, display entities) pub no_physics: AtomicBool, + pub synched_data: synched_entity_data::SynchedEntityData, /// Multiplies movement for one tick before being reset pub movement_multiplier: AtomicCell>, /// Determines whether the entity's velocity needs to be sent @@ -1013,6 +1013,7 @@ impl Entity { has_no_gravity: AtomicBool::new(false), scoreboard_tags: std::sync::Mutex::new(HashSet::new()), no_physics: AtomicBool::new(false), + synched_data: synched_entity_data::SynchedEntityData::new(), movement_multiplier: AtomicCell::new(Vector3::default()), velocity_dirty: AtomicBool::new(true), removed: AtomicBool::new(false), @@ -1142,13 +1143,8 @@ impl Entity { entity_data_flag::ALWAYS_SHOW_NAME as u8, visible, ); - self.send_meta_data( - &[Metadata::new( - tracked_data::entity::DATA_CUSTOM_NAME, - Some(name), - )], - Some(&bedrock_meta), - ); + self.set_synced_data(tracked_data::entity::DATA_CUSTOM_NAME, Some(name)); + self.send_bedrock_actor_data(&bedrock_meta); } pub fn set_custom_name_visible(&self, visible: bool) { @@ -1170,13 +1166,8 @@ impl Entity { entity_data_flag::ALWAYS_SHOW_NAME as u8, visible, ); - self.send_meta_data( - &[Metadata::new( - tracked_data::entity::DATA_CUSTOM_NAME_VISIBLE, - visible, - )], - Some(&bedrock_meta), - ); + self.set_synced_data(tracked_data::entity::DATA_CUSTOM_NAME_VISIBLE, visible); + self.send_bedrock_actor_data(&bedrock_meta); } pub fn is_silent(&self) -> bool { @@ -1185,10 +1176,7 @@ impl Entity { pub fn set_silent(&self, silent: bool) { self.silent.store(silent, Ordering::Relaxed); - self.send_meta_data( - &[Metadata::new(tracked_data::entity::DATA_SILENT, silent)], - None, - ); + self.set_synced_data(tracked_data::entity::DATA_SILENT, silent); } pub fn has_no_gravity(&self) -> bool { @@ -1197,13 +1185,7 @@ impl Entity { pub fn set_has_no_gravity(&self, no_gravity: bool) { self.has_no_gravity.store(no_gravity, Ordering::Relaxed); - self.send_meta_data( - &[Metadata::new( - tracked_data::entity::DATA_NO_GRAVITY, - no_gravity, - )], - None, - ); + self.set_synced_data(tracked_data::entity::DATA_NO_GRAVITY, no_gravity); } pub fn send_velocity(&self) { @@ -2618,13 +2600,11 @@ impl Entity { entity_data_key::FREEZING_EFFECT_STRENGTH, MetadataValue::Float(new_frozen_ticks as f32), ); - self.send_meta_data( - &[Metadata::new( - tracked_data::entity::DATA_TICKS_FROZEN, - VarInt(new_frozen_ticks), - )], - Some(&bedrock_meta), + self.set_synced_data( + tracked_data::entity::DATA_TICKS_FROZEN, + VarInt(new_frozen_ticks), ); + self.send_bedrock_actor_data(&bedrock_meta); } // Vanilla parity: full-freeze damage is tick-phase based. @@ -2648,13 +2628,11 @@ impl Entity { entity_data_key::FREEZING_EFFECT_STRENGTH, MetadataValue::Float(new_frozen_ticks as f32), ); - self.send_meta_data( - &[Metadata::new( - tracked_data::entity::DATA_TICKS_FROZEN, - VarInt(new_frozen_ticks), - )], - Some(&bedrock_meta), + self.set_synced_data( + tracked_data::entity::DATA_TICKS_FROZEN, + VarInt(new_frozen_ticks), ); + self.send_bedrock_actor_data(&bedrock_meta); } /// Returns the number of ticks the entity has been frozen. @@ -2969,13 +2947,7 @@ impl Entity { self.flags.fetch_and(!mask, Ordering::Relaxed) & !mask }; - self.send_meta_data( - &[Metadata::new( - tracked_data::entity::DATA_SHARED_FLAGS_ID, - new_je_flags, - )], - None, - ); + self.set_synced_data(tracked_data::entity::DATA_SHARED_FLAGS_ID, new_je_flags); if let Some(bedrock_flag) = flag.to_bedrock() { let (key, index) = if bedrock_flag >= 64 { @@ -3031,26 +3003,31 @@ impl Entity { .play_sound(sound, SoundCategory::Neutral, &self.pos.load()); } - pub fn send_meta_data( + pub fn set_synced_data( &self, - meta: &[Metadata], - bedrock_meta: Option<&SyncedActorDataList>, - ) { + tracked: pumpkin_data::tracked_data::TrackedData, + value: T, + ) -> bool { + if self.synched_data.set(tracked, value) { + self.send_dirty_entity_data(); + true + } else { + false + } + } + + pub fn send_bedrock_actor_data(&self, bedrock_meta: &SyncedActorDataList) { let world = self.world.load(); let players = world.players.load(); - - let mut java_recipients = Vec::new(); let mut bedrock_recipients = Vec::new(); if let Some(tracked) = world.entity_tracker.get_tracked_entity(self.entity_id) { for player in players.iter() { - if tracked.seen_by.contains(&player.gameprofile.id) - || player.entity_id() == self.entity_id + if (tracked.seen_by.contains(&player.gameprofile.id) + || player.entity_id() == self.entity_id) + && let ClientPlatform::Bedrock(client) = player.client.as_ref() { - match player.client.as_ref() { - ClientPlatform::Java(_) => java_recipients.push(player), - ClientPlatform::Bedrock(client) => bedrock_recipients.push(client), - } + bedrock_recipients.push(client); } } } else { @@ -3059,51 +3036,86 @@ impl Entity { let center = player.get_entity().chunk_pos.load(); let view_distance = crate::world::chunker::get_view_distance(player).get() as i32; - if is_within_view_distance(chunk_pos, center, view_distance) { - match player.client.as_ref() { - ClientPlatform::Java(_) => java_recipients.push(player), - ClientPlatform::Bedrock(client) => bedrock_recipients.push(client), - } + if is_within_view_distance(chunk_pos, center, view_distance) + && let ClientPlatform::Bedrock(client) = player.client.as_ref() + { + bedrock_recipients.push(client); } } } + let packet = CSetActorData { + target_runtime_id: VarULong(self.entity_id as u64), + actor_data: SyncedActorDataList(bedrock_meta.0.clone()), + synced_properties: PropertySyncData { + int_entries_list: std::collections::HashMap::new(), + float_entries_list: std::collections::HashMap::new(), + }, + tick: VarULong(0), + }; + for recipient in bedrock_recipients { + if let Ok(packet_data) = recipient.serialize_packet(&packet) { + recipient.try_enqueue_packet(packet_data); + } + } + } + + pub fn send_dirty_entity_data(&self) { + if !self.synched_data.is_dirty() { + return; + } + + let world = self.world.load(); + let players = world.players.load(); + + let mut java_recipients = Vec::new(); + + if let Some(tracked) = world.entity_tracker.get_tracked_entity(self.entity_id) { + for player in players.iter() { + if (tracked.seen_by.contains(&player.gameprofile.id) + || player.entity_id() == self.entity_id) + && let ClientPlatform::Java(_) = player.client.as_ref() + { + java_recipients.push(player); + } + } + } else { + let chunk_pos = self.chunk_pos.load(); + for player in players.iter() { + let center = player.get_entity().chunk_pos.load(); + let view_distance = crate::world::chunker::get_view_distance(player).get() as i32; + + if is_within_view_distance(chunk_pos, center, view_distance) + && let ClientPlatform::Java(_) = player.client.as_ref() + { + java_recipients.push(player); + } + } + } + + if java_recipients.is_empty() { + return; + } + let recipients_by_version = World::collect_java_recipients_by_version(java_recipients.into_iter()); for (version, recipients) in recipients_by_version { - if version < JavaMinecraftVersion::V_1_21 { + // TODO: Support older versions + if version < JavaMinecraftVersion::V_26_2 { continue; } - let mut buf = Vec::new(); - for m in meta { - let _ = m.write(&mut buf, &version); - } - buf.put_u8(255); - let packet = CSetEntityMetadata::new(self.entity_id.into(), buf.into()); - if let Ok(packet_data) = JavaClient::serialize_packet_for_version(&packet, version) { - for recipient in recipients { - recipient.try_enqueue_packet(packet_data.clone()); - } - } - } - - if let Some(bedrock_meta) = bedrock_meta { - let packet = CSetActorData { - target_runtime_id: VarULong(self.entity_id as u64), - actor_data: SyncedActorDataList(bedrock_meta.0.clone()), - synced_properties: PropertySyncData { - int_entries_list: std::collections::HashMap::new(), - float_entries_list: std::collections::HashMap::new(), - }, - tick: VarULong(0), - }; - for recipient in bedrock_recipients { - if let Ok(packet_data) = recipient.serialize_packet(&packet) { - recipient.try_enqueue_packet(packet_data); + if let Some(buf) = self.synched_data.pack_dirty_for_version(&version) { + let packet = CSetEntityMetadata::new(self.entity_id.into(), buf); + if let Ok(packet_data) = JavaClient::serialize_packet_for_version(&packet, version) + { + for recipient in recipients { + recipient.try_enqueue_packet(packet_data.clone()); + } } } } + self.synched_data.clear_dirty(); } pub fn set_pose(&self, pose: EntityPose) { @@ -3142,10 +3154,8 @@ impl Entity { entity_data_key::HEIGHT, MetadataValue::Float(dimension.height), ); - self.send_meta_data( - &[Metadata::new(tracked_data::entity::DATA_POSE, VarInt(pose))], - Some(&bedrock_meta), - ); + self.set_synced_data(tracked_data::entity::DATA_POSE, VarInt(pose)); + self.send_bedrock_actor_data(&bedrock_meta); } /// Checks if the entity is invulnerable to the given damage type, considering both general invulnerability and specific immunities. diff --git a/crates/pumpkin/src/entity/passive/allay.rs b/crates/pumpkin/src/entity/passive/allay.rs index 0f8400595..0ca495e26 100644 --- a/crates/pumpkin/src/entity/passive/allay.rs +++ b/crates/pumpkin/src/entity/passive/allay.rs @@ -10,7 +10,6 @@ use pumpkin_data::item_stack::ItemStack; use pumpkin_data::particle::Particle; use pumpkin_data::sound::{Sound, SoundCategory}; use pumpkin_nbt::compound::NbtCompound; -use pumpkin_protocol::java::client::play::Metadata; use pumpkin_util::math::vector3::Vector3; use uuid::Uuid; @@ -76,13 +75,7 @@ impl AllayEntity { pub fn set_dancing(&self, dancing: bool) { self.dancing.store(dancing, Ordering::Relaxed); let entity = self.get_entity(); - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::allay::DATA_DANCING, - dancing, - )], - None, - ); + entity.set_synced_data(pumpkin_data::tracked_data::allay::DATA_DANCING, dancing); } #[must_use] @@ -93,12 +86,9 @@ impl AllayEntity { pub fn set_can_duplicate(&self, can_duplicate: bool) { self.can_duplicate.store(can_duplicate, Ordering::Relaxed); let entity = self.get_entity(); - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::allay::DATA_CAN_DUPLICATE, - can_duplicate, - )], - None, + entity.set_synced_data( + pumpkin_data::tracked_data::allay::DATA_CAN_DUPLICATE, + can_duplicate, ); } } @@ -144,19 +134,13 @@ impl Mob for AllayEntity { fn mob_init_data_tracker(&self) { let entity = self.get_entity(); - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::allay::DATA_DANCING, - self.is_dancing(), - )], - None, + entity.set_synced_data( + pumpkin_data::tracked_data::allay::DATA_DANCING, + self.is_dancing(), ); - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::allay::DATA_CAN_DUPLICATE, - self.can_duplicate(), - )], - None, + entity.set_synced_data( + pumpkin_data::tracked_data::allay::DATA_CAN_DUPLICATE, + self.can_duplicate(), ); } diff --git a/crates/pumpkin/src/entity/passive/armadillo.rs b/crates/pumpkin/src/entity/passive/armadillo.rs index 9c7b28af6..ea03aab6b 100644 --- a/crates/pumpkin/src/entity/passive/armadillo.rs +++ b/crates/pumpkin/src/entity/passive/armadillo.rs @@ -11,7 +11,6 @@ use pumpkin_data::sound::{Sound, SoundCategory}; use pumpkin_data::tag::{self, Taggable}; use pumpkin_nbt::compound::NbtCompound; use pumpkin_protocol::codec::var_int::VarInt; -use pumpkin_protocol::java::client::play::Metadata; use crate::entity::{ Entity, EntityBase, @@ -171,12 +170,9 @@ impl ArmadilloEntity { self.state.store(state.id(), Ordering::Relaxed); self.in_state_ticks.store(0, Ordering::Relaxed); let entity = self.get_entity(); - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::armadillo::ARMADILLO_STATE, - VarInt(state.id()), - )], - None, + entity.set_synced_data( + pumpkin_data::tracked_data::armadillo::ARMADILLO_STATE, + VarInt(state.id()), ); } @@ -412,20 +408,11 @@ impl Mob for ArmadilloEntity { let entity = self.get_entity(); let is_baby = entity.age.load(Ordering::Relaxed) < 0; if is_baby { - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::armadillo::BABY_ID, - true, - )], - None, - ); + entity.set_synced_data(pumpkin_data::tracked_data::armadillo::BABY_ID, true); } - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::armadillo::ARMADILLO_STATE, - VarInt(self.get_state().id()), - )], - None, + entity.set_synced_data( + pumpkin_data::tracked_data::armadillo::ARMADILLO_STATE, + VarInt(self.get_state().id()), ); } diff --git a/crates/pumpkin/src/entity/passive/axolotl.rs b/crates/pumpkin/src/entity/passive/axolotl.rs index fb53ee1be..97e15ed99 100644 --- a/crates/pumpkin/src/entity/passive/axolotl.rs +++ b/crates/pumpkin/src/entity/passive/axolotl.rs @@ -10,7 +10,6 @@ use pumpkin_data::sound::{Sound, SoundCategory}; use pumpkin_data::tag::{self, Taggable}; use pumpkin_nbt::compound::NbtCompound; use pumpkin_protocol::codec::var_int::VarInt; -use pumpkin_protocol::java::client::play::Metadata; use rand::RngExt; use crate::entity::{ @@ -184,12 +183,9 @@ impl AxolotlEntity { pub fn set_variant(&self, variant: AxolotlVariant) { self.variant.store(variant.id(), Ordering::Relaxed); let entity = self.get_entity(); - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::axolotl::DATA_VARIANT, - VarInt(variant.id()), - )], - None, + entity.set_synced_data( + pumpkin_data::tracked_data::axolotl::DATA_VARIANT, + VarInt(variant.id()), ); } @@ -201,12 +197,9 @@ impl AxolotlEntity { pub fn set_playing_dead(&self, playing_dead: bool) { self.playing_dead.store(playing_dead, Ordering::Relaxed); let entity = self.get_entity(); - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::axolotl::DATA_PLAYING_DEAD, - playing_dead, - )], - None, + entity.set_synced_data( + pumpkin_data::tracked_data::axolotl::DATA_PLAYING_DEAD, + playing_dead, ); } @@ -218,12 +211,9 @@ impl AxolotlEntity { pub fn set_from_bucket(&self, from_bucket: bool) { self.from_bucket.store(from_bucket, Ordering::Relaxed); let entity = self.get_entity(); - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::axolotl::FROM_BUCKET, - from_bucket, - )], - None, + entity.set_synced_data( + pumpkin_data::tracked_data::axolotl::FROM_BUCKET, + from_bucket, ); } } @@ -286,34 +276,19 @@ impl Mob for AxolotlEntity { let entity = self.get_entity(); let is_baby = entity.age.load(Ordering::Relaxed) < 0; if is_baby { - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::axolotl::DATA_BABY_ID, - true, - )], - None, - ); + entity.set_synced_data(pumpkin_data::tracked_data::axolotl::DATA_BABY_ID, true); } - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::axolotl::DATA_VARIANT, - VarInt(self.get_variant().id()), - )], - None, + entity.set_synced_data( + pumpkin_data::tracked_data::axolotl::DATA_VARIANT, + VarInt(self.get_variant().id()), ); - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::axolotl::DATA_PLAYING_DEAD, - self.is_playing_dead(), - )], - None, + entity.set_synced_data( + pumpkin_data::tracked_data::axolotl::DATA_PLAYING_DEAD, + self.is_playing_dead(), ); - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::axolotl::FROM_BUCKET, - self.is_from_bucket(), - )], - None, + entity.set_synced_data( + pumpkin_data::tracked_data::axolotl::FROM_BUCKET, + self.is_from_bucket(), ); } diff --git a/crates/pumpkin/src/entity/passive/bee.rs b/crates/pumpkin/src/entity/passive/bee.rs index 5db640683..065a1bef8 100644 --- a/crates/pumpkin/src/entity/passive/bee.rs +++ b/crates/pumpkin/src/entity/passive/bee.rs @@ -8,7 +8,6 @@ use pumpkin_data::item_stack::ItemStack; use pumpkin_data::sound::Sound; use pumpkin_data::tag::{self, Taggable}; use pumpkin_nbt::compound::NbtCompound; -use pumpkin_protocol::java::client::play::Metadata; use crate::entity::{ Entity, EntityBase, @@ -103,12 +102,9 @@ impl BeeEntity { let new_flags = if val { current | flag } else { current & !flag }; self.flags.store(new_flags, Ordering::Relaxed); let entity = self.get_entity(); - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::bee::DATA_FLAGS_ID, - new_flags as i8, - )], - None, + entity.set_synced_data( + pumpkin_data::tracked_data::bee::DATA_FLAGS_ID, + new_flags as i8, ); } @@ -220,20 +216,11 @@ impl Mob for BeeEntity { let entity = self.get_entity(); let is_baby = entity.age.load(Ordering::Relaxed) < 0; if is_baby { - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::bee::DATA_BABY_ID, - true, - )], - None, - ); + entity.set_synced_data(pumpkin_data::tracked_data::bee::DATA_BABY_ID, true); } - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::bee::DATA_FLAGS_ID, - self.flags.load(Ordering::Relaxed) as i8, - )], - None, + entity.set_synced_data( + pumpkin_data::tracked_data::bee::DATA_FLAGS_ID, + self.flags.load(Ordering::Relaxed) as i8, ); } diff --git a/crates/pumpkin/src/entity/passive/camel.rs b/crates/pumpkin/src/entity/passive/camel.rs index 4f4653128..43dbd8488 100644 --- a/crates/pumpkin/src/entity/passive/camel.rs +++ b/crates/pumpkin/src/entity/passive/camel.rs @@ -9,7 +9,6 @@ use pumpkin_data::item_stack::ItemStack; use pumpkin_data::sound::{Sound, SoundCategory}; use pumpkin_data::tag::{self, Taggable}; use pumpkin_nbt::compound::NbtCompound; -use pumpkin_protocol::java::client::play::Metadata; use crate::entity::{ Entity, EntityBase, @@ -87,12 +86,9 @@ impl CamelEntity { let new_flags = if val { current | flag } else { current & !flag }; self.flags.store(new_flags, Ordering::Relaxed); let entity = self.get_entity(); - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::camel::DATA_ID_FLAGS, - new_flags as i8, - )], - None, + entity.set_synced_data( + pumpkin_data::tracked_data::camel::DATA_ID_FLAGS, + new_flags as i8, ); } @@ -113,13 +109,7 @@ impl CamelEntity { pub fn set_dashing(&self, dashing: bool) { self.dashing.store(dashing, Ordering::Relaxed); let entity = self.get_entity(); - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::camel::DASH, - dashing, - )], - None, - ); + entity.set_synced_data(pumpkin_data::tracked_data::camel::DASH, dashing); } } @@ -169,28 +159,13 @@ impl Mob for CamelEntity { let entity = self.get_entity(); let is_baby = entity.age.load(Ordering::Relaxed) < 0; if is_baby { - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::camel::DATA_BABY_ID, - true, - )], - None, - ); + entity.set_synced_data(pumpkin_data::tracked_data::camel::DATA_BABY_ID, true); } - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::camel::DATA_ID_FLAGS, - self.flags.load(Ordering::Relaxed) as i8, - )], - None, - ); - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::camel::DASH, - self.is_dashing(), - )], - None, + entity.set_synced_data( + pumpkin_data::tracked_data::camel::DATA_ID_FLAGS, + self.flags.load(Ordering::Relaxed) as i8, ); + entity.set_synced_data(pumpkin_data::tracked_data::camel::DASH, self.is_dashing()); } fn mob_interact(&self, player: &Arc, item_stack: &mut ItemStack) -> bool { diff --git a/crates/pumpkin/src/entity/passive/cat.rs b/crates/pumpkin/src/entity/passive/cat.rs index a3691dd8a..20e8c44bf 100644 --- a/crates/pumpkin/src/entity/passive/cat.rs +++ b/crates/pumpkin/src/entity/passive/cat.rs @@ -10,7 +10,6 @@ use pumpkin_data::tag::{self, Taggable}; use pumpkin_nbt::compound::NbtCompound; use pumpkin_protocol::bedrock::server::actor_event::ActorEventID; use pumpkin_protocol::codec::var_int::VarInt; -use pumpkin_protocol::java::client::play::Metadata; use rand::RngExt; use uuid::Uuid; @@ -186,24 +185,18 @@ impl CatEntity { pub fn set_collar_color(&self, color: u8) { self.collar_color.store(color, Ordering::Relaxed); let entity = self.get_entity(); - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::cat::CAT_COLLAR_COLOR, - VarInt(color as i32), - )], - None, + entity.set_synced_data( + pumpkin_data::tracked_data::cat::CAT_COLLAR_COLOR, + VarInt(color as i32), ); } pub fn set_sitting(&self, sitting: bool) { self.set_in_sitting_pose(sitting); let entity = self.get_entity(); - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::cat::TAMEABLE_FLAGS, - self.get_tame_flags(), - )], - None, + entity.set_synced_data( + pumpkin_data::tracked_data::cat::TAMEABLE_FLAGS, + self.get_tame_flags(), ); } @@ -211,56 +204,32 @@ impl CatEntity { self.tamable_data.is_tame.store(tame, Ordering::Relaxed); self.set_owner(owner); let entity = self.get_entity(); - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::cat::TAMEABLE_FLAGS, - self.get_tame_flags(), - )], - None, - ); - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::cat::OWNER_UUID, - owner, - )], - None, + entity.set_synced_data( + pumpkin_data::tracked_data::cat::TAMEABLE_FLAGS, + self.get_tame_flags(), ); + entity.set_synced_data(pumpkin_data::tracked_data::cat::OWNER_UUID, owner); } pub fn set_variant(&self, variant: u8) { self.variant.store(variant, Ordering::Relaxed); let entity = self.get_entity(); - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::cat::CAT_VARIANT, - VarInt(variant as i32), - )], - None, + entity.set_synced_data( + pumpkin_data::tracked_data::cat::CAT_VARIANT, + VarInt(variant as i32), ); } pub fn set_lying(&self, lying: bool) { self.is_lying.store(lying, Ordering::Relaxed); let entity = self.get_entity(); - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::cat::IS_LYING, - lying, - )], - None, - ); + entity.set_synced_data(pumpkin_data::tracked_data::cat::IS_LYING, lying); } pub fn set_relax_state_one(&self, relax: bool) { self.relax_state_one.store(relax, Ordering::Relaxed); let entity = self.get_entity(); - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::cat::RELAX_STATE_ONE, - relax, - )], - None, - ); + entity.set_synced_data(pumpkin_data::tracked_data::cat::RELAX_STATE_ONE, relax); } pub fn play_eating_sound(&self) { @@ -371,62 +340,35 @@ impl Mob for CatEntity { let entity = self.get_entity(); let is_baby = entity.age.load(Ordering::Relaxed) < 0; if is_baby { - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::cat::BABY_ID, - true, - )], - None, - ); + entity.set_synced_data(pumpkin_data::tracked_data::cat::BABY_ID, true); } - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::cat::TAMEABLE_FLAGS, - self.get_tame_flags(), - )], - None, + entity.set_synced_data( + pumpkin_data::tracked_data::cat::TAMEABLE_FLAGS, + self.get_tame_flags(), ); - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::cat::OWNER_UUID, - self.get_owner(), - )], - None, + entity.set_synced_data( + pumpkin_data::tracked_data::cat::OWNER_UUID, + self.get_owner(), ); - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::cat::CAT_VARIANT, - VarInt(self.variant.load(Ordering::Relaxed) as i32), - )], - None, + entity.set_synced_data( + pumpkin_data::tracked_data::cat::CAT_VARIANT, + VarInt(self.variant.load(Ordering::Relaxed) as i32), ); - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::cat::IS_LYING, - self.is_lying.load(Ordering::Relaxed), - )], - None, + entity.set_synced_data( + pumpkin_data::tracked_data::cat::IS_LYING, + self.is_lying.load(Ordering::Relaxed), ); - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::cat::RELAX_STATE_ONE, - self.relax_state_one.load(Ordering::Relaxed), - )], - None, + entity.set_synced_data( + pumpkin_data::tracked_data::cat::RELAX_STATE_ONE, + self.relax_state_one.load(Ordering::Relaxed), ); - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::cat::CAT_COLLAR_COLOR, - VarInt(self.collar_color.load(Ordering::Relaxed) as i32), - )], - None, + entity.set_synced_data( + pumpkin_data::tracked_data::cat::CAT_COLLAR_COLOR, + VarInt(self.collar_color.load(Ordering::Relaxed) as i32), ); - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::cat::SOUND_VARIANT, - VarInt(self.sound_variant.load(Ordering::Relaxed) as i32), - )], - None, + entity.set_synced_data( + pumpkin_data::tracked_data::cat::SOUND_VARIANT, + VarInt(self.sound_variant.load(Ordering::Relaxed) as i32), ); } diff --git a/crates/pumpkin/src/entity/passive/chicken.rs b/crates/pumpkin/src/entity/passive/chicken.rs index d8a0db691..b271467f1 100644 --- a/crates/pumpkin/src/entity/passive/chicken.rs +++ b/crates/pumpkin/src/entity/passive/chicken.rs @@ -150,20 +150,11 @@ impl Mob for ChickenEntity { let entity = self.get_entity(); let is_baby = entity.age.load(Ordering::Relaxed) < 0; if is_baby { - entity.send_meta_data( - &[pumpkin_protocol::java::client::play::Metadata::new( - pumpkin_data::tracked_data::chicken::BABY_ID, - true, - )], - None, - ); + entity.set_synced_data(pumpkin_data::tracked_data::chicken::BABY_ID, true); } - entity.send_meta_data( - &[pumpkin_protocol::java::client::play::Metadata::new( - pumpkin_data::tracked_data::chicken::VARIANT, - VarInt(self.variant.load(Ordering::Relaxed) as i32), - )], - None, + entity.set_synced_data( + pumpkin_data::tracked_data::chicken::VARIANT, + VarInt(self.variant.load(Ordering::Relaxed) as i32), ); } diff --git a/crates/pumpkin/src/entity/passive/copper_golem.rs b/crates/pumpkin/src/entity/passive/copper_golem.rs index 1687b3eb0..d369b9a0f 100644 --- a/crates/pumpkin/src/entity/passive/copper_golem.rs +++ b/crates/pumpkin/src/entity/passive/copper_golem.rs @@ -10,7 +10,6 @@ use pumpkin_data::sound::{Sound, SoundCategory}; use pumpkin_data::tag::{self, Taggable}; use pumpkin_nbt::compound::NbtCompound; use pumpkin_protocol::codec::var_int::VarInt; -use pumpkin_protocol::java::client::play::Metadata; use crate::entity::{ Entity, EntityBase, @@ -148,12 +147,9 @@ impl CopperGolemEntity { pub fn set_weather_state(&self, state: WeatherState) { self.weather_state.store(state.id(), Ordering::Relaxed); let entity = self.get_entity(); - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::copper_golem::WEATHER_STATE, - VarInt(state.id()), - )], - None, + entity.set_synced_data( + pumpkin_data::tracked_data::copper_golem::WEATHER_STATE, + VarInt(state.id()), ); } @@ -165,12 +161,9 @@ impl CopperGolemEntity { pub fn set_state(&self, state: CopperGolemState) { self.state.store(state.id(), Ordering::Relaxed); let entity = self.get_entity(); - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::copper_golem::COPPER_GOLEM_STATE, - VarInt(state.id()), - )], - None, + entity.set_synced_data( + pumpkin_data::tracked_data::copper_golem::COPPER_GOLEM_STATE, + VarInt(state.id()), ); } @@ -237,18 +230,13 @@ impl Mob for CopperGolemEntity { fn mob_init_data_tracker(&self) { let entity = self.get_entity(); - entity.send_meta_data( - &[ - Metadata::new( - pumpkin_data::tracked_data::copper_golem::WEATHER_STATE, - VarInt(self.get_weather_state().id()), - ), - Metadata::new( - pumpkin_data::tracked_data::copper_golem::COPPER_GOLEM_STATE, - VarInt(self.get_state().id()), - ), - ], - None, + entity.set_synced_data( + pumpkin_data::tracked_data::copper_golem::WEATHER_STATE, + VarInt(self.get_weather_state().id()), + ); + entity.set_synced_data( + pumpkin_data::tracked_data::copper_golem::COPPER_GOLEM_STATE, + VarInt(self.get_state().id()), ); } diff --git a/crates/pumpkin/src/entity/passive/donkey.rs b/crates/pumpkin/src/entity/passive/donkey.rs index c95405162..57ea9fdf5 100644 --- a/crates/pumpkin/src/entity/passive/donkey.rs +++ b/crates/pumpkin/src/entity/passive/donkey.rs @@ -10,7 +10,6 @@ use pumpkin_data::item_stack::ItemStack; use pumpkin_data::sound::{Sound, SoundCategory}; use pumpkin_data::tag::{self, Taggable}; use pumpkin_nbt::compound::NbtCompound; -use pumpkin_protocol::java::client::play::Metadata; use uuid::Uuid; use crate::entity::{ @@ -98,12 +97,9 @@ impl DonkeyEntity { let new_flags = if val { current | flag } else { current & !flag }; self.flags.store(new_flags, Ordering::Relaxed); let entity = self.get_entity(); - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::donkey::DATA_ID_FLAGS, - new_flags as i8, - )], - None, + entity.set_synced_data( + pumpkin_data::tracked_data::donkey::DATA_ID_FLAGS, + new_flags as i8, ); } @@ -133,13 +129,7 @@ impl DonkeyEntity { pub fn set_has_chest(&self, val: bool) { self.has_chest.store(val, Ordering::Relaxed); let entity = self.get_entity(); - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::donkey::DATA_ID_CHEST, - val, - )], - None, - ); + entity.set_synced_data(pumpkin_data::tracked_data::donkey::DATA_ID_CHEST, val); } } @@ -209,27 +199,15 @@ impl Mob for DonkeyEntity { let entity = self.get_entity(); let is_baby = entity.age.load(Ordering::Relaxed) < 0; if is_baby { - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::donkey::DATA_BABY_ID, - true, - )], - None, - ); + entity.set_synced_data(pumpkin_data::tracked_data::donkey::DATA_BABY_ID, true); } - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::donkey::DATA_ID_CHEST, - self.has_chest(), - )], - None, + entity.set_synced_data( + pumpkin_data::tracked_data::donkey::DATA_ID_CHEST, + self.has_chest(), ); - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::donkey::DATA_ID_FLAGS, - self.flags.load(Ordering::Relaxed) as i8, - )], - None, + entity.set_synced_data( + pumpkin_data::tracked_data::donkey::DATA_ID_FLAGS, + self.flags.load(Ordering::Relaxed) as i8, ); } diff --git a/crates/pumpkin/src/entity/passive/fox.rs b/crates/pumpkin/src/entity/passive/fox.rs index 7c163da6c..6ff6bfd00 100644 --- a/crates/pumpkin/src/entity/passive/fox.rs +++ b/crates/pumpkin/src/entity/passive/fox.rs @@ -10,7 +10,6 @@ use pumpkin_data::sound::Sound; use pumpkin_data::tag::{self, Taggable}; use pumpkin_nbt::compound::NbtCompound; use pumpkin_protocol::codec::var_int::VarInt; -use pumpkin_protocol::java::client::play::Metadata; use crate::entity::{ Entity, EntityBase, @@ -122,12 +121,9 @@ impl FoxEntity { pub fn set_variant(&self, variant: FoxVariant) { self.variant.store(variant.id(), Ordering::Relaxed); let entity = self.get_entity(); - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::fox::TYPE_ID, - VarInt(variant.id()), - )], - None, + entity.set_synced_data( + pumpkin_data::tracked_data::fox::TYPE_ID, + VarInt(variant.id()), ); } @@ -144,13 +140,7 @@ impl FoxEntity { }; self.flags.store(new_flags, Ordering::Relaxed); let entity = self.get_entity(); - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::fox::FLAGS_ID, - new_flags, - )], - None, - ); + entity.set_synced_data(pumpkin_data::tracked_data::fox::FLAGS_ID, new_flags); } #[must_use] @@ -286,27 +276,15 @@ impl Mob for FoxEntity { let entity = self.get_entity(); let is_baby = entity.age.load(Ordering::Relaxed) < 0; if is_baby { - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::fox::BABY_ID, - true, - )], - None, - ); + entity.set_synced_data(pumpkin_data::tracked_data::fox::BABY_ID, true); } - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::fox::TYPE_ID, - VarInt(self.get_variant().id()), - )], - None, + entity.set_synced_data( + pumpkin_data::tracked_data::fox::TYPE_ID, + VarInt(self.get_variant().id()), ); - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::fox::FLAGS_ID, - self.flags.load(Ordering::Relaxed), - )], - None, + entity.set_synced_data( + pumpkin_data::tracked_data::fox::FLAGS_ID, + self.flags.load(Ordering::Relaxed), ); } diff --git a/crates/pumpkin/src/entity/passive/frog.rs b/crates/pumpkin/src/entity/passive/frog.rs index 8049297f5..36f1844a1 100644 --- a/crates/pumpkin/src/entity/passive/frog.rs +++ b/crates/pumpkin/src/entity/passive/frog.rs @@ -10,7 +10,6 @@ use pumpkin_data::sound::Sound; use pumpkin_data::tag::{self, Taggable}; use pumpkin_nbt::compound::NbtCompound; use pumpkin_protocol::codec::var_int::VarInt; -use pumpkin_protocol::java::client::play::Metadata; use crate::entity::{ Entity, EntityBase, @@ -122,12 +121,9 @@ impl FrogEntity { pub fn set_variant(&self, variant: FrogVariant) { self.variant.store(variant.id(), Ordering::Relaxed); let entity = self.get_entity(); - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::frog::VARIANT, - VarInt(variant.id()), - )], - None, + entity.set_synced_data( + pumpkin_data::tracked_data::frog::VARIANT, + VarInt(variant.id()), ); } } @@ -180,20 +176,11 @@ impl Mob for FrogEntity { let entity = self.get_entity(); let is_baby = entity.age.load(Ordering::Relaxed) < 0; if is_baby { - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::frog::BABY_ID, - true, - )], - None, - ); + entity.set_synced_data(pumpkin_data::tracked_data::frog::BABY_ID, true); } - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::frog::VARIANT, - VarInt(self.get_variant().id()), - )], - None, + entity.set_synced_data( + pumpkin_data::tracked_data::frog::VARIANT, + VarInt(self.get_variant().id()), ); } diff --git a/crates/pumpkin/src/entity/passive/goat.rs b/crates/pumpkin/src/entity/passive/goat.rs index c2e947dc1..944d1ce92 100644 --- a/crates/pumpkin/src/entity/passive/goat.rs +++ b/crates/pumpkin/src/entity/passive/goat.rs @@ -9,7 +9,6 @@ use pumpkin_data::item_stack::ItemStack; use pumpkin_data::sound::{Sound, SoundCategory}; use pumpkin_data::tag::{self, Taggable}; use pumpkin_nbt::compound::NbtCompound; -use pumpkin_protocol::java::client::play::Metadata; use crate::entity::{ Entity, EntityBase, @@ -81,12 +80,9 @@ impl GoatEntity { pub fn set_screaming(&self, screaming: bool) { self.is_screaming.store(screaming, Ordering::Relaxed); let entity = self.get_entity(); - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::goat::DATA_IS_SCREAMING_GOAT, - screaming, - )], - None, + entity.set_synced_data( + pumpkin_data::tracked_data::goat::DATA_IS_SCREAMING_GOAT, + screaming, ); } @@ -98,12 +94,9 @@ impl GoatEntity { pub fn set_has_left_horn(&self, has_horn: bool) { self.has_left_horn.store(has_horn, Ordering::Relaxed); let entity = self.get_entity(); - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::goat::DATA_HAS_LEFT_HORN, - has_horn, - )], - None, + entity.set_synced_data( + pumpkin_data::tracked_data::goat::DATA_HAS_LEFT_HORN, + has_horn, ); } @@ -115,12 +108,9 @@ impl GoatEntity { pub fn set_has_right_horn(&self, has_horn: bool) { self.has_right_horn.store(has_horn, Ordering::Relaxed); let entity = self.get_entity(); - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::goat::DATA_HAS_RIGHT_HORN, - has_horn, - )], - None, + entity.set_synced_data( + pumpkin_data::tracked_data::goat::DATA_HAS_RIGHT_HORN, + has_horn, ); } } @@ -177,34 +167,19 @@ impl Mob for GoatEntity { let entity = self.get_entity(); let is_baby = entity.age.load(Ordering::Relaxed) < 0; if is_baby { - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::goat::DATA_BABY_ID, - true, - )], - None, - ); + entity.set_synced_data(pumpkin_data::tracked_data::goat::DATA_BABY_ID, true); } - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::goat::DATA_IS_SCREAMING_GOAT, - self.is_screaming(), - )], - None, + entity.set_synced_data( + pumpkin_data::tracked_data::goat::DATA_IS_SCREAMING_GOAT, + self.is_screaming(), ); - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::goat::DATA_HAS_LEFT_HORN, - self.has_left_horn(), - )], - None, + entity.set_synced_data( + pumpkin_data::tracked_data::goat::DATA_HAS_LEFT_HORN, + self.has_left_horn(), ); - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::goat::DATA_HAS_RIGHT_HORN, - self.has_right_horn(), - )], - None, + entity.set_synced_data( + pumpkin_data::tracked_data::goat::DATA_HAS_RIGHT_HORN, + self.has_right_horn(), ); } diff --git a/crates/pumpkin/src/entity/passive/happy_ghast.rs b/crates/pumpkin/src/entity/passive/happy_ghast.rs index d06480960..4eedf5c28 100644 --- a/crates/pumpkin/src/entity/passive/happy_ghast.rs +++ b/crates/pumpkin/src/entity/passive/happy_ghast.rs @@ -9,7 +9,6 @@ use pumpkin_data::item_stack::ItemStack; use pumpkin_data::sound::Sound; use pumpkin_data::tag::{self, Taggable}; use pumpkin_nbt::compound::NbtCompound; -use pumpkin_protocol::java::client::play::Metadata; use crate::entity::{ Entity, EntityBase, @@ -89,12 +88,9 @@ impl HappyGhastEntity { self.is_leash_holder .store(is_leash_holder, Ordering::Relaxed); let entity = self.get_entity(); - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::happy_ghast::IS_LEASH_HOLDER, - is_leash_holder, - )], - None, + entity.set_synced_data( + pumpkin_data::tracked_data::happy_ghast::IS_LEASH_HOLDER, + is_leash_holder, ); } @@ -102,12 +98,9 @@ impl HappyGhastEntity { let stays_still = self.server_still_timeout.load(Ordering::Relaxed) > 0; self.stays_still.store(stays_still, Ordering::Relaxed); let entity = self.get_entity(); - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::happy_ghast::STAYS_STILL, - stays_still, - )], - None, + entity.set_synced_data( + pumpkin_data::tracked_data::happy_ghast::STAYS_STILL, + stays_still, ); } } @@ -192,26 +185,15 @@ impl Mob for HappyGhastEntity { let entity = self.get_entity(); let is_baby = entity.age.load(Ordering::Relaxed) < 0; if is_baby { - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::happy_ghast::BABY_ID, - true, - )], - None, - ); + entity.set_synced_data(pumpkin_data::tracked_data::happy_ghast::BABY_ID, true); } - entity.send_meta_data( - &[ - Metadata::new( - pumpkin_data::tracked_data::happy_ghast::IS_LEASH_HOLDER, - self.is_leash_holder.load(Ordering::Relaxed), - ), - Metadata::new( - pumpkin_data::tracked_data::happy_ghast::STAYS_STILL, - self.stays_still.load(Ordering::Relaxed), - ), - ], - None, + entity.set_synced_data( + pumpkin_data::tracked_data::happy_ghast::IS_LEASH_HOLDER, + self.is_leash_holder.load(Ordering::Relaxed), + ); + entity.set_synced_data( + pumpkin_data::tracked_data::happy_ghast::STAYS_STILL, + self.stays_still.load(Ordering::Relaxed), ); } diff --git a/crates/pumpkin/src/entity/passive/horse.rs b/crates/pumpkin/src/entity/passive/horse.rs index c7ac024a9..4bab70b39 100644 --- a/crates/pumpkin/src/entity/passive/horse.rs +++ b/crates/pumpkin/src/entity/passive/horse.rs @@ -11,7 +11,6 @@ use pumpkin_data::sound::{Sound, SoundCategory}; use pumpkin_data::tag::{self, Taggable}; use pumpkin_nbt::compound::NbtCompound; use pumpkin_protocol::codec::var_int::VarInt; -use pumpkin_protocol::java::client::play::Metadata; use rand::RngExt; use uuid::Uuid; @@ -103,12 +102,9 @@ impl HorseEntity { pub fn set_variant(&self, variant: i32) { self.variant.store(variant, Ordering::Relaxed); let entity = self.get_entity(); - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::horse::DATA_ID_TYPE_VARIANT, - VarInt(variant), - )], - None, + entity.set_synced_data( + pumpkin_data::tracked_data::horse::DATA_ID_TYPE_VARIANT, + VarInt(variant), ); } @@ -122,12 +118,9 @@ impl HorseEntity { let new_flags = if val { current | flag } else { current & !flag }; self.flags.store(new_flags, Ordering::Relaxed); let entity = self.get_entity(); - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::horse::DATA_ID_FLAGS, - new_flags as i8, - )], - None, + entity.set_synced_data( + pumpkin_data::tracked_data::horse::DATA_ID_FLAGS, + new_flags as i8, ); } @@ -216,27 +209,15 @@ impl Mob for HorseEntity { let entity = self.get_entity(); let is_baby = entity.age.load(Ordering::Relaxed) < 0; if is_baby { - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::horse::DATA_BABY_ID, - true, - )], - None, - ); + entity.set_synced_data(pumpkin_data::tracked_data::horse::DATA_BABY_ID, true); } - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::horse::DATA_ID_TYPE_VARIANT, - VarInt(self.get_variant()), - )], - None, + entity.set_synced_data( + pumpkin_data::tracked_data::horse::DATA_ID_TYPE_VARIANT, + VarInt(self.get_variant()), ); - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::horse::DATA_ID_FLAGS, - self.flags.load(Ordering::Relaxed) as i8, - )], - None, + entity.set_synced_data( + pumpkin_data::tracked_data::horse::DATA_ID_FLAGS, + self.flags.load(Ordering::Relaxed) as i8, ); } diff --git a/crates/pumpkin/src/entity/passive/iron_golem.rs b/crates/pumpkin/src/entity/passive/iron_golem.rs index f3307197c..8610d7394 100644 --- a/crates/pumpkin/src/entity/passive/iron_golem.rs +++ b/crates/pumpkin/src/entity/passive/iron_golem.rs @@ -8,7 +8,6 @@ use pumpkin_data::item::Item; use pumpkin_data::item_stack::ItemStack; use pumpkin_data::sound::{Sound, SoundCategory}; use pumpkin_nbt::compound::NbtCompound; -use pumpkin_protocol::java::client::play::Metadata; use pumpkin_util::GameMode; use crate::entity::{ @@ -93,13 +92,7 @@ impl IronGolemEntity { self.player_created.store(value, Ordering::Relaxed); let entity = self.get_entity(); let flag: u8 = u8::from(value); - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::iron_golem::FLAGS_ID, - flag, - )], - None, - ); + entity.set_synced_data(pumpkin_data::tracked_data::iron_golem::FLAGS_ID, flag); } pub fn offer_flower(&self, offer: bool) { @@ -150,13 +143,7 @@ impl Mob for IronGolemEntity { fn mob_init_data_tracker(&self) { let entity = self.get_entity(); let flag: u8 = u8::from(self.is_player_created()); - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::iron_golem::FLAGS_ID, - flag, - )], - None, - ); + entity.set_synced_data(pumpkin_data::tracked_data::iron_golem::FLAGS_ID, flag); } fn mob_interact(&self, player: &Arc, item_stack: &mut ItemStack) -> bool { diff --git a/crates/pumpkin/src/entity/passive/llama.rs b/crates/pumpkin/src/entity/passive/llama.rs index e5b5ef6ae..577426752 100644 --- a/crates/pumpkin/src/entity/passive/llama.rs +++ b/crates/pumpkin/src/entity/passive/llama.rs @@ -11,7 +11,6 @@ use pumpkin_data::sound::{Sound, SoundCategory}; use pumpkin_data::tag::{self, Taggable}; use pumpkin_nbt::compound::NbtCompound; use pumpkin_protocol::codec::var_int::VarInt; -use pumpkin_protocol::java::client::play::Metadata; use rand::RngExt; use uuid::Uuid; @@ -162,12 +161,9 @@ impl LlamaEntity { pub fn set_variant(&self, variant: LlamaVariant) { self.variant.store(variant.id(), Ordering::Relaxed); let entity = self.get_entity(); - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::llama::DATA_VARIANT_ID, - VarInt(variant.id()), - )], - None, + entity.set_synced_data( + pumpkin_data::tracked_data::llama::DATA_VARIANT_ID, + VarInt(variant.id()), ); } @@ -179,12 +175,9 @@ impl LlamaEntity { pub fn set_strength(&self, strength: i32) { self.strength.store(strength, Ordering::Relaxed); let entity = self.get_entity(); - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::llama::DATA_STRENGTH_ID, - VarInt(strength), - )], - None, + entity.set_synced_data( + pumpkin_data::tracked_data::llama::DATA_STRENGTH_ID, + VarInt(strength), ); } @@ -198,12 +191,9 @@ impl LlamaEntity { let new_flags = if val { current | flag } else { current & !flag }; self.flags.store(new_flags, Ordering::Relaxed); let entity = self.get_entity(); - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::llama::DATA_ID_FLAGS, - new_flags as i8, - )], - None, + entity.set_synced_data( + pumpkin_data::tracked_data::llama::DATA_ID_FLAGS, + new_flags as i8, ); } @@ -224,13 +214,7 @@ impl LlamaEntity { pub fn set_has_chest(&self, val: bool) { self.has_chest.store(val, Ordering::Relaxed); let entity = self.get_entity(); - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::llama::DATA_ID_CHEST, - val, - )], - None, - ); + entity.set_synced_data(pumpkin_data::tracked_data::llama::DATA_ID_CHEST, val); } pub fn spit(&self, target: &Arc) { @@ -334,41 +318,23 @@ impl Mob for LlamaEntity { let entity = self.get_entity(); let is_baby = entity.age.load(Ordering::Relaxed) < 0; if is_baby { - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::llama::DATA_BABY_ID, - true, - )], - None, - ); + entity.set_synced_data(pumpkin_data::tracked_data::llama::DATA_BABY_ID, true); } - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::llama::DATA_VARIANT_ID, - VarInt(self.get_variant().id()), - )], - None, + entity.set_synced_data( + pumpkin_data::tracked_data::llama::DATA_VARIANT_ID, + VarInt(self.get_variant().id()), ); - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::llama::DATA_STRENGTH_ID, - VarInt(self.get_strength()), - )], - None, + entity.set_synced_data( + pumpkin_data::tracked_data::llama::DATA_STRENGTH_ID, + VarInt(self.get_strength()), ); - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::llama::DATA_ID_CHEST, - self.has_chest(), - )], - None, + entity.set_synced_data( + pumpkin_data::tracked_data::llama::DATA_ID_CHEST, + self.has_chest(), ); - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::llama::DATA_ID_FLAGS, - self.flags.load(Ordering::Relaxed) as i8, - )], - None, + entity.set_synced_data( + pumpkin_data::tracked_data::llama::DATA_ID_FLAGS, + self.flags.load(Ordering::Relaxed) as i8, ); } diff --git a/crates/pumpkin/src/entity/passive/mooshroom.rs b/crates/pumpkin/src/entity/passive/mooshroom.rs index 834dc6984..46c1902df 100644 --- a/crates/pumpkin/src/entity/passive/mooshroom.rs +++ b/crates/pumpkin/src/entity/passive/mooshroom.rs @@ -12,7 +12,6 @@ use pumpkin_data::sound::{Sound, SoundCategory}; use pumpkin_data::tag::{self, Taggable}; use pumpkin_nbt::compound::NbtCompound; use pumpkin_protocol::codec::var_int::VarInt; -use pumpkin_protocol::java::client::play::Metadata; use pumpkin_util::math::vector3::Vector3; use uuid::Uuid; @@ -126,12 +125,9 @@ impl MooshroomEntity { pub fn set_variant(&self, variant: MooshroomVariant) { self.variant.store(variant.id(), Ordering::Relaxed); let entity = self.get_entity(); - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::mooshroom::DATA_TYPE, - VarInt(variant.id()), - )], - None, + entity.set_synced_data( + pumpkin_data::tracked_data::mooshroom::DATA_TYPE, + VarInt(variant.id()), ); } } @@ -180,20 +176,11 @@ impl Mob for MooshroomEntity { let entity = self.get_entity(); let is_baby = entity.age.load(Ordering::Relaxed) < 0; if is_baby { - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::mooshroom::DATA_BABY_ID, - true, - )], - None, - ); + entity.set_synced_data(pumpkin_data::tracked_data::mooshroom::DATA_BABY_ID, true); } - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::mooshroom::DATA_TYPE, - VarInt(self.get_variant().id()), - )], - None, + entity.set_synced_data( + pumpkin_data::tracked_data::mooshroom::DATA_TYPE, + VarInt(self.get_variant().id()), ); } diff --git a/crates/pumpkin/src/entity/passive/mule.rs b/crates/pumpkin/src/entity/passive/mule.rs index 2c4f73056..cf16d912d 100644 --- a/crates/pumpkin/src/entity/passive/mule.rs +++ b/crates/pumpkin/src/entity/passive/mule.rs @@ -10,7 +10,6 @@ use pumpkin_data::item_stack::ItemStack; use pumpkin_data::sound::{Sound, SoundCategory}; use pumpkin_data::tag::{self, Taggable}; use pumpkin_nbt::compound::NbtCompound; -use pumpkin_protocol::java::client::play::Metadata; use uuid::Uuid; use crate::entity::{ @@ -97,12 +96,9 @@ impl MuleEntity { let new_flags = if val { current | flag } else { current & !flag }; self.flags.store(new_flags, Ordering::Relaxed); let entity = self.get_entity(); - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::mule::DATA_ID_FLAGS, - new_flags as i8, - )], - None, + entity.set_synced_data( + pumpkin_data::tracked_data::mule::DATA_ID_FLAGS, + new_flags as i8, ); } @@ -132,13 +128,7 @@ impl MuleEntity { pub fn set_has_chest(&self, val: bool) { self.has_chest.store(val, Ordering::Relaxed); let entity = self.get_entity(); - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::mule::DATA_ID_CHEST, - val, - )], - None, - ); + entity.set_synced_data(pumpkin_data::tracked_data::mule::DATA_ID_CHEST, val); } } @@ -208,27 +198,15 @@ impl Mob for MuleEntity { let entity = self.get_entity(); let is_baby = entity.age.load(Ordering::Relaxed) < 0; if is_baby { - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::mule::DATA_BABY_ID, - true, - )], - None, - ); + entity.set_synced_data(pumpkin_data::tracked_data::mule::DATA_BABY_ID, true); } - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::mule::DATA_ID_CHEST, - self.has_chest(), - )], - None, + entity.set_synced_data( + pumpkin_data::tracked_data::mule::DATA_ID_CHEST, + self.has_chest(), ); - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::mule::DATA_ID_FLAGS, - self.flags.load(Ordering::Relaxed) as i8, - )], - None, + entity.set_synced_data( + pumpkin_data::tracked_data::mule::DATA_ID_FLAGS, + self.flags.load(Ordering::Relaxed) as i8, ); } diff --git a/crates/pumpkin/src/entity/passive/nautilus.rs b/crates/pumpkin/src/entity/passive/nautilus.rs index a6cb27305..cff939d44 100644 --- a/crates/pumpkin/src/entity/passive/nautilus.rs +++ b/crates/pumpkin/src/entity/passive/nautilus.rs @@ -14,7 +14,6 @@ use pumpkin_data::{ sound::{Sound, SoundCategory}, }; use pumpkin_nbt::compound::NbtCompound; -use pumpkin_protocol::java::client::play::Metadata; use pumpkin_util::math::vector3::Vector3; use crate::entity::{ @@ -56,13 +55,10 @@ impl NautilusEntity { pub fn set_dashing(&self, dashing: bool) { self.is_dashing.store(dashing, Ordering::Relaxed); - self.mob_entity.living_entity.entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::nautilus::DASH, - dashing, - )], - None, - ); + self.mob_entity + .living_entity + .entity + .set_synced_data(pumpkin_data::tracked_data::nautilus::DASH, dashing); } pub fn is_tame(&self) -> bool { @@ -258,12 +254,9 @@ impl Mob for NautilusEntity { } fn mob_init_data_tracker(&self) { - self.mob_entity.living_entity.entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::nautilus::DASH, - self.is_dashing(), - )], - None, + self.mob_entity.living_entity.entity.set_synced_data( + pumpkin_data::tracked_data::nautilus::DASH, + self.is_dashing(), ); } diff --git a/crates/pumpkin/src/entity/passive/ocelot.rs b/crates/pumpkin/src/entity/passive/ocelot.rs index 95e1026c1..6f7a5f18b 100644 --- a/crates/pumpkin/src/entity/passive/ocelot.rs +++ b/crates/pumpkin/src/entity/passive/ocelot.rs @@ -9,7 +9,6 @@ use pumpkin_data::item_stack::ItemStack; use pumpkin_data::tag::{self, Taggable}; use pumpkin_nbt::compound::NbtCompound; use pumpkin_protocol::bedrock::server::actor_event::ActorEventID; -use pumpkin_protocol::java::client::play::Metadata; use rand::RngExt; use crate::entity::{ @@ -109,13 +108,7 @@ impl OcelotEntity { pub fn set_trusting(&self, trusting: bool) { self.is_trusting.store(trusting, Ordering::Relaxed); let entity = self.get_entity(); - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::ocelot::TRUSTING, - trusting, - )], - None, - ); + entity.set_synced_data(pumpkin_data::tracked_data::ocelot::TRUSTING, trusting); } } @@ -151,20 +144,11 @@ impl Mob for OcelotEntity { let entity = self.get_entity(); let is_baby = entity.age.load(Ordering::Relaxed) < 0; if is_baby { - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::ocelot::BABY_ID, - true, - )], - None, - ); + entity.set_synced_data(pumpkin_data::tracked_data::ocelot::BABY_ID, true); } - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::ocelot::TRUSTING, - self.is_trusting.load(Ordering::Relaxed), - )], - None, + entity.set_synced_data( + pumpkin_data::tracked_data::ocelot::TRUSTING, + self.is_trusting.load(Ordering::Relaxed), ); } diff --git a/crates/pumpkin/src/entity/passive/panda.rs b/crates/pumpkin/src/entity/passive/panda.rs index 6c0b4369d..43ddf4cba 100644 --- a/crates/pumpkin/src/entity/passive/panda.rs +++ b/crates/pumpkin/src/entity/passive/panda.rs @@ -10,7 +10,6 @@ use pumpkin_data::sound::Sound; use pumpkin_data::tag::{self, Taggable}; use pumpkin_nbt::compound::NbtCompound; use pumpkin_protocol::codec::var_int::VarInt; -use pumpkin_protocol::java::client::play::Metadata; use rand::RngExt; use crate::entity::{ @@ -167,12 +166,9 @@ impl PandaEntity { pub fn set_main_gene(&self, gene: PandaGene) { self.main_gene.store(gene.id(), Ordering::Relaxed); let entity = self.get_entity(); - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::panda::MAIN_GENE_ID, - gene.id() as i8, - )], - None, + entity.set_synced_data( + pumpkin_data::tracked_data::panda::MAIN_GENE_ID, + gene.id() as i8, ); } @@ -184,12 +180,9 @@ impl PandaEntity { pub fn set_hidden_gene(&self, gene: PandaGene) { self.hidden_gene.store(gene.id(), Ordering::Relaxed); let entity = self.get_entity(); - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::panda::HIDDEN_GENE_ID, - gene.id() as i8, - )], - None, + entity.set_synced_data( + pumpkin_data::tracked_data::panda::HIDDEN_GENE_ID, + gene.id() as i8, ); } @@ -203,12 +196,9 @@ impl PandaEntity { let new_flags = if val { current | flag } else { current & !flag }; self.flags.store(new_flags, Ordering::Relaxed); let entity = self.get_entity(); - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::panda::DATA_ID_FLAGS, - new_flags as i8, - )], - None, + entity.set_synced_data( + pumpkin_data::tracked_data::panda::DATA_ID_FLAGS, + new_flags as i8, ); } @@ -300,55 +290,31 @@ impl Mob for PandaEntity { let entity = self.get_entity(); let is_baby = entity.age.load(Ordering::Relaxed) < 0; if is_baby { - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::panda::DATA_BABY_ID, - true, - )], - None, - ); + entity.set_synced_data(pumpkin_data::tracked_data::panda::DATA_BABY_ID, true); } - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::panda::MAIN_GENE_ID, - self.main_gene.load(Ordering::Relaxed) as i8, - )], - None, + entity.set_synced_data( + pumpkin_data::tracked_data::panda::MAIN_GENE_ID, + self.main_gene.load(Ordering::Relaxed) as i8, ); - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::panda::HIDDEN_GENE_ID, - self.hidden_gene.load(Ordering::Relaxed) as i8, - )], - None, + entity.set_synced_data( + pumpkin_data::tracked_data::panda::HIDDEN_GENE_ID, + self.hidden_gene.load(Ordering::Relaxed) as i8, ); - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::panda::DATA_ID_FLAGS, - self.flags.load(Ordering::Relaxed) as i8, - )], - None, + entity.set_synced_data( + pumpkin_data::tracked_data::panda::DATA_ID_FLAGS, + self.flags.load(Ordering::Relaxed) as i8, ); - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::panda::UNHAPPY_COUNTER, - VarInt(self.unhappy_counter.load(Ordering::Relaxed)), - )], - None, + entity.set_synced_data( + pumpkin_data::tracked_data::panda::UNHAPPY_COUNTER, + VarInt(self.unhappy_counter.load(Ordering::Relaxed)), ); - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::panda::SNEEZE_COUNTER, - VarInt(self.sneeze_counter.load(Ordering::Relaxed)), - )], - None, + entity.set_synced_data( + pumpkin_data::tracked_data::panda::SNEEZE_COUNTER, + VarInt(self.sneeze_counter.load(Ordering::Relaxed)), ); - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::panda::EAT_COUNTER, - VarInt(self.eat_counter.load(Ordering::Relaxed)), - )], - None, + entity.set_synced_data( + pumpkin_data::tracked_data::panda::EAT_COUNTER, + VarInt(self.eat_counter.load(Ordering::Relaxed)), ); } diff --git a/crates/pumpkin/src/entity/passive/polar_bear.rs b/crates/pumpkin/src/entity/passive/polar_bear.rs index c87641c70..01f8c7322 100644 --- a/crates/pumpkin/src/entity/passive/polar_bear.rs +++ b/crates/pumpkin/src/entity/passive/polar_bear.rs @@ -7,7 +7,6 @@ use pumpkin_data::entity::EntityType; use pumpkin_data::item_stack::ItemStack; use pumpkin_data::sound::Sound; use pumpkin_nbt::compound::NbtCompound; -use pumpkin_protocol::java::client::play::Metadata; use crate::entity::{ Entity, EntityBase, @@ -91,12 +90,9 @@ impl PolarBearEntity { pub fn set_standing(&self, standing: bool) { self.standing.store(standing, Ordering::Relaxed); let entity = self.get_entity(); - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::polar_bear::DATA_STANDING_ID, - standing, - )], - None, + entity.set_synced_data( + pumpkin_data::tracked_data::polar_bear::DATA_STANDING_ID, + standing, ); } } @@ -142,20 +138,11 @@ impl Mob for PolarBearEntity { let entity = self.get_entity(); let is_baby = entity.age.load(Ordering::Relaxed) < 0; if is_baby { - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::polar_bear::DATA_BABY_ID, - true, - )], - None, - ); + entity.set_synced_data(pumpkin_data::tracked_data::polar_bear::DATA_BABY_ID, true); } - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::polar_bear::DATA_STANDING_ID, - self.is_standing(), - )], - None, + entity.set_synced_data( + pumpkin_data::tracked_data::polar_bear::DATA_STANDING_ID, + self.is_standing(), ); } diff --git a/crates/pumpkin/src/entity/passive/rabbit.rs b/crates/pumpkin/src/entity/passive/rabbit.rs index 5a3256711..14508f687 100644 --- a/crates/pumpkin/src/entity/passive/rabbit.rs +++ b/crates/pumpkin/src/entity/passive/rabbit.rs @@ -10,7 +10,6 @@ use pumpkin_data::sound::Sound; use pumpkin_data::tag::{self, Taggable}; use pumpkin_nbt::compound::NbtCompound; use pumpkin_protocol::codec::var_int::VarInt; -use pumpkin_protocol::java::client::play::Metadata; use rand::RngExt; use crate::entity::{ @@ -142,12 +141,9 @@ impl RabbitEntity { pub fn set_variant(&self, variant: RabbitVariant) { self.variant.store(variant.id(), Ordering::Relaxed); let entity = self.get_entity(); - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::rabbit::DATA_TYPE_ID, - VarInt(variant.id()), - )], - None, + entity.set_synced_data( + pumpkin_data::tracked_data::rabbit::DATA_TYPE_ID, + VarInt(variant.id()), ); if variant == RabbitVariant::Evil { @@ -232,20 +228,11 @@ impl Mob for RabbitEntity { let entity = self.get_entity(); let is_baby = entity.age.load(Ordering::Relaxed) < 0; if is_baby { - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::rabbit::DATA_BABY_ID, - true, - )], - None, - ); + entity.set_synced_data(pumpkin_data::tracked_data::rabbit::DATA_BABY_ID, true); } - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::rabbit::DATA_TYPE_ID, - VarInt(self.get_variant().id()), - )], - None, + entity.set_synced_data( + pumpkin_data::tracked_data::rabbit::DATA_TYPE_ID, + VarInt(self.get_variant().id()), ); } diff --git a/crates/pumpkin/src/entity/passive/sheep.rs b/crates/pumpkin/src/entity/passive/sheep.rs index 8fead6178..b7d342daf 100644 --- a/crates/pumpkin/src/entity/passive/sheep.rs +++ b/crates/pumpkin/src/entity/passive/sheep.rs @@ -5,7 +5,6 @@ use std::sync::{ use pumpkin_data::{entity::EntityType, item::Item}; use pumpkin_nbt::compound::NbtCompound; -use pumpkin_protocol::java::client::play::Metadata; use rand::RngExt; use crate::entity::{ @@ -85,13 +84,10 @@ impl SheepEntity { fn set_packed_and_sync(&self, byte: u8) { self.color_and_sheared.store(byte, Ordering::Relaxed); - self.mob_entity.living_entity.entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::sheep::WOOL_ID, - byte as i8, - )], - None, - ); + self.mob_entity + .living_entity + .entity + .set_synced_data(pumpkin_data::tracked_data::sheep::WOOL_ID, byte as i8); } pub fn set_color(&self, color: u8) { diff --git a/crates/pumpkin/src/entity/passive/skeleton_horse.rs b/crates/pumpkin/src/entity/passive/skeleton_horse.rs index a74833c04..e9d430dc1 100644 --- a/crates/pumpkin/src/entity/passive/skeleton_horse.rs +++ b/crates/pumpkin/src/entity/passive/skeleton_horse.rs @@ -9,7 +9,6 @@ use pumpkin_data::item::Item; use pumpkin_data::item_stack::ItemStack; use pumpkin_data::sound::{Sound, SoundCategory}; use pumpkin_nbt::compound::NbtCompound; -use pumpkin_protocol::java::client::play::Metadata; use uuid::Uuid; use crate::entity::{ @@ -84,12 +83,9 @@ impl SkeletonHorseEntity { let new_flags = if val { current | flag } else { current & !flag }; self.flags.store(new_flags, Ordering::Relaxed); let entity = self.get_entity(); - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::skeleton_horse::DATA_ID_FLAGS, - new_flags as i8, - )], - None, + entity.set_synced_data( + pumpkin_data::tracked_data::skeleton_horse::DATA_ID_FLAGS, + new_flags as i8, ); } @@ -167,20 +163,14 @@ impl Mob for SkeletonHorseEntity { let entity = self.get_entity(); let is_baby = entity.age.load(Ordering::Relaxed) < 0; if is_baby { - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::skeleton_horse::DATA_BABY_ID, - true, - )], - None, + entity.set_synced_data( + pumpkin_data::tracked_data::skeleton_horse::DATA_BABY_ID, + true, ); } - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::skeleton_horse::DATA_ID_FLAGS, - self.flags.load(Ordering::Relaxed) as i8, - )], - None, + entity.set_synced_data( + pumpkin_data::tracked_data::skeleton_horse::DATA_ID_FLAGS, + self.flags.load(Ordering::Relaxed) as i8, ); } diff --git a/crates/pumpkin/src/entity/passive/sniffer.rs b/crates/pumpkin/src/entity/passive/sniffer.rs index 65e7401c7..daf233791 100644 --- a/crates/pumpkin/src/entity/passive/sniffer.rs +++ b/crates/pumpkin/src/entity/passive/sniffer.rs @@ -10,7 +10,6 @@ use pumpkin_data::sound::{Sound, SoundCategory}; use pumpkin_data::tag::{self, Taggable}; use pumpkin_nbt::compound::NbtCompound; use pumpkin_protocol::codec::var_int::VarInt; -use pumpkin_protocol::java::client::play::Metadata; use pumpkin_util::math::position::BlockPos; use pumpkin_util::math::vector3::Vector3; @@ -122,12 +121,9 @@ impl SnifferEntity { pub fn set_state(&self, state: SnifferState) { self.state.store(state.id(), Ordering::Relaxed); let entity = self.get_entity(); - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::sniffer::STATE, - VarInt(state.id()), - )], - None, + entity.set_synced_data( + pumpkin_data::tracked_data::sniffer::STATE, + VarInt(state.id()), ); } @@ -190,12 +186,9 @@ impl SnifferEntity { let current_ticks = world.level_time.try_lock().map_or(0, |t| t.world_age); let drop_tick = current_ticks as i32 + DIGGING_DROP_SEED_OFFSET_TICKS; self.drop_seed_at_tick.store(drop_tick, Ordering::Relaxed); - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::sniffer::DROP_SEED_AT_TICK, - VarInt(drop_tick), - )], - None, + entity.set_synced_data( + pumpkin_data::tracked_data::sniffer::DROP_SEED_AT_TICK, + VarInt(drop_tick), ); world.send_entity_status(entity, EntityStatus::SnifferDiggingSound, None); } @@ -384,26 +377,15 @@ impl Mob for SnifferEntity { let entity = self.get_entity(); let is_baby = entity.age.load(Ordering::Relaxed) < 0; if is_baby { - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::sniffer::BABY_ID, - true, - )], - None, - ); + entity.set_synced_data(pumpkin_data::tracked_data::sniffer::BABY_ID, true); } - entity.send_meta_data( - &[ - Metadata::new( - pumpkin_data::tracked_data::sniffer::STATE, - VarInt(self.get_state().id()), - ), - Metadata::new( - pumpkin_data::tracked_data::sniffer::DROP_SEED_AT_TICK, - VarInt(self.drop_seed_at_tick.load(Ordering::Relaxed)), - ), - ], - None, + entity.set_synced_data( + pumpkin_data::tracked_data::sniffer::STATE, + VarInt(self.get_state().id()), + ); + entity.set_synced_data( + pumpkin_data::tracked_data::sniffer::DROP_SEED_AT_TICK, + VarInt(self.drop_seed_at_tick.load(Ordering::Relaxed)), ); } diff --git a/crates/pumpkin/src/entity/passive/snow_golem.rs b/crates/pumpkin/src/entity/passive/snow_golem.rs index 904e17077..1937b5ffd 100644 --- a/crates/pumpkin/src/entity/passive/snow_golem.rs +++ b/crates/pumpkin/src/entity/passive/snow_golem.rs @@ -6,7 +6,6 @@ use pumpkin_data::item::Item; use pumpkin_data::item_stack::ItemStack; use pumpkin_data::sound::{Sound, SoundCategory}; use pumpkin_nbt::compound::NbtCompound; -use pumpkin_protocol::java::client::play::Metadata; use crate::entity::{ Entity, EntityBase, @@ -84,12 +83,9 @@ impl SnowGolemEntity { self.has_pumpkin.store(has_pumpkin, Ordering::Relaxed); let entity = self.get_entity(); let flags = if has_pumpkin { 16u8 } else { 0u8 }; - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::snow_golem::DATA_PUMPKIN_ID, - flags as i8, - )], - None, + entity.set_synced_data( + pumpkin_data::tracked_data::snow_golem::DATA_PUMPKIN_ID, + flags as i8, ); } @@ -140,12 +136,9 @@ impl Mob for SnowGolemEntity { fn mob_init_data_tracker(&self) { let entity = self.get_entity(); let flags = if self.has_pumpkin() { 16u8 } else { 0u8 }; - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::snow_golem::DATA_PUMPKIN_ID, - flags as i8, - )], - None, + entity.set_synced_data( + pumpkin_data::tracked_data::snow_golem::DATA_PUMPKIN_ID, + flags as i8, ); } diff --git a/crates/pumpkin/src/entity/passive/strider.rs b/crates/pumpkin/src/entity/passive/strider.rs index 947109700..d22224713 100644 --- a/crates/pumpkin/src/entity/passive/strider.rs +++ b/crates/pumpkin/src/entity/passive/strider.rs @@ -7,7 +7,6 @@ use pumpkin_data::item_stack::ItemStack; use pumpkin_data::sound::{Sound, SoundCategory}; use pumpkin_data::tag::{self, Taggable}; use pumpkin_nbt::compound::NbtCompound; -use pumpkin_protocol::java::client::play::Metadata; use crate::entity::{ Entity, EntityBase, @@ -80,12 +79,9 @@ impl StriderEntity { pub fn set_suffocating(&self, suffocating: bool) { self.suffocating.store(suffocating, Ordering::Relaxed); let entity = self.get_entity(); - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::strider::DATA_SUFFOCATING, - suffocating, - )], - None, + entity.set_synced_data( + pumpkin_data::tracked_data::strider::DATA_SUFFOCATING, + suffocating, ); } } @@ -154,20 +150,11 @@ impl Mob for StriderEntity { let entity = self.get_entity(); let is_baby = entity.age.load(Ordering::Relaxed) < 0; if is_baby { - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::strider::DATA_BABY_ID, - true, - )], - None, - ); + entity.set_synced_data(pumpkin_data::tracked_data::strider::DATA_BABY_ID, true); } - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::strider::DATA_SUFFOCATING, - self.is_suffocating(), - )], - None, + entity.set_synced_data( + pumpkin_data::tracked_data::strider::DATA_SUFFOCATING, + self.is_suffocating(), ); } diff --git a/crates/pumpkin/src/entity/passive/tamable.rs b/crates/pumpkin/src/entity/passive/tamable.rs index f19e3f2b5..6b6389d64 100644 --- a/crates/pumpkin/src/entity/passive/tamable.rs +++ b/crates/pumpkin/src/entity/passive/tamable.rs @@ -2,7 +2,6 @@ use std::sync::atomic::{AtomicBool, Ordering::Relaxed}; use crossbeam::atomic::AtomicCell; use pumpkin_nbt::compound::NbtCompound; -use pumpkin_protocol::java::client::play::Metadata; use uuid::Uuid; use crate::entity::passive::animal::Animal; @@ -46,12 +45,9 @@ pub trait TamableAnimal: Animal { if tame { flags |= TAME_FLAG; } - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::tamable_animal::DATA_FLAGS_ID, - flags as i8, - )], - None, + entity.set_synced_data( + pumpkin_data::tracked_data::tamable_animal::DATA_FLAGS_ID, + flags as i8, ); } @@ -69,12 +65,9 @@ pub trait TamableAnimal: Animal { if self.is_tame() { flags |= TAME_FLAG; } - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::tamable_animal::DATA_FLAGS_ID, - flags as i8, - )], - None, + entity.set_synced_data( + pumpkin_data::tracked_data::tamable_animal::DATA_FLAGS_ID, + flags as i8, ); } @@ -94,12 +87,9 @@ pub trait TamableAnimal: Animal { let mob_entity = self.get_mob_entity(); let entity = &mob_entity.living_entity.entity; self.get_tamable_data().owner.store(owner); - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::tamable_animal::DATA_OWNERUUID_ID, - owner, - )], - None, + entity.set_synced_data( + pumpkin_data::tracked_data::tamable_animal::DATA_OWNERUUID_ID, + owner, ); } diff --git a/crates/pumpkin/src/entity/passive/trader_llama.rs b/crates/pumpkin/src/entity/passive/trader_llama.rs index 302066da4..89b8af7a1 100644 --- a/crates/pumpkin/src/entity/passive/trader_llama.rs +++ b/crates/pumpkin/src/entity/passive/trader_llama.rs @@ -11,7 +11,6 @@ use pumpkin_data::sound::{Sound, SoundCategory}; use pumpkin_data::tag::{self, Taggable}; use pumpkin_nbt::compound::NbtCompound; use pumpkin_protocol::codec::var_int::VarInt; -use pumpkin_protocol::java::client::play::Metadata; use rand::RngExt; use uuid::Uuid; @@ -162,12 +161,9 @@ impl TraderLlamaEntity { pub fn set_variant(&self, variant: TraderLlamaVariant) { self.variant.store(variant.id(), Ordering::Relaxed); let entity = self.get_entity(); - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::trader_llama::DATA_VARIANT_ID, - VarInt(variant.id()), - )], - None, + entity.set_synced_data( + pumpkin_data::tracked_data::trader_llama::DATA_VARIANT_ID, + VarInt(variant.id()), ); } @@ -179,12 +175,9 @@ impl TraderLlamaEntity { pub fn set_strength(&self, strength: i32) { self.strength.store(strength, Ordering::Relaxed); let entity = self.get_entity(); - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::trader_llama::DATA_STRENGTH_ID, - VarInt(strength), - )], - None, + entity.set_synced_data( + pumpkin_data::tracked_data::trader_llama::DATA_STRENGTH_ID, + VarInt(strength), ); } @@ -198,12 +191,9 @@ impl TraderLlamaEntity { let new_flags = if val { current | flag } else { current & !flag }; self.flags.store(new_flags, Ordering::Relaxed); let entity = self.get_entity(); - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::trader_llama::DATA_ID_FLAGS, - new_flags as i8, - )], - None, + entity.set_synced_data( + pumpkin_data::tracked_data::trader_llama::DATA_ID_FLAGS, + new_flags as i8, ); } @@ -224,13 +214,7 @@ impl TraderLlamaEntity { pub fn set_has_chest(&self, val: bool) { self.has_chest.store(val, Ordering::Relaxed); let entity = self.get_entity(); - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::trader_llama::DATA_ID_CHEST, - val, - )], - None, - ); + entity.set_synced_data(pumpkin_data::tracked_data::trader_llama::DATA_ID_CHEST, val); } pub fn spit(&self, target: &Arc) { @@ -334,41 +318,23 @@ impl Mob for TraderLlamaEntity { let entity = self.get_entity(); let is_baby = entity.age.load(Ordering::Relaxed) < 0; if is_baby { - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::trader_llama::DATA_BABY_ID, - true, - )], - None, - ); + entity.set_synced_data(pumpkin_data::tracked_data::trader_llama::DATA_BABY_ID, true); } - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::trader_llama::DATA_VARIANT_ID, - VarInt(self.get_variant().id()), - )], - None, + entity.set_synced_data( + pumpkin_data::tracked_data::trader_llama::DATA_VARIANT_ID, + VarInt(self.get_variant().id()), ); - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::trader_llama::DATA_STRENGTH_ID, - VarInt(self.get_strength()), - )], - None, + entity.set_synced_data( + pumpkin_data::tracked_data::trader_llama::DATA_STRENGTH_ID, + VarInt(self.get_strength()), ); - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::trader_llama::DATA_ID_CHEST, - self.has_chest(), - )], - None, + entity.set_synced_data( + pumpkin_data::tracked_data::trader_llama::DATA_ID_CHEST, + self.has_chest(), ); - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::trader_llama::DATA_ID_FLAGS, - self.flags.load(Ordering::Relaxed) as i8, - )], - None, + entity.set_synced_data( + pumpkin_data::tracked_data::trader_llama::DATA_ID_FLAGS, + self.flags.load(Ordering::Relaxed) as i8, ); } diff --git a/crates/pumpkin/src/entity/passive/turtle.rs b/crates/pumpkin/src/entity/passive/turtle.rs index 03e3cc773..443ca9618 100644 --- a/crates/pumpkin/src/entity/passive/turtle.rs +++ b/crates/pumpkin/src/entity/passive/turtle.rs @@ -9,7 +9,6 @@ use pumpkin_data::item_stack::ItemStack; use pumpkin_data::sound::Sound; use pumpkin_data::tag::{self, Taggable}; use pumpkin_nbt::compound::NbtCompound; -use pumpkin_protocol::java::client::play::Metadata; use crate::entity::{ Entity, EntityBase, @@ -79,13 +78,7 @@ impl TurtleEntity { pub fn set_has_egg(&self, has_egg: bool) { self.has_egg.store(has_egg, Ordering::Relaxed); let entity = self.get_entity(); - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::turtle::HAS_EGG, - has_egg, - )], - None, - ); + entity.set_synced_data(pumpkin_data::tracked_data::turtle::HAS_EGG, has_egg); } #[must_use] @@ -96,13 +89,7 @@ impl TurtleEntity { pub fn set_laying_egg(&self, laying_egg: bool) { self.laying_egg.store(laying_egg, Ordering::Relaxed); let entity = self.get_entity(); - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::turtle::LAYING_EGG, - laying_egg, - )], - None, - ); + entity.set_synced_data(pumpkin_data::tracked_data::turtle::LAYING_EGG, laying_egg); } } @@ -152,27 +139,12 @@ impl Mob for TurtleEntity { let entity = self.get_entity(); let is_baby = entity.age.load(Ordering::Relaxed) < 0; if is_baby { - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::turtle::DATA_BABY_ID, - true, - )], - None, - ); + entity.set_synced_data(pumpkin_data::tracked_data::turtle::DATA_BABY_ID, true); } - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::turtle::HAS_EGG, - self.has_egg(), - )], - None, - ); - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::turtle::LAYING_EGG, - self.is_laying_egg(), - )], - None, + entity.set_synced_data(pumpkin_data::tracked_data::turtle::HAS_EGG, self.has_egg()); + entity.set_synced_data( + pumpkin_data::tracked_data::turtle::LAYING_EGG, + self.is_laying_egg(), ); } diff --git a/crates/pumpkin/src/entity/passive/villager/mod.rs b/crates/pumpkin/src/entity/passive/villager/mod.rs index cbcaf5f0d..6fb671d12 100644 --- a/crates/pumpkin/src/entity/passive/villager/mod.rs +++ b/crates/pumpkin/src/entity/passive/villager/mod.rs @@ -431,13 +431,12 @@ impl VillagerEntity { // Send initial metadata let bedrock_metadata = Self::bedrock_metadata(villager_data, 0); - mob_arc.get_entity().send_meta_data( - &[Metadata::new( - tracked_data::villager::VILLAGER_DATA, - villager_data, - )], - Some(&bedrock_metadata), - ); + mob_arc + .get_entity() + .set_synced_data(tracked_data::villager::VILLAGER_DATA, villager_data); + mob_arc + .get_entity() + .send_bedrock_actor_data(&bedrock_metadata); mob_arc } @@ -506,10 +505,9 @@ impl VillagerEntity { old_profession }; let bedrock_metadata = Self::bedrock_metadata(data, self.xp.load(Ordering::Relaxed)); - self.get_entity().send_meta_data( - &[Metadata::new(tracked_data::villager::VILLAGER_DATA, data)], - Some(&bedrock_metadata), - ); + self.get_entity() + .set_synced_data(tracked_data::villager::VILLAGER_DATA, data); + self.get_entity().send_bedrock_actor_data(&bedrock_metadata); if old_profession != data.profession { self.offers @@ -829,13 +827,9 @@ impl VillagerEntity { .lock() .unwrap_or_else(std::sync::PoisonError::into_inner); let bedrock_metadata = Self::bedrock_metadata(villager_data, current_xp); - self.get_entity().send_meta_data( - &[Metadata::new( - tracked_data::villager::VILLAGER_DATA, - villager_data, - )], - Some(&bedrock_metadata), - ); + self.get_entity() + .set_synced_data(tracked_data::villager::VILLAGER_DATA, villager_data); + self.get_entity().send_bedrock_actor_data(&bedrock_metadata); if reward_exp { ExperienceOrbEntity::spawn(world, self.get_entity().pos.load(), xp_gain as u32); @@ -1278,13 +1272,7 @@ impl VillagerEntity { pub fn set_unhappy(&self) { let entity = self.get_entity(); self.unhappy_counter.store(40, Ordering::Relaxed); - entity.send_meta_data( - &[Metadata::new( - tracked_data::villager::UNHAPPY_COUNTER, - VarInt(40), - )], - None, - ); + entity.set_synced_data(tracked_data::villager::UNHAPPY_COUNTER, VarInt(40)); entity.world.load().send_entity_status( entity, pumpkin_data::entity::EntityStatus::VillagerAngry, @@ -1548,12 +1536,9 @@ impl VillagerEntity { let unhappy_counter = unhappy_counter - 1; self.unhappy_counter .store(unhappy_counter, Ordering::Relaxed); - self.get_entity().send_meta_data( - &[Metadata::new( - tracked_data::villager::UNHAPPY_COUNTER, - VarInt(unhappy_counter), - )], - None, + self.get_entity().set_synced_data( + tracked_data::villager::UNHAPPY_COUNTER, + VarInt(unhappy_counter), ); } self.trade_sound_cooldown @@ -1650,12 +1635,9 @@ impl VillagerEntity { if is_sleeping { // Wake up if bed was broken self.get_entity().set_pose(EntityPose::Standing); - self.get_entity().send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::villager::SLEEPING_POS_ID, - None::, - )], - None, + self.get_entity().set_synced_data( + pumpkin_data::tracked_data::villager::SLEEPING_POS_ID, + None::, ); } } @@ -1749,12 +1731,9 @@ impl VillagerEntity { BedBlock::set_occupied(true, &world, block, &home_pos, state.id); self.get_entity().set_pose(EntityPose::Sleeping); - self.get_entity().send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::villager::SLEEPING_POS_ID, - Some(home_pos), - )], - None, + self.get_entity().set_synced_data( + pumpkin_data::tracked_data::villager::SLEEPING_POS_ID, + Some(home_pos), ); } } @@ -1771,23 +1750,17 @@ impl VillagerEntity { } self.get_entity().set_pose(EntityPose::Standing); - self.get_entity().send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::villager::SLEEPING_POS_ID, - None::, - )], - None, + self.get_entity().set_synced_data( + pumpkin_data::tracked_data::villager::SLEEPING_POS_ID, + None::, ); } } else if is_sleeping { // Wake up during the day self.get_entity().set_pose(EntityPose::Standing); - self.get_entity().send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::villager::SLEEPING_POS_ID, - None::, - )], - None, + self.get_entity().set_synced_data( + pumpkin_data::tracked_data::villager::SLEEPING_POS_ID, + None::, ); } @@ -2249,15 +2222,10 @@ impl Mob for VillagerEntity { .lock() .unwrap_or_else(std::sync::PoisonError::into_inner); let bedrock_metadata = Self::bedrock_metadata(data, self.xp.load(Ordering::Relaxed)); - entity.send_meta_data( - &[Metadata::new(tracked_data::villager::VILLAGER_DATA, data)], - Some(&bedrock_metadata), - ); + entity.set_synced_data(tracked_data::villager::VILLAGER_DATA, data); + entity.send_bedrock_actor_data(&bedrock_metadata); if entity.age.load(Ordering::Relaxed) < 0 { - entity.send_meta_data( - &[Metadata::new(tracked_data::villager::BABY_ID, true)], - None, - ); + entity.set_synced_data(tracked_data::villager::BABY_ID, true); } } diff --git a/crates/pumpkin/src/entity/passive/wolf.rs b/crates/pumpkin/src/entity/passive/wolf.rs index f0670e7bd..45aa67795 100644 --- a/crates/pumpkin/src/entity/passive/wolf.rs +++ b/crates/pumpkin/src/entity/passive/wolf.rs @@ -9,7 +9,6 @@ use pumpkin_data::item_stack::ItemStack; use pumpkin_data::tag::{self, Taggable}; use pumpkin_nbt::compound::NbtCompound; use pumpkin_protocol::codec::var_int::VarInt; -use pumpkin_protocol::java::client::play::Metadata; use rand::RngExt; use crate::entity::{ @@ -292,41 +291,23 @@ impl Mob for WolfEntity { let entity = self.get_entity(); let is_baby = entity.age.load(Ordering::Relaxed) < 0; if is_baby { - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::wolf::BABY_ID, - true, - )], - None, - ); + entity.set_synced_data(pumpkin_data::tracked_data::wolf::BABY_ID, true); } - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::wolf::TAMEABLE_FLAGS, - self.get_tame_flags(), - )], - None, + entity.set_synced_data( + pumpkin_data::tracked_data::wolf::TAMEABLE_FLAGS, + self.get_tame_flags(), ); - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::wolf::COLLAR_COLOR, - VarInt(self.collar_color.load(Ordering::Relaxed) as i32), - )], - None, + entity.set_synced_data( + pumpkin_data::tracked_data::wolf::COLLAR_COLOR, + VarInt(self.collar_color.load(Ordering::Relaxed) as i32), ); - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::wolf::WOLF_VARIANT_ID, - VarInt(self.variant.load(Ordering::Relaxed) as i32), - )], - None, + entity.set_synced_data( + pumpkin_data::tracked_data::wolf::WOLF_VARIANT_ID, + VarInt(self.variant.load(Ordering::Relaxed) as i32), ); - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::wolf::OWNER_UUID, - self.get_owner(), - )], - None, + entity.set_synced_data( + pumpkin_data::tracked_data::wolf::OWNER_UUID, + self.get_owner(), ); } @@ -392,12 +373,9 @@ impl WolfEntity { pub fn set_collar_color(&self, color: u8) { self.collar_color.store(color, Ordering::Relaxed); let entity = self.get_entity(); - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::wolf::COLLAR_COLOR, - VarInt(color as i32), - )], - None, + entity.set_synced_data( + pumpkin_data::tracked_data::wolf::COLLAR_COLOR, + VarInt(color as i32), ); } } diff --git a/crates/pumpkin/src/entity/passive/zombie_horse.rs b/crates/pumpkin/src/entity/passive/zombie_horse.rs index f7a806606..8d9567937 100644 --- a/crates/pumpkin/src/entity/passive/zombie_horse.rs +++ b/crates/pumpkin/src/entity/passive/zombie_horse.rs @@ -9,7 +9,6 @@ use pumpkin_data::item::Item; use pumpkin_data::item_stack::ItemStack; use pumpkin_data::sound::{Sound, SoundCategory}; use pumpkin_nbt::compound::NbtCompound; -use pumpkin_protocol::java::client::play::Metadata; use uuid::Uuid; use crate::entity::{ @@ -84,12 +83,9 @@ impl ZombieHorseEntity { let new_flags = if val { current | flag } else { current & !flag }; self.flags.store(new_flags, Ordering::Relaxed); let entity = self.get_entity(); - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::zombie_horse::DATA_ID_FLAGS, - new_flags as i8, - )], - None, + entity.set_synced_data( + pumpkin_data::tracked_data::zombie_horse::DATA_ID_FLAGS, + new_flags as i8, ); } @@ -167,20 +163,11 @@ impl Mob for ZombieHorseEntity { let entity = self.get_entity(); let is_baby = entity.age.load(Ordering::Relaxed) < 0; if is_baby { - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::zombie_horse::DATA_BABY_ID, - true, - )], - None, - ); + entity.set_synced_data(pumpkin_data::tracked_data::zombie_horse::DATA_BABY_ID, true); } - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::zombie_horse::DATA_ID_FLAGS, - self.flags.load(Ordering::Relaxed) as i8, - )], - None, + entity.set_synced_data( + pumpkin_data::tracked_data::zombie_horse::DATA_ID_FLAGS, + self.flags.load(Ordering::Relaxed) as i8, ); } diff --git a/crates/pumpkin/src/entity/player.rs b/crates/pumpkin/src/entity/player.rs index 5b2b5d73d..6707168b9 100644 --- a/crates/pumpkin/src/entity/player.rs +++ b/crates/pumpkin/src/entity/player.rs @@ -269,8 +269,8 @@ use pumpkin_protocol::java::client::play::{ 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, + CUpdateTime, GameEvent, MapIcon, MapPatch, PlayerAction, PlayerInfoFlags, PlayerSpawnData, + PreviousMessage, Statistic, }; use pumpkin_protocol::java::server::play::{ SClickSlot, SContainerButtonClick, SRenameItem, SlotActionType, @@ -487,7 +487,7 @@ pub struct Player { pub experience_pick_up_delay: Mutex, pub chunk_sender: Mutex, pub chunk_listener: Mutex, Weak)>>, - pub held_chunk_tickets: Mutex>, + pub held_chunk_tickets: Mutex, Option)>>, pub chunk_send_epoch: AtomicU32, pub has_played_before: AtomicBool, root_vehicle_uuid: AtomicCell>, @@ -1107,14 +1107,22 @@ impl Player { .take(); if let Some((view_level, sim_level)) = held { let center = self.get_entity().chunk_pos.load(); - lock.remove_ticket(center, view_level); - lock.remove_ticket(center, sim_level); + if let Some(view) = view_level { + lock.remove_ticket(center, view); + } + if let Some(sim) = sim_level { + lock.remove_ticket(center, sim); + } } lock.send_change(); level.should_unload.store(true, Ordering::Relaxed); level.level_channel.notify(); } + pub fn update_chunk_tickets_for_gamemode(self: &Arc) { + crate::world::chunker::update_position(self); + } + pub fn change_world_chunks( &self, old_level: &Arc, @@ -1919,12 +1927,9 @@ impl Player { self.living_entity .entity .set_pos(bed_head_pos.to_f64().add_raw(0.5, 0.6875, 0.5)); - self.get_entity().send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::player::SLEEPING_POS_ID, - Some(bed_head_pos), - )], - None, + self.get_entity().set_synced_data( + pumpkin_data::tracked_data::player::SLEEPING_POS_ID, + Some(bed_head_pos), ); self.get_entity().set_velocity(Vector3::default()); @@ -2083,12 +2088,9 @@ impl Player { self.living_entity.entity.set_pose(EntityPose::Standing); self.living_entity.entity.set_pos(self.position()); - self.living_entity.entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::player::SLEEPING_POS_ID, - None::, - )], - None, + self.living_entity.entity.set_synced_data( + pumpkin_data::tracked_data::player::SLEEPING_POS_ID, + None::, ); self.set_stat( @@ -4512,28 +4514,21 @@ impl Player { /// Send the player's skin layers and used hand to all players. pub fn send_client_information(&self) { let config = self.config.load(); - self.living_entity.entity.send_meta_data( - &[ - // v26.x - Metadata::new( - pumpkin_data::tracked_data::player::PLAYER_MODE_CUSTOMISATION, - config.skin_parts, - ), - Metadata::new( - pumpkin_data::tracked_data::player::PLAYER_MAIN_HAND, - config.main_hand as u8, - ), - // v1.21.x - Metadata::new( - pumpkin_data::tracked_data::player::PLAYER_MODE_CUSTOMIZATION_ID, - config.skin_parts, - ), - Metadata::new( - pumpkin_data::tracked_data::player::MAIN_ARM_ID, - config.main_hand as u8, - ), - ], - None, + self.living_entity.entity.set_synced_data( + pumpkin_data::tracked_data::player::PLAYER_MODE_CUSTOMISATION, + config.skin_parts, + ); + self.living_entity.entity.set_synced_data( + pumpkin_data::tracked_data::player::PLAYER_MAIN_HAND, + config.main_hand as u8, + ); + self.living_entity.entity.set_synced_data( + pumpkin_data::tracked_data::player::PLAYER_MODE_CUSTOMIZATION_ID, + config.skin_parts, + ); + self.living_entity.entity.set_synced_data( + pumpkin_data::tracked_data::player::MAIN_ARM_ID, + config.main_hand as u8, ); } diff --git a/crates/pumpkin/src/entity/projectile/arrow.rs b/crates/pumpkin/src/entity/projectile/arrow.rs index 250c8117b..b043c002b 100644 --- a/crates/pumpkin/src/entity/projectile/arrow.rs +++ b/crates/pumpkin/src/entity/projectile/arrow.rs @@ -326,25 +326,15 @@ impl ArrowEntity { pub fn set_critical(&self, critical: bool) { self.is_critical.store(critical, Ordering::Relaxed); let flags = self.get_flags(); - self.entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::abstract_arrow::ID_FLAGS, - flags, - )], - None, - ); + self.entity + .set_synced_data(pumpkin_data::tracked_data::abstract_arrow::ID_FLAGS, flags); } pub fn set_no_physics(&self, no_physics: bool) { self.no_physics.store(no_physics, Ordering::Relaxed); let flags = self.get_flags(); - self.entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::abstract_arrow::ID_FLAGS, - flags, - )], - None, - ); + self.entity + .set_synced_data(pumpkin_data::tracked_data::abstract_arrow::ID_FLAGS, flags); } #[must_use] @@ -363,12 +353,9 @@ impl ArrowEntity { pub fn set_pierce_level(&self, level: u8) { self.pierce_level.store(level, Ordering::Relaxed); - self.entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::abstract_arrow::PIERCE_LEVEL, - level, - )], - None, + self.entity.set_synced_data( + pumpkin_data::tracked_data::abstract_arrow::PIERCE_LEVEL, + level, ); } @@ -476,22 +463,14 @@ impl EntityBase for ArrowEntity { let in_ground = self.in_ground.load(Ordering::Relaxed); if entity.entity_type.id == EntityType::SPECTRAL_ARROW.id { - entity.send_meta_data( - &[ - Metadata::new(pumpkin_data::tracked_data::spectral_arrow::ID_FLAGS, flags), - Metadata::new( - pumpkin_data::tracked_data::spectral_arrow::PIERCE_LEVEL, - pierce, - ), - ], - None, + entity.set_synced_data(pumpkin_data::tracked_data::spectral_arrow::ID_FLAGS, flags); + entity.set_synced_data( + pumpkin_data::tracked_data::spectral_arrow::PIERCE_LEVEL, + pierce, ); - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::spectral_arrow::IN_GROUND, - in_ground, - )], - None, + entity.set_synced_data( + pumpkin_data::tracked_data::spectral_arrow::IN_GROUND, + in_ground, ); } else { let item_stack = self @@ -499,27 +478,10 @@ impl EntityBase for ArrowEntity { .read() .unwrap_or_else(std::sync::PoisonError::into_inner); let color = Self::get_effect_color(&item_stack); - entity.send_meta_data( - &[ - Metadata::new(pumpkin_data::tracked_data::arrow::ID_FLAGS, flags), - Metadata::new(pumpkin_data::tracked_data::arrow::PIERCE_LEVEL, pierce), - ], - None, - ); - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::arrow::IN_GROUND, - in_ground, - )], - None, - ); - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::arrow::ID_EFFECT_COLOR, - color, - )], - None, - ); + entity.set_synced_data(pumpkin_data::tracked_data::arrow::ID_FLAGS, flags); + entity.set_synced_data(pumpkin_data::tracked_data::arrow::PIERCE_LEVEL, pierce); + entity.set_synced_data(pumpkin_data::tracked_data::arrow::IN_GROUND, in_ground); + entity.set_synced_data(pumpkin_data::tracked_data::arrow::ID_EFFECT_COLOR, color); } if self.is_on_fire() { @@ -635,12 +597,9 @@ impl EntityBase for ArrowEntity { let block = world.get_block(&pos); if block.is_air() { self.in_ground.store(false, Ordering::Relaxed); - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::abstract_arrow::IN_GROUND, - false, - )], - None, + entity.set_synced_data( + pumpkin_data::tracked_data::abstract_arrow::IN_GROUND, + false, ); let mut vel = entity.velocity.load(); vel.x *= rand::random::() * 0.2; @@ -901,13 +860,7 @@ impl EntityBase for ArrowEntity { entity.velocity.store(Vector3::new(0.0, 0.0, 0.0)); // Notify client that arrow is in ground - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::abstract_arrow::IN_GROUND, - true, - )], - None, - ); + entity.set_synced_data(pumpkin_data::tracked_data::abstract_arrow::IN_GROUND, true); // Play sound with vanilla pitch formula let sound_pitch = 1.2 / (rand::random::() * 0.2 + 0.9); diff --git a/crates/pumpkin/src/entity/projectile/egg.rs b/crates/pumpkin/src/entity/projectile/egg.rs index 89d659850..6e7bd1ff3 100644 --- a/crates/pumpkin/src/entity/projectile/egg.rs +++ b/crates/pumpkin/src/entity/projectile/egg.rs @@ -11,7 +11,6 @@ use pumpkin_data::item::Item; use pumpkin_data::item_stack::ItemStack; use pumpkin_protocol::bedrock::server::actor_event::ActorEventID; use pumpkin_protocol::codec::item_stack_seralizer::ItemStackSerializer; -use pumpkin_protocol::java::client::play::Metadata; use pumpkin_util::math::vector3::Vector3; use uuid::Uuid; @@ -75,12 +74,9 @@ impl EntityBase for EggEntity { .unwrap_or_else(std::sync::PoisonError::into_inner); // Sync the item stack so the client renders the correct color/variant - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::egg::ITEM_STACK, - &ItemStackSerializer::from(stack.clone()), - )], - None, + entity.set_synced_data( + pumpkin_data::tracked_data::egg::ITEM_STACK, + ItemStackSerializer::from(stack.clone()), ); } diff --git a/crates/pumpkin/src/entity/projectile/eye_of_ender.rs b/crates/pumpkin/src/entity/projectile/eye_of_ender.rs index e4e185a5e..329578a79 100644 --- a/crates/pumpkin/src/entity/projectile/eye_of_ender.rs +++ b/crates/pumpkin/src/entity/projectile/eye_of_ender.rs @@ -173,17 +173,14 @@ impl EntityBase for EyeOfEnder { } fn init_data_tracker(&self) { - self.entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::eye_of_ender::ITEM_STACK, - &ItemStackSerializer::from( - self.item_stack - .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner) - .clone(), - ), - )], - None, + self.entity.set_synced_data( + pumpkin_data::tracked_data::eye_of_ender::ITEM_STACK, + ItemStackSerializer::from( + self.item_stack + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .clone(), + ), ); } diff --git a/crates/pumpkin/src/entity/projectile/fireball.rs b/crates/pumpkin/src/entity/projectile/fireball.rs index 2fba10b79..59a89475b 100644 --- a/crates/pumpkin/src/entity/projectile/fireball.rs +++ b/crates/pumpkin/src/entity/projectile/fireball.rs @@ -5,7 +5,6 @@ use pumpkin_data::item::Item; use pumpkin_data::item_stack::ItemStack; use pumpkin_nbt::compound::NbtCompound; use pumpkin_protocol::codec::item_stack_seralizer::ItemStackSerializer; -use pumpkin_protocol::java::client::play::Metadata; use pumpkin_util::math::atomic_f32::AtomicF32; use pumpkin_util::math::vector3::Vector3; @@ -118,12 +117,9 @@ impl FireballEntity { .write() .unwrap_or_else(std::sync::PoisonError::into_inner) = new_item.clone(); - self.get_entity().send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::fireball::ITEM_STACK, - &ItemStackSerializer::from(new_item), - )], - None, + self.get_entity().set_synced_data( + pumpkin_data::tracked_data::fireball::ITEM_STACK, + ItemStackSerializer::from(new_item), ); } @@ -200,12 +196,9 @@ impl EntityBase for FireballEntity { .read() .unwrap_or_else(std::sync::PoisonError::into_inner); - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::fireball::ITEM_STACK, - &ItemStackSerializer::from(stack.clone()), - )], - None, + entity.set_synced_data( + pumpkin_data::tracked_data::fireball::ITEM_STACK, + ItemStackSerializer::from(stack.clone()), ); } diff --git a/crates/pumpkin/src/entity/projectile/firework_rocket.rs b/crates/pumpkin/src/entity/projectile/firework_rocket.rs index 00b15c94f..807a66801 100644 --- a/crates/pumpkin/src/entity/projectile/firework_rocket.rs +++ b/crates/pumpkin/src/entity/projectile/firework_rocket.rs @@ -5,7 +5,7 @@ use crate::{ }; use pumpkin_data::entity::EntityStatus; use pumpkin_protocol::bedrock::server::actor_event::ActorEventID; -use pumpkin_protocol::{codec::optional_int::OptionalInt, java::client::play::Metadata}; +use pumpkin_protocol::codec::optional_int::OptionalInt; use pumpkin_util::{ math::vector3::Vector3, random::{RandomGenerator, RandomImpl, get_seed, xoroshiro128::Xoroshiro}, @@ -60,12 +60,9 @@ impl FireworkRocketEntity { .into(), }; - rocket.entity.entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::firework_rocket::ATTACHED_TO_TARGET, - OptionalInt(Some(shooter.entity_id)), - )], - None, + rocket.entity.entity.set_synced_data( + pumpkin_data::tracked_data::firework_rocket::ATTACHED_TO_TARGET, + OptionalInt(Some(shooter.entity_id)), ); rocket diff --git a/crates/pumpkin/src/entity/projectile/fishing_bobber.rs b/crates/pumpkin/src/entity/projectile/fishing_bobber.rs index bd02d0686..170ae0a4e 100644 --- a/crates/pumpkin/src/entity/projectile/fishing_bobber.rs +++ b/crates/pumpkin/src/entity/projectile/fishing_bobber.rs @@ -7,7 +7,6 @@ use crate::{ }; use pumpkin_data::item_stack::ItemStack; use pumpkin_data::sound::{Sound, SoundCategory}; -use pumpkin_protocol::java::client::play::Metadata; use pumpkin_util::math::boundingbox::BoundingBox; use pumpkin_util::math::vector3::Vector3; @@ -204,12 +203,9 @@ impl FishingBobberEntity { if ebb.intersects(&search_box) { self.hooked_entity_id .store(cand.get_entity().entity_id, Ordering::Relaxed); - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::fishing_bobber::HOOKED_ENTITY, - cand.get_entity().entity_id + 1, - )], - None, + entity.set_synced_data( + pumpkin_data::tracked_data::fishing_bobber::HOOKED_ENTITY, + cand.get_entity().entity_id + 1, ); return; } diff --git a/crates/pumpkin/src/entity/projectile/lingering_potion.rs b/crates/pumpkin/src/entity/projectile/lingering_potion.rs index ec0fb110f..d09b4c66d 100644 --- a/crates/pumpkin/src/entity/projectile/lingering_potion.rs +++ b/crates/pumpkin/src/entity/projectile/lingering_potion.rs @@ -76,14 +76,9 @@ impl EntityBase for LingeringPotionEntity { .unwrap_or_else(std::sync::PoisonError::into_inner); // Sync the item stack so the client renders the correct potion type - entity.send_meta_data( - &[pumpkin_protocol::java::client::play::Metadata::new( - pumpkin_data::tracked_data::lingering_potion::ITEM_STACK, - &pumpkin_protocol::codec::item_stack_seralizer::ItemStackSerializer::from( - stack.clone(), - ), - )], - None, + entity.set_synced_data( + pumpkin_data::tracked_data::lingering_potion::ITEM_STACK, + pumpkin_protocol::codec::item_stack_seralizer::ItemStackSerializer::from(stack.clone()), ); } diff --git a/crates/pumpkin/src/entity/projectile/splash_potion.rs b/crates/pumpkin/src/entity/projectile/splash_potion.rs index caebe1e66..17c2eb5e0 100644 --- a/crates/pumpkin/src/entity/projectile/splash_potion.rs +++ b/crates/pumpkin/src/entity/projectile/splash_potion.rs @@ -111,14 +111,9 @@ impl EntityBase for SplashPotionEntity { .unwrap_or_else(std::sync::PoisonError::into_inner); // Sync the item stack - entity.send_meta_data( - &[pumpkin_protocol::java::client::play::Metadata::new( - pumpkin_data::tracked_data::splash_potion::ITEM_STACK, - &pumpkin_protocol::codec::item_stack_seralizer::ItemStackSerializer::from( - stack.clone(), - ), - )], - None, + entity.set_synced_data( + pumpkin_data::tracked_data::splash_potion::ITEM_STACK, + pumpkin_protocol::codec::item_stack_seralizer::ItemStackSerializer::from(stack.clone()), ); } diff --git a/crates/pumpkin/src/entity/projectile/wither_skull.rs b/crates/pumpkin/src/entity/projectile/wither_skull.rs index 86ee2004f..697e98d32 100644 --- a/crates/pumpkin/src/entity/projectile/wither_skull.rs +++ b/crates/pumpkin/src/entity/projectile/wither_skull.rs @@ -2,7 +2,6 @@ use std::sync::atomic::{AtomicBool, Ordering}; use pumpkin_data::{damage::DamageType, effect::StatusEffect, potion::Effect, tracked_data}; use pumpkin_nbt::compound::NbtCompound; -use pumpkin_protocol::java::client::play::Metadata; use pumpkin_util::{Difficulty, math::vector3::Vector3}; use crate::{ @@ -69,13 +68,9 @@ impl WitherSkullEntity { pub fn set_dangerous(&self, dangerous: bool) { self.dangerous.store(dangerous, Ordering::Relaxed); - self.thrown.entity.send_meta_data( - &[Metadata::new( - tracked_data::wither_skull::DATA_DANGEROUS, - dangerous, - )], - None, - ); + self.thrown + .entity + .set_synced_data(tracked_data::wither_skull::DATA_DANGEROUS, dangerous); } } @@ -96,12 +91,9 @@ impl EntityBase for WitherSkullEntity { fn init_data_tracker(&self) { let entity = self.get_entity(); - entity.send_meta_data( - &[Metadata::new( - tracked_data::wither_skull::DATA_DANGEROUS, - self.is_dangerous(), - )], - None, + entity.set_synced_data( + tracked_data::wither_skull::DATA_DANGEROUS, + self.is_dangerous(), ); } diff --git a/crates/pumpkin/src/entity/synched_entity_data.rs b/crates/pumpkin/src/entity/synched_entity_data.rs new file mode 100644 index 000000000..25280d235 --- /dev/null +++ b/crates/pumpkin/src/entity/synched_entity_data.rs @@ -0,0 +1,221 @@ +use std::collections::HashMap; +use std::sync::Mutex; +use std::sync::atomic::{AtomicBool, Ordering}; + +use bytes::BufMut; +use pumpkin_data::meta_data_type::MetaDataType; +use pumpkin_data::tracked_data::{TrackedData, TrackedId}; +use pumpkin_protocol::java::client::play::{Metadata, MetadataSerializer}; +use pumpkin_protocol::ser::WritingError; +use pumpkin_util::version::JavaMinecraftVersion; + +pub trait ErasedSerializer: Send + Sync { + fn write( + &self, + index: TrackedId, + r#type: MetaDataType, + writer: &mut dyn std::io::Write, + version: &JavaMinecraftVersion, + ) -> Result<(), WritingError>; + + fn write_canonical(&self, index: TrackedId, r#type: MetaDataType) -> Vec; +} + +struct SerializerHolder { + value: T, +} + +impl ErasedSerializer + for SerializerHolder +{ + fn write( + &self, + index: TrackedId, + r#type: MetaDataType, + writer: &mut dyn std::io::Write, + version: &JavaMinecraftVersion, + ) -> Result<(), WritingError> { + let meta = Metadata::new_raw(index, r#type, &self.value); + meta.write(writer, version) + } + + fn write_canonical(&self, index: TrackedId, r#type: MetaDataType) -> Vec { + let mut buf = Vec::new(); + let meta = Metadata::new_raw(index, r#type, &self.value); + let _ = meta.write(&mut buf, &JavaMinecraftVersion::V_26_2); + buf + } +} + +pub struct DataItem { + pub tracked: TrackedData, + pub serializer: Box, + pub canonical_bytes: Vec, + pub dirty: bool, + pub is_default: bool, +} + +pub struct SynchedEntityData { + items: Mutex>, + is_dirty: AtomicBool, +} + +impl Default for SynchedEntityData { + fn default() -> Self { + Self::new() + } +} + +impl SynchedEntityData { + #[must_use] + pub fn new() -> Self { + Self { + items: Mutex::new(HashMap::new()), + is_dirty: AtomicBool::new(false), + } + } + + pub fn define( + &self, + tracked: TrackedData, + value: T, + ) { + let id = tracked.id.v26_2; + let holder = SerializerHolder { value }; + let canonical_bytes = holder.write_canonical(tracked.id, tracked.r#type); + let mut items = self + .items + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + items.insert( + id, + DataItem { + tracked, + serializer: Box::new(holder), + canonical_bytes, + dirty: false, + is_default: true, + }, + ); + } + + pub fn set( + &self, + tracked: TrackedData, + value: T, + ) -> bool { + let id = tracked.id.v26_2; + let holder = SerializerHolder { value }; + let new_canonical = holder.write_canonical(tracked.id, tracked.r#type); + + let mut items = self + .items + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + if let Some(item) = items.get_mut(&id) { + if item.canonical_bytes == new_canonical { + return false; + } + item.canonical_bytes = new_canonical; + item.serializer = Box::new(holder); + item.dirty = true; + item.is_default = false; + } else { + items.insert( + id, + DataItem { + tracked, + serializer: Box::new(holder), + canonical_bytes: new_canonical, + dirty: true, + is_default: false, + }, + ); + } + self.is_dirty.store(true, Ordering::Release); + true + } + + #[must_use] + pub fn is_dirty(&self) -> bool { + self.is_dirty.load(Ordering::Acquire) + } + + pub fn pack_dirty_for_version(&self, version: &JavaMinecraftVersion) -> Option> { + if !self.is_dirty.load(Ordering::Acquire) { + return None; + } + + let items = self + .items + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + let mut buf = Vec::new(); + let mut has_any = false; + + for item in items.values() { + if item.dirty { + let before_len = buf.len(); + if item + .serializer + .write(item.tracked.id, item.tracked.r#type, &mut buf, version) + .is_ok() + && buf.len() > before_len + { + has_any = true; + } + } + } + + if !has_any { + return None; + } + + buf.put_u8(255); + Some(buf.into_boxed_slice()) + } + + pub fn clear_dirty(&self) { + let mut items = self + .items + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + for item in items.values_mut() { + item.dirty = false; + } + self.is_dirty.store(false, Ordering::Release); + } + + pub fn get_non_default_values_for_version( + &self, + version: &JavaMinecraftVersion, + ) -> Option> { + let items = self + .items + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + let mut buf = Vec::new(); + let mut has_any = false; + + for item in items.values() { + if !item.is_default { + let before_len = buf.len(); + if item + .serializer + .write(item.tracked.id, item.tracked.r#type, &mut buf, version) + .is_ok() + && buf.len() > before_len + { + has_any = true; + } + } + } + + if !has_any { + return None; + } + + buf.put_u8(255); + Some(buf.into_boxed_slice()) + } +} diff --git a/crates/pumpkin/src/entity/tnt.rs b/crates/pumpkin/src/entity/tnt.rs index 332752135..8fa7dd4e9 100644 --- a/crates/pumpkin/src/entity/tnt.rs +++ b/crates/pumpkin/src/entity/tnt.rs @@ -2,7 +2,7 @@ use super::{Entity, EntityBase, living::LivingEntity}; use crate::server::Server; use core::f32; use pumpkin_data::Block; -use pumpkin_protocol::{codec::var_int::VarInt, java::client::play::Metadata}; +use pumpkin_protocol::codec::var_int::VarInt; use pumpkin_util::math::vector3::Vector3; use std::{ f64::consts::TAU, @@ -78,18 +78,13 @@ impl EntityBase for TNTEntity { self.entity .set_velocity(Vector3::new(-pos.sin() * 0.02, 0.2, -pos.cos() * 0.02)); - self.entity.send_meta_data( - &[ - Metadata::new( - pumpkin_data::tracked_data::tnt::FUSE_ID, - VarInt(self.fuse.load(Relaxed) as i32), - ), - Metadata::new( - pumpkin_data::tracked_data::tnt::BLOCK_STATE_ID, - VarInt(i32::from(Block::TNT.default_state.id.as_u16())), - ), - ], - None, + self.entity.set_synced_data( + pumpkin_data::tracked_data::tnt::FUSE_ID, + VarInt(self.fuse.load(Relaxed) as i32), + ); + self.entity.set_synced_data( + pumpkin_data::tracked_data::tnt::BLOCK_STATE_ID, + VarInt(i32::from(Block::TNT.default_state.id.as_u16())), ); } diff --git a/crates/pumpkin/src/entity/vehicle/boat.rs b/crates/pumpkin/src/entity/vehicle/boat.rs index 792ba0a92..090fded9d 100644 --- a/crates/pumpkin/src/entity/vehicle/boat.rs +++ b/crates/pumpkin/src/entity/vehicle/boat.rs @@ -10,8 +10,6 @@ use crate::server::Server; use pumpkin_data::damage::DamageType; use pumpkin_data::item_stack::ItemStack; -use pumpkin_protocol::java::client::play::Metadata; - use pumpkin_util::math::vector3::Vector3; use crate::entity::vehicle::vehicle::VehicleEntity; @@ -37,13 +35,12 @@ impl BoatEntity { self.left_paddle_moving.store(left, Ordering::Relaxed); self.right_paddle_moving.store(right, Ordering::Relaxed); - self.vehicle.entity.send_meta_data( - &[ - Metadata::new(pumpkin_data::tracked_data::boat::ID_PADDLE_LEFT, left), - Metadata::new(pumpkin_data::tracked_data::boat::ID_PADDLE_RIGHT, right), - ], - None, - ); + self.vehicle + .entity + .set_synced_data(pumpkin_data::tracked_data::boat::ID_PADDLE_LEFT, left); + self.vehicle + .entity + .set_synced_data(pumpkin_data::tracked_data::boat::ID_PADDLE_RIGHT, right); } fn send_wobble_metadata(&self) { diff --git a/crates/pumpkin/src/entity/vehicle/minecart/furnace.rs b/crates/pumpkin/src/entity/vehicle/minecart/furnace.rs index 014d8cf51..740195f2a 100644 --- a/crates/pumpkin/src/entity/vehicle/minecart/furnace.rs +++ b/crates/pumpkin/src/entity/vehicle/minecart/furnace.rs @@ -5,7 +5,6 @@ use pumpkin_data::item_stack::ItemStack; use pumpkin_data::particle::Particle; use pumpkin_data::tag::{self, Taggable}; use pumpkin_nbt::compound::NbtCompound; -use pumpkin_protocol::java::client::play::Metadata; use pumpkin_util::math::vector3::Vector3; use rand::RngExt; @@ -28,12 +27,9 @@ impl FurnaceMinecart { } fn set_fueled(entity: &Entity, fueled: bool) { - entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::furnace_minecart::ID_FUEL, - fueled, - )], - None, + entity.set_synced_data( + pumpkin_data::tracked_data::furnace_minecart::ID_FUEL, + fueled, ); } diff --git a/crates/pumpkin/src/entity/vehicle/vehicle.rs b/crates/pumpkin/src/entity/vehicle/vehicle.rs index 984bb1a3f..24f9af2ba 100644 --- a/crates/pumpkin/src/entity/vehicle/vehicle.rs +++ b/crates/pumpkin/src/entity/vehicle/vehicle.rs @@ -2,7 +2,6 @@ use crossbeam::atomic::AtomicCell; use std::sync::atomic::{AtomicI32, Ordering}; use crate::entity::Entity; -use pumpkin_protocol::java::client::play::Metadata; use crate::entity::EntityBase; use pumpkin_protocol::codec::var_int::VarInt; @@ -134,25 +133,17 @@ impl VehicleEntity { } pub fn send_wobble_metadata(&self) { - self.entity.send_meta_data( - &[ - Metadata::new( - pumpkin_data::tracked_data::boat::ID_HURT, - VarInt(self.get_hurt_time()), - ), - Metadata::new( - pumpkin_data::tracked_data::boat::ID_HURTDIR, - VarInt(self.get_hurt_dir()), - ), - ], - None, + self.entity.set_synced_data( + pumpkin_data::tracked_data::boat::ID_HURT, + VarInt(self.get_hurt_time()), ); - self.entity.send_meta_data( - &[Metadata::new( - pumpkin_data::tracked_data::boat::ID_DAMAGE, - self.get_damage(), - )], - None, + self.entity.set_synced_data( + pumpkin_data::tracked_data::boat::ID_HURTDIR, + VarInt(self.get_hurt_dir()), + ); + self.entity.set_synced_data( + pumpkin_data::tracked_data::boat::ID_DAMAGE, + self.get_damage(), ); } diff --git a/crates/pumpkin/src/item/items/spawn_egg.rs b/crates/pumpkin/src/item/items/spawn_egg.rs index 2157d2114..12eb5884a 100644 --- a/crates/pumpkin/src/item/items/spawn_egg.rs +++ b/crates/pumpkin/src/item/items/spawn_egg.rs @@ -184,13 +184,8 @@ impl ItemBehaviour for SpawnEggItem { mob.get_entity() .age .store(-24000, std::sync::atomic::Ordering::Relaxed); - mob.get_entity().send_meta_data( - &[pumpkin_protocol::java::client::play::Metadata::new( - pumpkin_data::tracked_data::ageable_mob::DATA_BABY_ID, - true, - )], - None, - ); + mob.get_entity() + .set_synced_data(pumpkin_data::tracked_data::ageable_mob::DATA_BABY_ID, true); apply_entity_variant(item, mob.as_ref()); world.spawn_entity(mob); item.decrement_unless_creative(player.gamemode.load(), 1); diff --git a/crates/pumpkin/src/world/chunker.rs b/crates/pumpkin/src/world/chunker.rs index 2499590c9..4ac9ecb45 100644 --- a/crates/pumpkin/src/world/chunker.rs +++ b/crates/pumpkin/src/world/chunker.rs @@ -40,6 +40,7 @@ pub fn is_within_view_distance( (target.x - center.x).abs().max((target.y - center.y).abs()) <= view_distance } +#[allow(clippy::too_many_lines)] pub fn update_position(player: &Arc) { let entity = &player.get_entity(); let new_chunk_center = entity.chunk_pos.load(); @@ -85,26 +86,46 @@ pub fn update_position(player: &Arc) { .lock() .unwrap_or_else(std::sync::PoisonError::into_inner); + let is_spectator = player.is_spectator(); + let spectators_generate_chunks = world + .level_info + .load() + .game_rules + .spectators_generate_chunks; + + let new_view_level = (!is_spectator || spectators_generate_chunks).then(|| { + pumpkin_world::chunk_system::ChunkLoading::get_level_from_view_distance( + u8::from(view_distance) + 1, + ) + }); + + let new_sim_level = (!is_spectator).then(|| { + let sim_dist = world.server.upgrade().map_or(10, |s| { + s.advanced_config.networking.java.simulation_distance.get() + }); + pumpkin_world::chunk_system::ChunkLoading::get_level_from_simulation_distance(sim_dist) + }); + { let mut lock = level .chunk_loading .lock() .unwrap_or_else(std::sync::PoisonError::into_inner); - let new_level = pumpkin_world::chunk_system::ChunkLoading::get_level_from_view_distance( - u8::from(view_distance) + 1, - ); - lock.add_ticket(new_chunk_center, new_level); - let sim_dist = world.server.upgrade().map_or(10, |s| { - s.advanced_config.networking.java.simulation_distance.get() - }); - let sim_level = - pumpkin_world::chunk_system::ChunkLoading::get_level_from_simulation_distance(sim_dist); - lock.add_ticket(new_chunk_center, sim_level); + if let Some(view) = new_view_level { + lock.add_ticket(new_chunk_center, view); + } + if let Some(sim) = new_sim_level { + lock.add_ticket(new_chunk_center, sim); + } - if let Some((held_view, held_sim)) = held_tickets.replace((new_level, sim_level)) { - lock.remove_ticket(old_cylindrical.center, held_view); - lock.remove_ticket(old_cylindrical.center, held_sim); + if let Some((held_view, held_sim)) = held_tickets.replace((new_view_level, new_sim_level)) { + if let Some(view) = held_view { + lock.remove_ticket(old_cylindrical.center, view); + } + if let Some(sim) = held_sim { + lock.remove_ticket(old_cylindrical.center, sim); + } } lock.send_change(); }; diff --git a/crates/pumpkin/src/world/entity_tracker.rs b/crates/pumpkin/src/world/entity_tracker.rs index 06778311f..0c4507663 100644 --- a/crates/pumpkin/src/world/entity_tracker.rs +++ b/crates/pumpkin/src/world/entity_tracker.rs @@ -1,14 +1,21 @@ use std::sync::Arc; use bytes::BufMut; +use crossbeam::atomic::AtomicCell; use dashmap::DashMap; use dashmap::DashSet; use pumpkin_protocol::bedrock::client::remove_actor::CRemoveActor; +use pumpkin_protocol::codec::item_stack_seralizer::ItemStackSerializer; +use pumpkin_protocol::codec::var_int::VarInt; use pumpkin_protocol::codec::var_long::VarLong; use pumpkin_protocol::java::client::play::{ - CEntityVelocity, CHeadRot, CRemoveEntities, CSetEntityMetadata, Metadata, + CEntityVelocity, CHeadRot, CRemoveEntities, CSetEntityMetadata, CSetEquipment, CSetPassengers, + Metadata, }; use pumpkin_protocol::{BClientPacket, ClientPacket}; +use pumpkin_util::GameMode; +use pumpkin_util::math::get_section_cord; +use pumpkin_util::math::vector3::Vector3; use pumpkin_util::version::JavaMinecraftVersion; use uuid::Uuid; @@ -19,8 +26,6 @@ use crate::net::java::JavaClient; use crate::world::World; use crate::world::chunker::{get_view_distance, is_within_view_distance}; -/// Represents an entity tracked in the world and its current watchers, -/// corresponding to Vanilla's `ChunkMap.TrackedEntity`. pub struct TrackedEntity { pub entity: Arc, pub entity_id: i32, @@ -28,6 +33,7 @@ pub struct TrackedEntity { pub update_interval: u32, pub track_deltas: bool, pub seen_by: DashSet, + pub last_section_pos: AtomicCell>, } impl TrackedEntity { @@ -39,6 +45,12 @@ impl TrackedEntity { track_deltas: bool, ) -> Self { let entity_id = entity.get_entity().entity_id; + let pos = entity.get_entity().pos.load(); + let last_section_pos = Vector3::new( + get_section_cord(pos.x.floor() as i32), + get_section_cord(pos.y.floor() as i32), + get_section_cord(pos.z.floor() as i32), + ); Self { entity, entity_id, @@ -46,25 +58,43 @@ impl TrackedEntity { update_interval, track_deltas, seen_by: DashSet::new(), + last_section_pos: AtomicCell::new(last_section_pos), + } + } + + fn collect_indirect_passengers( + entity: &Arc, + result: &mut Vec>, + ) { + if let Ok(passengers) = entity.get_entity().passengers.try_lock() { + for passenger in passengers.iter() { + result.push(passenger.clone()); + Self::collect_indirect_passengers(passenger, result); + } } } - /// Gets the effective tracking range in chunks, taking passengers into account. #[must_use] pub fn get_effective_range(&self) -> u32 { let mut effective_range = self.tracking_range; - if let Ok(passengers) = self.entity.get_entity().passengers.try_lock() { - for passenger in passengers.iter() { - let passenger_range = passenger.get_entity().entity_type.client_tracking_range; - if passenger_range > effective_range { - effective_range = passenger_range; - } + let mut passengers = Vec::new(); + Self::collect_indirect_passengers(&self.entity, &mut passengers); + for passenger in passengers { + let passenger_range = passenger.get_entity().entity_type.client_tracking_range; + if passenger_range > effective_range { + effective_range = passenger_range; } } effective_range } - /// Updates visibility for a single player. + fn broadcast_to_player(&self, player: &Player) -> bool { + self.entity.get_player().is_none_or(|target_player| { + player.gamemode.load() == GameMode::Spectator + || target_player.gamemode.load() != GameMode::Spectator + }) + } + pub fn update_player(&self, player: &Arc, _world: &World) { if player.get_entity().entity_id == self.entity_id { return; @@ -86,7 +116,7 @@ impl TrackedEntity { let player_chunk = player_entity.chunk_pos.load(); let in_view = is_within_view_distance(entity_chunk, player_chunk, player_vd); - let is_visible = dist_sq <= range_sq && in_view; + let is_visible = dist_sq <= range_sq && self.broadcast_to_player(player) && in_view; if is_visible { if self.seen_by.insert(player.gameprofile.id) { @@ -97,14 +127,13 @@ impl TrackedEntity { } } - /// Updates visibility for a list of players. pub fn update_players(&self, players: &[Arc], world: &World) { for player in players { self.update_player(player, world); } } - /// Sends spawn and initial state packets to the new watcher. + #[allow(clippy::too_many_lines)] pub fn add_pairing(&self, player: &Arc) { player.client.try_enqueue_spawn_packet(&self.entity); @@ -169,9 +198,81 @@ impl TrackedEntity { client.try_enqueue_packet(data); } } + + if let ClientPlatform::Java(client) = player.client.as_ref() { + let version = client.version.load(); + // TODO: Support older versions + if version >= JavaMinecraftVersion::V_26_2 + && let Some(non_default) = self + .entity + .get_entity() + .synched_data + .get_non_default_values_for_version(&version) + { + let packet = CSetEntityMetadata::new(self.entity_id.into(), non_default); + if let Ok(packet_data) = JavaClient::serialize_packet_for_version(&packet, version) + { + client.try_enqueue_packet(packet_data); + } + } + } + + if let Some(living) = self.entity.get_living_entity() + && let Ok(equipment_guard) = living.entity_equipment.try_lock() + { + let mut equipment_list = Vec::new(); + for (slot, item_stack) in &equipment_guard.equipment { + if !item_stack.is_empty() { + equipment_list.push((slot.discriminant(), item_stack.clone())); + } + } + if !equipment_list.is_empty() { + let equipment: Vec<(i8, ItemStackSerializer)> = equipment_list + .iter() + .map(|(slot, stack)| (*slot, ItemStackSerializer::from(stack.clone()))) + .collect(); + let packet = CSetEquipment::new(self.entity_id.into(), equipment); + if let ClientPlatform::Java(client) = player.client.as_ref() + && let Ok(data) = client.serialize_packet(&packet) + { + client.try_enqueue_packet(data); + } + } + } + + if let Ok(passengers) = self.entity.get_entity().passengers.try_lock() + && !passengers.is_empty() + { + let passenger_ids: Vec = passengers + .iter() + .map(|p| VarInt(p.get_entity().entity_id)) + .collect(); + let packet = CSetPassengers::new(VarInt(self.entity_id), &passenger_ids); + if let ClientPlatform::Java(client) = player.client.as_ref() + && let Ok(data) = client.serialize_packet(&packet) + { + client.try_enqueue_packet(data); + } + } + + if let Ok(vehicle_guard) = self.entity.get_entity().vehicle.try_lock() + && let Some(vehicle) = vehicle_guard.as_ref() + && let Ok(vehicle_passengers) = vehicle.get_entity().passengers.try_lock() + { + let passenger_ids: Vec = vehicle_passengers + .iter() + .map(|p| VarInt(p.get_entity().entity_id)) + .collect(); + let packet = + CSetPassengers::new(VarInt(vehicle.get_entity().entity_id), &passenger_ids); + if let ClientPlatform::Java(client) = player.client.as_ref() + && let Ok(data) = client.serialize_packet(&packet) + { + client.try_enqueue_packet(data); + } + } } - /// Sends despawn packet to a player leaving visibility range. pub fn remove_pairing(&self, player: &Arc) { let entity_ids = [self.entity_id.into()]; match player.client.as_ref() { @@ -190,7 +291,6 @@ impl TrackedEntity { } } - /// Broadcasts removal of this entity to all current watchers. pub fn broadcast_removed(&self, world: &World) { let entity_ids = [self.entity_id.into()]; let je_packet = CRemoveEntities::new(&entity_ids); @@ -217,12 +317,10 @@ impl TrackedEntity { self.seen_by.clear(); } - /// Removes a player by UUID without sending despawn packet (used on disconnect). pub fn remove_player(&self, player_uuid: &Uuid) { self.seen_by.remove(player_uuid); } - /// Sends a Java client packet to all tracking players. pub fn send_to_tracking_players(&self, packet: &P, world: &World) { let players = world.players.load(); let recipients = players @@ -232,7 +330,6 @@ impl TrackedEntity { World::broadcast_java_grouped(packet, recipients_by_version); } - /// Sends a Bedrock client packet to all tracking players. pub fn send_to_tracking_players_bedrock( &self, packet: &P, @@ -250,7 +347,6 @@ impl TrackedEntity { World::broadcast_bedrock_grouped(packet, recipients); } - /// Sends an editioned packet (Java + Bedrock) to all tracking players. pub fn send_to_tracking_players_editioned( &self, je_packet: &J, @@ -276,7 +372,6 @@ impl TrackedEntity { World::broadcast_bedrock_grouped(be_packet, bedrock_recipients.into_iter()); } - /// Sends a packet to all tracking players and the entity itself if it is a player. pub fn send_to_tracking_players_and_self( &self, packet: &P, @@ -288,7 +383,6 @@ impl TrackedEntity { } } - /// Sends an editioned packet to all tracking players and the entity itself if it is a player. pub fn send_to_tracking_players_and_self_editioned< J: ClientPacket + Sync, B: BClientPacket + Sync, @@ -304,7 +398,6 @@ impl TrackedEntity { } } - /// Sends a packet to tracking players filtered by predicate. pub fn send_to_tracking_players_filtered bool>( &self, packet: &P, @@ -319,7 +412,6 @@ impl TrackedEntity { World::broadcast_java_grouped(packet, recipients_by_version); } - /// Sends an editioned packet to tracking players filtered by predicate. pub fn send_to_tracking_players_filtered_editioned< J: ClientPacket + Sync, B: BClientPacket + Sync, @@ -351,7 +443,6 @@ impl TrackedEntity { } } -/// The entity tracking system for a world, corresponding to Vanilla's `ChunkMap.entityMap`. pub struct EntityTracker { pub entity_map: DashMap>, } @@ -387,6 +478,18 @@ impl EntityTracker { .is_some_and(|t| !t.seen_by.is_empty()) } + pub fn for_each_entity_tracked_by)>( + &self, + player: &Player, + mut f: F, + ) { + for entry in &self.entity_map { + if entry.value().seen_by.contains(&player.gameprofile.id) { + f(&entry.value().entity); + } + } + } + pub fn add_entity(&self, entity: &Arc, world: &World) { let entity_type = entity.get_entity().entity_type; let range = entity_type.client_tracking_range; @@ -408,7 +511,6 @@ impl EntityTracker { let players = world.players.load(); tracked.update_players(players.as_ref(), world); - // If the newly added entity is a player, update all other tracked entities for this player if let Some(player) = entity.get_player() && let Some(player_arc) = world.get_player_by_uuid(player.gameprofile.id) { @@ -435,6 +537,15 @@ impl EntityTracker { } pub fn update_player_position(&self, player: &Arc, world: &World) { + let pos = player.get_entity().pos.load(); + let new_pos = Vector3::new( + get_section_cord(pos.x.floor() as i32), + get_section_cord(pos.y.floor() as i32), + get_section_cord(pos.z.floor() as i32), + ); + if let Some(tracked) = self.entity_map.get(&player.get_entity().entity_id) { + tracked.last_section_pos.store(new_pos); + } for entry in &self.entity_map { if *entry.key() == player.get_entity().entity_id { let players = world.players.load(); @@ -447,6 +558,13 @@ impl EntityTracker { pub fn update_entity_position(&self, entity: &dyn EntityBase, world: &World) { if let Some(tracked) = self.entity_map.get(&entity.get_entity().entity_id) { + let pos = entity.get_entity().pos.load(); + let new_pos = Vector3::new( + get_section_cord(pos.x.floor() as i32), + get_section_cord(pos.y.floor() as i32), + get_section_cord(pos.z.floor() as i32), + ); + tracked.last_section_pos.store(new_pos); let players = world.players.load(); tracked.update_players(players.as_ref(), world); } @@ -454,8 +572,39 @@ impl EntityTracker { pub fn update_all(&self, world: &World) { let players = world.players.load(); + let mut moved_players = Vec::new(); + for entry in &self.entity_map { - entry.value().update_players(players.as_ref(), world); + let tracked = entry.value(); + let pos = tracked.entity.get_entity().pos.load(); + let new_pos = Vector3::new( + get_section_cord(pos.x.floor() as i32), + get_section_cord(pos.y.floor() as i32), + get_section_cord(pos.z.floor() as i32), + ); + let old_pos = tracked.last_section_pos.load(); + if old_pos != new_pos { + tracked.update_players(players.as_ref(), world); + if let Some(player) = tracked.entity.get_player() + && let Some(player_arc) = world.get_player_by_uuid(player.gameprofile.id) + { + moved_players.push(player_arc); + } + tracked.last_section_pos.store(new_pos); + } + } + + if !moved_players.is_empty() { + for entry in &self.entity_map { + entry.value().update_players(&moved_players, world); + } + } + + for entry in &self.entity_map { + let tracked = entry.value(); + if tracked.entity.get_entity().synched_data.is_dirty() { + tracked.entity.get_entity().send_dirty_entity_data(); + } } } } diff --git a/crates/pumpkin/src/world/mod.rs b/crates/pumpkin/src/world/mod.rs index 65560bf19..9ccaa327f 100644 --- a/crates/pumpkin/src/world/mod.rs +++ b/crates/pumpkin/src/world/mod.rs @@ -419,6 +419,9 @@ impl World { s.advanced_config.networking.java.simulation_distance.get() }) as i32; for player in self.players.load().iter() { + if player.is_spectator() { + continue; + } let center = player.get_entity().chunk_pos.load(); for dx in -sim_dist..=sim_dist { for dy in -sim_dist..=sim_dist { diff --git a/crates/pumpkin/src/world/natural_spawner.rs b/crates/pumpkin/src/world/natural_spawner.rs index 4c1f87284..d87f956e1 100644 --- a/crates/pumpkin/src/world/natural_spawner.rs +++ b/crates/pumpkin/src/world/natural_spawner.rs @@ -3,8 +3,11 @@ use crate::entity::EntityBase; use crate::entity::r#type::{check_spawn_rules, from_type}; use crate::world::World; use arc_swap::ArcSwap; +use crossbeam::atomic::AtomicCell; +use dashmap::DashMap; use pumpkin_data::biome::Spawner; use pumpkin_data::chunk::Biome; +use pumpkin_data::dimension::Dimension; use pumpkin_data::entity::{EntityType, MobCategory, SpawnLocation}; use pumpkin_data::tag::Block::MINECRAFT_PREVENT_MOB_SPAWNING_INSIDE; use pumpkin_data::tag::Fluid::{MINECRAFT_LAVA, MINECRAFT_WATER}; @@ -24,13 +27,11 @@ use rand::seq::IndexedRandom; use rand::{RngExt, rng}; use std::fmt; use std::sync::Arc; +use std::sync::atomic::{AtomicI32, Ordering::Relaxed}; use uuid::Uuid; const MAGIC_NUMBER: i32 = 17 * 17; -use dashmap::DashMap; -use std::sync::atomic::{AtomicI32, Ordering::Relaxed}; - pub struct MobCounts([AtomicI32; 8]); impl Default for MobCounts { @@ -65,6 +66,7 @@ impl MobCounts { pub fn remove(&self, category: &'static MobCategory) { self.0[category.id].fetch_sub(1, Relaxed); } + #[inline] pub fn can_spawn(&self, category: &'static MobCategory) -> bool { self.0[category.id].load(Relaxed) < category.max @@ -103,7 +105,7 @@ impl Default for LocalMobCapCalculator { } impl fmt::Debug for LocalMobCapCalculator { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("LocalMobCapCalculator") .field("world", &"") .finish() @@ -127,7 +129,7 @@ impl LocalMobCapCalculator { if player.gamemode.load() == GameMode::Spectator { continue; } - if Self::calc_distance(chunk_pos, &player.position()) < 16384. { + if Self::calc_distance(chunk_pos, &player.position()) < 16384.0 { players.push(player.entity_id()); } } @@ -180,12 +182,19 @@ impl LocalMobCapCalculator { } #[derive(Debug, Clone)] -struct PointCharge(Vector3, f64); +struct PointCharge(BlockPos, f64); impl PointCharge { fn get_potential_change(&self, pos: &BlockPos) -> f64 { - let dst = self.0.sub(&pos.to_f64()).length(); - self.1 / dst + let dx = self.0.0.x - pos.0.x; + let dy = self.0.0.y - pos.0.y; + let dz = self.0.0.z - pos.0.z; + let dist_sq = (dx * dx + dy * dy + dz * dz) as f64; + if dist_sq == 0.0 { + f64::INFINITY + } else { + self.1 / dist_sq.sqrt() + } } } @@ -205,31 +214,31 @@ impl Clone for PotentialCalculator { impl PotentialCalculator { pub fn add_charge(&self, pos: &BlockPos, charge: f64) { - if charge != 0. { + if charge != 0.0 { self.0 .lock() .unwrap_or_else(std::sync::PoisonError::into_inner) - .push(PointCharge(pos.to_f64(), charge)); + .push(PointCharge(*pos, charge)); } } pub fn remove_charge(&self, pos: &BlockPos, charge: f64) { - if charge != 0. { + if charge != 0.0 { let mut charges = self .0 .lock() .unwrap_or_else(std::sync::PoisonError::into_inner); - let pos_f64 = pos.to_f64(); - if let Some(idx) = charges.iter().position(|c| c.0 == pos_f64 && c.1 == charge) { + if let Some(idx) = charges.iter().position(|c| c.0 == *pos && c.1 == charge) { charges.swap_remove(idx); } } } + pub fn get_potential_energy_change(&self, pos: &BlockPos, charge: f64) -> f64 { - if charge == 0. { - return 0.; + if charge == 0.0 { + return 0.0; } - let mut sum: f64 = 0.; + let mut sum: f64 = 0.0; let charges = self .0 .lock() @@ -241,14 +250,11 @@ impl PotentialCalculator { } } -use crossbeam::atomic::AtomicCell; - pub struct SpawnState { spawnable_chunk_count: i32, pub mob_category_counts: MobCounts, spawn_potential: PotentialCalculator, local_mob_cap_calculator: LocalMobCapCalculator, - // unmodifiable_mob_category_counts: MobCounts, seems only for debug last_checked: AtomicCell>, } @@ -265,7 +271,7 @@ impl Clone for SpawnState { } impl fmt::Debug for SpawnState { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("SpawnState") .field("spawnable_chunk_count", &self.spawnable_chunk_count) .field("mob_category_counts", &self.mob_category_counts) @@ -293,9 +299,15 @@ impl SpawnState { } pub fn add_entity(&self, world: &World, entity: &dyn EntityBase) { + if let Some(mob) = entity.get_mob() + && (mob.get_mob_entity().persistence_required.load(Relaxed) + || mob.requires_custom_persistence()) + { + return; + } let base_entity = entity.get_entity(); let entity_type = base_entity.entity_type; - if !entity_type.mob || entity_type.category == &MobCategory::MISC { + if entity_type.category == &MobCategory::MISC { return; } let entity_pos = base_entity.block_pos.load(); @@ -303,20 +315,26 @@ impl SpawnState { if let Some(cost) = biome.spawn_costs.get(entity_type.resource_name) { self.spawn_potential.add_charge(&entity_pos, cost.charge); } - if entity_type.mob { + if entity.get_mob().is_some() || entity_type.mob { self.local_mob_cap_calculator.add_mob( base_entity.chunk_pos.load(), world, entity_type.category, ); - self.mob_category_counts.add(entity_type.category); } + self.mob_category_counts.add(entity_type.category); } pub fn remove_entity(&self, world: &World, entity: &dyn EntityBase) { + if let Some(mob) = entity.get_mob() + && (mob.get_mob_entity().persistence_required.load(Relaxed) + || mob.requires_custom_persistence()) + { + return; + } let base_entity = entity.get_entity(); let entity_type = base_entity.entity_type; - if !entity_type.mob || entity_type.category == &MobCategory::MISC { + if entity_type.category == &MobCategory::MISC { return; } let entity_pos = base_entity.block_pos.load(); @@ -324,14 +342,14 @@ impl SpawnState { if let Some(cost) = biome.spawn_costs.get(entity_type.resource_name) { self.spawn_potential.remove_charge(&entity_pos, cost.charge); } - if entity_type.mob { + if entity.get_mob().is_some() || entity_type.mob { self.local_mob_cap_calculator.remove_mob( base_entity.chunk_pos.load(), world, entity_type.category, ); - self.mob_category_counts.remove(entity_type.category); } + self.mob_category_counts.remove(entity_type.category); } pub fn new( @@ -344,22 +362,27 @@ impl SpawnState { let counter = MobCounts::default(); let active_chunks = world.active_chunks.load(); for entity in entities.load().iter() { - let entity = entity.get_entity(); - let entity_type = entity.entity_type; - if !entity_type.mob || entity_type.category == &MobCategory::MISC { - // TODO (mob.isPersistenceRequired() || mob.requiresCustomPersistence()) + if let Some(mob) = entity.get_mob() + && (mob.get_mob_entity().persistence_required.load(Relaxed) + || mob.requires_custom_persistence()) + { continue; } - let chunk_pos = entity.chunk_pos.load(); + let base_entity = entity.get_entity(); + let entity_type = base_entity.entity_type; + if entity_type.category == &MobCategory::MISC { + continue; + } + let chunk_pos = base_entity.chunk_pos.load(); if !active_chunks.contains(&chunk_pos) { continue; } - let entity_pos = entity.block_pos.load(); - let biome = entity.current_biome.load(); + let entity_pos = base_entity.block_pos.load(); + let biome = base_entity.current_biome.load(); if let Some(cost) = biome.spawn_costs.get(entity_type.resource_name) { potential.add_charge(&entity_pos, cost.charge); } - if entity_type.mob { + if entity.get_mob().is_some() || entity_type.mob { local_mob_cap.add_mob(chunk_pos, world, entity_type.category); } counter.add(entity_type.category); @@ -372,11 +395,13 @@ impl SpawnState { last_checked: AtomicCell::new(None), } } + #[inline] pub fn can_spawn_for_category_global(&self, category: &'static MobCategory) -> bool { self.mob_category_counts.0[category.id].load(Relaxed) < category.max * self.spawnable_chunk_count / MAGIC_NUMBER } + pub fn can_spawn_for_category_local( &self, world: &Arc, @@ -386,20 +411,20 @@ impl SpawnState { self.local_mob_cap_calculator .can_spawn(category, world, chunk_pos) } + pub fn can_spawn( &self, entity_type: &'static EntityType, pos: &BlockPos, world: &Arc, ) -> bool { - // TODO get biome let biome = world.level.get_rough_biome(pos); biome .spawn_costs .get(entity_type.resource_name) .map_or_else( || { - self.last_checked.store(Some((*pos, entity_type, 0.))); + self.last_checked.store(Some((*pos, entity_type, 0.0))); true }, |cost| { @@ -411,6 +436,7 @@ impl SpawnState { }, ) } + pub fn after_spawn( &self, entity_type: &'static EntityType, @@ -427,12 +453,11 @@ impl SpawnState { }; let charge = charge.unwrap_or_else(|| { - // TODO get biome let biome = world.level.get_rough_biome(pos); biome .spawn_costs .get(entity_type.resource_name) - .map_or(0., |cost| cost.charge) + .map_or(0.0, |cost| cost.charge) }); self.spawn_potential.add_charge(pos, charge); @@ -483,7 +508,6 @@ pub fn spawn_for_chunk( spawn_list: &Vec<&'static MobCategory>, is_thundering: bool, ) -> Vec> { - // debug!("spawn for chunk {:?}", chunk_pos); let mut entities = Vec::new(); for category in spawn_list { if spawn_state.can_spawn_for_category_local(world, category, chunk_pos) { @@ -502,6 +526,7 @@ pub fn spawn_for_chunk( } entities } + pub fn get_random_pos_within( min_y: i32, chunk_pos: &Vector2, @@ -531,7 +556,7 @@ pub fn spawn_mobs_for_chunk_generation( let mob_settings = &biome.spawners; let creatures = &mob_settings.creature; - if creatures.is_empty() { + if creatures.is_empty() || !world.level_info.load().game_rules.spawn_mobs { return; } @@ -544,7 +569,7 @@ pub fn spawn_mobs_for_chunk_generation( }; let count = spawner_data.min_count - + rand::random_range(0..(1 + spawner_data.max_count - spawner_data.min_count).max(1)); + + rand::random_range(0..=(spawner_data.max_count - spawner_data.min_count).max(0)); let name = spawner_data .r#type .strip_prefix("minecraft:") @@ -561,7 +586,6 @@ pub fn spawn_mobs_for_chunk_generation( for _ in 0..count { let mut success = false; - // Try 4 times to find a valid spot in the immediate area for _ in 0..4 { if success { break; @@ -569,29 +593,35 @@ pub fn spawn_mobs_for_chunk_generation( let pos = get_top_non_colliding_pos(world, cache, entity_type, x, z); - if is_spawn_position_ok_cache(cache, &pos, entity_type) { - let spawn_pos_f64 = Vector3::new( - f64::from(pos.0.x) + 0.5, - f64::from(pos.0.y), - f64::from(pos.0.z) + 0.5, - ); + if entity_type.summonable && is_spawn_position_ok_cache(cache, &pos, entity_type) { + let width = f64::from(entity_type.dimension[0]); + let fx = + (f64::from(x)).clamp(f64::from(xo) + width, f64::from(xo) + 16.0 - width); + let fz = + (f64::from(z)).clamp(f64::from(zo) + width, f64::from(zo) + 16.0 - width); + let spawn_pos_f64 = Vector3::new(fx, f64::from(pos.0.y), fz); + let check_pos = BlockPos::new(fx.floor() as i32, pos.0.y, fz.floor() as i32); - let entity = from_type(entity_type, spawn_pos_f64, world, Uuid::new_v4()); - entity - .get_entity() - .set_rotation(rand::random::() * 360., 0.); - world.spawn_entity_non_save(entity); - success = true; + if world.is_space_empty(entity_type.get_spawn_bounding_box( + fx, + f64::from(pos.0.y), + fz, + )) && check_spawn_rules(entity_type, world, &check_pos, false) + { + let entity = from_type(entity_type, spawn_pos_f64, world, Uuid::new_v4()); + entity + .get_entity() + .set_rotation(rand::random::() * 360.0, 0.0); + world.spawn_entity_non_save(entity); + success = true; + } } - // Random jitter for the next mob in the group x += rand::random_range(0..5) - rand::random_range(0..5); z += rand::random_range(0..5) - rand::random_range(0..5); - - // Keep group within the chunk bounds - if x < xo || x >= xo + 16 || z < zo || z >= zo + 16 { - x = start_x; - z = start_z; + while x < xo || x >= xo + 16 || z < zo || z >= zo + 16 { + x = start_x + rand::random_range(0..5) - rand::random_range(0..5); + z = start_z + rand::random_range(0..5) - rand::random_range(0..5); } } } @@ -613,7 +643,6 @@ pub fn get_top_non_colliding_pos( loop { y -= 1; pos_vec.y = y; - // Use UFCS to avoid the ambiguity error from earlier if GenerationCache::get_block_state(cache, &pos_vec) .to_state() .is_air() @@ -641,275 +670,6 @@ pub fn get_top_non_colliding_pos( adjust_spawn_position_cache(cache, pos, entity_type) } -pub fn spawn_category_for_position( - category: &'static MobCategory, - world: &Arc, - pos: BlockPos, - chunk_pos: &Vector2, - spawn_state: &SpawnState, - is_thundering: bool, -) -> Vec> { - let mut batch_buffer = vec![]; - let mut spawn_cluster_size = 0; - let player_positions: Vec<_> = world.players.load().iter().map(|p| p.position()).collect(); - - 'group_loop: for _ in 0..3 { - let mut new_x = pos.0.x; - let mut new_z = pos.0.z; - - let mut random_group_size = (rng().random::() * 4.).ceil() as i32; - let mut inc = 0; - let mut current_spawner = None; - - 'spawn_loop: while inc < random_group_size { - new_x += rng().random_range(0..6) - rng().random_range(0..6); - new_z += rng().random_range(0..6) - rng().random_range(0..6); - let mut new_pos = BlockPos::new(new_x, pos.0.y, new_z); - - if current_spawner.is_none() { - let Some(spawner) = get_random_spawn_mob_at(world, category, &new_pos) else { - break 'spawn_loop; - }; - current_spawner = Some(spawner); - random_group_size = rng().random_range(spawner.min_count..=spawner.max_count); - } - - let Some(spawner) = current_spawner else { - break 'spawn_loop; - }; - let name = spawner - .r#type - .strip_prefix("minecraft:") - .unwrap_or(spawner.r#type); - let Some(entity_type) = EntityType::from_name(name) else { - break 'spawn_loop; - }; - - new_pos = adjust_spawn_position(world, new_pos, entity_type); - - let spawn_pos_f64 = Vector3::new( - f64::from(new_pos.0.x) + 0.5, - f64::from(new_pos.0.y), - f64::from(new_pos.0.z) + 0.5, - ); - - let player_distance = get_nearest_player(&spawn_pos_f64, &player_positions); - if !is_right_distance_to_player_and_spawn_point(&new_pos, player_distance, chunk_pos) { - inc += 1; - continue; - } - - if !is_valid_spawn_position_for_type( - world, - &new_pos, - category, - entity_type, - player_distance, - is_thundering, - ) { - inc += 1; - continue; - } - if !spawn_state.can_spawn(entity_type, &new_pos, world) { - inc += 1; - continue; - } - - let entity = from_type(entity_type, spawn_pos_f64, world, Uuid::new_v4()); - entity - .get_entity() - .set_rotation(rng().random::() * 360., 0.); - - spawn_cluster_size += 1; - batch_buffer.push(entity); - spawn_state.after_spawn(entity_type, &new_pos, world); - if spawn_cluster_size >= entity_type.limit_per_chunk { - break 'group_loop; - } - - inc += 1; - } - } - batch_buffer -} - -#[must_use] -pub fn get_nearest_player(pos: &Vector3, player_positions: &[Vector3]) -> f64 { - let mut min_dst_sq = f64::MAX; - - for player_pos in player_positions { - let cur_dst_sq = player_pos.squared_distance_to_vec(pos); - if cur_dst_sq < min_dst_sq { - min_dst_sq = cur_dst_sq; - } - } - min_dst_sq -} - -#[must_use] -pub fn is_right_distance_to_player_and_spawn_point( - pos: &BlockPos, - distance: f64, - chunk_pos: &Vector2, -) -> bool { - if distance <= 24. * 24. { - return false; - } - // TODO getSharedSpawnPos/WorldSpawnPoint - if pos.to_centered_f64().squared_distance_to(0., 0., 0.) <= 24. * 24. { - return false; - } - #[expect(clippy::nonminimal_bool)] - { - chunk_pos == &Vector2::new(get_section_cord(pos.0.x), get_section_cord(pos.0.z)) || false // TODO canSpawnEntitiesInChunk(ChunkPos chunkPos) - } -} - -#[must_use] -pub fn get_random_spawn_mob_at( - world: &Arc, - category: &'static MobCategory, - block_pos: &BlockPos, -) -> Option<&'static Spawner> { - // TODO Holder holder = level.getBiome(pos); - let biome = world.level.get_rough_biome(block_pos); - if category == &MobCategory::WATER_AMBIENT - && biome.has_tag(&MINECRAFT_REDUCE_WATER_AMBIENT_SPAWNS) - && rng().random::() < 0.98f32 - { - None - } else { - // TODO isInNetherFortressBounds(pos, level, cetagory, structureManager) then NetherFortressStructure.FORTRESS_ENEMIES - // TODO structureManager.getAllStructuresAt(pos); ChunkGenerator::getMobsAt - match category.id { - id if id == MobCategory::MONSTER.id => biome.spawners.monster, - id if id == MobCategory::CREATURE.id => biome.spawners.creature, - id if id == MobCategory::AMBIENT.id => biome.spawners.ambient, - id if id == MobCategory::AXOLOTLS.id => biome.spawners.axolotls, - id if id == MobCategory::UNDERGROUND_WATER_CREATURE.id => { - biome.spawners.underground_water_creature - } - id if id == MobCategory::WATER_CREATURE.id => biome.spawners.water_creature, - id if id == MobCategory::WATER_AMBIENT.id => biome.spawners.water_ambient, - id if id == MobCategory::MISC.id => biome.spawners.misc, - _ => biome.spawners.misc, - } - .choose(&mut rng()) - } -} - -pub fn is_valid_spawn_position_for_type( - world: &Arc, - block_pos: &BlockPos, - category: &'static MobCategory, - entity_type: &'static EntityType, - distance: f64, - is_thundering: bool, -) -> bool { - // TODO !SpawnPlacements.checkSpawnRules(entityType, level, EntitySpawnReason.NATURAL, pos, level.random) - if category == &MobCategory::MISC { - return false; - } - if !entity_type.can_spawn_far_from_player - && distance - > f64::from(entity_type.category.despawn_distance) - * f64::from(entity_type.category.despawn_distance) - { - return false; - } - if !entity_type.summonable { - return false; - } - if !is_spawn_position_ok(world, block_pos, entity_type) { - return false; - } - if !check_spawn_rules(entity_type, world, block_pos, is_thundering) { - return false; - } - if !world.is_space_empty(entity_type.get_spawn_bounding_box( - f64::from(block_pos.0.x) + 0.5, - f64::from(block_pos.0.y), - f64::from(block_pos.0.z) + 0.5, - )) { - return false; - } - true -} - -pub fn is_spawn_position_ok( - world: &Arc, - block_pos: &BlockPos, - entity_type: &'static EntityType, -) -> bool { - match entity_type.spawn_restriction.location { - SpawnLocation::InLava => world.get_fluid(block_pos).has_tag(&MINECRAFT_LAVA), - SpawnLocation::InWater => { - // TODO !level.getBlockState(blockPos).isRedstoneConductor(level, blockPos) - let above_state = world.get_block_state(&block_pos.up()); - world.get_fluid(block_pos).has_tag(&MINECRAFT_WATER) && !above_state.is_full_cube() - } - SpawnLocation::OnGround => { - let down = world.get_block_state(&block_pos.down()); - let up = world.get_block_state(&block_pos.up()); - let cur = world.get_block_state(block_pos); - // TODO: blockState.allowsSpawning - let is_valid_spawn_below = - down.is_side_solid(BlockDirection::Up) && down.luminance < 14; - - if is_valid_spawn_below { - is_valid_empty_spawn_block(cur) && is_valid_empty_spawn_block(up) - } else { - false - } - } - SpawnLocation::Unrestricted => true, - } -} - -/// Cache-based version of `is_spawn_position_ok` used during world generation. -pub fn is_spawn_position_ok_cache( - cache: &dyn GenerationCache, - block_pos: &BlockPos, - entity_type: &'static EntityType, -) -> bool { - let pos_vec = block_pos.0; - let state = GenerationCache::get_block_state(cache, &pos_vec).to_state(); - - match entity_type.spawn_restriction.location { - SpawnLocation::InLava => { - // During generation, we check the block state's liquid property and tag - state.is_liquid() && Block::from_state_id(state.id).has_tag(&MINECRAFT_LAVA) - } - SpawnLocation::InWater => { - let above_pos = block_pos.up().0; - let above_state = GenerationCache::get_block_state(cache, &above_pos).to_state(); - - state.is_liquid() - && Block::from_state_id(state.id).has_tag(&MINECRAFT_WATER) - && !above_state.is_full_cube() - } - SpawnLocation::OnGround => { - let down_pos = block_pos.down().0; - let up_pos = block_pos.up().0; - - let down = GenerationCache::get_block_state(cache, &down_pos).to_state(); - let up = GenerationCache::get_block_state(cache, &up_pos).to_state(); - - // Logic: solid surface below and low enough light level (if applicable in generation) - let is_valid_spawn_below = - down.is_side_solid(BlockDirection::Up) && down.luminance < 14; - - if is_valid_spawn_below { - is_valid_empty_spawn_block(state) && is_valid_empty_spawn_block(up) - } else { - false - } - } - SpawnLocation::Unrestricted => true, - } -} - -/// Cache-based version of `adjust_spawn_position` used during world generation. pub fn adjust_spawn_position_cache( cache: &dyn GenerationCache, pos: BlockPos, @@ -950,22 +710,395 @@ pub fn adjust_spawn_position( pos } +#[allow(clippy::too_many_lines)] +pub fn spawn_category_for_position( + category: &'static MobCategory, + world: &Arc, + pos: BlockPos, + chunk_pos: &Vector2, + spawn_state: &SpawnState, + is_thundering: bool, +) -> Vec> { + let mut batch_buffer = Vec::new(); + let y_start = pos.0.y; + let state = world.get_block_state(&pos); + if state.is_solid_block() || state.is_full_cube() { + return batch_buffer; + } + + let mut cluster_size = 0; + + for _ in 0..3 { + let mut x = pos.0.x; + let mut z = pos.0.z; + let mut current_spawner: Option<&'static Spawner> = None; + let mut max = (rng().random::() * 4.0).ceil() as i32; + let mut group_size = 0; + let mut ll = 0; + + while ll < max { + x += rng().random_range(0..6) - rng().random_range(0..6); + z += rng().random_range(0..6) - rng().random_range(0..6); + let check_pos = BlockPos::new(x, y_start, z); + let xx = f64::from(x) + 0.5; + let zz = f64::from(z) + 0.5; + + let nearest_player_distance_sq = { + let players = world.players.load(); + let mut min_dst_sq = f64::MAX; + let mut found = false; + for player in players.iter() { + if player.gamemode.load() != GameMode::Spectator { + let ppos = player.position(); + let dx = xx - ppos.x; + let dy = f64::from(y_start) - ppos.y; + let dz = zz - ppos.z; + let dist_sq = dx * dx + dy * dy + dz * dz; + if dist_sq < min_dst_sq { + min_dst_sq = dist_sq; + found = true; + } + } + } + found.then_some(min_dst_sq) + }; + + if let Some(player_distance_sq) = nearest_player_distance_sq + && is_right_distance_to_player_and_spawn_point( + world, + &check_pos, + chunk_pos, + player_distance_sq, + ) + { + if current_spawner.is_none() { + let Some(spawner) = get_random_spawn_mob_at(world, category, &check_pos) else { + break; + }; + current_spawner = Some(spawner); + max = spawner.min_count + + rng().random_range(0..=(spawner.max_count - spawner.min_count).max(0)); + } + + let Some(spawner) = current_spawner else { + break; + }; + let name = spawner + .r#type + .strip_prefix("minecraft:") + .unwrap_or(spawner.r#type); + let Some(entity_type) = EntityType::from_name(name) else { + break; + }; + + if is_valid_spawn_position_for_type( + world, + &check_pos, + category, + entity_type, + spawner.r#type, + player_distance_sq, + is_thundering, + ) && spawn_state.can_spawn(entity_type, &check_pos, world) + { + let spawn_pos_f64 = Vector3::new(xx, f64::from(y_start), zz); + let entity = from_type(entity_type, spawn_pos_f64, world, Uuid::new_v4()); + entity + .get_entity() + .set_rotation(rng().random::() * 360.0, 0.0); + + let is_valid_for_mob = entity.get_mob().is_none_or(|mob| { + let despawn_dist = f64::from(entity_type.category.despawn_distance); + !(player_distance_sq > despawn_dist * despawn_dist + && mob.remove_when_far_away(player_distance_sq)) + }); + + if is_valid_for_mob { + cluster_size += 1; + group_size += 1; + batch_buffer.push(entity); + spawn_state.after_spawn(entity_type, &check_pos, world); + if cluster_size >= entity_type.limit_per_chunk { + return batch_buffer; + } + + if entity_type.resource_name == "tropical_fish" && group_size >= 8 { + break; + } + } + } + } + + ll += 1; + } + } + batch_buffer +} + #[must_use] -pub fn is_valid_empty_spawn_block(state: &'static BlockState) -> bool { +pub fn get_nearest_player(pos: &Vector3, player_positions: &[Vector3]) -> f64 { + let mut min_dst_sq = f64::MAX; + + for player_pos in player_positions { + let cur_dst_sq = player_pos.squared_distance_to_vec(pos); + if cur_dst_sq < min_dst_sq { + min_dst_sq = cur_dst_sq; + } + } + min_dst_sq +} + +#[must_use] +pub fn is_right_distance_to_player_and_spawn_point( + world: &World, + pos: &BlockPos, + chunk_pos: &Vector2, + nearest_player_distance_sq: f64, +) -> bool { + if nearest_player_distance_sq <= 576.0 { + return false; + } + + if world.dimension == Dimension::OVERWORLD { + let level_info = world.level_info.load(); + let dx = f64::from(level_info.spawn_x - pos.0.x); + let dy = (f64::from(level_info.spawn_y) + 0.5) - f64::from(pos.0.y); + let dz = f64::from(level_info.spawn_z - pos.0.z); + if dx * dx + dy * dy + dz * dz < 576.0 { + return false; + } + } + + let chunk_x = get_section_cord(pos.0.x); + let chunk_z = get_section_cord(pos.0.z); + let target_chunk = Vector2::new(chunk_x, chunk_z); + target_chunk == *chunk_pos + || (world.active_chunks.load().contains(&target_chunk) + && world + .worldborder + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .contains_block(pos.0.x, pos.0.z)) +} + +#[must_use] +pub fn can_spawn_mob_at( + world: &World, + category: &'static MobCategory, + spawner_type: &'static str, + pos: &BlockPos, +) -> bool { + let biome = world.level.get_rough_biome(pos); + let spawners = match category.id { + id if id == MobCategory::MONSTER.id => biome.spawners.monster, + id if id == MobCategory::CREATURE.id => biome.spawners.creature, + id if id == MobCategory::AMBIENT.id => biome.spawners.ambient, + id if id == MobCategory::AXOLOTLS.id => biome.spawners.axolotls, + id if id == MobCategory::UNDERGROUND_WATER_CREATURE.id => { + biome.spawners.underground_water_creature + } + id if id == MobCategory::WATER_CREATURE.id => biome.spawners.water_creature, + id if id == MobCategory::WATER_AMBIENT.id => biome.spawners.water_ambient, + id if id == MobCategory::MISC.id => biome.spawners.misc, + _ => biome.spawners.misc, + }; + let target = spawner_type + .strip_prefix("minecraft:") + .unwrap_or(spawner_type); + spawners.iter().any(|s| { + let name = s.r#type.strip_prefix("minecraft:").unwrap_or(s.r#type); + name == target + }) +} + +#[must_use] +pub fn get_random_spawn_mob_at( + world: &Arc, + category: &'static MobCategory, + block_pos: &BlockPos, +) -> Option<&'static Spawner> { + let biome = world.level.get_rough_biome(block_pos); + if category == &MobCategory::WATER_AMBIENT + && biome.has_tag(&MINECRAFT_REDUCE_WATER_AMBIENT_SPAWNS) + && rng().random::() < 0.98f32 + { + None + } else { + match category.id { + id if id == MobCategory::MONSTER.id => biome.spawners.monster, + id if id == MobCategory::CREATURE.id => biome.spawners.creature, + id if id == MobCategory::AMBIENT.id => biome.spawners.ambient, + id if id == MobCategory::AXOLOTLS.id => biome.spawners.axolotls, + id if id == MobCategory::UNDERGROUND_WATER_CREATURE.id => { + biome.spawners.underground_water_creature + } + id if id == MobCategory::WATER_CREATURE.id => biome.spawners.water_creature, + id if id == MobCategory::WATER_AMBIENT.id => biome.spawners.water_ambient, + id if id == MobCategory::MISC.id => biome.spawners.misc, + _ => biome.spawners.misc, + } + .choose(&mut rng()) + } +} + +#[must_use] +pub fn is_valid_spawn_position_for_type( + world: &Arc, + block_pos: &BlockPos, + category: &'static MobCategory, + entity_type: &'static EntityType, + spawner_type: &'static str, + distance: f64, + is_thundering: bool, +) -> bool { + if category == &MobCategory::MISC || entity_type.category == &MobCategory::MISC { + return false; + } + if !entity_type.can_spawn_far_from_player + && distance + > f64::from(entity_type.category.despawn_distance) + * f64::from(entity_type.category.despawn_distance) + { + return false; + } + if !entity_type.summonable || !can_spawn_mob_at(world, category, spawner_type, block_pos) { + return false; + } + if !is_spawn_position_ok(world, block_pos, entity_type) { + return false; + } + if !check_spawn_rules(entity_type, world, block_pos, is_thundering) { + return false; + } + if !world.is_space_empty(entity_type.get_spawn_bounding_box( + f64::from(block_pos.0.x) + 0.5, + f64::from(block_pos.0.y), + f64::from(block_pos.0.z) + 0.5, + )) { + return false; + } + true +} + +#[must_use] +pub fn is_spawn_position_ok( + world: &Arc, + block_pos: &BlockPos, + entity_type: &'static EntityType, +) -> bool { + let within_bounds = world + .worldborder + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) + .contains_block(block_pos.0.x, block_pos.0.z); + + match entity_type.spawn_restriction.location { + SpawnLocation::InLava => { + within_bounds && world.get_fluid(block_pos).has_tag(&MINECRAFT_LAVA) + } + SpawnLocation::InWater => { + if !within_bounds { + return false; + } + let above_state = world.get_block_state(&block_pos.up()); + world.get_fluid(block_pos).has_tag(&MINECRAFT_WATER) && !above_state.is_full_cube() + } + SpawnLocation::OnGround => { + if !within_bounds { + return false; + } + let down = world.get_block_state(&block_pos.down()); + let up = world.get_block_state(&block_pos.up()); + let cur = world.get_block_state(block_pos); + let is_valid_spawn_below = + down.is_side_solid(BlockDirection::Up) && down.luminance < 14; + + if is_valid_spawn_below { + is_valid_empty_spawn_block(cur, entity_type) + && is_valid_empty_spawn_block(up, entity_type) + } else { + false + } + } + SpawnLocation::Unrestricted => true, + } +} + +#[must_use] +pub fn is_spawn_position_ok_cache( + cache: &dyn GenerationCache, + block_pos: &BlockPos, + entity_type: &'static EntityType, +) -> bool { + let pos_vec = block_pos.0; + let state = GenerationCache::get_block_state(cache, &pos_vec).to_state(); + + match entity_type.spawn_restriction.location { + SpawnLocation::InLava => { + state.is_liquid() && Block::from_state_id(state.id).has_tag(&MINECRAFT_LAVA) + } + SpawnLocation::InWater => { + let above_pos = block_pos.up().0; + let above_state = GenerationCache::get_block_state(cache, &above_pos).to_state(); + + state.is_liquid() + && Block::from_state_id(state.id).has_tag(&MINECRAFT_WATER) + && !above_state.is_full_cube() + } + SpawnLocation::OnGround => { + let down_pos = block_pos.down().0; + let up_pos = block_pos.up().0; + + let down = GenerationCache::get_block_state(cache, &down_pos).to_state(); + let up = GenerationCache::get_block_state(cache, &up_pos).to_state(); + + let is_valid_spawn_below = + down.is_side_solid(BlockDirection::Up) && down.luminance < 14; + + if is_valid_spawn_below { + is_valid_empty_spawn_block(state, entity_type) + && is_valid_empty_spawn_block(up, entity_type) + } else { + false + } + } + SpawnLocation::Unrestricted => true, + } +} + +#[must_use] +pub fn is_valid_empty_spawn_block( + state: &'static BlockState, + entity_type: &'static EntityType, +) -> bool { if state.is_full_cube() { return false; } - // if state.is_signal_source() { - // return false; - // } if state.is_liquid() { return false; } if Block::from_state_id(state.id).has_tag(&MINECRAFT_PREVENT_MOB_SPAWNING_INSIDE) { return false; } - - // TODO: !entityType.isBlockDangerous(blockState) - // (e.g., preventing spawns inside Sweet Berry Bushes, Wither Roses, or Fire) - true + !is_block_dangerous(entity_type, state) +} + +fn is_block_dangerous(entity_type: &'static EntityType, state: &'static BlockState) -> bool { + let block = Block::from_state_id(state.id); + if !entity_type.fire_immune && is_burning_block(block) { + return true; + } + block.id == Block::WITHER_ROSE.id + || block.id == Block::SWEET_BERRY_BUSH.id + || block.id == Block::CACTUS.id + || block.id == Block::POWDER_SNOW.id +} + +fn is_burning_block(block: &Block) -> bool { + block.has_tag(&pumpkin_data::tag::Block::MINECRAFT_FIRE) + || block.id == Block::LAVA.id + || block.id == Block::MAGMA_BLOCK.id + || block.id == Block::LAVA_CAULDRON.id + || block.id == Block::CAMPFIRE.id + || block.id == Block::SOUL_CAMPFIRE.id }