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() .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] #[must_use]
pub const fn opposite(&self) -> Self { pub const fn opposite(&self) -> Self {
match 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] 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] Self::horizontal()[random.next_bounded_i32(Self::horizontal().len() as i32 - 1) as usize]
} }
@ -159,8 +159,13 @@ impl BlockDirection {
} }
#[must_use] #[must_use]
pub const fn horizontal() -> [Self; 4] { pub const fn horizontal() -> [HorizontalFacing; 4] {
[Self::North, Self::South, Self::West, Self::East] [
HorizontalFacing::North,
HorizontalFacing::South,
HorizontalFacing::West,
HorizontalFacing::East,
]
} }
#[must_use] #[must_use]
@ -261,6 +266,7 @@ impl BlockDirection {
pub trait FacingExt { pub trait FacingExt {
fn to_block_direction(&self) -> BlockDirection; fn to_block_direction(&self) -> BlockDirection;
fn to_horizontal_facing(&self) -> Option<HorizontalFacing>;
} }
impl FacingExt for Facing { impl FacingExt for Facing {
@ -274,6 +280,15 @@ impl FacingExt for Facing {
Self::Down => BlockDirection::Down, 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 { pub trait HorizontalFacingExt {

View File

@ -107,7 +107,7 @@ impl BlockState {
self.state_flags & HAS_RANDOM_TICKS != 0 self.state_flags & HAS_RANDOM_TICKS != 0
} }
///`isSideSolidFullSquare()` in Java! ///`isFaceSturdy()` in Java!
#[must_use] #[must_use]
pub const fn is_side_solid(&self, side: BlockDirection) -> bool { pub const fn is_side_solid(&self, side: BlockDirection) -> bool {
match side { match side {

View File

@ -560796,6 +560796,22 @@ impl HorizontalFacing {
.into() .into()
} }
#[must_use] #[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 { pub const fn opposite(&self) -> Self {
match self { match self {
Self::North => Self::South, Self::North => Self::South,

View File

@ -2,6 +2,7 @@ use pumpkin_data::{
Block, BlockDirection, Block, BlockDirection,
block_properties::{ block_properties::{
BlockProperties, BrownMushroomBlockLikeProperties, ChorusFlowerLikeProperties, BlockProperties, BrownMushroomBlockLikeProperties, ChorusFlowerLikeProperties,
HorizontalFacing,
}, },
tag, tag,
}; };
@ -51,7 +52,7 @@ impl ChorusPlantFeature {
fn all_neighbors_empty<T: GenerationCache>( fn all_neighbors_empty<T: GenerationCache>(
chunk: &T, chunk: &T,
pos: &BlockPos, pos: &BlockPos,
ignore: Option<BlockDirection>, ignore: Option<HorizontalFacing>,
) -> bool { ) -> bool {
for dir in BlockDirection::horizontal() { for dir in BlockDirection::horizontal() {
if Some(dir) == ignore { if Some(dir) == ignore {
@ -139,12 +140,9 @@ fn grow_tree_recursive<T: GenerationCache>(
stems += 1; stems += 1;
} }
// The 4 horizontal directions
const HORIZ: [BlockDirection; 4] = BlockDirection::horizontal();
for _ in 0..stems { for _ in 0..stems {
// Pick a random horizontal direction // 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 = top.offset(dir.to_offset());
let target_below = target.down(); let target_below = target.down();

View File

@ -44,7 +44,7 @@ impl CoralClawFeature {
k = random.next_bounded_i32(3) + 2; k = random.next_bounded_i32(3) + 2;
} else { } else {
pos = pos.up(); pos = pos.up();
let _directions = [direction2, BlockDirection::Up]; //let _directions = [direction2, BlockDirection::Up];
direction3 = direction2; // TODO: make this random direction3 = direction2; // TODO: make this random
k = random.next_bounded_i32(3) + 5; k = random.next_bounded_i32(3) + 5;
} }

View File

@ -68,13 +68,12 @@ impl CoralFeature {
.properties(wall_coral.default_state.id) .properties(wall_coral.default_state.id)
.unwrap() .unwrap()
.to_props(); .to_props();
let facing = dir.to_facing();
// Set the right Axis // Set the right Axis
let props: Vec<(&str, &str)> = original_props let props: Vec<(&str, &str)> = original_props
.iter() .iter()
.map(|(key, value)| { .map(|(key, value)| {
if *key == "facing" { if *key == "facing" {
(*key, facing.to_value()) (*key, dir.to_value())
} else { } else {
(*key, *value) (*key, *value)
} }

View File

@ -148,7 +148,7 @@ impl DungeonFeature {
BlockProperties, ChestLikeProperties, BlockProperties, ChestLikeProperties,
}; };
let mut props = ChestLikeProperties::default(&Block::CHEST); 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); let state_id = props.to_state_id(&Block::CHEST);
pumpkin_data::BlockState::from_id(state_id) pumpkin_data::BlockState::from_id(state_id)
} else { } 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::{ use pumpkin_util::{
math::{int_provider::IntProvider, position::BlockPos}, math::{int_provider::IntProvider, position::BlockPos},
random::{RandomGenerator, RandomImpl}, random::{RandomGenerator, RandomImpl},
@ -78,7 +78,7 @@ impl MangroveRootPlacer {
chunk: &mut T, chunk: &mut T,
random: &mut RandomGenerator, random: &mut RandomGenerator,
pos: BlockPos, pos: BlockPos,
direction: BlockDirection, direction: HorizontalFacing,
origin: BlockPos, origin: BlockPos,
offshoots: &mut Vec<BlockPos>, offshoots: &mut Vec<BlockPos>,
root_length: i32, root_length: i32,
@ -109,7 +109,7 @@ impl MangroveRootPlacer {
fn get_offshoot_positions( fn get_offshoot_positions(
&self, &self,
pos: BlockPos, pos: BlockPos,
direction: BlockDirection, direction: HorizontalFacing,
random: &mut RandomGenerator, random: &mut RandomGenerator,
origin: BlockPos, origin: BlockPos,
) -> Vec<BlockPos> { ) -> Vec<BlockPos> {

View File

@ -1,4 +1,5 @@
use pumpkin_data::BlockState; use pumpkin_data::BlockState;
use pumpkin_data::block_properties::HorizontalFacing;
use pumpkin_util::math::int_provider::IntProvider; use pumpkin_util::math::int_provider::IntProvider;
use pumpkin_util::math::position::BlockPos; use pumpkin_util::math::position::BlockPos;
use pumpkin_util::random::{RandomGenerator, RandomImpl}; use pumpkin_util::random::{RandomGenerator, RandomImpl};
@ -85,7 +86,7 @@ impl UpwardsBranchingTrunkPlacer {
nodes: &mut Vec<TreeNode>, nodes: &mut Vec<TreeNode>,
logs: &mut Vec<BlockPos>, logs: &mut Vec<BlockPos>,
current_pos: BlockPos, current_pos: BlockPos,
branch_dir: BlockDirection, branch_dir: HorizontalFacing,
branch_pos: i32, branch_pos: i32,
mut branch_steps: i32, mut branch_steps: i32,
) { ) {

View File

@ -9,7 +9,8 @@ use crate::{
use pumpkin_data::{ use pumpkin_data::{
Block, BlockDirection, FacingExt, HorizontalFacingExt, Block, BlockDirection, FacingExt, HorizontalFacingExt,
block_properties::{ block_properties::{
BlockProperties, Facing, LadderLikeProperties, MangroveRootsLikeProperties, BlockProperties, Facing, HorizontalFacing, LadderLikeProperties,
MangroveRootsLikeProperties,
}, },
tag::{self, Taggable}, tag::{self, Taggable},
}; };
@ -18,7 +19,9 @@ use pumpkin_world::{
BlockStateId, BlockStateId,
world::{BlockAccessor, BlockFlags}, world::{BlockAccessor, BlockFlags},
}; };
pub struct CoralFanBlock; pub struct CoralFanBlock;
impl BlockMetadata for CoralFanBlock { impl BlockMetadata for CoralFanBlock {
fn ids() -> Box<[u16]> { fn ids() -> Box<[u16]> {
let alive_wall_fans = tag::Block::MINECRAFT_WALL_CORALS.1; let alive_wall_fans = tag::Block::MINECRAFT_WALL_CORALS.1;
@ -81,18 +84,18 @@ impl BlockBehaviour for CoralFanBlock {
for dir in directions { for dir in directions {
if dir != Facing::Up if dir != Facing::Up
&& dir != Facing::Down && 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 { let Some(wall_block) = get_corresponding_wall_fan_type(args.block) else {
return 0; return 0;
}; };
let mut coral_wall_fan_props = CoralWallFanLikeProperties::default(&wall_block); let mut coral_wall_fan_props = CoralWallFanLikeProperties::default(&wall_block);
coral_wall_fan_props.waterlogged = args.replacing.water_source(); coral_wall_fan_props.waterlogged = args.replacing.water_source();
coral_wall_fan_props.facing = dir coral_wall_fan_props.facing = dir.opposite().to_horizontal_facing().unwrap();
.opposite()
.to_block_direction()
.to_horizontal_facing()
.unwrap();
return coral_wall_fan_props.to_state_id(&wall_block); return coral_wall_fan_props.to_state_id(&wall_block);
} }
} }
@ -104,6 +107,7 @@ impl BlockBehaviour for CoralFanBlock {
0 0
}) })
} }
fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> { fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> {
Box::pin(async move { Box::pin(async move {
if !scan_for_water(args.world, args.position).await { 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, ()> { fn on_scheduled_tick<'a>(&'a self, args: OnScheduledTickArgs<'a>) -> BlockFuture<'a, ()> {
Box::pin(async move { Box::pin(async move {
if !scan_for_water(args.world, args.position).await && !is_dead_coral(args.block) { if !scan_for_water(args.world, args.position).await && !is_dead_coral(args.block) {
let current_state = args.world.get_block_state(args.position); 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 dead_block_state_id = if is_wall_fan(args.block) {
let props = let mut props =
CoralWallFanLikeProperties::from_state_id(current_state.id, args.block); CoralWallFanLikeProperties::from_state_id(current_state.id, args.block);
props.waterlogged = false;
props.to_state_id(&get_dead_type(args.block)) props.to_state_id(&get_dead_type(args.block))
} else { } 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)) props.to_state_id(&get_dead_type(args.block))
}; };
args.world args.world
.set_block_state(args.position, dead_block_state_id, BlockFlags::empty()) .set_block_state(args.position, dead_block_state_id, BlockFlags::empty())
.await; .await;
} }
}) })
} }
fn can_place_at<'a>(&'a self, args: CanPlaceAtArgs<'a>) -> bool { fn can_place_at<'a>(&'a self, args: CanPlaceAtArgs<'a>) -> bool {
let support_block = args.block_accessor.get_block_state(&args.position.down()); 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) { 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) { if is_wall_fan(args.block) {
let props = CoralWallFanLikeProperties::from_state_id(args.state_id, args.block); let props = CoralWallFanLikeProperties::from_state_id(args.state_id, args.block);
if props.facing.to_block_direction().opposite() == args.direction if props.facing.to_block_direction().opposite() == args.direction
&& !can_place_at( && !can_place_at(args.world, args.position, props.facing.opposite())
args.world,
args.position,
props.facing.to_block_direction().opposite(),
)
{ {
return 0; return 0;
} }
@ -164,15 +172,18 @@ impl BlockBehaviour for CoralFanBlock {
return 0; return 0;
} }
} }
args.state_id args.state_id
}) })
} }
} }
fn get_default_coral_fan_state_id(block: &Block, waterlogged: bool) -> BlockStateId { fn get_default_coral_fan_state_id(block: &Block, waterlogged: bool) -> BlockStateId {
let mut props = CoralFanLikeProperties::default(block); let mut props = CoralFanLikeProperties::default(block);
props.waterlogged = waterlogged; props.waterlogged = waterlogged;
props.to_state_id(block) props.to_state_id(block)
} }
fn is_wall_fan(block: &Block) -> bool { fn is_wall_fan(block: &Block) -> bool {
block.has_tag(&tag::Block::MINECRAFT_WALL_CORALS) block.has_tag(&tag::Block::MINECRAFT_WALL_CORALS)
|| block == &Block::DEAD_BRAIN_CORAL_WALL_FAN || 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_HORN_CORAL_WALL_FAN
|| block == &Block::DEAD_TUBE_CORAL_WALL_FAN || block == &Block::DEAD_TUBE_CORAL_WALL_FAN
} }
fn get_dead_type(block: &Block) -> Block { fn get_dead_type(block: &Block) -> Block {
if block == &Block::BRAIN_CORAL_FAN { if block == &Block::BRAIN_CORAL_FAN {
Block::DEAD_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 Block::DEAD_TUBE_CORAL_WALL_FAN
} }
} }
fn get_corresponding_wall_fan_type(block: &Block) -> Option<Block> { fn get_corresponding_wall_fan_type(block: &Block) -> Option<Block> {
if block == &Block::TUBE_CORAL_FAN { if block == &Block::TUBE_CORAL_FAN {
Some(Block::TUBE_CORAL_WALL_FAN) Some(Block::TUBE_CORAL_WALL_FAN)
@ -229,8 +242,9 @@ fn get_corresponding_wall_fan_type(block: &Block) -> Option<Block> {
None 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 world
.get_block_state(&block_pos.offset(facing.to_offset())) .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::Block;
use pumpkin_data::BlockDirection; use pumpkin_data::BlockDirection;
use pumpkin_data::tag::Taggable;
use pumpkin_util::math::position::BlockPos; use pumpkin_util::math::position::BlockPos;
use rand::RngExt; use rand::RngExt;
@ -14,8 +15,8 @@ pub mod coral_plant;
pub async fn scan_for_water(world: &Arc<World>, pos: &BlockPos) -> bool { pub async fn scan_for_water(world: &Arc<World>, pos: &BlockPos) -> bool {
for direction in BlockDirection::all() { for direction in BlockDirection::all() {
let neighbor_pos = pos.offset(direction.to_offset()); let neighbor_pos = pos.offset(direction.to_offset());
let block = world.get_block(&neighbor_pos); let block = world.get_fluid(&neighbor_pos);
if block == &Block::WATER { if block.has_tag(&pumpkin_data::tag::Fluid::MINECRAFT_WATER) {
return true; return true;
} }
} }

View File

@ -1,6 +1,5 @@
use crate::entity::EntityBase; use crate::entity::EntityBase;
use pumpkin_data::BlockDirection; use pumpkin_data::BlockDirection;
use pumpkin_data::HorizontalFacingExt;
use pumpkin_data::block_properties::Axis; use pumpkin_data::block_properties::Axis;
use pumpkin_data::block_properties::BlockProperties; use pumpkin_data::block_properties::BlockProperties;
use pumpkin_data::block_properties::DoorHinge; use pumpkin_data::block_properties::DoorHinge;
@ -112,14 +111,14 @@ async fn get_hinge(
) -> DoorHinge { ) -> DoorHinge {
let top_pos = pos.up(); let top_pos = pos.up();
let left_dir = facing.rotate_counter_clockwise(); 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 (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 top_state = world.get_block_state(&top_facing);
let right_dir = facing.rotate_clockwise(); 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 (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 top_right_state = world.get_block_state(&top_right);
let has_left_door = world let has_left_door = world
@ -139,7 +138,7 @@ async fn get_hinge(
if (!has_left_door || has_right_door) && score <= 0 { if (!has_left_door || has_right_door) && score <= 0 {
if (!has_right_door || has_left_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; let hit = use_item.cursor_pos;
if (offset.x >= 0 || hit.z > 0.5) if (offset.x >= 0 || hit.z > 0.5)
&& (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 crate::block::OnPlaceArgs;
use pumpkin_data::BlockDirection; use pumpkin_data::BlockDirection;
use pumpkin_data::BlockState; use pumpkin_data::BlockState;
use pumpkin_data::HorizontalFacingExt;
use pumpkin_data::block_properties::BlockProperties; use pumpkin_data::block_properties::BlockProperties;
use pumpkin_data::block_properties::HorizontalFacing;
use pumpkin_data::tag::Taggable; use pumpkin_data::tag::Taggable;
use pumpkin_data::{Block, tag}; use pumpkin_data::{Block, tag};
use pumpkin_util::math::position::BlockPos; 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_pos = block_pos.offset(direction.to_offset());
let (other_block, other_block_state) = world.get_block_and_state(&other_block_pos); 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 { match direction {
BlockDirection::North => fence_props.north = connected, HorizontalFacing::North => fence_props.north = connected,
BlockDirection::South => fence_props.south = connected, HorizontalFacing::South => fence_props.south = connected,
BlockDirection::West => fence_props.west = connected, HorizontalFacing::West => fence_props.west = connected,
BlockDirection::East => fence_props.east = connected, HorizontalFacing::East => fence_props.east = connected,
_ => {}
} }
} }

View File

@ -103,19 +103,17 @@ impl FireBlockBase {
} }
let dir = if direction.is_horizontal() { let dir = if direction.is_horizontal() {
direction.rotate_counter_clockwise() direction
.rotate_counter_clockwise()
.to_horizontal_axis()
.unwrap()
} else { } else {
BlockDirection::random_horizontal(&mut RandomGenerator::Xoroshiro( BlockDirection::random_horizontal(&mut RandomGenerator::Xoroshiro(
Xoroshiro::from_seed(rand::rng().random()), Xoroshiro::from_seed(rand::rng().random()),
)) ))
.to_axis()
}; };
NetherPortal::get_new_portal( NetherPortal::get_new_portal(world, block_pos, dir).is_some()
world,
block_pos,
dir.to_horizontal_axis()
.expect("Direction should be horizontal"),
)
.is_some()
} }
/// Shared fire collision behavior used by `fire` and `soul_fire`. /// 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::GetStateForNeighborUpdateArgs;
use crate::block::OnPlaceArgs; use crate::block::OnPlaceArgs;
use pumpkin_data::BlockDirection; use pumpkin_data::BlockDirection;
use pumpkin_data::HorizontalFacingExt;
use pumpkin_data::block_properties::BlockProperties; use pumpkin_data::block_properties::BlockProperties;
use pumpkin_data::block_properties::HorizontalFacing;
use pumpkin_data::tag::Taggable; use pumpkin_data::tag::Taggable;
use pumpkin_data::{Block, tag}; use pumpkin_data::{Block, tag};
use pumpkin_util::math::position::BlockPos; 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 (other_block, other_block_state) = world.get_block_and_state(&other_block_pos);
let connected = other_block == block 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::C_GLASS_PANES)
|| other_block == &Block::IRON_BARS || other_block == &Block::IRON_BARS
|| other_block.has_tag(&tag::Block::MINECRAFT_WALLS); || other_block.has_tag(&tag::Block::MINECRAFT_WALLS);
match direction { match direction {
BlockDirection::North => pane_props.north = connected, HorizontalFacing::North => pane_props.north = connected,
BlockDirection::South => pane_props.south = connected, HorizontalFacing::South => pane_props.south = connected,
BlockDirection::West => pane_props.west = connected, HorizontalFacing::West => pane_props.west = connected,
BlockDirection::East => pane_props.east = connected, HorizontalFacing::East => pane_props.east = connected,
_ => {}
} }
} }

View File

@ -2,7 +2,9 @@ use crate::block::BlockFuture;
use crate::block::GetStateForNeighborUpdateArgs; use crate::block::GetStateForNeighborUpdateArgs;
use crate::block::OnPlaceArgs; use crate::block::OnPlaceArgs;
use pumpkin_data::BlockDirection; use pumpkin_data::BlockDirection;
use pumpkin_data::HorizontalFacingExt;
use pumpkin_data::block_properties::BlockProperties; use pumpkin_data::block_properties::BlockProperties;
use pumpkin_data::block_properties::HorizontalFacing;
use pumpkin_data::tag::Taggable; use pumpkin_data::tag::Taggable;
use pumpkin_data::{Block, tag}; use pumpkin_data::{Block, tag};
use pumpkin_macros::pumpkin_block; 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 (other_block, other_block_state) = world.get_block_and_state(&other_block_pos);
let connected = other_block == block 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::C_GLASS_PANES)
|| other_block.has_tag(&tag::Block::MINECRAFT_WALLS); || other_block.has_tag(&tag::Block::MINECRAFT_WALLS);
match direction { match direction {
BlockDirection::North => bars_props.north = connected, HorizontalFacing::North => bars_props.north = connected,
BlockDirection::South => bars_props.south = connected, HorizontalFacing::South => bars_props.south = connected,
BlockDirection::West => bars_props.west = connected, HorizontalFacing::West => bars_props.west = connected,
BlockDirection::East => bars_props.east = connected, HorizontalFacing::East => bars_props.east = connected,
_ => {}
} }
} }

View File

@ -45,7 +45,6 @@ impl BlockBehaviour for LadderBlock {
} }
props.facing = dir props.facing = dir
.opposite() .opposite()
.to_block_direction()
.to_horizontal_facing() .to_horizontal_facing()
.expect("Opposite of horizontal direction should be horizontal"); .expect("Opposite of horizontal direction should be horizontal");
return props.to_state_id(args.block); return props.to_state_id(args.block);
@ -59,7 +58,7 @@ impl BlockBehaviour for LadderBlock {
//this won't happen //this won't happen
return false; 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; return true;
} }
} }

View File

@ -5,9 +5,10 @@ use crate::block::{
}; };
use pumpkin_data::{ use pumpkin_data::{
Block, BlockDirection, Block, BlockDirection,
block_properties::{BlockProperties, WallTorchLikeProperties, WheatLikeProperties}, block_properties::{
tag, BlockProperties, HorizontalFacing, WallTorchLikeProperties, WheatLikeProperties,
tag::Taggable, },
tag::{self, Taggable},
}; };
use pumpkin_util::{ use pumpkin_util::{
math::position::BlockPos, math::position::BlockPos,
@ -37,14 +38,14 @@ impl StemBlock {
props.to_state_id(block) 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 { let attached_block = match block.id {
id if id == Block::PUMPKIN_STEM.id => &Block::ATTACHED_PUMPKIN_STEM, id if id == Block::PUMPKIN_STEM.id => &Block::ATTACHED_PUMPKIN_STEM,
id if id == Block::MELON_STEM.id => &Block::ATTACHED_MELON_STEM, id if id == Block::MELON_STEM.id => &Block::ATTACHED_MELON_STEM,
_ => &Block::ATTACHED_MELON_STEM, // Should never happen _ => &Block::ATTACHED_MELON_STEM, // Should never happen
}; };
let mut props = AttachedStemProperties::default(attached_block); let mut props = AttachedStemProperties::default(attached_block);
props.facing = dir.to_horizontal_facing().unwrap(); props.facing = dir;
props.to_state_id(attached_block) 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> { fn get_output_level<'a>(&'a self, world: &'a World, pos: BlockPos) -> BlockFuture<'a, u8> {
Box::pin(async move { Box::pin(async move {
if let Some(blockentity) = world.get_block_entity(&pos) 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); return comparator.output_signal.load(Ordering::Relaxed);
} }
0 0
@ -178,24 +175,25 @@ impl RedstoneGateBlock<ComparatorLikeProperties> for ComparatorBlock {
return; return;
} }
let i = self.calculate_output_signal(world, pos, state, block).await; let i = self.calculate_output_signal(world, pos, state, block).await;
let j = RedstoneGateBlock::get_output_level(self, world, pos).await; let j = RedstoneGateBlock::get_output_level(self, world, pos).await;
let props = ComparatorLikeProperties::from_state_id(state.id, block); let props = ComparatorLikeProperties::from_state_id(state.id, block);
if i != j if i != j
|| props.powered || props.powered
!= RedstoneGateBlock::has_power(self, world, pos, state, block).await != RedstoneGateBlock::has_power(self, world, pos, state, block).await
{ {
world.schedule_block_tick( let priority =
block,
pos,
RedstoneGateBlock::get_update_delay_internal(self, state.id, block),
if RedstoneGateBlock::is_target_not_aligned(self, world, pos, state, block) { if RedstoneGateBlock::is_target_not_aligned(self, world, pos, state, block) {
TickPriority::High TickPriority::High
} else { } else {
TickPriority::Normal 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, self, world, pos, state.id, block, false,
) )
.await; .await;
if i > j { if i > j {
true true
} else { } else {
@ -244,7 +243,6 @@ impl RedstoneGateBlock<ComparatorLikeProperties> for ComparatorBlock {
let source_pos = pos.offset(facing.to_offset()); let source_pos = pos.offset(facing.to_offset());
let (source_block, source_state) = world.get_block_and_state(&source_pos); 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) if let Some(pumpkin_block) = world.block_registry.get_pumpkin_block(source_block.id)
&& let Some(level) = pumpkin_block && let Some(level) = pumpkin_block
.get_comparator_output(GetComparatorOutputArgs { .get_comparator_output(GetComparatorOutputArgs {
@ -259,25 +257,28 @@ impl RedstoneGateBlock<ComparatorLikeProperties> for ComparatorBlock {
} }
if redstone_level < 15 && source_state.is_solid_block() { if redstone_level < 15 && source_state.is_solid_block() {
let source_pos = source_pos.offset(facing.to_offset()); let deeper_source_pos = source_pos.offset(facing.to_offset());
let (source_block, source_state) = world.get_block_and_state(&source_pos); 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 =
let itemframe_level = Self::get_attached_itemframe_level(world, facing, source_pos); 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) = 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 pumpkin_block
.get_comparator_output(GetComparatorOutputArgs { .get_comparator_output(GetComparatorOutputArgs {
world, world,
block: source_block, block: deeper_block,
state: source_state, state: deeper_state,
position: &source_pos, position: &deeper_source_pos,
}) })
.await .await
} else { } else {
None None
}; };
if let Some(level) = itemframe_level.max(block_level) { if let Some(level) = itemframe_level.max(block_level) {
return level; return level;
} }
@ -287,7 +288,7 @@ impl RedstoneGateBlock<ComparatorLikeProperties> for ComparatorBlock {
} }
fn get_update_delay_internal(&self, _state_id: BlockStateId, _block: &Block) -> u8 { 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_pos: BlockPos,
block: &Block, 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 { props.mode = match props.mode {
ModeComparator::Compare => ModeComparator::Subtract, ModeComparator::Compare => ModeComparator::Subtract,
ModeComparator::Subtract => ModeComparator::Compare, ModeComparator::Subtract => ModeComparator::Compare,
}; };
let state_id = props.to_state_id(block); let state_id = props.to_state_id(block);
world world
.set_block_state(&block_pos, state_id, BlockFlags::empty()) .set_block_state(&block_pos, state_id, BlockFlags::empty())
@ -320,12 +326,18 @@ impl ComparatorBlock {
block: &Block, block: &Block,
) -> u8 { ) -> u8 {
let power = self.get_power(world, pos, state, block).await; let power = self.get_power(world, pos, state, block).await;
if power == 0 {
return 0;
}
let sub_power = self let sub_power = self
.get_max_input_level_sides(world, pos, state.id, block, false) .get_max_input_level_sides(world, pos, state.id, block, false)
.await; .await;
if sub_power >= power {
if sub_power > power {
return 0; return 0;
} }
let props = ComparatorLikeProperties::from_state_id(state.id, block); let props = ComparatorLikeProperties::from_state_id(state.id, block);
if props.mode == ModeComparator::Subtract { if props.mode == ModeComparator::Subtract {
power - sub_power power - sub_power
@ -346,10 +358,10 @@ impl ComparatorBlock {
entity.get_entity().entity_type == &EntityType::ITEM_FRAME entity.get_entity().entity_type == &EntityType::ITEM_FRAME
&& entity.get_entity().get_horizontal_facing() == facing && entity.get_entity().get_horizontal_facing() == facing
}); });
if let Some(_itemframe) = itemframes.next()
&& itemframes.next().is_none() if itemframes.next().is_some() && itemframes.next().is_none() {
{ // Vanilla Parity TODO:
// TODO itemframe.getComparatorPower() // Return itemframe.getAnalogOutput() which calculates based on item rotation
return Some(1); return Some(1);
} }
None None
@ -358,22 +370,21 @@ impl ComparatorBlock {
async fn update(&self, world: &Arc<World>, pos: BlockPos, state: &BlockState, block: &Block) { 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 future_level = i32::from(self.calculate_output_signal(world, pos, state, block).await);
let mut now_level = 0; let mut now_level = 0;
if let Some(blockentity) = world.get_block_entity(&pos) 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)); now_level = i32::from(comparator.output_signal.load(Ordering::Relaxed));
comparator comparator
.output_signal .output_signal
.store(future_level as u8, Ordering::Relaxed); .store(future_level as u8, Ordering::Relaxed);
} }
let mut props = ComparatorLikeProperties::from_state_id(state.id, block); let mut props = ComparatorLikeProperties::from_state_id(state.id, block);
if now_level != future_level || props.mode == ModeComparator::Compare { if now_level != future_level || props.mode == ModeComparator::Compare {
let future_power = self.has_power(world, pos, state, block).await; let future_power = self.has_power(world, pos, state, block).await;
let now_power = props.powered; let now_power = props.powered;
if now_power && !future_power { if now_power && !future_power {
props.powered = false; props.powered = false;
world world
@ -385,6 +396,7 @@ impl ComparatorBlock {
.set_block_state(&pos, props.to_state_id(block), BlockFlags::NOTIFY_LISTENERS) .set_block_state(&pos, props.to_state_id(block), BlockFlags::NOTIFY_LISTENERS)
.await; .await;
} }
RedstoneGateBlock::update_target(self, world, pos, props.to_state_id(block), block) RedstoneGateBlock::update_target(self, world, pos, props.to_state_id(block), block)
.await; .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::blocks::redstone::block_receives_redstone_power;
use crate::block::registry::BlockActionResult; use crate::block::registry::BlockActionResult;
use crate::block::{ use crate::block::{
BlockBehaviour, BlockFuture, NormalUseArgs, OnNeighborUpdateArgs, OnPlaceArgs, BlockBehaviour, BlockFuture, NormalUseArgs, OnNeighborUpdateArgs, OnPlaceArgs,
OnScheduledTickArgs, PlacedArgs, OnScheduledTickArgs, PlacedArgs,
}; };
use crate::entity::Entity;
use crate::entity::EntityBase;
use crate::entity::item::ItemEntity; use crate::entity::item::ItemEntity;
use crate::entity::{Entity, EntityBase};
use crate::block::entities::dropper::DropperBlockEntity; use crate::block::entities::dropper::DropperBlockEntity;
use crate::block::entities::hopper::HopperBlockEntity; use crate::block::entities::hopper::HopperBlockEntity;
@ -26,9 +29,6 @@ use pumpkin_world::BlockStateId;
use pumpkin_world::inventory::Inventory; use pumpkin_world::inventory::Inventory;
use pumpkin_world::tick::TickPriority; use pumpkin_world::tick::TickPriority;
use pumpkin_world::world::BlockFlags; use pumpkin_world::world::BlockFlags;
use rand::{Rng, RngExt, rng};
use std::sync::Arc;
use tokio::sync::Mutex;
struct DropperScreenFactory(Arc<dyn Inventory>); struct DropperScreenFactory(Arc<dyn Inventory>);
@ -120,10 +120,12 @@ impl BlockBehaviour for DropperBlock {
Box::pin(async move { Box::pin(async move {
let powered = block_receives_redstone_power(args.world, args.position).await let powered = block_receives_redstone_power(args.world, args.position).await
|| block_receives_redstone_power(args.world, &args.position.up()).await; || block_receives_redstone_power(args.world, &args.position.up()).await;
let mut props = DispenserLikeProperties::from_state_id( let mut props = DispenserLikeProperties::from_state_id(
args.world.get_block_state(args.position).id, args.world.get_block_state(args.position).id,
args.block, args.block,
); );
if powered && !props.triggered { if powered && !props.triggered {
args.world args.world
.schedule_block_tick(args.block, *args.position, 4, TickPriority::Normal); .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, ()> { fn on_scheduled_tick<'a>(&'a self, args: OnScheduledTickArgs<'a>) -> BlockFuture<'a, ()> {
Box::pin(async move { Box::pin(async move {
if let Some(block_entity) = args.world.get_block_entity(args.position) { if let Some(block_entity) = args.world.get_block_entity(args.position) {
let dropper = block_entity // Safely extract the dropper instead of unwrap()
.as_any() let Some(dropper) = block_entity.as_any().downcast_ref::<DropperBlockEntity>()
.downcast_ref::<DropperBlockEntity>() else {
.unwrap(); return;
};
if let Some(mut item) = dropper.get_random_slot().await { if let Some(mut item) = dropper.get_random_slot().await {
let props = DispenserLikeProperties::from_state_id( let props = DispenserLikeProperties::from_state_id(
args.world.get_block_state(args.position).id, args.world.get_block_state(args.position).id,
args.block, args.block,
); );
if let Some(entity) = args.world.get_block_entity(
&args let target_pos = args
.position .position
.offset(props.facing.to_block_direction().to_offset()), .offset(props.facing.to_block_direction().to_offset());
) && let Some(container) = entity.get_inventory()
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 backup = item.clone();
let one_item = item.split(1); let one_item = item.split(1);
if HopperBlockEntity::add_one_item(dropper, container.as_ref(), one_item) if HopperBlockEntity::add_one_item(dropper, container.as_ref(), one_item)
.await .await
{ {
return; return;
} }
*item = backup; *item = backup;
return; return;
} }
// No container found, dispense item into the world
let drop_item = item.split(1); let drop_item = item.split(1);
let facing = to_normal(props.facing); let facing = to_normal(props.facing);
let mut position = args.position.to_centered_f64().add(&(facing * 0.7)); let mut position = args.position.to_centered_f64().add(&(facing * 0.7));
position.y -= match props.facing { position.y -= match props.facing {
Facing::Up | Facing::Down => 0.125, Facing::Up | Facing::Down => 0.125,
_ => 0.15625, _ => 0.15625,
}; };
let entity = Entity::new(args.world.clone(), position, &EntityType::ITEM); let entity = Entity::new(args.world.clone(), position, &EntityType::ITEM);
let rd = rng().random::<f64>().mul_add(0.1, 0.2); let rd = rng().random::<f64>().mul_add(0.1, 0.2);
let velocity = Vector3::new( let velocity = Vector3::new(
triangle(&mut rng(), facing.x * rd, 0.017_227_5 * 6.), triangle(&mut rng(), facing.x * rd, 0.017_227_5 * 6.),
triangle(&mut rng(), 0.2, 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.), triangle(&mut rng(), facing.z * rd, 0.017_227_5 * 6.),
); );
let item_entity = Arc::new(ItemEntity::new_with_velocity( let item_entity = Arc::new(ItemEntity::new_with_velocity(
entity, drop_item, velocity, 40, entity, drop_item, velocity, 40,
)); ));
args.world.spawn_entity(item_entity).await; args.world.spawn_entity(item_entity).await;
args.world.sync_world_event( args.world.sync_world_event(
WorldEvent::SoundDispenserDispense, WorldEvent::SoundDispenserDispense,
*args.position, *args.position,
0, 0,
); );
args.world.sync_world_event( args.world.sync_world_event(
WorldEvent::SoundDispenserProjectileLaunch, WorldEvent::ParticlesShootSmoke,
*args.position, *args.position,
to_data3d(props.facing), to_data3d(props.facing),
); );
} else { } else {
args.world args.world.sync_world_event(
.sync_world_event(WorldEvent::SoundDispenserFail, *args.position, 0); WorldEvent::SoundDispenserDispense,
*args.position,
0,
);
} }
} }
}) })

View File

@ -79,11 +79,7 @@ impl BlockBehaviour for RedstoneTorchBlock {
&& can_place_at(world, location, dir.to_block_direction()) && can_place_at(world, location, dir.to_block_direction())
{ {
let mut torch_props = RWallTorchProps::default(&Block::REDSTONE_WALL_TORCH); let mut torch_props = RWallTorchProps::default(&Block::REDSTONE_WALL_TORCH);
torch_props.facing = dir torch_props.facing = dir.opposite().to_horizontal_facing().unwrap();
.opposite()
.to_block_direction()
.to_horizontal_facing()
.unwrap();
return torch_props.to_state_id(&Block::REDSTONE_WALL_TORCH); return torch_props.to_state_id(&Block::REDSTONE_WALL_TORCH);
} }
} }
@ -103,7 +99,7 @@ impl BlockBehaviour for RedstoneTorchBlock {
return true; return true;
} }
for dir in BlockDirection::horizontal() { 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; return true;
} }
} }

View File

@ -1,7 +1,7 @@
use std::sync::Arc; use std::sync::Arc;
use pumpkin_data::block_properties::{ use pumpkin_data::block_properties::{
BlockProperties, EastRedstone, NorthRedstone, ObserverLikeProperties, BlockProperties, EastRedstone, HorizontalFacing, NorthRedstone, ObserverLikeProperties,
RedstoneWireLikeProperties, RepeaterLikeProperties, SouthRedstone, WestRedstone, RedstoneWireLikeProperties, RepeaterLikeProperties, SouthRedstone, WestRedstone,
}; };
use pumpkin_data::{Block, BlockDirection, BlockState, HorizontalFacingExt}; use pumpkin_data::{Block, BlockDirection, BlockState, HorizontalFacingExt};
@ -116,7 +116,7 @@ impl BlockBehaviour for RedstoneWireBlock {
args.world args.world
.replace_with_state_for_neighbor_update( .replace_with_state_for_neighbor_update(
&up_block_pos, &up_block_pos,
direction.opposite(), direction.opposite().to_block_direction(),
args.flags, args.flags,
) )
.await; .await;
@ -128,7 +128,7 @@ impl BlockBehaviour for RedstoneWireBlock {
args.world args.world
.replace_with_state_for_neighbor_update( .replace_with_state_for_neighbor_update(
&down_block_pos, &down_block_pos,
direction.opposite(), direction.opposite().to_block_direction(),
args.flags, args.flags,
) )
.await; .await;
@ -201,7 +201,7 @@ impl BlockBehaviour for RedstoneWireBlock {
Box::pin(async move { Box::pin(async move {
let wire = RedstoneWireProperties::from_state_id(args.state.id, args.block); let wire = RedstoneWireProperties::from_state_id(args.state.id, args.block);
if args.direction == BlockDirection::Up 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 wire.power
} else { } else {
@ -217,7 +217,7 @@ impl BlockBehaviour for RedstoneWireBlock {
Box::pin(async move { Box::pin(async move {
let wire = RedstoneWireProperties::from_state_id(args.state.id, args.block); let wire = RedstoneWireProperties::from_state_id(args.state.id, args.block);
if args.direction == BlockDirection::Up 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 wire.power
} else { } else {
@ -398,18 +398,17 @@ pub async fn get_regulated_sides(
} }
trait RedstoneWireLikePropertiesExt { 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; //fn get_connection_type(&self, direction: BlockDirection) -> WireConnection;
} }
impl RedstoneWireLikePropertiesExt for RedstoneWireLikeProperties { impl RedstoneWireLikePropertiesExt for RedstoneWireLikeProperties {
fn is_side_connected(&self, direction: BlockDirection) -> bool { fn is_side_connected(&self, direction: HorizontalFacing) -> bool {
match direction { match direction {
BlockDirection::North => self.north.to_wire_connection().is_connected(), HorizontalFacing::North => self.north.to_wire_connection().is_connected(),
BlockDirection::South => self.south.to_wire_connection().is_connected(), HorizontalFacing::South => self.south.to_wire_connection().is_connected(),
BlockDirection::East => self.east.to_wire_connection().is_connected(), HorizontalFacing::East => self.east.to_wire_connection().is_connected(),
BlockDirection::West => self.west.to_wire_connection().is_connected(), HorizontalFacing::West => self.west.to_wire_connection().is_connected(),
_ => false,
} }
} }

View File

@ -4,6 +4,7 @@ use std::sync::atomic::Ordering;
use crate::block::entities::sign::SignBlockEntity; use crate::block::entities::sign::SignBlockEntity;
use pumpkin_data::Block; use pumpkin_data::Block;
use pumpkin_data::BlockDirection; use pumpkin_data::BlockDirection;
use pumpkin_data::HorizontalFacingExt;
use pumpkin_data::block_properties::EnumVariants; use pumpkin_data::block_properties::EnumVariants;
use pumpkin_data::tag::Taggable; use pumpkin_data::tag::Taggable;
use pumpkin_inventory::screen_handler::InventoryPlayer; use pumpkin_inventory::screen_handler::InventoryPlayer;
@ -74,7 +75,7 @@ impl SignBlock {
let mut side_direction = None; let mut side_direction = None;
for direction in BlockDirection::horizontal() { for direction in BlockDirection::horizontal() {
let pos = position.offset(direction.to_offset()); 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); side_direction = Some(direction);
break; break;
} }
@ -82,7 +83,7 @@ impl SignBlock {
SupportInfo { SupportInfo {
above_is_valid, 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() { for d in pumpkin_data::BlockDirection::horizontal() {
let wall_pos = args.position.offset(d.to_offset()); let wall_pos = args.position.offset(d.to_offset());
let (block, state) = args.block_accessor.get_block_and_state(&wall_pos); 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_LEAVES)
|| block.has_tag(&pumpkin_data::tag::Block::MINECRAFT_SIGNS) || 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); let mut torch_props = WallTorchProps::default(&wall_block);
torch_props.facing = dir torch_props.facing = dir.opposite().to_horizontal_facing().unwrap();
.opposite()
.to_block_direction()
.to_horizontal_facing()
.unwrap();
return torch_props.to_state_id(&wall_block); return torch_props.to_state_id(&wall_block);
} }
} }
@ -98,7 +94,7 @@ impl BlockBehaviour for TorchBlock {
return true; return true;
} }
for dir in BlockDirection::horizontal() { 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; return true;
} }
} }

View File

@ -8,6 +8,7 @@ use pumpkin_inventory::player::player_inventory::PlayerInventory;
use pumpkin_inventory::screen_handler::InventoryPlayer; use pumpkin_inventory::screen_handler::InventoryPlayer;
use pumpkin_protocol::bedrock::client::take_item_actor::CTakeItemActor; use pumpkin_protocol::bedrock::client::take_item_actor::CTakeItemActor;
use pumpkin_protocol::bedrock::server::actor_event::{ActorEventType, SActorEvent}; use pumpkin_protocol::bedrock::server::actor_event::{ActorEventType, SActorEvent};
use pumpkin_protocol::codec::var_ulong::VarULong;
use pumpkin_util::GameMode; use pumpkin_util::GameMode;
use pumpkin_util::Hand; use pumpkin_util::Hand;
use pumpkin_util::math::position::BlockPos; use pumpkin_util::math::position::BlockPos;
@ -207,11 +208,11 @@ impl LivingEntity {
&CTakeItemEntity::new( &CTakeItemEntity::new(
item.entity_id.into(), item.entity_id.into(),
self.entity.entity_id.into(), self.entity.entity_id.into(),
stack_amount.try_into().unwrap(), VarInt(stack_amount as i32),
), ),
&CTakeItemActor::new( &CTakeItemActor::new(
item.entity_id.try_into().unwrap(), VarULong(item.entity_id as u64),
self.entity.entity_id.try_into().unwrap(), 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_int::VarInt;
use pumpkin_protocol::codec::var_long::VarLong; use pumpkin_protocol::codec::var_long::VarLong;
use pumpkin_protocol::java::client::play::{ use pumpkin_protocol::java::client::play::{
Animation, CAcknowledgeBlockChange, CActionBar, CAwardStats, CChangeDifficulty, Animation, CActionBar, CAwardStats, CChangeDifficulty, CCloseContainer, CCombatDeath,
CCloseContainer, CCombatDeath, CCustomPayload, CDisguisedChatMessage, CEntityAnimation, CCustomPayload, CDisguisedChatMessage, CEntityAnimation, CEntityPositionSync, CGameEvent,
CEntityPositionSync, CGameEvent, CItemCooldown, CMapItemData, COpenScreen, CParticle, CItemCooldown, CMapItemData, COpenScreen, CParticle, CPlayerAbilities, CPlayerInfoUpdate,
CPlayerAbilities, CPlayerInfoUpdate, CPlayerPosition, CPlayerSpawnPosition, CRespawn, CPlayerPosition, CPlayerSpawnPosition, CRespawn, CSetCamera, CSetContainerContent,
CSetCamera, CSetContainerContent, CSetContainerProperty, CSetContainerSlot, CSetCursorItem, CSetContainerProperty, CSetContainerSlot, CSetCursorItem, CSetEquipment, CSetExperience,
CSetEquipment, CSetExperience, CSetHealth, CSetPlayerInventory, CSetSelectedSlot, CSoundEffect, CSetHealth, CSetPlayerInventory, CSetSelectedSlot, CSoundEffect, CStopSound, CSubtitle,
CStopSound, CSubtitle, CSystemChatMessage, CTabList, CTitleAnimation, CTitleText, CUnloadChunk, CSystemChatMessage, CTabList, CTitleAnimation, CTitleText, CUnloadChunk, CUpdateMobEffect,
CUpdateMobEffect, CUpdateTime, GameEvent, MapIcon, MapPatch, Metadata, PlayerAction, CUpdateTime, GameEvent, MapIcon, MapPatch, Metadata, PlayerAction, PlayerInfoFlags,
PlayerInfoFlags, PlayerSpawnData, PreviousMessage, Statistic, PlayerSpawnData, PreviousMessage, Statistic,
}; };
use pumpkin_protocol::java::server::play::{ use pumpkin_protocol::java::server::play::{
SClickSlot, SContainerButtonClick, SRenameItem, SlotActionType, SClickSlot, SContainerButtonClick, SRenameItem, SlotActionType,
@ -452,7 +452,6 @@ pub struct Player {
pub mining: AtomicBool, pub mining: AtomicBool,
pub start_mining_time: AtomicI32, pub start_mining_time: AtomicI32,
pub tick_counter: AtomicI32, pub tick_counter: AtomicI32,
pub packet_sequence: AtomicI32,
pub mining_pos: Mutex<BlockPos>, pub mining_pos: Mutex<BlockPos>,
pub last_input: AtomicI8, pub last_input: AtomicI8,
/// A counter for teleport IDs used to track pending teleports. /// A counter for teleport IDs used to track pending teleports.
@ -646,7 +645,6 @@ impl Player {
open_container: AtomicCell::new(None), open_container: AtomicCell::new(None),
open_container_pos: AtomicCell::new(None), open_container_pos: AtomicCell::new(None),
tick_counter: AtomicI32::new(0), tick_counter: AtomicI32::new(0),
packet_sequence: AtomicI32::new(-1),
start_mining_time: AtomicI32::new(0), start_mining_time: AtomicI32::new(0),
last_input: AtomicI8::new(0), last_input: AtomicI8::new(0),
carried_item: Mutex::new(None), carried_item: Mutex::new(None),
@ -1859,7 +1857,6 @@ impl Player {
.await; .await;
} }
// TODO Abstract the chunk sending
#[expect(clippy::too_many_lines)] #[expect(clippy::too_many_lines)]
pub async fn tick(self: &Arc<Self>, server: &Server) { pub async fn tick(self: &Arc<Self>, server: &Server) {
if let Some(camera_id) = self.camera_target_id.load() { if let Some(camera_id) = self.camera_target_id.load() {
@ -1900,13 +1897,6 @@ impl Player {
// return; // 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 // Statistics updates
{ {
let mut stats = self.stats.lock().await; let mut stats = self.stats.lock().await;

View File

@ -511,6 +511,11 @@ impl PumpkinServer {
match udp_result { match udp_result {
Ok((len, client_addr)) => { Ok((len, client_addr)) => {
if len > 0 { 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 id = udp_buf[0];
let is_online = id & 128 != 0; let is_online = id & 128 != 0;
@ -531,7 +536,7 @@ impl PumpkinServer {
*master_client_id_counter += 1; *master_client_id_counter += 1;
let new_client = Arc::new(BedrockClient::new( let new_client = Arc::new(BedrockClient::new(
self.udp_socket.as_ref().unwrap().clone(), socket,
client_addr, client_addr,
be_clients be_clients
)); ));

View File

@ -1,9 +1,9 @@
use pumpkin_protocol::java::client::play::{ use pumpkin_protocol::java::client::play::{
CChunkBatchEnd, CChunkBatchStart, CChunkData, CPlayDisconnect, CAcknowledgeBlockChange, CChunkBatchEnd, CChunkBatchStart, CChunkData, CPlayDisconnect,
}; };
use pumpkin_world::level::SyncChunk; use pumpkin_world::level::SyncChunk;
use std::net::SocketAddr; 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::time::{Instant, SystemTime, UNIX_EPOCH};
use std::{io::Write, sync::Arc}; use std::{io::Write, sync::Arc};
@ -118,6 +118,8 @@ pub struct JavaClient {
pub keep_alive_id: AtomicCell<i64>, pub keep_alive_id: AtomicCell<i64>,
/// The last time we sent a keep alive packet. /// The last time we sent a keep alive packet.
pub last_keep_alive_time: AtomicCell<Instant>, pub last_keep_alive_time: AtomicCell<Instant>,
pub packet_sequence: AtomicI32,
} }
pub enum OutgoingPacketType { pub enum OutgoingPacketType {
@ -173,6 +175,7 @@ impl JavaClient {
wait_for_keep_alive: AtomicBool::new(false), wait_for_keep_alive: AtomicBool::new(false),
keep_alive_id: AtomicCell::new(0), keep_alive_id: AtomicCell::new(0),
last_keep_alive_time: AtomicCell::new(std::time::Instant::now()), last_keep_alive_time: AtomicCell::new(std::time::Instant::now()),
packet_sequence: AtomicI32::new(-1),
} }
} }
pub async fn set_encryption( pub async fn set_encryption(
@ -272,6 +275,13 @@ impl JavaClient {
self.last_keep_alive_time.store(Instant::now()); self.last_keep_alive_time.store(Instant::now());
let packet = pumpkin_protocol::java::client::play::CKeepAlive::new(keep_alive_id); let packet = pumpkin_protocol::java::client::play::CKeepAlive::new(keep_alive_id);
self.enqueue_packet(&packet).await; 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 // 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 { if sequence < 0 {
error!("Expected packet sequence >= 0"); error!("Expected packet sequence >= 0");
} }
player.packet_sequence.store( self.packet_sequence.store(
player.packet_sequence.load(Ordering::Relaxed).max(sequence), self.packet_sequence.load(Ordering::Relaxed).max(sequence),
Ordering::Relaxed, Ordering::Relaxed,
); );
} }

View File

@ -1,7 +1,8 @@
use std::sync::Arc; 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_util::math::{boundingbox::BoundingBox, position::BlockPos, vector3::Vector3};
use pumpkin_world::chunk::ChunkData;
use rustc_hash::FxHashMap; use rustc_hash::FxHashMap;
use crate::{ use crate::{
@ -27,51 +28,102 @@ impl Explosion {
&self, &self,
world: &World, world: &World,
) -> FxHashMap<BlockPos, (&'static Block, &'static BlockState)> { ) -> FxHashMap<BlockPos, (&'static Block, &'static BlockState)> {
// Somethings are not vanilla here but make it way faster
let mut map = FxHashMap::default(); 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 x in 0..16 {
for y 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 { if x > 0 && x < 15 && y > 0 && y < 15 && z > 0 && z < 15 {
continue; continue;
} }
let mut x = f64::from(x) / 7.5 - 1.0; let mut dir_x = f64::from(x) / 7.5 - 1.0;
let mut y = f64::from(y) / 7.5 - 1.0; let mut dir_y = f64::from(y) / 7.5 - 1.0;
let mut z = f64::from(z) / 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(); let length = (dir_x * dir_x + dir_y * dir_y + dir_z * dir_z).sqrt();
x *= sqrt; dir_x /= length;
y *= sqrt; dir_y /= length;
z *= sqrt; dir_z /= length;
let mut pos_x = self.pos.x; 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 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); let mut h = self.power * random_val.mul_add(0.6, 0.7);
while h > 0.0 { while h > 0.0 {
let block_pos = BlockPos::floored(pos_x, pos_y, pos_z); 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) { if !world.is_in_build_limit(block_pos) {
// // Pass by reference break;
// continue 'block2; }
// }
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 { if !state.is_air() || !fluid_state.is_empty {
let resistance = let resistance =
fluid_state.blast_resistance.max(block.blast_resistance); fluid_state.blast_resistance.max(block.blast_resistance);
h -= resistance * 0.3;
h -= (resistance + 0.3) * 0.3;
if h > 0.0 { if h > 0.0 {
map.insert(block_pos, (block, state)); map.insert(block_pos, (block, state));
} }
} }
pos_x += x * 0.3;
pos_y += y * 0.3; pos_x += dir_x * 0.3;
pos_z += z * 0.3; pos_y += dir_y * 0.3;
pos_z += dir_z * 0.3;
h -= 0.225_000_01; h -= 0.225_000_01;
} }
} }

View File

@ -54,7 +54,7 @@ use border::Worldborder;
use bytes::BufMut; use bytes::BufMut;
use explosion::Explosion; use explosion::Explosion;
use pumpkin_config::BasicConfiguration; 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::block_rotation::{Mirror, Rotation};
use pumpkin_data::chunk_gen_settings::GenerationSettings; use pumpkin_data::chunk_gen_settings::GenerationSettings;
use pumpkin_data::data_component_impl::EquipmentSlot; use pumpkin_data::data_component_impl::EquipmentSlot;
@ -73,7 +73,7 @@ use pumpkin_data::{
sound_id_remap::remap_sound_id_for_version, sound_id_remap::remap_sound_id_for_version,
world::{RAW, WorldEvent}, 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::crafting::recipe_provider::RecipeProvider;
use pumpkin_inventory::screen_handler::InventoryPlayer; use pumpkin_inventory::screen_handler::InventoryPlayer;
use pumpkin_nbt::{compound::NbtCompound, to_bytes_unnamed}; use pumpkin_nbt::{compound::NbtCompound, to_bytes_unnamed};
@ -1324,7 +1324,7 @@ impl World {
false false
} }
// FlowableFluid.getVelocity() // FlowingFluid.getFlow()
pub fn get_fluid_velocity( pub fn get_fluid_velocity(
&self, &self,
pos0: BlockPos, pos0: BlockPos,
@ -1334,66 +1334,57 @@ impl World {
let mut velo = Vector3::default(); let mut velo = Vector3::default();
for dir in BlockDirection::horizontal() { for dir in BlockDirection::horizontal() {
let mut amplitude = 0.0;
let offset = dir.to_offset(); let offset = dir.to_offset();
let pos = pos0.offset(offset); let pos = pos0.offset(offset);
let (neighbor_fluid, neighbor_state) = self.get_fluid_and_fluid_state(&pos);
if neighbor_fluid.matches_type(fluid0) {
let mut neighbor_height = neighbor_state.height;
let mut amplitude = 0.0;
if neighbor_height == 0.0 {
let block_state_id = self.get_block_state_id(&pos); let block_state_id = self.get_block_state_id(&pos);
let fluid = Fluid::from_state_id(block_state_id).unwrap_or(&Fluid::EMPTY);
if fluid.id == Fluid::EMPTY.id {
let block = Block::get_raw_id_from_state_id(block_state_id); let block = Block::get_raw_id_from_state_id(block_state_id);
let block_state = BlockState::from_id(block_state_id); let block_state = BlockState::from_id(block_state_id);
let blocks_movement = block_state.is_solid() let blocks_movement = blocks_movement(block_state, block);
&& block != Block::COBWEB
&& block != Block::BAMBOO_SAPLING;
if !blocks_movement { if !blocks_movement {
let down_pos = pos.down(); 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) { if down_fluid.matches_type(fluid0) {
amplitude = f64::from(state0.height - down_state.height) + 0.888_888_9; 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) { } else if neighbor_height > 0.0 {
continue; 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 {
continue;
} }
if amplitude != 0.0 {
velo.x += f64::from(offset.x) * amplitude; velo.x += f64::from(offset.x) * amplitude;
velo.z += f64::from(offset.z) * amplitude; velo.z += f64::from(offset.z) * amplitude;
} }
}
// TODO: FALLING }
if state0.falling { if state0.falling {
for dir in BlockDirection::horizontal() { for dir in BlockDirection::horizontal() {
let pos = pos0.offset(dir.to_offset()); let pos = pos0.offset(dir.to_offset());
if self.is_flow_blocked(fluid0.id, pos, dir) if self.is_solid_face(fluid0.id, pos, dir.to_block_direction())
|| self.is_flow_blocked(fluid0.id, pos.up(), dir) || self.is_solid_face(fluid0.id, pos.up(), dir.to_block_direction())
{ {
if velo.length_squared() != 0.0 { if velo.length_squared() != 0.0 {
velo = velo.normalize(); velo = velo.normalize();
} }
velo.y -= 6.0; velo.y -= 6.0;
break; break;
} }
} }
@ -1406,9 +1397,8 @@ impl World {
} }
} }
// FlowableFluid.isFlowBlocked() // FlowingFluid.isSolidFace()
fn is_solid_face(&self, fluid0_id: u16, pos: BlockPos, direction: BlockDirection) -> bool {
fn is_flow_blocked(&self, fluid0_id: u16, pos: BlockPos, direction: BlockDirection) -> bool {
let id = self.get_block_state_id(&pos); let id = self.get_block_state_id(&pos);
let fluid = Fluid::from_state_id(id).unwrap_or(&Fluid::EMPTY); 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 { fn is_valid_portal(world: &World, pos: BlockPos) -> bool {
for dir in BlockDirection::horizontal() { for dir in BlockDirection::horizontal() {
let facing = dir.to_horizontal_facing().unwrap();
let mid_pos = pos.offset_dir(dir.to_offset(), 2); let mid_pos = pos.offset_dir(dir.to_offset(), 2);
let left_pos = mid_pos.offset_dir(facing.rotate_clockwise().to_offset(), 1); let left_pos = mid_pos.offset_dir(dir.rotate_clockwise().to_offset(), 1);
let right_pos = mid_pos.offset_dir(facing.rotate_counter_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 (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); let (left_block, left_state) = world.get_block_and_state_id(&left_pos);
@ -73,6 +72,8 @@ impl EndPortal {
let right_properties = let right_properties =
EndPortalFrameProperties::from_state_id(right_state, right_block); EndPortalFrameProperties::from_state_id(right_state, right_block);
let facing = dir.to_facing();
if left_properties.facing != facing.opposite() if left_properties.facing != facing.opposite()
|| mid_properties.facing != facing.opposite() || mid_properties.facing != facing.opposite()
|| right_properties.facing != facing.opposite() || right_properties.facing != facing.opposite()