aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xazalea-block/azalea-block-macros/src/lib.rs21
-rwxr-xr-xazalea-block/src/lib.rs6
-rw-r--r--azalea-client/src/client.rs6
-rwxr-xr-xazalea-entity/src/data.rs2
-rw-r--r--azalea-entity/src/lib.rs19
-rw-r--r--azalea-entity/src/plugin/mod.rs71
-rw-r--r--azalea-physics/src/collision/mod.rs11
-rw-r--r--azalea-physics/src/lib.rs124
-rw-r--r--azalea/src/auto_tool.rs2
-rw-r--r--azalea/src/pathfinder/simulation.rs36
10 files changed, 237 insertions, 61 deletions
diff --git a/azalea-block/azalea-block-macros/src/lib.rs b/azalea-block/azalea-block-macros/src/lib.rs
index 270b49c7..3b983fb4 100755
--- a/azalea-block/azalea-block-macros/src/lib.rs
+++ b/azalea-block/azalea-block-macros/src/lib.rs
@@ -327,17 +327,11 @@ pub fn make_block_states(input: TokenStream) -> TokenStream {
}
property_enums.extend(quote! {
- #[derive(Debug, Clone, Copy)]
+ #[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum #property_struct_name {
#property_enum_variants
}
- // impl Property for #property_struct_name {
- // type Value = Self;
-
- // fn try_from_block_state
- // }
-
impl From<u32> for #property_struct_name {
fn from(value: u32) -> Self {
match value {
@@ -354,13 +348,9 @@ pub fn make_block_states(input: TokenStream) -> TokenStream {
property_variant_types = vec!["true".to_string(), "false".to_string()];
property_enums.extend(quote! {
- #[derive(Debug, Clone, Copy)]
+ #[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct #property_struct_name(pub bool);
- // impl Property for #property_struct_name {
- // type Value = bool;
- // }
-
impl From<u32> for #property_struct_name {
fn from(value: u32) -> Self {
match value {
@@ -542,10 +532,9 @@ pub fn make_block_states(input: TokenStream) -> TokenStream {
// add to properties_to_state_ids
let property_variants = properties_to_state_ids
.entry(property_value_name_ident.to_string())
- .or_insert_with(Vec::new);
- let property_variant_data = property_variants
- .iter_mut()
- .find(|v| v.ident.to_string() == variant.to_string());
+ .or_default();
+ let property_variant_data =
+ property_variants.iter_mut().find(|v| v.ident == variant);
if let Some(property_variant_data) = property_variant_data {
property_variant_data.block_state_ids.push(state_id);
} else {
diff --git a/azalea-block/src/lib.rs b/azalea-block/src/lib.rs
index d590bcea..983de579 100755
--- a/azalea-block/src/lib.rs
+++ b/azalea-block/src/lib.rs
@@ -167,6 +167,12 @@ impl From<FluidState> for BlockState {
}
}
+impl From<BlockState> for azalea_registry::Block {
+ fn from(value: BlockState) -> Self {
+ Box::<dyn Block>::from(value).as_registry_block()
+ }
+}
+
#[cfg(test)]
mod tests {
use super::*;
diff --git a/azalea-client/src/client.rs b/azalea-client/src/client.rs
index 8a98df31..2dec635d 100644
--- a/azalea-client/src/client.rs
+++ b/azalea-client/src/client.rs
@@ -731,7 +731,11 @@ async fn run_schedule_loop(
.map(|last_tick| last_tick.elapsed() > Duration::from_millis(50))
.unwrap_or(true)
{
- last_tick = Some(Instant::now());
+ if let Some(last_tick) = &mut last_tick {
+ *last_tick += Duration::from_millis(50);
+ } else {
+ last_tick = Some(Instant::now());
+ }
ecs.run_schedule(GameTick);
}
diff --git a/azalea-entity/src/data.rs b/azalea-entity/src/data.rs
index 83779b21..b0a05e74 100755
--- a/azalea-entity/src/data.rs
+++ b/azalea-entity/src/data.rs
@@ -134,7 +134,7 @@ pub struct Rotations {
pub z: f32,
}
-#[derive(Clone, Debug, Copy, McBuf, Default, Component)]
+#[derive(Clone, Debug, Copy, McBuf, Default, Component, Eq, PartialEq)]
pub enum Pose {
#[default]
Standing = 0,
diff --git a/azalea-entity/src/lib.rs b/azalea-entity/src/lib.rs
index bf3dfc82..eb5b5b25 100644
--- a/azalea-entity/src/lib.rs
+++ b/azalea-entity/src/lib.rs
@@ -116,8 +116,13 @@ pub fn on_pos(offset: f32, chunk_storage: &ChunkStorage, pos: &Position) -> Bloc
/// The Minecraft UUID of the entity. For players, this is their actual player
/// UUID, and for other entities it's just random.
-#[derive(Component, Deref, DerefMut, Clone, Copy)]
+#[derive(Component, Deref, DerefMut, Clone, Copy, Default)]
pub struct EntityUuid(Uuid);
+impl EntityUuid {
+ pub fn new(uuid: Uuid) -> Self {
+ Self(uuid)
+ }
+}
impl Debug for EntityUuid {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
(self.0).fmt(f)
@@ -228,6 +233,10 @@ pub struct Physics {
pub bounding_box: AABB,
pub has_impulse: bool,
+
+ pub horizontal_collision: bool,
+ // pub minor_horizontal_collision: bool,
+ pub vertical_collision: bool,
}
impl Physics {
@@ -246,6 +255,9 @@ impl Physics {
dimensions,
has_impulse: false,
+
+ horizontal_collision: false,
+ vertical_collision: false,
}
}
}
@@ -311,6 +323,7 @@ pub struct EntityBundle {
pub attributes: Attributes,
pub jumping: Jumping,
pub fluid_on_eyes: FluidOnEyes,
+ pub on_climbable: OnClimbable,
}
impl EntityBundle {
@@ -346,6 +359,7 @@ impl EntityBundle {
jumping: Jumping(false),
fluid_on_eyes: FluidOnEyes(azalea_registry::Fluid::Empty),
+ on_climbable: OnClimbable(false),
}
}
}
@@ -373,6 +387,9 @@ impl FluidOnEyes {
}
}
+#[derive(Component, Clone, Debug, PartialEq, Deref, DerefMut)]
+pub struct OnClimbable(bool);
+
// #[cfg(test)]
// mod tests {
// use super::*;
diff --git a/azalea-entity/src/plugin/mod.rs b/azalea-entity/src/plugin/mod.rs
index 9950e6ba..4b6d9979 100644
--- a/azalea-entity/src/plugin/mod.rs
+++ b/azalea-entity/src/plugin/mod.rs
@@ -3,6 +3,7 @@ mod relative_updates;
use std::collections::HashSet;
+use azalea_block::BlockState;
use azalea_core::position::{BlockPos, ChunkPos, Vec3};
use azalea_world::{InstanceContainer, InstanceName, MinecraftEntityId};
use bevy_app::{App, Plugin, PreUpdate, Update};
@@ -11,7 +12,8 @@ use derive_more::{Deref, DerefMut};
use tracing::debug;
use crate::{
- metadata::Health, Dead, EyeHeight, FluidOnEyes, LocalEntity, LookDirection, Physics, Position,
+ metadata::Health, Dead, EyeHeight, FluidOnEyes, LocalEntity, LookDirection, OnClimbable,
+ Physics, Position,
};
use indexing::EntityUuidIndex;
@@ -48,6 +50,7 @@ impl Plugin for EntityPlugin {
add_dead,
clamp_look_direction,
update_fluid_on_eyes,
+ update_on_climbable,
),
),
)
@@ -106,6 +109,72 @@ pub fn update_fluid_on_eyes(
}
}
+pub fn update_on_climbable(
+ mut query: Query<(&mut OnClimbable, &Position, &InstanceName)>,
+ instance_container: Res<InstanceContainer>,
+) {
+ for (mut on_climbable, position, instance_name) in query.iter_mut() {
+ // TODO: there's currently no gamemode component that can be accessed from here,
+ // maybe LocalGameMode should be replaced with two components, maybe called
+ // EntityGameMode and PreviousGameMode?
+
+ // if game_mode == GameMode::Spectator {
+ // continue;
+ // }
+
+ let Some(instance) = instance_container.get(instance_name) else {
+ continue;
+ };
+
+ let instance = instance.read();
+
+ let block_pos = BlockPos::from(position);
+ let block_state_at_feet = instance.get_block_state(&block_pos).unwrap_or_default();
+ let block_at_feet = Box::<dyn azalea_block::Block>::from(block_state_at_feet);
+ let registry_block_at_feet = block_at_feet.as_registry_block();
+
+ **on_climbable = azalea_registry::tags::blocks::CLIMBABLE.contains(&registry_block_at_feet)
+ || (azalea_registry::tags::blocks::TRAPDOORS.contains(&registry_block_at_feet)
+ && is_trapdoor_useable_as_ladder(block_state_at_feet, block_pos, &instance));
+ }
+}
+
+fn is_trapdoor_useable_as_ladder(
+ block_state: BlockState,
+ block_pos: BlockPos,
+ instance: &azalea_world::Instance,
+) -> bool {
+ // trapdoor must be open
+ if !block_state
+ .property::<azalea_block::properties::Open>()
+ .unwrap_or_default()
+ {
+ return false;
+ }
+
+ // block below must be a ladder
+ let block_below = instance
+ .get_block_state(&block_pos.down(1))
+ .unwrap_or_default();
+ let registry_block_below =
+ Box::<dyn azalea_block::Block>::from(block_below).as_registry_block();
+ if registry_block_below != azalea_registry::Block::Ladder {
+ return false;
+ }
+ // and the ladder must be facing the same direction as the trapdoor
+ let ladder_facing = block_below
+ .property::<azalea_block::properties::Facing>()
+ .expect("ladder block must have facing property");
+ let trapdoor_facing = block_state
+ .property::<azalea_block::properties::Facing>()
+ .expect("trapdoor block must have facing property");
+ if ladder_facing != trapdoor_facing {
+ return false;
+ }
+
+ true
+}
+
/// A component that lists all the local player entities that have this entity
/// loaded. If this is empty, the entity will be removed from the ECS.
#[derive(Component, Clone, Deref, DerefMut)]
diff --git a/azalea-physics/src/collision/mod.rs b/azalea-physics/src/collision/mod.rs
index 2c739b24..72151b6b 100644
--- a/azalea-physics/src/collision/mod.rs
+++ b/azalea-physics/src/collision/mod.rs
@@ -136,7 +136,7 @@ pub fn move_colliding(
_mover_type: &MoverType,
movement: &Vec3,
world: &Instance,
- mut position: Mut<azalea_entity::Position>,
+ position: &mut Mut<azalea_entity::Position>,
physics: &mut azalea_entity::Physics,
) -> Result<(), MoveEntityError> {
// TODO: do all these
@@ -175,8 +175,8 @@ pub fn move_colliding(
}
};
- if new_pos != **position {
- **position = new_pos;
+ if new_pos != ***position {
+ ***position = new_pos;
}
}
@@ -185,11 +185,14 @@ pub fn move_colliding(
let horizontal_collision = x_collision || z_collision;
let vertical_collision = movement.y != collide_result.y;
let on_ground = vertical_collision && movement.y < 0.;
+
+ physics.horizontal_collision = horizontal_collision;
+ physics.vertical_collision = vertical_collision;
physics.on_ground = on_ground;
// TODO: minecraft checks for a "minor" horizontal collision here
- let _block_pos_below = azalea_entity::on_pos_legacy(&world.chunks, &position);
+ let _block_pos_below = azalea_entity::on_pos_legacy(&world.chunks, position);
// let _block_state_below = self
// .world
// .get_block_state(&block_pos_below)
diff --git a/azalea-physics/src/lib.rs b/azalea-physics/src/lib.rs
index eec6fdf1..a69c1bcf 100644
--- a/azalea-physics/src/lib.rs
+++ b/azalea-physics/src/lib.rs
@@ -13,7 +13,7 @@ use azalea_core::{
};
use azalea_entity::{
metadata::Sprinting, move_relative, Attributes, InLoadedChunk, Jumping, LocalEntity,
- LookDirection, Physics, Position,
+ LookDirection, OnClimbable, Physics, Pose, Position,
};
use azalea_world::{Instance, InstanceContainer, InstanceName};
use bevy_app::{App, Plugin};
@@ -52,14 +52,28 @@ fn travel(
&mut LookDirection,
&mut Position,
Option<&Sprinting>,
+ Option<&Pose>,
&Attributes,
&InstanceName,
+ &OnClimbable,
+ &Jumping,
),
(With<LocalEntity>, With<InLoadedChunk>),
>,
instance_container: Res<InstanceContainer>,
) {
- for (mut physics, direction, position, sprinting, attributes, world_name) in &mut query {
+ for (
+ mut physics,
+ direction,
+ position,
+ sprinting,
+ pose,
+ attributes,
+ world_name,
+ on_climbable,
+ jumping,
+ ) in &mut query
+ {
let world_lock = instance_container
.get(world_name)
.expect("All entities should be in a valid world");
@@ -95,13 +109,18 @@ fn travel(
// this applies the current delta
let mut movement = handle_relative_friction_and_calculate_movement(
- block_friction,
- &world,
- &mut physics,
- &direction,
- position,
- attributes,
- sprinting.map(|s| **s).unwrap_or(false),
+ HandleRelativeFrictionAndCalculateMovementOpts {
+ block_friction,
+ world: &world,
+ physics: &mut physics,
+ direction: &direction,
+ position,
+ attributes,
+ is_sprinting: sprinting.map(|s| **s).unwrap_or(false),
+ on_climbable,
+ pose,
+ jumping,
+ },
);
movement.y -= gravity;
@@ -223,15 +242,33 @@ fn get_block_pos_below_that_affects_movement(position: &Position) -> BlockPos {
)
}
-fn handle_relative_friction_and_calculate_movement(
+// opts for handle_relative_friction_and_calculate_movement
+struct HandleRelativeFrictionAndCalculateMovementOpts<'a> {
block_friction: f32,
- world: &Instance,
- physics: &mut Physics,
- direction: &LookDirection,
- // this is kept as a Mut for bevy change tracking
- position: Mut<Position>,
- attributes: &Attributes,
+ world: &'a Instance,
+ physics: &'a mut Physics,
+ direction: &'a LookDirection,
+ position: Mut<'a, Position>,
+ attributes: &'a Attributes,
is_sprinting: bool,
+ on_climbable: &'a OnClimbable,
+ pose: Option<&'a Pose>,
+ jumping: &'a Jumping,
+}
+
+fn handle_relative_friction_and_calculate_movement(
+ HandleRelativeFrictionAndCalculateMovementOpts {
+ block_friction,
+ world,
+ physics,
+ direction,
+ mut position,
+ attributes,
+ is_sprinting,
+ on_climbable,
+ pose,
+ jumping,
+ }: HandleRelativeFrictionAndCalculateMovementOpts<'_>,
) -> Vec3 {
move_relative(
physics,
@@ -243,12 +280,14 @@ fn handle_relative_friction_and_calculate_movement(
z: physics.zza as f64,
},
);
- // entity.delta = entity.handle_on_climbable(entity.delta);
+
+ physics.velocity = handle_on_climbable(physics.velocity, on_climbable, &position, world, pose);
+
move_colliding(
&MoverType::Own,
&physics.velocity.clone(),
world,
- position,
+ &mut position,
physics,
)
.expect("Entity should exist.");
@@ -258,11 +297,58 @@ fn handle_relative_friction_and_calculate_movement(
// || entity.getFeetBlockState().is(Blocks.POWDER_SNOW) &&
// PowderSnowBlock.canEntityWalkOnPowderSnow(entity))) { var3 = new
// Vec3(var3.x, 0.2D, var3.z); }
- // TODO: powdered snow
+
+ if physics.horizontal_collision || **jumping {
+ let block_at_feet: azalea_registry::Block = world
+ .chunks
+ .get_block_state(&(*position).into())
+ .unwrap_or_default()
+ .into();
+
+ // TODO: powdered snow
+ if **on_climbable || block_at_feet == azalea_registry::Block::PowderSnow {
+ physics.velocity.y = 0.2;
+ }
+ }
physics.velocity
}
+fn handle_on_climbable(
+ velocity: Vec3,
+ on_climbable: &OnClimbable,
+ position: &Position,
+ world: &Instance,
+ pose: Option<&Pose>,
+) -> Vec3 {
+ if !**on_climbable {
+ return velocity;
+ }
+
+ // minecraft does resetFallDistance here
+
+ const CLIMBING_SPEED: f64 = 0.15_f32 as f64;
+
+ let x = f64::clamp(velocity.x, -CLIMBING_SPEED, CLIMBING_SPEED);
+ let z = f64::clamp(velocity.z, -CLIMBING_SPEED, CLIMBING_SPEED);
+ let mut y = f64::max(velocity.y, -CLIMBING_SPEED);
+
+ // sneaking on ladders/vines
+ if y < 0.0
+ && pose.copied() == Some(Pose::Sneaking)
+ && azalea_registry::Block::from(
+ world
+ .chunks
+ .get_block_state(&position.into())
+ .unwrap_or_default(),
+ ) != azalea_registry::Block::Scaffolding
+ {
+ y = 0.;
+ }
+
+ Vec3 { x, y, z }
+}
+
// private float getFrictionInfluencedSpeed(float friction) {
// return this.onGround ? this.getSpeed() * (0.21600002F / (friction *
// friction * friction)) : this.flyingSpeed; }
diff --git a/azalea/src/auto_tool.rs b/azalea/src/auto_tool.rs
index 55ec6924..2cf53085 100644
--- a/azalea/src/auto_tool.rs
+++ b/azalea/src/auto_tool.rs
@@ -43,6 +43,8 @@ pub fn best_tool_in_hotbar_for_block(block: BlockState, menu: &Menu) -> BestTool
dimensions: Default::default(),
bounding_box: Default::default(),
has_impulse: Default::default(),
+ horizontal_collision: Default::default(),
+ vertical_collision: Default::default(),
},
&FluidOnEyes::new(Fluid::Empty),
)
diff --git a/azalea/src/pathfinder/simulation.rs b/azalea/src/pathfinder/simulation.rs
index 88261b7f..b41d895a 100644
--- a/azalea/src/pathfinder/simulation.rs
+++ b/azalea/src/pathfinder/simulation.rs
@@ -7,13 +7,13 @@ use azalea_client::{
};
use azalea_core::{position::Vec3, resource_location::ResourceLocation, tick::GameTick};
use azalea_entity::{
- attributes::AttributeInstance, metadata::Sprinting, Attributes, EntityDimensions, Physics,
- Position,
+ attributes::AttributeInstance, Attributes, EntityDimensions, Physics, Position,
};
-use azalea_world::{ChunkStorage, Instance, InstanceContainer, InstanceName, MinecraftEntityId};
+use azalea_world::{ChunkStorage, Instance, InstanceContainer, MinecraftEntityId};
use bevy_app::App;
use bevy_ecs::prelude::*;
use parking_lot::RwLock;
+use uuid::Uuid;
#[derive(Bundle, Clone)]
pub struct SimulatedPlayerBundle {
@@ -82,24 +82,24 @@ impl Simulation {
schedule.set_executor_kind(bevy_ecs::schedule::ExecutorKind::SingleThreaded);
});
- let entity = app
- .world
- .spawn((
- MinecraftEntityId(0),
- InstanceName(instance_name),
- azalea_entity::LocalEntity,
- azalea_entity::Jumping::default(),
- azalea_entity::LookDirection::default(),
- Sprinting(true),
- azalea_entity::metadata::Player,
- azalea_entity::EyeHeight::new(player.physics.dimensions.height * 0.85),
- player,
- ))
- .id();
+ let mut entity = app.world.spawn((
+ MinecraftEntityId(0),
+ azalea_entity::LocalEntity,
+ azalea_entity::metadata::PlayerMetadataBundle::default(),
+ azalea_entity::EntityBundle::new(
+ Uuid::nil(),
+ *player.position,
+ azalea_registry::EntityKind::Player,
+ instance_name,
+ ),
+ ));
+ entity.insert(player);
+
+ let entity_id = entity.id();
Self {
app,
- entity,
+ entity: entity_id,
_instance: instance,
}
}