aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormat <git@matdoes.dev>2024-12-27 12:30:24 +0000
committermat <git@matdoes.dev>2024-12-27 12:35:25 +0000
commit5693191b57973136188bdade2144a69ac61a9f8a (patch)
treed60611362019bc4134404ff62104743884efe4e2
parent04036b6e4afe1e3eb59cd861a871e17ea5680f5f (diff)
downloadazalea-drasl-5693191b57973136188bdade2144a69ac61a9f8a.tar.xz
implement fluid_shape
-rw-r--r--Cargo.lock1
-rwxr-xr-xazalea-block/src/lib.rs44
-rw-r--r--azalea-entity/src/plugin/mod.rs2
-rw-r--r--azalea-physics/Cargo.toml1
-rw-r--r--azalea-physics/src/clip.rs63
-rw-r--r--azalea-physics/src/collision/mod.rs60
-rwxr-xr-xazalea-physics/src/collision/shape.rs2
-rw-r--r--azalea/src/pathfinder/mod.rs5
8 files changed, 145 insertions, 33 deletions
diff --git a/Cargo.lock b/Cargo.lock
index a6081a95..60b0a0df 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -463,6 +463,7 @@ dependencies = [
"bevy_ecs",
"bevy_time",
"parking_lot",
+ "tracing",
"uuid",
]
diff --git a/azalea-block/src/lib.rs b/azalea-block/src/lib.rs
index 65dd581e..4719ef42 100755
--- a/azalea-block/src/lib.rs
+++ b/azalea-block/src/lib.rs
@@ -8,7 +8,8 @@ mod range;
use core::fmt::Debug;
use std::{
any::Any,
- io::{Cursor, Write},
+ fmt,
+ io::{self, Cursor, Write},
};
use azalea_buf::{AzaleaRead, AzaleaReadVar, AzaleaWrite, AzaleaWriteVar, BufReadError};
@@ -115,13 +116,13 @@ impl AzaleaRead for BlockState {
}
}
impl AzaleaWrite for BlockState {
- fn azalea_write(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
+ fn azalea_write(&self, buf: &mut impl Write) -> Result<(), io::Error> {
u32::azalea_write_var(&(self.id as u32), buf)
}
}
-impl std::fmt::Debug for BlockState {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+impl Debug for BlockState {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"BlockState(id: {}, {:?})",
@@ -134,50 +135,69 @@ impl std::fmt::Debug for BlockState {
#[derive(Clone, Debug)]
pub struct FluidState {
pub fluid: azalea_registry::Fluid,
- pub height: u8,
+ /// 0 = empty, 8 = full, 9 = max.
+ ///
+ /// 9 is meant to be used when there's another fluid block of the same type
+ /// above it, but it's usually unused by this struct.
+ pub amount: u8,
+}
+impl FluidState {
+ /// A floating point number in between 0 and 1 representing the height (as a
+ /// percentage of a full block) of the fluid.
+ pub fn height(&self) -> f32 {
+ self.amount as f32 / 9.
+ }
}
impl Default for FluidState {
fn default() -> Self {
Self {
fluid: azalea_registry::Fluid::Empty,
- height: 0,
+ amount: 0,
}
}
}
impl From<BlockState> for FluidState {
fn from(state: BlockState) -> Self {
+ // note that 8 here might be treated as 9 in some cases if there's another fluid
+ // block of the same type above it
+
if state
.property::<crate::properties::Waterlogged>()
.unwrap_or_default()
{
Self {
fluid: azalea_registry::Fluid::Water,
- height: 15,
+ amount: 8,
}
} else {
let block = Box::<dyn Block>::from(state);
if let Some(water) = block.downcast_ref::<crate::blocks::Water>() {
Self {
fluid: azalea_registry::Fluid::Water,
- height: water.level as u8,
+ amount: to_or_from_legacy_fluid_level(water.level as u8),
}
} else if let Some(lava) = block.downcast_ref::<crate::blocks::Lava>() {
Self {
fluid: azalea_registry::Fluid::Lava,
- height: lava.level as u8,
+ amount: to_or_from_legacy_fluid_level(lava.level as u8),
}
} else {
Self {
fluid: azalea_registry::Fluid::Empty,
- height: 0,
+ amount: 0,
}
}
}
}
}
+// see FlowingFluid.getLegacyLevel
+fn to_or_from_legacy_fluid_level(level: u8) -> u8 {
+ 8_u8.saturating_sub(level)
+}
+
impl From<FluidState> for BlockState {
fn from(state: FluidState) -> Self {
match state.fluid {
@@ -185,14 +205,14 @@ impl From<FluidState> for BlockState {
azalea_registry::Fluid::Water | azalea_registry::Fluid::FlowingWater => {
BlockState::from(crate::blocks::Water {
level: crate::properties::WaterLevel::from(
- state.height as BlockStateIntegerRepr,
+ state.amount as BlockStateIntegerRepr,
),
})
}
azalea_registry::Fluid::Lava | azalea_registry::Fluid::FlowingLava => {
BlockState::from(crate::blocks::Lava {
level: crate::properties::LavaLevel::from(
- state.height as BlockStateIntegerRepr,
+ state.amount as BlockStateIntegerRepr,
),
})
}
diff --git a/azalea-entity/src/plugin/mod.rs b/azalea-entity/src/plugin/mod.rs
index 90d7f1c5..92918c69 100644
--- a/azalea-entity/src/plugin/mod.rs
+++ b/azalea-entity/src/plugin/mod.rs
@@ -104,7 +104,7 @@ pub fn update_fluid_on_eyes(
.read()
.get_fluid_state(&eye_block_pos)
.unwrap_or_default();
- let fluid_cutoff_y = eye_block_pos.y as f64 + (fluid_at_eye.height as f64 / 16f64);
+ let fluid_cutoff_y = eye_block_pos.y as f64 + (fluid_at_eye.amount as f64 / 16f64);
if fluid_cutoff_y > adjusted_eye_y {
**fluid_on_eyes = fluid_at_eye.fluid;
} else {
diff --git a/azalea-physics/Cargo.toml b/azalea-physics/Cargo.toml
index 8b963d06..60d80787 100644
--- a/azalea-physics/Cargo.toml
+++ b/azalea-physics/Cargo.toml
@@ -19,4 +19,5 @@ azalea-registry = { path = "../azalea-registry", version = "0.11.0" }
azalea-world = { path = "../azalea-world", version = "0.11.0" }
bevy_app = { workspace = true }
bevy_ecs = { workspace = true }
+tracing = { workspace = true }
parking_lot = { workspace = true }
diff --git a/azalea-physics/src/clip.rs b/azalea-physics/src/clip.rs
index fd6bbf0b..4a374f58 100644
--- a/azalea-physics/src/clip.rs
+++ b/azalea-physics/src/clip.rs
@@ -20,12 +20,11 @@ pub struct ClipContext {
// pub collision_context: EntityCollisionContext,
}
impl ClipContext {
- // minecraft passes in the world and blockpos here... but it doesn't actually
- // seem necessary?
-
/// Get the shape of given block, using the type of shape set in
/// [`Self::block_shape_type`].
pub fn block_shape(&self, block_state: BlockState) -> &VoxelShape {
+ // minecraft passes in the world and blockpos to this function but it's not
+ // actually necessary. it is for fluid_shape though
match self.block_shape_type {
BlockShapeType::Collider => block_state.collision_shape(),
BlockShapeType::Outline => block_state.outline_shape(),
@@ -41,6 +40,19 @@ impl ClipContext {
}
}
}
+
+ pub fn fluid_shape(
+ &self,
+ fluid_state: FluidState,
+ world: &ChunkStorage,
+ pos: &BlockPos,
+ ) -> &VoxelShape {
+ if self.fluid_pick_type.can_pick(&fluid_state) {
+ crate::collision::fluid_shape(&fluid_state, world, pos)
+ } else {
+ &EMPTY_SHAPE
+ }
+ }
}
#[derive(Debug, Copy, Clone)]
@@ -63,6 +75,17 @@ pub enum FluidPickType {
Any,
Water,
}
+impl FluidPickType {
+ pub fn can_pick(&self, fluid_state: &FluidState) -> bool {
+ match self {
+ Self::None => false,
+ Self::SourceOnly => fluid_state.amount == 8,
+ Self::Any => fluid_state.fluid != azalea_registry::Fluid::Empty,
+ Self::Water => fluid_state.fluid == azalea_registry::Fluid::Water,
+ }
+ }
+}
+
#[derive(Debug, Clone)]
pub struct EntityCollisionContext {
pub descending: bool,
@@ -81,15 +104,29 @@ pub fn clip(chunk_storage: &ChunkStorage, context: ClipContext) -> BlockHitResul
let block_state = chunk_storage.get_block_state(block_pos).unwrap_or_default();
let fluid_state = FluidState::from(block_state);
- // TODO: add fluid stuff to this (see getFluidState in vanilla source)
let block_shape = ctx.block_shape(block_state);
+ let interaction_clip = clip_with_interaction_override(
+ &ctx.from,
+ &ctx.to,
+ block_pos,
+ block_shape,
+ &block_state,
+ );
+ let fluid_shape = ctx.fluid_shape(fluid_state, chunk_storage, block_pos);
+ let fluid_clip = fluid_shape.clip(&ctx.from, &ctx.to, block_pos);
- clip_with_interaction_override(&ctx.from, &ctx.to, block_pos, block_shape, &block_state)
- // let block_distance = if let Some(block_hit_result) =
- // block_hit_result { context.from.distance_squared_to(&
- // block_hit_result.location) } else {
- // f64::INFINITY
- // };
+ let distance_to_interaction = interaction_clip
+ .map(|hit| ctx.from.distance_squared_to(&hit.location))
+ .unwrap_or(f64::MAX);
+ let distance_to_fluid = fluid_clip
+ .map(|hit| ctx.from.distance_squared_to(&hit.location))
+ .unwrap_or(f64::MAX);
+
+ if distance_to_interaction <= distance_to_fluid {
+ interaction_clip
+ } else {
+ fluid_clip
+ }
},
|context| {
let vec = context.from - context.to;
@@ -107,9 +144,10 @@ fn clip_with_interaction_override(
to: &Vec3,
block_pos: &BlockPos,
block_shape: &VoxelShape,
- block_state: &BlockState,
+ _block_state: &BlockState,
) -> Option<BlockHitResult> {
let block_hit_result = block_shape.clip(from, to, block_pos);
+
if let Some(block_hit_result) = block_hit_result {
// TODO: minecraft calls .getInteractionShape here
// getInteractionShape is empty for almost every shape except cauldons,
@@ -123,9 +161,10 @@ fn clip_with_interaction_override(
return Some(block_hit_result.with_direction(interaction_hit_result.direction));
}
}
+
Some(block_hit_result)
} else {
- block_hit_result
+ None
}
}
diff --git a/azalea-physics/src/collision/mod.rs b/azalea-physics/src/collision/mod.rs
index 913cedac..bff9d6f8 100644
--- a/azalea-physics/src/collision/mod.rs
+++ b/azalea-physics/src/collision/mod.rs
@@ -4,14 +4,21 @@ mod mergers;
mod shape;
mod world_collisions;
-use std::ops::Add;
-
-use azalea_core::{aabb::AABB, direction::Axis, math::EPSILON, position::Vec3};
-use azalea_world::{Instance, MoveEntityError};
+use std::{ops::Add, sync::LazyLock};
+
+use azalea_block::FluidState;
+use azalea_core::{
+ aabb::AABB,
+ direction::Axis,
+ math::EPSILON,
+ position::{BlockPos, Vec3},
+};
+use azalea_world::{ChunkStorage, Instance, MoveEntityError};
use bevy_ecs::world::Mut;
pub use blocks::BlockWithShape;
pub use discrete_voxel_shape::*;
pub use shape::*;
+use tracing::warn;
use self::world_collisions::get_block_collisions;
@@ -333,3 +340,48 @@ fn collide_with_shapes(
z: z_movement,
}
}
+
+/// Get the [`VoxelShape`] for the given fluid state.
+///
+/// The instance and position are required so it can check if the block above is
+/// also the same fluid type.
+pub fn fluid_shape(
+ fluid: &FluidState,
+ world: &ChunkStorage,
+ pos: &BlockPos,
+) -> &'static VoxelShape {
+ if fluid.amount == 9 {
+ let fluid_state_above = world.get_fluid_state(&pos.up(1)).unwrap_or_default();
+ if fluid_state_above.fluid == fluid.fluid {
+ return &BLOCK_SHAPE;
+ }
+ }
+
+ // pre-calculate these in a LazyLock so this function can return a
+ // reference instead
+
+ static FLUID_SHAPES: LazyLock<[VoxelShape; 10]> = LazyLock::new(|| {
+ [
+ calculate_shape_for_fluid(0),
+ calculate_shape_for_fluid(1),
+ calculate_shape_for_fluid(2),
+ calculate_shape_for_fluid(3),
+ calculate_shape_for_fluid(4),
+ calculate_shape_for_fluid(5),
+ calculate_shape_for_fluid(6),
+ calculate_shape_for_fluid(7),
+ calculate_shape_for_fluid(8),
+ calculate_shape_for_fluid(9),
+ ]
+ });
+
+ if fluid.amount > 9 {
+ warn!("Tried to calculate shape for fluid with height > 9: {fluid:?} at {pos}");
+ return &EMPTY_SHAPE;
+ }
+
+ &FLUID_SHAPES[fluid.amount as usize]
+}
+fn calculate_shape_for_fluid(amount: u8) -> VoxelShape {
+ box_shape(0.0, 0.0, 0.0, 1.0, (f32::from(amount) / 9.0) as f64, 1.0)
+}
diff --git a/azalea-physics/src/collision/shape.rs b/azalea-physics/src/collision/shape.rs
index e7ac9c2e..fb733cae 100755
--- a/azalea-physics/src/collision/shape.rs
+++ b/azalea-physics/src/collision/shape.rs
@@ -413,7 +413,7 @@ impl VoxelShape {
VoxelShape::Cube(s) => s.find_index(axis, coord),
_ => {
let upper_limit = (self.shape().size(axis) + 1) as i32;
- binary_search(0, upper_limit, &|t| coord < self.get(axis, t as usize)) - 1
+ binary_search(0, upper_limit, |t| coord < self.get(axis, t as usize)) - 1
}
}
}
diff --git a/azalea/src/pathfinder/mod.rs b/azalea/src/pathfinder/mod.rs
index 9f8f9052..01375590 100644
--- a/azalea/src/pathfinder/mod.rs
+++ b/azalea/src/pathfinder/mod.rs
@@ -340,8 +340,6 @@ pub fn calculate_path(opts: CalculatePathOpts) -> Option<PathFoundEvent> {
call_successors_fn(&cached_world, &opts.mining_cache, opts.successors_fn, pos)
};
- let path;
-
let start_time = Instant::now();
let astar::Path {
@@ -375,7 +373,7 @@ pub fn calculate_path(opts: CalculatePathOpts) -> Option<PathFoundEvent> {
debug!(" {}", movement.target.apply(origin));
}
- path = movements.into_iter().collect::<VecDeque<_>>();
+ let path = movements.into_iter().collect::<VecDeque<_>>();
let goto_id_now = opts.goto_id_atomic.load(atomic::Ordering::SeqCst);
if goto_id != goto_id_now {
@@ -520,6 +518,7 @@ pub fn path_found_listener(
}
}
+#[allow(clippy::type_complexity)]
pub fn timeout_movement(
mut query: Query<(
Entity,