Skip to content

Commit

Permalink
Initial implementation of fluid pushing
Browse files Browse the repository at this point in the history
  • Loading branch information
mat-1 committed Jan 9, 2025
1 parent 56e0f61 commit 7d92a7e
Show file tree
Hide file tree
Showing 20 changed files with 366 additions and 120 deletions.
9 changes: 9 additions & 0 deletions azalea-block/src/behavior.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ pub struct BlockBehavior {
pub destroy_time: f32,
pub explosion_resistance: f32,
pub requires_correct_tool_for_drops: bool,

pub force_solid: Option<bool>,
}

impl Default for BlockBehavior {
Expand All @@ -14,6 +16,7 @@ impl Default for BlockBehavior {
destroy_time: 0.,
explosion_resistance: 0.,
requires_correct_tool_for_drops: false,
force_solid: None,
}
}
}
Expand Down Expand Up @@ -52,4 +55,10 @@ impl BlockBehavior {
self.requires_correct_tool_for_drops = true;
self
}

// TODO: currently unused
pub fn force_solid(mut self, force_solid: bool) -> Self {
self.force_solid = Some(force_solid);
self
}
}
6 changes: 6 additions & 0 deletions azalea-block/src/block_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,14 @@ pub struct BlockState {
}

impl BlockState {
/// A shortcut for getting the air block state, since it always has an ID of
/// 0.
pub const AIR: BlockState = BlockState { id: 0 };

/// Whether the block state is possible to exist in vanilla Minecraft.
///
/// It's equivalent to checking that the state ID is not greater than
/// [`Self::MAX_STATE`].
#[inline]
pub fn is_valid_state(state_id: BlockStateIntegerRepr) -> bool {
state_id <= Self::MAX_STATE
Expand Down
42 changes: 38 additions & 4 deletions azalea-block/src/fluid_state.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
use crate::block_state::{BlockState, BlockStateIntegerRepr};
use crate::{
block_state::{BlockState, BlockStateIntegerRepr},
Block,
};

#[derive(Clone, Debug)]
pub struct FluidState {
Expand All @@ -12,6 +15,13 @@ pub struct FluidState {
/// basically the opposite (0 = full, 8 = empty). You can convert between
/// the two representations with [`to_or_from_legacy_fluid_level`].
pub amount: u8,

/// Whether this fluid is at the max level and there's another fluid of the
/// same type above it.
///
/// TODO: this is currently unused (always false), make this actually get
/// set (see FlowingFluid.getFlowing)
pub falling: bool,
}
impl FluidState {
/// A floating point number in between 0 and 1 representing the height (as a
Expand All @@ -20,9 +30,28 @@ impl FluidState {
self.amount as f32 / 9.
}

pub fn get_flow(world: &Instance, pos: BlockPos) {
let _ = world;
let _ = pos;
pub fn affects_flow(&self, other: &FluidState) -> bool {
other.amount == 0 || self.is_same_kind(other)
}

pub fn is_same_kind(&self, other: &FluidState) -> bool {
(other.is_water() && self.is_water())
|| (other.is_lava() && self.is_lava())
|| (self.amount == 0 && other.amount == 0)
}

pub fn is_water(&self) -> bool {
matches!(
self.fluid,
azalea_registry::Fluid::Water | azalea_registry::Fluid::FlowingWater
)
}

pub fn is_lava(&self) -> bool {
matches!(
self.fluid,
azalea_registry::Fluid::Lava | azalea_registry::Fluid::FlowingLava
)
}
}

Expand All @@ -31,6 +60,7 @@ impl Default for FluidState {
Self {
fluid: azalea_registry::Fluid::Empty,
amount: 0,
falling: false,
}
}
}
Expand All @@ -47,23 +77,27 @@ impl From<BlockState> for FluidState {
Self {
fluid: azalea_registry::Fluid::Water,
amount: 8,
falling: false,
}
} else {
let block = Box::<dyn Block>::from(state);
if let Some(water) = block.downcast_ref::<crate::blocks::Water>() {
Self {
fluid: azalea_registry::Fluid::Water,
amount: to_or_from_legacy_fluid_level(water.level as u8),
falling: false,
}
} else if let Some(lava) = block.downcast_ref::<crate::blocks::Lava>() {
Self {
fluid: azalea_registry::Fluid::Lava,
amount: to_or_from_legacy_fluid_level(lava.level as u8),
falling: false,
}
} else {
Self {
fluid: azalea_registry::Fluid::Empty,
amount: 0,
falling: false,
}
}
}
Expand Down
10 changes: 3 additions & 7 deletions azalea-block/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,11 @@ mod generated;
mod range;

use core::fmt::Debug;
use std::{
any::Any,
fmt,
io::{self, Cursor, Write},
};
use std::any::Any;

use azalea_buf::{AzaleaRead, AzaleaReadVar, AzaleaWrite, AzaleaWriteVar, BufReadError};
pub use behavior::BlockBehavior;
use block_state::BlockState;
// re-exported for convenience
pub use block_state::BlockState;
pub use generated::{blocks, properties};
pub use range::BlockStates;

Expand Down
2 changes: 1 addition & 1 deletion azalea-client/src/mining.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use azalea_block::{Block, BlockState, FluidState};
use azalea_block::{fluid_state::FluidState, Block, BlockState};
use azalea_core::{direction::Direction, game_type::GameMode, position::BlockPos, tick::GameTick};
use azalea_entity::{mining::get_mine_progress, FluidOnEyes, Physics};
use azalea_inventory::ItemStack;
Expand Down
3 changes: 2 additions & 1 deletion azalea-client/src/movement.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,8 @@ impl Plugin for PlayerMovePlugin {
(tick_controls, local_player_ai_step)
.chain()
.in_set(PhysicsSet)
.before(ai_step),
.before(ai_step)
.before(azalea_physics::fluids::update_in_water_state_and_do_fluid_pushing),
send_sprinting_if_needed.after(azalea_entity::update_in_loaded_chunk),
send_position.after(PhysicsSet),
)
Expand Down
42 changes: 33 additions & 9 deletions azalea-core/src/direction.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use azalea_buf::AzBuf;

use crate::position::Vec3;
use crate::position::{BlockPos, Vec3};

#[derive(Clone, Copy, Debug, AzBuf, Default, Eq, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
Expand All @@ -15,6 +15,14 @@ pub enum Direction {
}

impl Direction {
pub const HORIZONTAL: [Direction; 4] = [
Direction::North,
Direction::South,
Direction::West,
Direction::East,
];
pub const VERTICAL: [Direction; 2] = [Direction::Down, Direction::Up];

pub fn nearest(vec: Vec3) -> Direction {
let mut best_direction = Direction::North;
let mut best_direction_amount = 0.0;
Expand All @@ -29,7 +37,7 @@ impl Direction {
]
.iter()
{
let amount = dir.normal().dot(vec);
let amount = dir.normal_vec3().dot(vec);
if amount > best_direction_amount {
best_direction = *dir;
best_direction_amount = amount;
Expand All @@ -39,17 +47,23 @@ impl Direction {
best_direction
}

pub fn normal(self) -> Vec3 {
#[inline]
pub fn normal(self) -> BlockPos {
match self {
Direction::Down => Vec3::new(0.0, -1.0, 0.0),
Direction::Up => Vec3::new(0.0, 1.0, 0.0),
Direction::North => Vec3::new(0.0, 0.0, -1.0),
Direction::South => Vec3::new(0.0, 0.0, 1.0),
Direction::West => Vec3::new(-1.0, 0.0, 0.0),
Direction::East => Vec3::new(1.0, 0.0, 0.0),
Direction::Down => BlockPos::new(0, -1, 0),
Direction::Up => BlockPos::new(0, 1, 0),
Direction::North => BlockPos::new(0, 0, -1),
Direction::South => BlockPos::new(0, 0, 1),
Direction::West => BlockPos::new(-1, 0, 0),
Direction::East => BlockPos::new(1, 0, 0),
}
}

#[inline]
pub fn normal_vec3(self) -> Vec3 {
self.normal().to_vec3_floored()
}

pub fn opposite(self) -> Direction {
match self {
Direction::Down => Direction::Up,
Expand All @@ -60,6 +74,16 @@ impl Direction {
Direction::East => Direction::West,
}
}

pub fn x(self) -> i32 {
self.normal().x
}
pub fn y(self) -> i32 {
self.normal().y
}
pub fn z(self) -> i32 {
self.normal().z
}
}

/// The four cardinal directions.
Expand Down
49 changes: 47 additions & 2 deletions azalea-core/src/position.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,12 @@ use std::{
fmt,
hash::Hash,
io::{Cursor, Write},
ops::{Add, AddAssign, Mul, Rem, Sub},
ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Rem, Sub},
};

use azalea_buf::{AzBuf, AzaleaRead, AzaleaWrite, BufReadError};

use crate::direction::Direction;
use crate::math;
use crate::resource_location::ResourceLocation;

Expand Down Expand Up @@ -138,7 +139,6 @@ macro_rules! vec3_impl {
}
}
}

impl Add for $name {
type Output = $name;

Expand All @@ -147,6 +147,18 @@ macro_rules! vec3_impl {
(&self).add(&rhs)
}
}
impl Add<$type> for $name {
type Output = Self;

#[inline]
fn add(self, rhs: $type) -> Self::Output {
Self {
x: self.x + rhs,
y: self.y + rhs,
z: self.z + rhs,
}
}
}

impl AddAssign for $name {
#[inline]
Expand Down Expand Up @@ -203,6 +215,35 @@ macro_rules! vec3_impl {
}
}
}
impl MulAssign<$type> for $name {
#[inline]
fn mul_assign(&mut self, multiplier: $type) {
self.x *= multiplier;
self.y *= multiplier;
self.z *= multiplier;
}
}

impl Div<$type> for $name {
type Output = Self;

#[inline]
fn div(self, divisor: $type) -> Self::Output {
Self {
x: self.x / divisor,
y: self.y / divisor,
z: self.z / divisor,
}
}
}
impl DivAssign<$type> for $name {
#[inline]
fn div_assign(&mut self, divisor: $type) {
self.x /= divisor;
self.y /= divisor;
self.z /= divisor;
}
}

impl From<($type, $type, $type)> for $name {
#[inline]
Expand Down Expand Up @@ -345,6 +386,10 @@ impl BlockPos {
z: self.z.max(other.z),
}
}

pub fn offset_with_direction(self, direction: Direction) -> Self {
self + direction.normal()
}
}

/// Chunk coordinates are used to represent where a chunk is in the world. You
Expand Down
2 changes: 1 addition & 1 deletion azalea-physics/src/clip.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use azalea_block::{BlockState, FluidState};
use azalea_block::{fluid_state::FluidState, BlockState};
use azalea_core::{
block_hit_result::BlockHitResult,
direction::Direction,
Expand Down
36 changes: 15 additions & 21 deletions azalea-physics/src/collision/discrete_voxel_shape.rs
Original file line number Diff line number Diff line change
Expand Up @@ -238,39 +238,33 @@ impl BitSetDiscreteVoxelShape {
var2: bool,
) {
let mut var3 = BitSetDiscreteVoxelShape::from(var0);
for var4 in 0..var3.y_size {
for var5 in 0..var3.x_size {
for y in 0..var3.y_size {
for x in 0..var3.x_size {
let mut var6 = None;
for var7 in 0..=var3.z_size {
if var3.is_full_wide(var5, var4, var7) {
for z in 0..=var3.z_size {
if var3.is_full_wide(x, y, z) {
if var2 {
if var6.is_none() {
var6 = Some(var7);
var6 = Some(z);
}
} else {
consumer(var5, var4, var7, var5 + 1, var4 + 1, var7 + 1);
consumer(x, y, z, x + 1, y + 1, z + 1);
}
} else if var6.is_some() {
let mut var8 = var5;
let mut var9 = var4;
var3.clear_z_strip(var6.unwrap(), var7, var5, var4);
while var3.is_z_strip_full(var6.unwrap(), var7, var8 + 1, var4) {
var3.clear_z_strip(var6.unwrap(), var7, var8 + 1, var4);
let mut var8 = x;
let mut var9 = y;
var3.clear_z_strip(var6.unwrap(), z, x, y);
while var3.is_z_strip_full(var6.unwrap(), z, var8 + 1, y) {
var3.clear_z_strip(var6.unwrap(), z, var8 + 1, y);
var8 += 1;
}
while var3.is_xz_rectangle_full(
var5,
var8 + 1,
var6.unwrap(),
var7,
var9 + 1,
) {
for var10 in var5..=var8 {
var3.clear_z_strip(var6.unwrap(), var7, var10, var9 + 1);
while var3.is_xz_rectangle_full(x, var8 + 1, var6.unwrap(), z, var9 + 1) {
for var10 in x..=var8 {
var3.clear_z_strip(var6.unwrap(), z, var10, var9 + 1);
}
var9 += 1;
}
consumer(var5, var4, var6.unwrap(), var8 + 1, var9 + 1, var7);
consumer(x, y, var6.unwrap(), var8 + 1, var9 + 1, z);
var6 = None;
}
}
Expand Down
Loading

0 comments on commit 7d92a7e

Please sign in to comment.