diff --git a/pumpkin-codegen/src/block.rs b/pumpkin-codegen/src/block.rs index b0b10081a..33292bc2e 100644 --- a/pumpkin-codegen/src/block.rs +++ b/pumpkin-codegen/src/block.rs @@ -1367,7 +1367,22 @@ pub fn build() -> TokenStream { } .into() } - + #[must_use] + pub const fn to_axis(&self) -> HorizontalAxis { + match self { + Self::North | Self::South => HorizontalAxis::Z, + Self::West | Self::East => HorizontalAxis::X, + } + } + #[must_use] + pub const fn to_facing(&self) -> HorizontalFacing { + match self { + Self::North => HorizontalFacing::North, + Self::South => HorizontalFacing::South, + Self::West => HorizontalFacing::West, + Self::East => HorizontalFacing::East, + } + } #[must_use] pub const fn opposite(&self) -> Self { match self { diff --git a/pumpkin-data/src/block_direction.rs b/pumpkin-data/src/block_direction.rs index f5cb653a3..f6a98028e 100644 --- a/pumpkin-data/src/block_direction.rs +++ b/pumpkin-data/src/block_direction.rs @@ -84,7 +84,7 @@ impl BlockDirection { Self::all()[random.next_bounded_i32(Self::all().len() as i32 - 1) as usize] } - pub fn random_horizontal(random: &mut RandomGenerator) -> Self { + pub fn random_horizontal(random: &mut RandomGenerator) -> HorizontalFacing { Self::horizontal()[random.next_bounded_i32(Self::horizontal().len() as i32 - 1) as usize] } @@ -159,8 +159,13 @@ impl BlockDirection { } #[must_use] - pub const fn horizontal() -> [Self; 4] { - [Self::North, Self::South, Self::West, Self::East] + pub const fn horizontal() -> [HorizontalFacing; 4] { + [ + HorizontalFacing::North, + HorizontalFacing::South, + HorizontalFacing::West, + HorizontalFacing::East, + ] } #[must_use] @@ -261,6 +266,7 @@ impl BlockDirection { pub trait FacingExt { fn to_block_direction(&self) -> BlockDirection; + fn to_horizontal_facing(&self) -> Option; } impl FacingExt for Facing { @@ -274,6 +280,15 @@ impl FacingExt for Facing { Self::Down => BlockDirection::Down, } } + fn to_horizontal_facing(&self) -> Option { + match self { + Self::North => Some(HorizontalFacing::North), + Self::South => Some(HorizontalFacing::South), + Self::West => Some(HorizontalFacing::West), + Self::East => Some(HorizontalFacing::East), + _ => None, + } + } } pub trait HorizontalFacingExt { diff --git a/pumpkin-data/src/block_state.rs b/pumpkin-data/src/block_state.rs index a88dd9352..f4bafc1a1 100644 --- a/pumpkin-data/src/block_state.rs +++ b/pumpkin-data/src/block_state.rs @@ -107,7 +107,7 @@ impl BlockState { self.state_flags & HAS_RANDOM_TICKS != 0 } - ///`isSideSolidFullSquare()` in Java! + ///`isFaceSturdy()` in Java! #[must_use] pub const fn is_side_solid(&self, side: BlockDirection) -> bool { match side { diff --git a/pumpkin-data/src/generated/block.rs b/pumpkin-data/src/generated/block.rs index 0662c5f46..4a6ea41a6 100644 --- a/pumpkin-data/src/generated/block.rs +++ b/pumpkin-data/src/generated/block.rs @@ -560796,6 +560796,22 @@ impl HorizontalFacing { .into() } #[must_use] + pub const fn to_axis(&self) -> HorizontalAxis { + match self { + Self::North | Self::South => HorizontalAxis::Z, + Self::West | Self::East => HorizontalAxis::X, + } + } + #[must_use] + pub const fn to_facing(&self) -> HorizontalFacing { + match self { + Self::North => HorizontalFacing::North, + Self::South => HorizontalFacing::South, + Self::West => HorizontalFacing::West, + Self::East => HorizontalFacing::East, + } + } + #[must_use] pub const fn opposite(&self) -> Self { match self { Self::North => Self::South, diff --git a/pumpkin-world/src/generation/feature/features/chorus_plant.rs b/pumpkin-world/src/generation/feature/features/chorus_plant.rs index 0a582609f..1ae4adb3b 100644 --- a/pumpkin-world/src/generation/feature/features/chorus_plant.rs +++ b/pumpkin-world/src/generation/feature/features/chorus_plant.rs @@ -2,6 +2,7 @@ use pumpkin_data::{ Block, BlockDirection, block_properties::{ BlockProperties, BrownMushroomBlockLikeProperties, ChorusFlowerLikeProperties, + HorizontalFacing, }, tag, }; @@ -51,7 +52,7 @@ impl ChorusPlantFeature { fn all_neighbors_empty( chunk: &T, pos: &BlockPos, - ignore: Option, + ignore: Option, ) -> bool { for dir in BlockDirection::horizontal() { if Some(dir) == ignore { @@ -139,12 +140,9 @@ fn grow_tree_recursive( stems += 1; } - // The 4 horizontal directions - const HORIZ: [BlockDirection; 4] = BlockDirection::horizontal(); - for _ in 0..stems { // Pick a random horizontal direction - let dir = HORIZ[random.next_bounded_i32(4) as usize]; + let dir = BlockDirection::horizontal()[random.next_bounded_i32(4) as usize]; let target = top.offset(dir.to_offset()); let target_below = target.down(); diff --git a/pumpkin-world/src/generation/feature/features/coral/coral_claw.rs b/pumpkin-world/src/generation/feature/features/coral/coral_claw.rs index 5726bfd0b..ab397fb63 100644 --- a/pumpkin-world/src/generation/feature/features/coral/coral_claw.rs +++ b/pumpkin-world/src/generation/feature/features/coral/coral_claw.rs @@ -44,7 +44,7 @@ impl CoralClawFeature { k = random.next_bounded_i32(3) + 2; } else { pos = pos.up(); - let _directions = [direction2, BlockDirection::Up]; + //let _directions = [direction2, BlockDirection::Up]; direction3 = direction2; // TODO: make this random k = random.next_bounded_i32(3) + 5; } diff --git a/pumpkin-world/src/generation/feature/features/coral/mod.rs b/pumpkin-world/src/generation/feature/features/coral/mod.rs index 6ebcf9fd6..35a4e7158 100644 --- a/pumpkin-world/src/generation/feature/features/coral/mod.rs +++ b/pumpkin-world/src/generation/feature/features/coral/mod.rs @@ -68,13 +68,12 @@ impl CoralFeature { .properties(wall_coral.default_state.id) .unwrap() .to_props(); - let facing = dir.to_facing(); // Set the right Axis let props: Vec<(&str, &str)> = original_props .iter() .map(|(key, value)| { if *key == "facing" { - (*key, facing.to_value()) + (*key, dir.to_value()) } else { (*key, *value) } diff --git a/pumpkin-world/src/generation/feature/features/monster_room.rs b/pumpkin-world/src/generation/feature/features/monster_room.rs index 28dd9c866..bbc09349b 100644 --- a/pumpkin-world/src/generation/feature/features/monster_room.rs +++ b/pumpkin-world/src/generation/feature/features/monster_room.rs @@ -148,7 +148,7 @@ impl DungeonFeature { BlockProperties, ChestLikeProperties, }; let mut props = ChestLikeProperties::default(&Block::CHEST); - props.facing = dir.opposite().to_cardinal_direction(); + props.facing = dir.opposite(); let state_id = props.to_state_id(&Block::CHEST); pumpkin_data::BlockState::from_id(state_id) } else { diff --git a/pumpkin-world/src/generation/feature/features/tree/root/mangrove.rs b/pumpkin-world/src/generation/feature/features/tree/root/mangrove.rs index d42d1ee04..ce448fa5d 100644 --- a/pumpkin-world/src/generation/feature/features/tree/root/mangrove.rs +++ b/pumpkin-world/src/generation/feature/features/tree/root/mangrove.rs @@ -1,4 +1,4 @@ -use pumpkin_data::{BlockDirection, BlockState, fluid::Fluid}; +use pumpkin_data::{BlockDirection, BlockState, block_properties::HorizontalFacing, fluid::Fluid}; use pumpkin_util::{ math::{int_provider::IntProvider, position::BlockPos}, random::{RandomGenerator, RandomImpl}, @@ -78,7 +78,7 @@ impl MangroveRootPlacer { chunk: &mut T, random: &mut RandomGenerator, pos: BlockPos, - direction: BlockDirection, + direction: HorizontalFacing, origin: BlockPos, offshoots: &mut Vec, root_length: i32, @@ -109,7 +109,7 @@ impl MangroveRootPlacer { fn get_offshoot_positions( &self, pos: BlockPos, - direction: BlockDirection, + direction: HorizontalFacing, random: &mut RandomGenerator, origin: BlockPos, ) -> Vec { diff --git a/pumpkin-world/src/generation/feature/features/tree/trunk/upwards_branching.rs b/pumpkin-world/src/generation/feature/features/tree/trunk/upwards_branching.rs index 75a3dc112..096054970 100644 --- a/pumpkin-world/src/generation/feature/features/tree/trunk/upwards_branching.rs +++ b/pumpkin-world/src/generation/feature/features/tree/trunk/upwards_branching.rs @@ -1,4 +1,5 @@ use pumpkin_data::BlockState; +use pumpkin_data::block_properties::HorizontalFacing; use pumpkin_util::math::int_provider::IntProvider; use pumpkin_util::math::position::BlockPos; use pumpkin_util::random::{RandomGenerator, RandomImpl}; @@ -85,7 +86,7 @@ impl UpwardsBranchingTrunkPlacer { nodes: &mut Vec, logs: &mut Vec, current_pos: BlockPos, - branch_dir: BlockDirection, + branch_dir: HorizontalFacing, branch_pos: i32, mut branch_steps: i32, ) { diff --git a/pumpkin/src/block/blocks/coral/coral_fan.rs b/pumpkin/src/block/blocks/coral/coral_fan.rs index a0766f9f8..07ef99abb 100644 --- a/pumpkin/src/block/blocks/coral/coral_fan.rs +++ b/pumpkin/src/block/blocks/coral/coral_fan.rs @@ -9,7 +9,8 @@ use crate::{ use pumpkin_data::{ Block, BlockDirection, FacingExt, HorizontalFacingExt, block_properties::{ - BlockProperties, Facing, LadderLikeProperties, MangroveRootsLikeProperties, + BlockProperties, Facing, HorizontalFacing, LadderLikeProperties, + MangroveRootsLikeProperties, }, tag::{self, Taggable}, }; @@ -18,7 +19,9 @@ use pumpkin_world::{ BlockStateId, world::{BlockAccessor, BlockFlags}, }; + pub struct CoralFanBlock; + impl BlockMetadata for CoralFanBlock { fn ids() -> Box<[u16]> { let alive_wall_fans = tag::Block::MINECRAFT_WALL_CORALS.1; @@ -81,18 +84,18 @@ impl BlockBehaviour for CoralFanBlock { for dir in directions { if dir != Facing::Up && dir != Facing::Down - && can_place_at(args.world, args.position, dir.to_block_direction()) + && can_place_at( + args.world, + args.position, + dir.to_horizontal_facing().unwrap(), + ) { let Some(wall_block) = get_corresponding_wall_fan_type(args.block) else { return 0; }; let mut coral_wall_fan_props = CoralWallFanLikeProperties::default(&wall_block); coral_wall_fan_props.waterlogged = args.replacing.water_source(); - coral_wall_fan_props.facing = dir - .opposite() - .to_block_direction() - .to_horizontal_facing() - .unwrap(); + coral_wall_fan_props.facing = dir.opposite().to_horizontal_facing().unwrap(); return coral_wall_fan_props.to_state_id(&wall_block); } } @@ -104,6 +107,7 @@ impl BlockBehaviour for CoralFanBlock { 0 }) } + fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> { Box::pin(async move { if !scan_for_water(args.world, args.position).await { @@ -111,24 +115,32 @@ impl BlockBehaviour for CoralFanBlock { } }) } + fn on_scheduled_tick<'a>(&'a self, args: OnScheduledTickArgs<'a>) -> BlockFuture<'a, ()> { Box::pin(async move { if !scan_for_water(args.world, args.position).await && !is_dead_coral(args.block) { let current_state = args.world.get_block_state(args.position); + + // VANILLA FIX: Explicitly set waterlogged to false when dying let dead_block_state_id = if is_wall_fan(args.block) { - let props = + let mut props = CoralWallFanLikeProperties::from_state_id(current_state.id, args.block); + props.waterlogged = false; props.to_state_id(&get_dead_type(args.block)) } else { - let props = CoralFanLikeProperties::from_state_id(current_state.id, args.block); + let mut props = + CoralFanLikeProperties::from_state_id(current_state.id, args.block); + props.waterlogged = false; props.to_state_id(&get_dead_type(args.block)) }; + args.world .set_block_state(args.position, dead_block_state_id, BlockFlags::empty()) .await; } }) } + fn can_place_at<'a>(&'a self, args: CanPlaceAtArgs<'a>) -> bool { let support_block = args.block_accessor.get_block_state(&args.position.down()); if support_block.is_center_solid(BlockDirection::Up) && !is_wall_fan(args.block) { @@ -150,11 +162,7 @@ impl BlockBehaviour for CoralFanBlock { if is_wall_fan(args.block) { let props = CoralWallFanLikeProperties::from_state_id(args.state_id, args.block); if props.facing.to_block_direction().opposite() == args.direction - && !can_place_at( - args.world, - args.position, - props.facing.to_block_direction().opposite(), - ) + && !can_place_at(args.world, args.position, props.facing.opposite()) { return 0; } @@ -164,15 +172,18 @@ impl BlockBehaviour for CoralFanBlock { return 0; } } + args.state_id }) } } + fn get_default_coral_fan_state_id(block: &Block, waterlogged: bool) -> BlockStateId { let mut props = CoralFanLikeProperties::default(block); props.waterlogged = waterlogged; props.to_state_id(block) } + fn is_wall_fan(block: &Block) -> bool { block.has_tag(&tag::Block::MINECRAFT_WALL_CORALS) || block == &Block::DEAD_BRAIN_CORAL_WALL_FAN @@ -181,6 +192,7 @@ fn is_wall_fan(block: &Block) -> bool { || block == &Block::DEAD_HORN_CORAL_WALL_FAN || block == &Block::DEAD_TUBE_CORAL_WALL_FAN } + fn get_dead_type(block: &Block) -> Block { if block == &Block::BRAIN_CORAL_FAN { Block::DEAD_BRAIN_CORAL_FAN @@ -204,6 +216,7 @@ fn get_dead_type(block: &Block) -> Block { Block::DEAD_TUBE_CORAL_WALL_FAN } } + fn get_corresponding_wall_fan_type(block: &Block) -> Option { if block == &Block::TUBE_CORAL_FAN { Some(Block::TUBE_CORAL_WALL_FAN) @@ -229,8 +242,9 @@ fn get_corresponding_wall_fan_type(block: &Block) -> Option { None } } -fn can_place_at(world: &dyn BlockAccessor, block_pos: &BlockPos, facing: BlockDirection) -> bool { + +fn can_place_at(world: &dyn BlockAccessor, block_pos: &BlockPos, facing: HorizontalFacing) -> bool { world .get_block_state(&block_pos.offset(facing.to_offset())) - .is_side_solid(facing.opposite()) + .is_side_solid(facing.opposite().to_block_direction()) } diff --git a/pumpkin/src/block/blocks/coral/mod.rs b/pumpkin/src/block/blocks/coral/mod.rs index 93e11978e..a4658e662 100644 --- a/pumpkin/src/block/blocks/coral/mod.rs +++ b/pumpkin/src/block/blocks/coral/mod.rs @@ -2,6 +2,7 @@ use std::sync::Arc; use pumpkin_data::Block; use pumpkin_data::BlockDirection; +use pumpkin_data::tag::Taggable; use pumpkin_util::math::position::BlockPos; use rand::RngExt; @@ -14,8 +15,8 @@ pub mod coral_plant; pub async fn scan_for_water(world: &Arc, pos: &BlockPos) -> bool { for direction in BlockDirection::all() { let neighbor_pos = pos.offset(direction.to_offset()); - let block = world.get_block(&neighbor_pos); - if block == &Block::WATER { + let block = world.get_fluid(&neighbor_pos); + if block.has_tag(&pumpkin_data::tag::Fluid::MINECRAFT_WATER) { return true; } } diff --git a/pumpkin/src/block/blocks/doors.rs b/pumpkin/src/block/blocks/doors.rs index fe5ff40be..f24031592 100644 --- a/pumpkin/src/block/blocks/doors.rs +++ b/pumpkin/src/block/blocks/doors.rs @@ -1,6 +1,5 @@ use crate::entity::EntityBase; use pumpkin_data::BlockDirection; -use pumpkin_data::HorizontalFacingExt; use pumpkin_data::block_properties::Axis; use pumpkin_data::block_properties::BlockProperties; use pumpkin_data::block_properties::DoorHinge; @@ -112,14 +111,14 @@ async fn get_hinge( ) -> DoorHinge { let top_pos = pos.up(); let left_dir = facing.rotate_counter_clockwise(); - let left_pos = pos.offset(left_dir.to_block_direction().to_offset()); + let left_pos = pos.offset(left_dir.to_offset()); let (left_block, left_state) = world.get_block_and_state(&left_pos); - let top_facing = top_pos.offset(facing.to_block_direction().to_offset()); + let top_facing = top_pos.offset(facing.to_offset()); let top_state = world.get_block_state(&top_facing); let right_dir = facing.rotate_clockwise(); - let right_pos = pos.offset(right_dir.to_block_direction().to_offset()); + let right_pos = pos.offset(right_dir.to_offset()); let (right_block, right_state) = world.get_block_and_state(&right_pos); - let top_right = top_pos.offset(facing.to_block_direction().to_offset()); + let top_right = top_pos.offset(facing.to_offset()); let top_right_state = world.get_block_state(&top_right); let has_left_door = world @@ -139,7 +138,7 @@ async fn get_hinge( if (!has_left_door || has_right_door) && score <= 0 { if (!has_right_door || has_left_door) && score >= 0 { - let offset = facing.to_block_direction().to_offset(); + let offset = facing.to_offset(); let hit = use_item.cursor_pos; if (offset.x >= 0 || hit.z > 0.5) && (offset.x <= 0 || hit.z < 0.5) diff --git a/pumpkin/src/block/blocks/fences.rs b/pumpkin/src/block/blocks/fences.rs index 73c360679..da738173b 100644 --- a/pumpkin/src/block/blocks/fences.rs +++ b/pumpkin/src/block/blocks/fences.rs @@ -3,7 +3,9 @@ use crate::block::GetStateForNeighborUpdateArgs; use crate::block::OnPlaceArgs; use pumpkin_data::BlockDirection; use pumpkin_data::BlockState; +use pumpkin_data::HorizontalFacingExt; use pumpkin_data::block_properties::BlockProperties; +use pumpkin_data::block_properties::HorizontalFacing; use pumpkin_data::tag::Taggable; use pumpkin_data::{Block, tag}; use pumpkin_util::math::position::BlockPos; @@ -53,13 +55,17 @@ pub fn compute_fence_state( let other_block_pos = block_pos.offset(direction.to_offset()); let (other_block, other_block_state) = world.get_block_and_state(&other_block_pos); - let connected = connects_to(block, other_block, other_block_state, direction); + let connected = connects_to( + block, + other_block, + other_block_state, + direction.to_block_direction(), + ); match direction { - BlockDirection::North => fence_props.north = connected, - BlockDirection::South => fence_props.south = connected, - BlockDirection::West => fence_props.west = connected, - BlockDirection::East => fence_props.east = connected, - _ => {} + HorizontalFacing::North => fence_props.north = connected, + HorizontalFacing::South => fence_props.south = connected, + HorizontalFacing::West => fence_props.west = connected, + HorizontalFacing::East => fence_props.east = connected, } } diff --git a/pumpkin/src/block/blocks/fire/mod.rs b/pumpkin/src/block/blocks/fire/mod.rs index cf5399b56..161256348 100644 --- a/pumpkin/src/block/blocks/fire/mod.rs +++ b/pumpkin/src/block/blocks/fire/mod.rs @@ -103,19 +103,17 @@ impl FireBlockBase { } let dir = if direction.is_horizontal() { - direction.rotate_counter_clockwise() + direction + .rotate_counter_clockwise() + .to_horizontal_axis() + .unwrap() } else { BlockDirection::random_horizontal(&mut RandomGenerator::Xoroshiro( Xoroshiro::from_seed(rand::rng().random()), )) + .to_axis() }; - NetherPortal::get_new_portal( - world, - block_pos, - dir.to_horizontal_axis() - .expect("Direction should be horizontal"), - ) - .is_some() + NetherPortal::get_new_portal(world, block_pos, dir).is_some() } /// Shared fire collision behavior used by `fire` and `soul_fire`. diff --git a/pumpkin/src/block/blocks/glass_panes.rs b/pumpkin/src/block/blocks/glass_panes.rs index 9eaeb13df..fef54d73c 100644 --- a/pumpkin/src/block/blocks/glass_panes.rs +++ b/pumpkin/src/block/blocks/glass_panes.rs @@ -2,7 +2,9 @@ use crate::block::BlockFuture; use crate::block::GetStateForNeighborUpdateArgs; use crate::block::OnPlaceArgs; use pumpkin_data::BlockDirection; +use pumpkin_data::HorizontalFacingExt; use pumpkin_data::block_properties::BlockProperties; +use pumpkin_data::block_properties::HorizontalFacing; use pumpkin_data::tag::Taggable; use pumpkin_data::{Block, tag}; use pumpkin_util::math::position::BlockPos; @@ -52,17 +54,16 @@ pub fn compute_pane_state( let (other_block, other_block_state) = world.get_block_and_state(&other_block_pos); let connected = other_block == block - || other_block_state.is_side_solid(direction.opposite()) + || other_block_state.is_side_solid(direction.opposite().to_block_direction()) || other_block.has_tag(&tag::Block::C_GLASS_PANES) || other_block == &Block::IRON_BARS || other_block.has_tag(&tag::Block::MINECRAFT_WALLS); match direction { - BlockDirection::North => pane_props.north = connected, - BlockDirection::South => pane_props.south = connected, - BlockDirection::West => pane_props.west = connected, - BlockDirection::East => pane_props.east = connected, - _ => {} + HorizontalFacing::North => pane_props.north = connected, + HorizontalFacing::South => pane_props.south = connected, + HorizontalFacing::West => pane_props.west = connected, + HorizontalFacing::East => pane_props.east = connected, } } diff --git a/pumpkin/src/block/blocks/iron_bars.rs b/pumpkin/src/block/blocks/iron_bars.rs index 6fe108f9e..4b49849b4 100644 --- a/pumpkin/src/block/blocks/iron_bars.rs +++ b/pumpkin/src/block/blocks/iron_bars.rs @@ -2,7 +2,9 @@ use crate::block::BlockFuture; use crate::block::GetStateForNeighborUpdateArgs; use crate::block::OnPlaceArgs; use pumpkin_data::BlockDirection; +use pumpkin_data::HorizontalFacingExt; use pumpkin_data::block_properties::BlockProperties; +use pumpkin_data::block_properties::HorizontalFacing; use pumpkin_data::tag::Taggable; use pumpkin_data::{Block, tag}; use pumpkin_macros::pumpkin_block; @@ -49,16 +51,15 @@ pub fn compute_bars_state( let (other_block, other_block_state) = world.get_block_and_state(&other_block_pos); let connected = other_block == block - || other_block_state.is_side_solid(direction.opposite()) + || other_block_state.is_side_solid(direction.opposite().to_block_direction()) || other_block.has_tag(&tag::Block::C_GLASS_PANES) || other_block.has_tag(&tag::Block::MINECRAFT_WALLS); match direction { - BlockDirection::North => bars_props.north = connected, - BlockDirection::South => bars_props.south = connected, - BlockDirection::West => bars_props.west = connected, - BlockDirection::East => bars_props.east = connected, - _ => {} + HorizontalFacing::North => bars_props.north = connected, + HorizontalFacing::South => bars_props.south = connected, + HorizontalFacing::West => bars_props.west = connected, + HorizontalFacing::East => bars_props.east = connected, } } diff --git a/pumpkin/src/block/blocks/ladder.rs b/pumpkin/src/block/blocks/ladder.rs index 0c68ab6e3..cce32bf8b 100644 --- a/pumpkin/src/block/blocks/ladder.rs +++ b/pumpkin/src/block/blocks/ladder.rs @@ -45,7 +45,6 @@ impl BlockBehaviour for LadderBlock { } props.facing = dir .opposite() - .to_block_direction() .to_horizontal_facing() .expect("Opposite of horizontal direction should be horizontal"); return props.to_state_id(args.block); @@ -59,7 +58,7 @@ impl BlockBehaviour for LadderBlock { //this won't happen return false; }; - if can_place_ladder_at(world, args.position, dir) { + if can_place_ladder_at(world, args.position, dir.to_block_direction()) { return true; } } diff --git a/pumpkin/src/block/blocks/plant/crop/gourds/stem.rs b/pumpkin/src/block/blocks/plant/crop/gourds/stem.rs index 0bc54db92..08596fbce 100644 --- a/pumpkin/src/block/blocks/plant/crop/gourds/stem.rs +++ b/pumpkin/src/block/blocks/plant/crop/gourds/stem.rs @@ -5,9 +5,10 @@ use crate::block::{ }; use pumpkin_data::{ Block, BlockDirection, - block_properties::{BlockProperties, WallTorchLikeProperties, WheatLikeProperties}, - tag, - tag::Taggable, + block_properties::{ + BlockProperties, HorizontalFacing, WallTorchLikeProperties, WheatLikeProperties, + }, + tag::{self, Taggable}, }; use pumpkin_util::{ math::position::BlockPos, @@ -37,14 +38,14 @@ impl StemBlock { props.to_state_id(block) } - fn get_attached_stem(dir: BlockDirection, block: &Block) -> BlockStateId { + fn get_attached_stem(dir: HorizontalFacing, block: &Block) -> BlockStateId { let attached_block = match block.id { id if id == Block::PUMPKIN_STEM.id => &Block::ATTACHED_PUMPKIN_STEM, id if id == Block::MELON_STEM.id => &Block::ATTACHED_MELON_STEM, _ => &Block::ATTACHED_MELON_STEM, // Should never happen }; let mut props = AttachedStemProperties::default(attached_block); - props.facing = dir.to_horizontal_facing().unwrap(); + props.facing = dir; props.to_state_id(attached_block) } diff --git a/pumpkin/src/block/blocks/redstone/comparator.rs b/pumpkin/src/block/blocks/redstone/comparator.rs index 7138c394d..bc6181123 100644 --- a/pumpkin/src/block/blocks/redstone/comparator.rs +++ b/pumpkin/src/block/blocks/redstone/comparator.rs @@ -154,12 +154,9 @@ impl RedstoneGateBlock for ComparatorBlock { fn get_output_level<'a>(&'a self, world: &'a World, pos: BlockPos) -> BlockFuture<'a, u8> { Box::pin(async move { if let Some(blockentity) = world.get_block_entity(&pos) - && blockentity.resource_location() == ComparatorBlockEntity::ID + && let Some(comparator) = + blockentity.as_any().downcast_ref::() { - let comparator = blockentity - .as_any() - .downcast_ref::() - .unwrap(); return comparator.output_signal.load(Ordering::Relaxed); } 0 @@ -178,24 +175,25 @@ impl RedstoneGateBlock for ComparatorBlock { return; } let i = self.calculate_output_signal(world, pos, state, block).await; - let j = RedstoneGateBlock::get_output_level(self, world, pos).await; - let props = ComparatorLikeProperties::from_state_id(state.id, block); if i != j || props.powered != RedstoneGateBlock::has_power(self, world, pos, state, block).await { - world.schedule_block_tick( - block, - pos, - RedstoneGateBlock::get_update_delay_internal(self, state.id, block), + let priority = if RedstoneGateBlock::is_target_not_aligned(self, world, pos, state, block) { TickPriority::High } else { TickPriority::Normal - }, + }; + + world.schedule_block_tick( + block, + pos, + RedstoneGateBlock::get_update_delay_internal(self, state.id, block), + priority, ); } }) @@ -217,6 +215,7 @@ impl RedstoneGateBlock for ComparatorBlock { self, world, pos, state.id, block, false, ) .await; + if i > j { true } else { @@ -244,7 +243,6 @@ impl RedstoneGateBlock for ComparatorBlock { let source_pos = pos.offset(facing.to_offset()); let (source_block, source_state) = world.get_block_and_state(&source_pos); - // Note: .get_comparator_output is assumed to be an async method returning Option if let Some(pumpkin_block) = world.block_registry.get_pumpkin_block(source_block.id) && let Some(level) = pumpkin_block .get_comparator_output(GetComparatorOutputArgs { @@ -259,25 +257,28 @@ impl RedstoneGateBlock for ComparatorBlock { } if redstone_level < 15 && source_state.is_solid_block() { - let source_pos = source_pos.offset(facing.to_offset()); - let (source_block, source_state) = world.get_block_and_state(&source_pos); + let deeper_source_pos = source_pos.offset(facing.to_offset()); + let (deeper_block, deeper_state) = world.get_block_and_state(&deeper_source_pos); - // Note: self.get_attached_itemframe_level is assumed to be an async method - let itemframe_level = Self::get_attached_itemframe_level(world, facing, source_pos); + let itemframe_level = + Self::get_attached_itemframe_level(world, facing, deeper_source_pos); + + // This is the correct way to handle the async call within the Option let block_level = if let Some(pumpkin_block) = - world.block_registry.get_pumpkin_block(source_block.id) + world.block_registry.get_pumpkin_block(deeper_block.id) { pumpkin_block .get_comparator_output(GetComparatorOutputArgs { world, - block: source_block, - state: source_state, - position: &source_pos, + block: deeper_block, + state: deeper_state, + position: &deeper_source_pos, }) .await } else { None }; + if let Some(level) = itemframe_level.max(block_level) { return level; } @@ -287,7 +288,7 @@ impl RedstoneGateBlock for ComparatorBlock { } fn get_update_delay_internal(&self, _state_id: BlockStateId, _block: &Block) -> u8 { - 2 + 2 // Vanilla Delay } } @@ -299,10 +300,15 @@ impl ComparatorBlock { block_pos: BlockPos, block: &Block, ) { + // Vanilla Parity TODO: + // playSound(player, pos, SoundEvents.COMPARATOR_CLICK, SoundSource.BLOCKS, 0.3F, pitch); + // Pitch is 0.55F if SUBTRACT, 0.5F if COMPARE. + props.mode = match props.mode { ModeComparator::Compare => ModeComparator::Subtract, ModeComparator::Subtract => ModeComparator::Compare, }; + let state_id = props.to_state_id(block); world .set_block_state(&block_pos, state_id, BlockFlags::empty()) @@ -320,12 +326,18 @@ impl ComparatorBlock { block: &Block, ) -> u8 { let power = self.get_power(world, pos, state, block).await; + if power == 0 { + return 0; + } + let sub_power = self .get_max_input_level_sides(world, pos, state.id, block, false) .await; - if sub_power >= power { + + if sub_power > power { return 0; } + let props = ComparatorLikeProperties::from_state_id(state.id, block); if props.mode == ModeComparator::Subtract { power - sub_power @@ -346,10 +358,10 @@ impl ComparatorBlock { entity.get_entity().entity_type == &EntityType::ITEM_FRAME && entity.get_entity().get_horizontal_facing() == facing }); - if let Some(_itemframe) = itemframes.next() - && itemframes.next().is_none() - { - // TODO itemframe.getComparatorPower() + + if itemframes.next().is_some() && itemframes.next().is_none() { + // Vanilla Parity TODO: + // Return itemframe.getAnalogOutput() which calculates based on item rotation return Some(1); } None @@ -358,22 +370,21 @@ impl ComparatorBlock { async fn update(&self, world: &Arc, pos: BlockPos, state: &BlockState, block: &Block) { let future_level = i32::from(self.calculate_output_signal(world, pos, state, block).await); let mut now_level = 0; + if let Some(blockentity) = world.get_block_entity(&pos) - && blockentity.resource_location() == ComparatorBlockEntity::ID + && let Some(comparator) = blockentity.as_any().downcast_ref::() { - let comparator = blockentity - .as_any() - .downcast_ref::() - .unwrap(); now_level = i32::from(comparator.output_signal.load(Ordering::Relaxed)); comparator .output_signal .store(future_level as u8, Ordering::Relaxed); } + let mut props = ComparatorLikeProperties::from_state_id(state.id, block); if now_level != future_level || props.mode == ModeComparator::Compare { let future_power = self.has_power(world, pos, state, block).await; let now_power = props.powered; + if now_power && !future_power { props.powered = false; world @@ -385,6 +396,7 @@ impl ComparatorBlock { .set_block_state(&pos, props.to_state_id(block), BlockFlags::NOTIFY_LISTENERS) .await; } + RedstoneGateBlock::update_target(self, world, pos, props.to_state_id(block), block) .await; } diff --git a/pumpkin/src/block/blocks/redstone/dropper.rs b/pumpkin/src/block/blocks/redstone/dropper.rs index 6f5e9f04d..6e1027cfe 100644 --- a/pumpkin/src/block/blocks/redstone/dropper.rs +++ b/pumpkin/src/block/blocks/redstone/dropper.rs @@ -1,12 +1,15 @@ +use rand::{Rng, RngExt, rng}; +use std::sync::Arc; +use tokio::sync::Mutex; + use crate::block::blocks::redstone::block_receives_redstone_power; use crate::block::registry::BlockActionResult; use crate::block::{ BlockBehaviour, BlockFuture, NormalUseArgs, OnNeighborUpdateArgs, OnPlaceArgs, OnScheduledTickArgs, PlacedArgs, }; -use crate::entity::Entity; -use crate::entity::EntityBase; use crate::entity::item::ItemEntity; +use crate::entity::{Entity, EntityBase}; use crate::block::entities::dropper::DropperBlockEntity; use crate::block::entities::hopper::HopperBlockEntity; @@ -26,9 +29,6 @@ use pumpkin_world::BlockStateId; use pumpkin_world::inventory::Inventory; use pumpkin_world::tick::TickPriority; use pumpkin_world::world::BlockFlags; -use rand::{Rng, RngExt, rng}; -use std::sync::Arc; -use tokio::sync::Mutex; struct DropperScreenFactory(Arc); @@ -120,10 +120,12 @@ impl BlockBehaviour for DropperBlock { Box::pin(async move { let powered = block_receives_redstone_power(args.world, args.position).await || block_receives_redstone_power(args.world, &args.position.up()).await; + let mut props = DispenserLikeProperties::from_state_id( args.world.get_block_state(args.position).id, args.block, ); + if powered && !props.triggered { args.world .schedule_block_tick(args.block, *args.position, 4, TickPriority::Normal); @@ -151,76 +153,79 @@ impl BlockBehaviour for DropperBlock { fn on_scheduled_tick<'a>(&'a self, args: OnScheduledTickArgs<'a>) -> BlockFuture<'a, ()> { Box::pin(async move { if let Some(block_entity) = args.world.get_block_entity(args.position) { - let dropper = block_entity - .as_any() - .downcast_ref::() - .unwrap(); + // Safely extract the dropper instead of unwrap() + let Some(dropper) = block_entity.as_any().downcast_ref::() + else { + return; + }; + if let Some(mut item) = dropper.get_random_slot().await { let props = DispenserLikeProperties::from_state_id( args.world.get_block_state(args.position).id, args.block, ); - if let Some(entity) = args.world.get_block_entity( - &args - .position - .offset(props.facing.to_block_direction().to_offset()), - ) && let Some(container) = entity.get_inventory() + + let target_pos = args + .position + .offset(props.facing.to_block_direction().to_offset()); + + if let Some(entity) = args.world.get_block_entity(&target_pos) + && let Some(container) = entity.get_inventory() { - // TODO check WorldlyContainer - let mut is_full = true; - for i in 0..container.size() { - let bind = container.get_stack(i).await; - let item = bind.lock().await; - if item.item_count < item.get_max_stack_size() { - is_full = false; - break; - } - } - if is_full { - return; - } - //TODO WorldlyContainer let backup = item.clone(); let one_item = item.split(1); + if HopperBlockEntity::add_one_item(dropper, container.as_ref(), one_item) .await { return; } + *item = backup; return; } + + // No container found, dispense item into the world let drop_item = item.split(1); let facing = to_normal(props.facing); let mut position = args.position.to_centered_f64().add(&(facing * 0.7)); + position.y -= match props.facing { Facing::Up | Facing::Down => 0.125, _ => 0.15625, }; + let entity = Entity::new(args.world.clone(), position, &EntityType::ITEM); let rd = rng().random::().mul_add(0.1, 0.2); + let velocity = Vector3::new( triangle(&mut rng(), facing.x * rd, 0.017_227_5 * 6.), triangle(&mut rng(), 0.2, 0.017_227_5 * 6.), triangle(&mut rng(), facing.z * rd, 0.017_227_5 * 6.), ); + let item_entity = Arc::new(ItemEntity::new_with_velocity( entity, drop_item, velocity, 40, )); args.world.spawn_entity(item_entity).await; + args.world.sync_world_event( WorldEvent::SoundDispenserDispense, *args.position, 0, ); + args.world.sync_world_event( - WorldEvent::SoundDispenserProjectileLaunch, + WorldEvent::ParticlesShootSmoke, *args.position, to_data3d(props.facing), ); } else { - args.world - .sync_world_event(WorldEvent::SoundDispenserFail, *args.position, 0); + args.world.sync_world_event( + WorldEvent::SoundDispenserDispense, + *args.position, + 0, + ); } } }) diff --git a/pumpkin/src/block/blocks/redstone/redstone_torch.rs b/pumpkin/src/block/blocks/redstone/redstone_torch.rs index f13d59872..7f3cb5668 100644 --- a/pumpkin/src/block/blocks/redstone/redstone_torch.rs +++ b/pumpkin/src/block/blocks/redstone/redstone_torch.rs @@ -79,11 +79,7 @@ impl BlockBehaviour for RedstoneTorchBlock { && can_place_at(world, location, dir.to_block_direction()) { let mut torch_props = RWallTorchProps::default(&Block::REDSTONE_WALL_TORCH); - torch_props.facing = dir - .opposite() - .to_block_direction() - .to_horizontal_facing() - .unwrap(); + torch_props.facing = dir.opposite().to_horizontal_facing().unwrap(); return torch_props.to_state_id(&Block::REDSTONE_WALL_TORCH); } } @@ -103,7 +99,7 @@ impl BlockBehaviour for RedstoneTorchBlock { return true; } for dir in BlockDirection::horizontal() { - if can_place_at(args.block_accessor, args.position, dir) { + if can_place_at(args.block_accessor, args.position, dir.to_block_direction()) { return true; } } diff --git a/pumpkin/src/block/blocks/redstone/redstone_wire.rs b/pumpkin/src/block/blocks/redstone/redstone_wire.rs index 7e9f42293..022c36271 100644 --- a/pumpkin/src/block/blocks/redstone/redstone_wire.rs +++ b/pumpkin/src/block/blocks/redstone/redstone_wire.rs @@ -1,7 +1,7 @@ use std::sync::Arc; use pumpkin_data::block_properties::{ - BlockProperties, EastRedstone, NorthRedstone, ObserverLikeProperties, + BlockProperties, EastRedstone, HorizontalFacing, NorthRedstone, ObserverLikeProperties, RedstoneWireLikeProperties, RepeaterLikeProperties, SouthRedstone, WestRedstone, }; use pumpkin_data::{Block, BlockDirection, BlockState, HorizontalFacingExt}; @@ -116,7 +116,7 @@ impl BlockBehaviour for RedstoneWireBlock { args.world .replace_with_state_for_neighbor_update( &up_block_pos, - direction.opposite(), + direction.opposite().to_block_direction(), args.flags, ) .await; @@ -128,7 +128,7 @@ impl BlockBehaviour for RedstoneWireBlock { args.world .replace_with_state_for_neighbor_update( &down_block_pos, - direction.opposite(), + direction.opposite().to_block_direction(), args.flags, ) .await; @@ -201,7 +201,7 @@ impl BlockBehaviour for RedstoneWireBlock { Box::pin(async move { let wire = RedstoneWireProperties::from_state_id(args.state.id, args.block); if args.direction == BlockDirection::Up - || wire.is_side_connected(args.direction.opposite()) + || wire.is_side_connected(args.direction.opposite().to_horizontal_facing().unwrap()) { wire.power } else { @@ -217,7 +217,7 @@ impl BlockBehaviour for RedstoneWireBlock { Box::pin(async move { let wire = RedstoneWireProperties::from_state_id(args.state.id, args.block); if args.direction == BlockDirection::Up - || wire.is_side_connected(args.direction.opposite()) + || wire.is_side_connected(args.direction.opposite().to_horizontal_facing().unwrap()) { wire.power } else { @@ -398,18 +398,17 @@ pub async fn get_regulated_sides( } trait RedstoneWireLikePropertiesExt { - fn is_side_connected(&self, direction: BlockDirection) -> bool; + fn is_side_connected(&self, direction: HorizontalFacing) -> bool; //fn get_connection_type(&self, direction: BlockDirection) -> WireConnection; } impl RedstoneWireLikePropertiesExt for RedstoneWireLikeProperties { - fn is_side_connected(&self, direction: BlockDirection) -> bool { + fn is_side_connected(&self, direction: HorizontalFacing) -> bool { match direction { - BlockDirection::North => self.north.to_wire_connection().is_connected(), - BlockDirection::South => self.south.to_wire_connection().is_connected(), - BlockDirection::East => self.east.to_wire_connection().is_connected(), - BlockDirection::West => self.west.to_wire_connection().is_connected(), - _ => false, + HorizontalFacing::North => self.north.to_wire_connection().is_connected(), + HorizontalFacing::South => self.south.to_wire_connection().is_connected(), + HorizontalFacing::East => self.east.to_wire_connection().is_connected(), + HorizontalFacing::West => self.west.to_wire_connection().is_connected(), } } diff --git a/pumpkin/src/block/blocks/signs.rs b/pumpkin/src/block/blocks/signs.rs index 2320748b5..801b5505a 100644 --- a/pumpkin/src/block/blocks/signs.rs +++ b/pumpkin/src/block/blocks/signs.rs @@ -4,6 +4,7 @@ use std::sync::atomic::Ordering; use crate::block::entities::sign::SignBlockEntity; use pumpkin_data::Block; use pumpkin_data::BlockDirection; +use pumpkin_data::HorizontalFacingExt; use pumpkin_data::block_properties::EnumVariants; use pumpkin_data::tag::Taggable; use pumpkin_inventory::screen_handler::InventoryPlayer; @@ -74,7 +75,7 @@ impl SignBlock { let mut side_direction = None; for direction in BlockDirection::horizontal() { let pos = position.offset(direction.to_offset()); - if Self::is_valid_support(world, &pos, direction.opposite()) { + if Self::is_valid_support(world, &pos, direction.opposite().to_block_direction()) { side_direction = Some(direction); break; } @@ -82,7 +83,7 @@ impl SignBlock { SupportInfo { above_is_valid, - side_direction, + side_direction: side_direction.map(|d| d.to_block_direction()), } } @@ -334,7 +335,7 @@ impl BlockBehaviour for SignBlock { for d in pumpkin_data::BlockDirection::horizontal() { let wall_pos = args.position.offset(d.to_offset()); let (block, state) = args.block_accessor.get_block_and_state(&wall_pos); - if state.is_side_solid(d.opposite()) + if state.is_side_solid(d.opposite().to_block_direction()) || block.has_tag(&pumpkin_data::tag::Block::MINECRAFT_LEAVES) || block.has_tag(&pumpkin_data::tag::Block::MINECRAFT_SIGNS) { diff --git a/pumpkin/src/block/blocks/torches.rs b/pumpkin/src/block/blocks/torches.rs index 303956e68..6bf5daccc 100644 --- a/pumpkin/src/block/blocks/torches.rs +++ b/pumpkin/src/block/blocks/torches.rs @@ -74,11 +74,7 @@ impl BlockBehaviour for TorchBlock { } }; let mut torch_props = WallTorchProps::default(&wall_block); - torch_props.facing = dir - .opposite() - .to_block_direction() - .to_horizontal_facing() - .unwrap(); + torch_props.facing = dir.opposite().to_horizontal_facing().unwrap(); return torch_props.to_state_id(&wall_block); } } @@ -98,7 +94,7 @@ impl BlockBehaviour for TorchBlock { return true; } for dir in BlockDirection::horizontal() { - if can_place_at(args.block_accessor, args.position, dir) { + if can_place_at(args.block_accessor, args.position, dir.to_block_direction()) { return true; } } diff --git a/pumpkin/src/entity/living.rs b/pumpkin/src/entity/living.rs index 73a531f90..c05173abf 100644 --- a/pumpkin/src/entity/living.rs +++ b/pumpkin/src/entity/living.rs @@ -8,6 +8,7 @@ use pumpkin_inventory::player::player_inventory::PlayerInventory; use pumpkin_inventory::screen_handler::InventoryPlayer; use pumpkin_protocol::bedrock::client::take_item_actor::CTakeItemActor; use pumpkin_protocol::bedrock::server::actor_event::{ActorEventType, SActorEvent}; +use pumpkin_protocol::codec::var_ulong::VarULong; use pumpkin_util::GameMode; use pumpkin_util::Hand; use pumpkin_util::math::position::BlockPos; @@ -207,11 +208,11 @@ impl LivingEntity { &CTakeItemEntity::new( item.entity_id.into(), self.entity.entity_id.into(), - stack_amount.try_into().unwrap(), + VarInt(stack_amount as i32), ), &CTakeItemActor::new( - item.entity_id.try_into().unwrap(), - self.entity.entity_id.try_into().unwrap(), + VarULong(item.entity_id as u64), + VarULong(self.entity.entity_id as u64), ), ); } diff --git a/pumpkin/src/entity/player.rs b/pumpkin/src/entity/player.rs index 24b877dd1..c80752d95 100644 --- a/pumpkin/src/entity/player.rs +++ b/pumpkin/src/entity/player.rs @@ -63,15 +63,15 @@ use pumpkin_protocol::bedrock::client::container_open::CContainerOpen; use pumpkin_protocol::codec::var_int::VarInt; use pumpkin_protocol::codec::var_long::VarLong; use pumpkin_protocol::java::client::play::{ - Animation, CAcknowledgeBlockChange, CActionBar, CAwardStats, CChangeDifficulty, - CCloseContainer, CCombatDeath, CCustomPayload, CDisguisedChatMessage, CEntityAnimation, - CEntityPositionSync, CGameEvent, CItemCooldown, CMapItemData, COpenScreen, CParticle, - CPlayerAbilities, CPlayerInfoUpdate, CPlayerPosition, CPlayerSpawnPosition, CRespawn, - CSetCamera, CSetContainerContent, CSetContainerProperty, CSetContainerSlot, CSetCursorItem, - CSetEquipment, CSetExperience, CSetHealth, CSetPlayerInventory, CSetSelectedSlot, CSoundEffect, - CStopSound, CSubtitle, CSystemChatMessage, CTabList, CTitleAnimation, CTitleText, CUnloadChunk, - CUpdateMobEffect, CUpdateTime, GameEvent, MapIcon, MapPatch, Metadata, PlayerAction, - PlayerInfoFlags, PlayerSpawnData, PreviousMessage, Statistic, + Animation, CActionBar, CAwardStats, CChangeDifficulty, CCloseContainer, CCombatDeath, + CCustomPayload, CDisguisedChatMessage, CEntityAnimation, CEntityPositionSync, CGameEvent, + CItemCooldown, CMapItemData, COpenScreen, CParticle, CPlayerAbilities, CPlayerInfoUpdate, + CPlayerPosition, CPlayerSpawnPosition, CRespawn, CSetCamera, CSetContainerContent, + CSetContainerProperty, CSetContainerSlot, CSetCursorItem, CSetEquipment, CSetExperience, + CSetHealth, CSetPlayerInventory, CSetSelectedSlot, CSoundEffect, CStopSound, CSubtitle, + CSystemChatMessage, CTabList, CTitleAnimation, CTitleText, CUnloadChunk, CUpdateMobEffect, + CUpdateTime, GameEvent, MapIcon, MapPatch, Metadata, PlayerAction, PlayerInfoFlags, + PlayerSpawnData, PreviousMessage, Statistic, }; use pumpkin_protocol::java::server::play::{ SClickSlot, SContainerButtonClick, SRenameItem, SlotActionType, @@ -452,7 +452,6 @@ pub struct Player { pub mining: AtomicBool, pub start_mining_time: AtomicI32, pub tick_counter: AtomicI32, - pub packet_sequence: AtomicI32, pub mining_pos: Mutex, pub last_input: AtomicI8, /// A counter for teleport IDs used to track pending teleports. @@ -646,7 +645,6 @@ impl Player { open_container: AtomicCell::new(None), open_container_pos: AtomicCell::new(None), tick_counter: AtomicI32::new(0), - packet_sequence: AtomicI32::new(-1), start_mining_time: AtomicI32::new(0), last_input: AtomicI8::new(0), carried_item: Mutex::new(None), @@ -1859,7 +1857,6 @@ impl Player { .await; } - // TODO Abstract the chunk sending #[expect(clippy::too_many_lines)] pub async fn tick(self: &Arc, server: &Server) { if let Some(camera_id) = self.camera_target_id.load() { @@ -1900,13 +1897,6 @@ impl Player { // return; // } - let seq = self.packet_sequence.swap(-1, Ordering::Relaxed); - if seq != -1 { - self.client - .send_packet_now(&CAcknowledgeBlockChange::new(seq.into())) - .await; - } - // Statistics updates { let mut stats = self.stats.lock().await; diff --git a/pumpkin/src/lib.rs b/pumpkin/src/lib.rs index 253bbd09e..3abd66358 100644 --- a/pumpkin/src/lib.rs +++ b/pumpkin/src/lib.rs @@ -511,6 +511,11 @@ impl PumpkinServer { match udp_result { Ok((len, client_addr)) => { if len > 0 { + let Some(socket) = self.udp_socket.clone() else { + error!("UDP socket disappeared during receive"); + return true; + }; + let id = udp_buf[0]; let is_online = id & 128 != 0; @@ -531,7 +536,7 @@ impl PumpkinServer { *master_client_id_counter += 1; let new_client = Arc::new(BedrockClient::new( - self.udp_socket.as_ref().unwrap().clone(), + socket, client_addr, be_clients )); diff --git a/pumpkin/src/net/java/mod.rs b/pumpkin/src/net/java/mod.rs index e2ab2133a..ff6de1db3 100644 --- a/pumpkin/src/net/java/mod.rs +++ b/pumpkin/src/net/java/mod.rs @@ -1,9 +1,9 @@ use pumpkin_protocol::java::client::play::{ - CChunkBatchEnd, CChunkBatchStart, CChunkData, CPlayDisconnect, + CAcknowledgeBlockChange, CChunkBatchEnd, CChunkBatchStart, CChunkData, CPlayDisconnect, }; use pumpkin_world::level::SyncChunk; use std::net::SocketAddr; -use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::atomic::{AtomicBool, AtomicI32, Ordering}; use std::time::{Instant, SystemTime, UNIX_EPOCH}; use std::{io::Write, sync::Arc}; @@ -118,6 +118,8 @@ pub struct JavaClient { pub keep_alive_id: AtomicCell, /// The last time we sent a keep alive packet. pub last_keep_alive_time: AtomicCell, + + pub packet_sequence: AtomicI32, } pub enum OutgoingPacketType { @@ -173,6 +175,7 @@ impl JavaClient { wait_for_keep_alive: AtomicBool::new(false), keep_alive_id: AtomicCell::new(0), last_keep_alive_time: AtomicCell::new(std::time::Instant::now()), + packet_sequence: AtomicI32::new(-1), } } pub async fn set_encryption( @@ -272,6 +275,13 @@ impl JavaClient { self.last_keep_alive_time.store(Instant::now()); let packet = pumpkin_protocol::java::client::play::CKeepAlive::new(keep_alive_id); self.enqueue_packet(&packet).await; + + let seq = self.packet_sequence.swap(-1, Ordering::Relaxed); + if seq != -1 { + self + .send_packet_now(&CAcknowledgeBlockChange::new(seq.into())) + .await; + } } // INCOMING PACKETS diff --git a/pumpkin/src/net/java/play.rs b/pumpkin/src/net/java/play.rs index 1dcc6f596..83141adab 100644 --- a/pumpkin/src/net/java/play.rs +++ b/pumpkin/src/net/java/play.rs @@ -2074,12 +2074,12 @@ impl JavaClient { } } - pub fn update_sequence(&self, player: &Player, sequence: i32) { + pub fn update_sequence(&self, _player: &Player, sequence: i32) { if sequence < 0 { error!("Expected packet sequence >= 0"); } - player.packet_sequence.store( - player.packet_sequence.load(Ordering::Relaxed).max(sequence), + self.packet_sequence.store( + self.packet_sequence.load(Ordering::Relaxed).max(sequence), Ordering::Relaxed, ); } diff --git a/pumpkin/src/world/explosion.rs b/pumpkin/src/world/explosion.rs index 06ef98d9c..bf31efb4e 100644 --- a/pumpkin/src/world/explosion.rs +++ b/pumpkin/src/world/explosion.rs @@ -1,7 +1,8 @@ use std::sync::Arc; -use pumpkin_data::{Block, BlockState, damage::DamageType, entity::EntityType}; +use pumpkin_data::{Block, BlockState, damage::DamageType, entity::EntityType, fluid::Fluid}; use pumpkin_util::math::{boundingbox::BoundingBox, position::BlockPos, vector3::Vector3}; +use pumpkin_world::chunk::ChunkData; use rustc_hash::FxHashMap; use crate::{ @@ -27,51 +28,102 @@ impl Explosion { &self, world: &World, ) -> FxHashMap { - // Somethings are not vanilla here but make it way faster let mut map = FxHashMap::default(); - let random_val = rand::random::(); + + let mut chunk_cache: FxHashMap< + pumpkin_util::math::vector2::Vector2, + Option>, + > = FxHashMap::default(); + for x in 0..16 { for y in 0..16 { - 'block2: for z in 0..16 { + for z in 0..16 { if x > 0 && x < 15 && y > 0 && y < 15 && z > 0 && z < 15 { continue; } - let mut x = f64::from(x) / 7.5 - 1.0; - let mut y = f64::from(y) / 7.5 - 1.0; - let mut z = f64::from(z) / 7.5 - 1.0; + let mut dir_x = f64::from(x) / 7.5 - 1.0; + let mut dir_y = f64::from(y) / 7.5 - 1.0; + let mut dir_z = f64::from(z) / 7.5 - 1.0; - let sqrt = 1.0 / (x * x + y * y + z * z).sqrt(); - x *= sqrt; - y *= sqrt; - z *= sqrt; + let length = (dir_x * dir_x + dir_y * dir_y + dir_z * dir_z).sqrt(); + dir_x /= length; + dir_y /= length; + dir_z /= length; let mut pos_x = self.pos.x; - let mut pos_y = self.pos.y + 0.0625; + let mut pos_y = self.pos.y; let mut pos_z = self.pos.z; + let random_val = rand::random::(); let mut h = self.power * random_val.mul_add(0.6, 0.7); + while h > 0.0 { let block_pos = BlockPos::floored(pos_x, pos_y, pos_z); - let (block, state) = world.get_block_and_state(&block_pos); - let (_, fluid_state) = world.get_fluid_and_fluid_state(&block_pos); - // if !world.is_in_build_limit(&block_pos) { - // // Pass by reference - // continue 'block2; - // } + if !world.is_in_build_limit(block_pos) { + break; + } + + let (chunk_pos, relative) = block_pos.chunk_and_chunk_relative_position(); + + let chunk_opt = chunk_cache.entry(chunk_pos).or_insert_with(|| { + world + .level + .read_chunk_sync(&chunk_pos, std::clone::Clone::clone) + }); + + let state_id = if let Some(chunk) = chunk_opt { + chunk + .section + .get_block_absolute_y( + relative.x as usize, + block_pos.0.y, + relative.z as usize, + ) + .unwrap_or(Block::AIR.default_state.id) + } else { + Block::AIR.default_state.id + }; + + let (block, state) = BlockState::from_id_with_block(state_id); + + let (_fluid, fluid_state) = Fluid::from_state_id(state_id).map_or_else( + || { + let is_waterlogged = + block.properties(state_id).is_some_and(|props| { + props + .to_props() + .into_iter() + .any(|(k, v)| k == "waterlogged" && v == "true") + }); + + if is_waterlogged { + (&Fluid::FLOWING_WATER, &Fluid::FLOWING_WATER.states[0]) + } else { + (&Fluid::EMPTY, &Fluid::EMPTY.states[0]) + } + }, + |raw_fluid| { + let f = raw_fluid.to_flowing(); + (f, &f.states[0]) + }, + ); if !state.is_air() || !fluid_state.is_empty { let resistance = fluid_state.blast_resistance.max(block.blast_resistance); - h -= resistance * 0.3; + + h -= (resistance + 0.3) * 0.3; + if h > 0.0 { map.insert(block_pos, (block, state)); } } - pos_x += x * 0.3; - pos_y += y * 0.3; - pos_z += z * 0.3; + + pos_x += dir_x * 0.3; + pos_y += dir_y * 0.3; + pos_z += dir_z * 0.3; h -= 0.225_000_01; } } diff --git a/pumpkin/src/world/mod.rs b/pumpkin/src/world/mod.rs index cec7416d0..e91f13ddd 100644 --- a/pumpkin/src/world/mod.rs +++ b/pumpkin/src/world/mod.rs @@ -54,7 +54,7 @@ use border::Worldborder; use bytes::BufMut; use explosion::Explosion; use pumpkin_config::BasicConfiguration; -use pumpkin_data::block_properties::is_air; +use pumpkin_data::block_properties::{blocks_movement, is_air}; use pumpkin_data::block_rotation::{Mirror, Rotation}; use pumpkin_data::chunk_gen_settings::GenerationSettings; use pumpkin_data::data_component_impl::EquipmentSlot; @@ -73,7 +73,7 @@ use pumpkin_data::{ sound_id_remap::remap_sound_id_for_version, world::{RAW, WorldEvent}, }; -use pumpkin_data::{BlockDirection, BlockState, translation}; +use pumpkin_data::{BlockDirection, BlockState, HorizontalFacingExt, translation}; use pumpkin_inventory::crafting::recipe_provider::RecipeProvider; use pumpkin_inventory::screen_handler::InventoryPlayer; use pumpkin_nbt::{compound::NbtCompound, to_bytes_unnamed}; @@ -1324,7 +1324,7 @@ impl World { false } - // FlowableFluid.getVelocity() + // FlowingFluid.getFlow() pub fn get_fluid_velocity( &self, pos0: BlockPos, @@ -1334,66 +1334,57 @@ impl World { let mut velo = Vector3::default(); for dir in BlockDirection::horizontal() { - let mut amplitude = 0.0; - let offset = dir.to_offset(); - let pos = pos0.offset(offset); - let block_state_id = self.get_block_state_id(&pos); + let (neighbor_fluid, neighbor_state) = self.get_fluid_and_fluid_state(&pos); - let fluid = Fluid::from_state_id(block_state_id).unwrap_or(&Fluid::EMPTY); + if neighbor_fluid.matches_type(fluid0) { + let mut neighbor_height = neighbor_state.height; + let mut amplitude = 0.0; - if fluid.id == Fluid::EMPTY.id { - let block = Block::get_raw_id_from_state_id(block_state_id); - let block_state = BlockState::from_id(block_state_id); + if neighbor_height == 0.0 { + let block_state_id = self.get_block_state_id(&pos); + let block = Block::get_raw_id_from_state_id(block_state_id); + let block_state = BlockState::from_id(block_state_id); - let blocks_movement = block_state.is_solid() - && block != Block::COBWEB - && block != Block::BAMBOO_SAPLING; + let blocks_movement = blocks_movement(block_state, block); - if !blocks_movement { - let down_pos = pos.down(); + if !blocks_movement { + let down_pos = pos.down(); + let (down_fluid, down_state) = self.get_fluid_and_fluid_state(&down_pos); - let (down_fluid, down_state) = self.get_fluid_and_fluid_state(&down_pos); - - if down_fluid.matches_type(fluid0) { - amplitude = f64::from(state0.height - down_state.height) + 0.888_888_9; + if down_fluid.matches_type(fluid0) { + neighbor_height = down_state.height; + if neighbor_height > 0.0 { + amplitude = f64::from(state0.height) + - (f64::from(neighbor_height) - 0.888_888_9); + } + } } - } - } else { - if !fluid.matches_type(fluid0) { - continue; + } else if neighbor_height > 0.0 { + amplitude = f64::from(state0.height) - f64::from(neighbor_height); } - //let state = fluid.get_state(block_state_id); - amplitude = f64::from(state0.height - fluid.states[0].height); + if amplitude != 0.0 { + velo.x += f64::from(offset.x) * amplitude; + velo.z += f64::from(offset.z) * amplitude; + } } - - if amplitude == 0.0 { - continue; - } - - velo.x += f64::from(offset.x) * amplitude; - - velo.z += f64::from(offset.z) * amplitude; } - // TODO: FALLING - if state0.falling { for dir in BlockDirection::horizontal() { let pos = pos0.offset(dir.to_offset()); - if self.is_flow_blocked(fluid0.id, pos, dir) - || self.is_flow_blocked(fluid0.id, pos.up(), dir) + if self.is_solid_face(fluid0.id, pos, dir.to_block_direction()) + || self.is_solid_face(fluid0.id, pos.up(), dir.to_block_direction()) { if velo.length_squared() != 0.0 { velo = velo.normalize(); } velo.y -= 6.0; - break; } } @@ -1406,9 +1397,8 @@ impl World { } } - // FlowableFluid.isFlowBlocked() - - fn is_flow_blocked(&self, fluid0_id: u16, pos: BlockPos, direction: BlockDirection) -> bool { + // FlowingFluid.isSolidFace() + fn is_solid_face(&self, fluid0_id: u16, pos: BlockPos, direction: BlockDirection) -> bool { let id = self.get_block_state_id(&pos); let fluid = Fluid::from_state_id(id).unwrap_or(&Fluid::EMPTY); diff --git a/pumpkin/src/world/portal/end.rs b/pumpkin/src/world/portal/end.rs index e4b0e9939..2c51d9989 100644 --- a/pumpkin/src/world/portal/end.rs +++ b/pumpkin/src/world/portal/end.rs @@ -52,10 +52,9 @@ impl EndPortal { fn is_valid_portal(world: &World, pos: BlockPos) -> bool { for dir in BlockDirection::horizontal() { - let facing = dir.to_horizontal_facing().unwrap(); let mid_pos = pos.offset_dir(dir.to_offset(), 2); - let left_pos = mid_pos.offset_dir(facing.rotate_clockwise().to_offset(), 1); - let right_pos = mid_pos.offset_dir(facing.rotate_counter_clockwise().to_offset(), 1); + let left_pos = mid_pos.offset_dir(dir.rotate_clockwise().to_offset(), 1); + let right_pos = mid_pos.offset_dir(dir.rotate_counter_clockwise().to_offset(), 1); let (mid_block, mid_state) = world.get_block_and_state_id(&mid_pos); let (left_block, left_state) = world.get_block_and_state_id(&left_pos); @@ -73,6 +72,8 @@ impl EndPortal { let right_properties = EndPortalFrameProperties::from_state_id(right_state, right_block); + let facing = dir.to_facing(); + if left_properties.facing != facing.opposite() || mid_properties.facing != facing.opposite() || right_properties.facing != facing.opposite()