aboutsummaryrefslogtreecommitdiff
path: root/azalea-physics
diff options
context:
space:
mode:
authormat <27899617+mat-1@users.noreply.github.com>2023-05-03 20:57:27 -0500
committerGitHub <noreply@github.com>2023-05-03 20:57:27 -0500
commit634cb8d72c6608512aedba19e5cd669104bc35ea (patch)
treef8e76ce9eb43403d29cc0cbcf9a4f51522419dc2 /azalea-physics
parent1fb4418f2c9cbd004c64c2f23d2d0352ee12c0e5 (diff)
downloadazalea-drasl-634cb8d72c6608512aedba19e5cd669104bc35ea.tar.xz
Inventory (#48)
* start adding azalea-inventory * design more of how inventories are defined * start working on az-inv-macros * inventory macro works * start adding inventory codegen * update some deps * add inventory codegen * manually write inventory menus * put the inventories in Client * start on containersetcontent * inventory menu should hopefully work * checks in containersetcontent * format a comment * move some variant matches * inventory.rs * inventory stuff * more inventory stuff * inventory/container tracking works * start adding interact function * sequence number * start adding HitResultComponent * implement traverse_blocks * start adding clip * add clip function * update_hit_result_component * start trying to fix * fix * make some stuff simpler * clippy * lever * chest * container handle * fix ambiguity * fix some doc tests * move some container stuff from az-client to azalea * clicking container * start implementing simulate_click * keep working on simulate click * implement more of simulate_click this is really boring * inventory fixes * start implementing shift clicking * fix panic in azalea-chat i hope * shift clicking implemented * more inventory stuff * fix items not showing in containers sometimes * fix test * fix all warnings * remove a println --------- Co-authored-by: mat <git@matdoes.dev>
Diffstat (limited to 'azalea-physics')
-rw-r--r--azalea-physics/Cargo.toml1
-rw-r--r--azalea-physics/src/clip.rs232
-rwxr-xr-xazalea-physics/src/collision/discrete_voxel_shape.rs1
-rw-r--r--azalea-physics/src/collision/mod.rs23
-rwxr-xr-xazalea-physics/src/collision/shape.rs62
-rw-r--r--azalea-physics/src/lib.rs49
6 files changed, 340 insertions, 28 deletions
diff --git a/azalea-physics/Cargo.toml b/azalea-physics/Cargo.toml
index dd579471..ca7390eb 100644
--- a/azalea-physics/Cargo.toml
+++ b/azalea-physics/Cargo.toml
@@ -11,6 +11,7 @@ version = "0.6.0"
[dependencies]
azalea-block = { path = "../azalea-block", version = "^0.6.0" }
azalea-core = { path = "../azalea-core", version = "^0.6.0" }
+azalea-inventory = { version = "0.1.0", path = "../azalea-inventory" }
azalea-registry = { path = "../azalea-registry", version = "^0.6.0" }
azalea-world = { path = "../azalea-world", version = "^0.6.0" }
bevy_app = "0.10.0"
diff --git a/azalea-physics/src/clip.rs b/azalea-physics/src/clip.rs
new file mode 100644
index 00000000..ca85c32a
--- /dev/null
+++ b/azalea-physics/src/clip.rs
@@ -0,0 +1,232 @@
+use azalea_block::BlockState;
+use azalea_core::{lerp, BlockHitResult, BlockPos, Direction, Vec3, EPSILON};
+use azalea_inventory::ItemSlot;
+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: ItemSlot,
+ // 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,
+ |context, 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 = context.block_shape(block_state);
+ clip_with_interaction_override(
+ &context.from,
+ &context.to,
+ block_pos,
+ block_shape,
+ &block_state,
+ )
+ // let block_distance = if let Some(block_hit_result) =
+ // block_hit_result { context.from.distance_to_sqr(&
+ // block_hit_result.location) } else {
+ // f64::MAX
+ // };
+ },
+ |context| {
+ let vec = context.from - context.to;
+ BlockHitResult::miss(
+ context.to,
+ Direction::nearest(vec),
+ BlockPos::from(context.to),
+ )
+ },
+ )
+}
+
+// default BlockHitResult clipWithInteractionOverride(Vec3 world, Vec3 from,
+// BlockPos to, VoxelShape shape, BlockState block) {
+// BlockHitResult blockHitResult = shape.clip(world, from, to);
+// if (blockHitResult != null) {
+// BlockHitResult var7 = block.getInteractionShape(this, to).clip(world,
+// from, to); if (var7 != null
+// && var7.getLocation().subtract(world).lengthSqr() <
+// blockHitResult.getLocation().subtract(world).lengthSqr()) { return
+// blockHitResult.withDirection(var7.getDirection()); }
+// }
+
+// return blockHitResult;
+// }
+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
+ // are there even any blocks that have a physics shape different from the
+ // interaction shape???
+ // (if not then you can delete this comment)
+ // (if there are then you have 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_to_sqr(from)
+ < block_hit_result.location.distance_to_sqr(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, &current_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. - right_before_start.x.fract()
+ } else {
+ right_before_start.x.fract().abs()
+ },
+ y: percentage_step.y
+ * if vec_sign.y > 0. {
+ 1. - right_before_start.y.fract()
+ } else {
+ right_before_start.y.fract().abs()
+ },
+ z: percentage_step.z
+ * if vec_sign.z > 0. {
+ 1. - right_before_start.z.fract()
+ } else {
+ right_before_start.z.fract().abs()
+ },
+ };
+
+ 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, &current_block) {
+ return data;
+ }
+ }
+}
diff --git a/azalea-physics/src/collision/discrete_voxel_shape.rs b/azalea-physics/src/collision/discrete_voxel_shape.rs
index 4a329398..2bcd1f61 100755
--- a/azalea-physics/src/collision/discrete_voxel_shape.rs
+++ b/azalea-physics/src/collision/discrete_voxel_shape.rs
@@ -45,6 +45,7 @@ impl DiscreteVoxelShape {
return false;
}
let (x, y, z) = (x as u32, y as u32, z as u32);
+
(x < self.size(Axis::X) && y < self.size(Axis::Y) && z < self.size(Axis::Z))
&& (self.is_full(x, y, z))
}
diff --git a/azalea-physics/src/collision/mod.rs b/azalea-physics/src/collision/mod.rs
index 53efd2fe..a99b5710 100644
--- a/azalea-physics/src/collision/mod.rs
+++ b/azalea-physics/src/collision/mod.rs
@@ -5,10 +5,7 @@ mod shape;
mod world_collisions;
use azalea_core::{Axis, Vec3, AABB, EPSILON};
-use azalea_world::{
- entity::{self},
- Instance, MoveEntityError,
-};
+use azalea_world::{entity, Instance, MoveEntityError};
pub use blocks::BlockWithShape;
pub use discrete_voxel_shape::*;
pub use shape::*;
@@ -219,7 +216,11 @@ fn collide_with_shapes(
if y_movement != 0. {
y_movement = Shapes::collide(&Axis::Y, &entity_box, collision_boxes, y_movement);
if y_movement != 0. {
- entity_box = entity_box.move_relative(0., y_movement, 0.);
+ entity_box = entity_box.move_relative(&Vec3 {
+ x: 0.,
+ y: y_movement,
+ z: 0.,
+ });
}
}
@@ -230,14 +231,22 @@ fn collide_with_shapes(
if more_z_movement && z_movement != 0. {
z_movement = Shapes::collide(&Axis::Z, &entity_box, collision_boxes, z_movement);
if z_movement != 0. {
- entity_box = entity_box.move_relative(0., 0., z_movement);
+ entity_box = entity_box.move_relative(&Vec3 {
+ x: 0.,
+ y: 0.,
+ z: z_movement,
+ });
}
}
if x_movement != 0. {
x_movement = Shapes::collide(&Axis::X, &entity_box, collision_boxes, x_movement);
if x_movement != 0. {
- entity_box = entity_box.move_relative(x_movement, 0., 0.);
+ entity_box = entity_box.move_relative(&Vec3 {
+ x: x_movement,
+ y: 0.,
+ z: 0.,
+ });
}
}
diff --git a/azalea-physics/src/collision/shape.rs b/azalea-physics/src/collision/shape.rs
index cc184591..29c1b440 100755
--- a/azalea-physics/src/collision/shape.rs
+++ b/azalea-physics/src/collision/shape.rs
@@ -1,9 +1,11 @@
use super::mergers::IndexMerger;
use crate::collision::{BitSetDiscreteVoxelShape, DiscreteVoxelShape, AABB};
-use azalea_core::{binary_search, Axis, AxisCycle, EPSILON};
+use azalea_core::{
+ binary_search, Axis, AxisCycle, BlockHitResult, BlockPos, Direction, Vec3, EPSILON,
+};
use std::{cmp, num::NonZeroU32};
-pub struct Shapes {}
+pub struct Shapes;
pub fn block_shape() -> VoxelShape {
let mut shape = BitSetDiscreteVoxelShape::new(1, 1, 1);
@@ -390,6 +392,33 @@ impl VoxelShape {
}
}
+ pub fn clip(&self, from: &Vec3, to: &Vec3, block_pos: &BlockPos) -> Option<BlockHitResult> {
+ if self.is_empty() {
+ return None;
+ }
+ let vector = to - from;
+ if vector.length_sqr() < EPSILON {
+ return None;
+ }
+ let right_after_start = from + &(vector * 0.0001);
+
+ if self.shape().is_full_wide(
+ self.find_index(Axis::X, right_after_start.x - block_pos.x as f64),
+ self.find_index(Axis::Y, right_after_start.y - block_pos.y as f64),
+ self.find_index(Axis::Z, right_after_start.z - block_pos.z as f64),
+ ) {
+ Some(BlockHitResult {
+ block_pos: *block_pos,
+ direction: Direction::nearest(vector).opposite(),
+ location: right_after_start,
+ inside: true,
+ miss: false,
+ })
+ } else {
+ AABB::clip_iterable(&self.to_aabbs(), from, to, block_pos)
+ }
+ }
+
pub fn collide(&self, axis: &Axis, entity_box: &AABB, movement: f64) -> f64 {
self.collide_x(AxisCycle::between(*axis, Axis::X), entity_box, movement)
}
@@ -531,19 +560,34 @@ impl VoxelShape {
let y_coords = self.get_coords(Axis::Y);
let z_coords = self.get_coords(Axis::Z);
self.shape().for_all_boxes(
- |var4x, var5, var6, var7, var8, var9| {
+ |min_x, min_y, min_z, max_x, max_y, max_z| {
consumer(
- x_coords[var4x as usize],
- y_coords[var5 as usize],
- z_coords[var6 as usize],
- x_coords[var7 as usize],
- y_coords[var8 as usize],
- z_coords[var9 as usize],
+ x_coords[min_x as usize],
+ y_coords[min_y as usize],
+ z_coords[min_z as usize],
+ x_coords[max_x as usize],
+ y_coords[max_y as usize],
+ z_coords[max_z as usize],
);
},
true,
);
}
+
+ pub fn to_aabbs(&self) -> Vec<AABB> {
+ let mut aabbs = Vec::new();
+ self.for_all_boxes(|min_x, min_y, min_z, max_x, max_y, max_z| {
+ aabbs.push(AABB {
+ min_x,
+ min_y,
+ min_z,
+ max_x,
+ max_y,
+ max_z,
+ });
+ });
+ aabbs
+ }
}
impl From<AABB> for VoxelShape {
diff --git a/azalea-physics/src/lib.rs b/azalea-physics/src/lib.rs
index 049091f7..57c2100e 100644
--- a/azalea-physics/src/lib.rs
+++ b/azalea-physics/src/lib.rs
@@ -1,14 +1,15 @@
#![doc = include_str!("../README.md")]
#![feature(trait_alias)]
+pub mod clip;
pub mod collision;
use azalea_block::{Block, BlockState};
use azalea_core::{BlockPos, Vec3};
use azalea_world::{
entity::{
- metadata::Sprinting, move_relative, Attributes, Jumping, Local, Physics, Position,
- WorldName,
+ clamp_look_direction, metadata::Sprinting, move_relative, Attributes, Jumping, Local,
+ LookDirection, Physics, Position, WorldName,
},
Instance, InstanceContainer,
};
@@ -30,7 +31,11 @@ pub struct PhysicsPlugin;
impl Plugin for PhysicsPlugin {
fn build(&self, app: &mut App) {
app.add_event::<ForceJumpEvent>()
- .add_system(force_jump_listener.before(azalea_world::entity::update_bounding_box))
+ .add_system(
+ force_jump_listener
+ .before(azalea_world::entity::update_bounding_box)
+ .after(clamp_look_direction),
+ )
.add_systems(
(ai_step, travel)
.chain()
@@ -43,11 +48,20 @@ impl Plugin for PhysicsPlugin {
/// Move the entity with the given acceleration while handling friction,
/// gravity, collisions, and some other stuff.
fn travel(
- mut query: Query<(&mut Physics, &mut Position, &Attributes, &WorldName), With<Local>>,
- world_container: Res<InstanceContainer>,
+ mut query: Query<
+ (
+ &mut Physics,
+ &mut LookDirection,
+ &mut Position,
+ &Attributes,
+ &WorldName,
+ ),
+ With<Local>,
+ >,
+ instance_container: Res<InstanceContainer>,
) {
- for (mut physics, mut position, attributes, world_name) in &mut query {
- let world_lock = world_container
+ for (mut physics, direction, mut position, attributes, world_name) in &mut query {
+ let world_lock = instance_container
.get(world_name)
.expect("All entities should be in a valid world");
let world = world_lock.read();
@@ -85,6 +99,7 @@ fn travel(
block_friction,
&world,
&mut physics,
+ &direction,
&mut position,
attributes,
);
@@ -158,13 +173,21 @@ pub fn ai_step(
pub struct ForceJumpEvent(pub Entity);
pub fn force_jump_listener(
- mut query: Query<(&mut Physics, &Position, &Sprinting, &WorldName)>,
- world_container: Res<InstanceContainer>,
+ mut query: Query<(
+ &mut Physics,
+ &Position,
+ &LookDirection,
+ &Sprinting,
+ &WorldName,
+ )>,
+ instance_container: Res<InstanceContainer>,
mut events: EventReader<ForceJumpEvent>,
) {
for event in events.iter() {
- if let Ok((mut physics, position, sprinting, world_name)) = query.get_mut(event.0) {
- let world_lock = world_container
+ if let Ok((mut physics, position, direction, sprinting, world_name)) =
+ query.get_mut(event.0)
+ {
+ let world_lock = instance_container
.get(world_name)
.expect("All entities should be in a valid world");
let world = world_lock.read();
@@ -178,7 +201,7 @@ pub fn force_jump_listener(
};
if **sprinting {
// sprint jumping gives some extra velocity
- let y_rot = physics.y_rot * 0.017453292;
+ let y_rot = direction.y_rot * 0.017453292;
physics.delta += Vec3 {
x: (-f32::sin(y_rot) * 0.2) as f64,
y: 0.,
@@ -204,11 +227,13 @@ fn handle_relative_friction_and_calculate_movement(
block_friction: f32,
world: &Instance,
physics: &mut Physics,
+ direction: &LookDirection,
position: &mut Position,
attributes: &Attributes,
) -> Vec3 {
move_relative(
physics,
+ direction,
get_friction_influenced_speed(physics, attributes, block_friction),
&Vec3 {
x: physics.xxa as f64,