1
2
Fork 0
mirror of https://github.com/mat-1/azalea.git synced 2025-08-02 06:16:04 +00:00
* start implementing climbing

* fix tests

* fix bots running at lower tick rate
This commit is contained in:
mat 2023-12-10 00:08:08 -06:00 committed by GitHub
parent f15f0325c0
commit 348c71b97b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 237 additions and 61 deletions

View file

@ -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 {

View file

@ -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::*;

View file

@ -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);
}

View file

@ -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,

View file

@ -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::*;

View file

@ -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)]

View file

@ -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)

View file

@ -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; }

View file

@ -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),
)

View file

@ -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,
}
}