mirror of
https://github.com/mat-1/azalea.git
synced 2025-08-02 14:26:04 +00:00
* start updating to 1.21.4 * fix block codegen and stop using block data from burger * rename packet related modules and structs to be simpler * ItemSlot -> ItemStack for more consistency with mojmap * .get() -> .into_packet() * simplify declare_state_packets by removing packet ids * rename read_from and write_into to azalea_read and azalea_write * rename McBufReadable and McBufWritable to AzaleaRead and AzaleaWrite * McBuf -> AzBuf * remove most uses of into_variant * update codegen and use resourcelocation names for packets * implement #[limit(i)] attribute for AzBuf derive macro * fixes for 1.21.4 * fix examples * update some physics code and fix ChatType * remove unused imports in codegen * re-add some things to migrate.py and update +mc version numbers automatically * downgrade to 1.21.3 lol
216 lines
6.7 KiB
Rust
216 lines
6.7 KiB
Rust
use azalea_block::BlockState;
|
|
use azalea_core::{
|
|
block_hit_result::BlockHitResult,
|
|
direction::Direction,
|
|
math::{self, lerp, EPSILON},
|
|
position::{BlockPos, Vec3},
|
|
};
|
|
use azalea_inventory::ItemStack;
|
|
use azalea_world::ChunkStorage;
|
|
use bevy_ecs::entity::Entity;
|
|
|
|
use crate::collision::{BlockWithShape, VoxelShape};
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub struct ClipContext {
|
|
pub from: Vec3,
|
|
pub to: Vec3,
|
|
pub block_shape_type: BlockShapeType,
|
|
pub fluid_pick_type: FluidPickType,
|
|
// pub collision_context: EntityCollisionContext,
|
|
}
|
|
impl ClipContext {
|
|
// minecraft passes in the world and blockpos here... but it doesn't actually
|
|
// seem necessary?
|
|
pub fn block_shape(&self, block_state: BlockState) -> &VoxelShape {
|
|
// TODO: implement the other shape getters
|
|
// (see the ClipContext.Block class in the vanilla source)
|
|
match self.block_shape_type {
|
|
BlockShapeType::Collider => block_state.shape(),
|
|
BlockShapeType::Outline => block_state.shape(),
|
|
BlockShapeType::Visual => block_state.shape(),
|
|
BlockShapeType::FallDamageResetting => block_state.shape(),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Copy, Clone)]
|
|
pub enum BlockShapeType {
|
|
Collider,
|
|
Outline,
|
|
Visual,
|
|
FallDamageResetting,
|
|
}
|
|
#[derive(Debug, Copy, Clone)]
|
|
pub enum FluidPickType {
|
|
None,
|
|
SourceOnly,
|
|
Any,
|
|
Water,
|
|
}
|
|
#[derive(Debug, Clone)]
|
|
pub struct EntityCollisionContext {
|
|
pub descending: bool,
|
|
pub entity_bottom: f64,
|
|
pub held_item: ItemStack,
|
|
// pub can_stand_on_fluid: Box<dyn Fn(&FluidState) -> bool>,
|
|
pub entity: Entity,
|
|
}
|
|
|
|
pub fn clip(chunk_storage: &ChunkStorage, context: ClipContext) -> BlockHitResult {
|
|
traverse_blocks(
|
|
context.from,
|
|
context.to,
|
|
context,
|
|
|ctx, block_pos| {
|
|
let block_state = chunk_storage.get_block_state(block_pos).unwrap_or_default();
|
|
// TODO: add fluid stuff to this (see getFluidState in vanilla source)
|
|
let block_shape = ctx.block_shape(block_state);
|
|
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
|
|
// };
|
|
},
|
|
|context| {
|
|
let vec = context.from - context.to;
|
|
BlockHitResult::miss(
|
|
context.to,
|
|
Direction::nearest(vec),
|
|
BlockPos::from(context.to),
|
|
)
|
|
},
|
|
)
|
|
}
|
|
|
|
fn clip_with_interaction_override(
|
|
from: &Vec3,
|
|
to: &Vec3,
|
|
block_pos: &BlockPos,
|
|
block_shape: &VoxelShape,
|
|
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
|
|
// some blocks (like tall grass) have a physics shape that's different from the
|
|
// interaction shape, so we need to implement BlockState::interaction_shape. lol
|
|
// have fun
|
|
let interaction_shape = block_state.shape();
|
|
let interaction_hit_result = interaction_shape.clip(from, to, block_pos);
|
|
if let Some(interaction_hit_result) = interaction_hit_result {
|
|
if interaction_hit_result.location.distance_squared_to(from)
|
|
< block_hit_result.location.distance_squared_to(from)
|
|
{
|
|
return Some(block_hit_result.with_direction(interaction_hit_result.direction));
|
|
}
|
|
}
|
|
Some(block_hit_result)
|
|
} else {
|
|
block_hit_result
|
|
}
|
|
}
|
|
|
|
pub fn traverse_blocks<C, T>(
|
|
from: Vec3,
|
|
to: Vec3,
|
|
context: C,
|
|
get_hit_result: impl Fn(&C, &BlockPos) -> Option<T>,
|
|
get_miss_result: impl Fn(&C) -> T,
|
|
) -> T {
|
|
if from == to {
|
|
return get_miss_result(&context);
|
|
}
|
|
|
|
let right_after_end = Vec3 {
|
|
x: lerp(-EPSILON, to.x, from.x),
|
|
y: lerp(-EPSILON, to.y, from.y),
|
|
z: lerp(-EPSILON, to.z, from.z),
|
|
};
|
|
|
|
let right_before_start = Vec3 {
|
|
x: lerp(-EPSILON, from.x, to.x),
|
|
y: lerp(-EPSILON, from.y, to.y),
|
|
z: lerp(-EPSILON, from.z, to.z),
|
|
};
|
|
|
|
let mut current_block = BlockPos::from(right_before_start);
|
|
if let Some(data) = get_hit_result(&context, ¤t_block) {
|
|
return data;
|
|
}
|
|
|
|
let vec = right_after_end - right_before_start;
|
|
|
|
/// Returns either -1, 0, or 1, depending on whether the number is negative,
|
|
/// zero, or positive.
|
|
///
|
|
/// This function exists because f64::signum doesn't check for 0.
|
|
fn get_number_sign(num: f64) -> f64 {
|
|
if num == 0. {
|
|
0.
|
|
} else {
|
|
num.signum()
|
|
}
|
|
}
|
|
|
|
let vec_sign = Vec3 {
|
|
x: get_number_sign(vec.x),
|
|
y: get_number_sign(vec.y),
|
|
z: get_number_sign(vec.z),
|
|
};
|
|
|
|
#[rustfmt::skip]
|
|
let percentage_step = Vec3 {
|
|
x: if vec_sign.x == 0. { f64::MAX } else { vec_sign.x / vec.x },
|
|
y: if vec_sign.y == 0. { f64::MAX } else { vec_sign.y / vec.y },
|
|
z: if vec_sign.z == 0. { f64::MAX } else { vec_sign.z / vec.z },
|
|
};
|
|
|
|
let mut percentage = Vec3 {
|
|
x: percentage_step.x
|
|
* if vec_sign.x > 0. {
|
|
1. - math::fract(right_before_start.x)
|
|
} else {
|
|
math::fract(right_before_start.x)
|
|
},
|
|
y: percentage_step.y
|
|
* if vec_sign.y > 0. {
|
|
1. - math::fract(right_before_start.y)
|
|
} else {
|
|
math::fract(right_before_start.y)
|
|
},
|
|
z: percentage_step.z
|
|
* if vec_sign.z > 0. {
|
|
1. - math::fract(right_before_start.z)
|
|
} else {
|
|
math::fract(right_before_start.z)
|
|
},
|
|
};
|
|
|
|
loop {
|
|
if percentage.x > 1. && percentage.y > 1. && percentage.z > 1. {
|
|
return get_miss_result(&context);
|
|
}
|
|
|
|
if percentage.x < percentage.y {
|
|
if percentage.x < percentage.z {
|
|
current_block.x += vec_sign.x as i32;
|
|
percentage.x += percentage_step.x;
|
|
} else {
|
|
current_block.z += vec_sign.z as i32;
|
|
percentage.z += percentage_step.z;
|
|
}
|
|
} else if percentage.y < percentage.z {
|
|
current_block.y += vec_sign.y as i32;
|
|
percentage.y += percentage_step.y;
|
|
} else {
|
|
current_block.z += vec_sign.z as i32;
|
|
percentage.z += percentage_step.z;
|
|
}
|
|
|
|
if let Some(data) = get_hit_result(&context, ¤t_block) {
|
|
return data;
|
|
}
|
|
}
|
|
}
|