chore: made tnt explosion bit faster

This commit is contained in:
Alexander Medvedev 2026-06-26 21:43:00 +02:00
parent 39596c1b9f
commit 322b7af6ae
33 changed files with 382 additions and 260 deletions

View File

@ -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 {

View File

@ -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<HorizontalFacing>;
}
impl FacingExt for Facing {
@ -274,6 +280,15 @@ impl FacingExt for Facing {
Self::Down => BlockDirection::Down,
}
}
fn to_horizontal_facing(&self) -> Option<HorizontalFacing> {
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 {

View File

@ -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 {

View File

@ -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,

View File

@ -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<T: GenerationCache>(
chunk: &T,
pos: &BlockPos,
ignore: Option<BlockDirection>,
ignore: Option<HorizontalFacing>,
) -> bool {
for dir in BlockDirection::horizontal() {
if Some(dir) == ignore {
@ -139,12 +140,9 @@ fn grow_tree_recursive<T: GenerationCache>(
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();

View File

@ -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;
}

View File

@ -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)
}

View File

@ -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 {

View File

@ -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<BlockPos>,
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<BlockPos> {

View File

@ -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<TreeNode>,
logs: &mut Vec<BlockPos>,
current_pos: BlockPos,
branch_dir: BlockDirection,
branch_dir: HorizontalFacing,
branch_pos: i32,
mut branch_steps: i32,
) {

View File

@ -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<Block> {
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<Block> {
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())
}

View File

@ -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<World>, 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;
}
}

View File

@ -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)

View File

@ -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,
}
}

View File

@ -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`.

View File

@ -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,
}
}

View File

@ -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,
}
}

View File

@ -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;
}
}

View File

@ -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)
}

View File

@ -154,12 +154,9 @@ impl RedstoneGateBlock<ComparatorLikeProperties> 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::<ComparatorBlockEntity>()
{
let comparator = blockentity
.as_any()
.downcast_ref::<ComparatorBlockEntity>()
.unwrap();
return comparator.output_signal.load(Ordering::Relaxed);
}
0
@ -178,24 +175,25 @@ impl RedstoneGateBlock<ComparatorLikeProperties> 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<ComparatorLikeProperties> for ComparatorBlock {
self, world, pos, state.id, block, false,
)
.await;
if i > j {
true
} else {
@ -244,7 +243,6 @@ impl RedstoneGateBlock<ComparatorLikeProperties> 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<u8>
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<ComparatorLikeProperties> 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<ComparatorLikeProperties> 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<World>, 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::<ComparatorBlockEntity>()
{
let comparator = blockentity
.as_any()
.downcast_ref::<ComparatorBlockEntity>()
.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;
}

View File

@ -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<dyn Inventory>);
@ -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::<DropperBlockEntity>()
.unwrap();
// Safely extract the dropper instead of unwrap()
let Some(dropper) = block_entity.as_any().downcast_ref::<DropperBlockEntity>()
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::<f64>().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,
);
}
}
})

View File

@ -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;
}
}

View File

@ -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(),
}
}

View File

@ -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)
{

View File

@ -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;
}
}

View File

@ -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),
),
);
}

View File

@ -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<BlockPos>,
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<Self>, 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;

View File

@ -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
));

View File

@ -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<i64>,
/// The last time we sent a keep alive packet.
pub last_keep_alive_time: AtomicCell<Instant>,
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

View File

@ -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,
);
}

View File

@ -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<BlockPos, (&'static Block, &'static BlockState)> {
// Somethings are not vanilla here but make it way faster
let mut map = FxHashMap::default();
let random_val = rand::random::<f32>();
let mut chunk_cache: FxHashMap<
pumpkin_util::math::vector2::Vector2<i32>,
Option<Arc<ChunkData>>,
> = 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::<f32>();
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;
}
}

View File

@ -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);

View File

@ -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()