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

Pathfinder (#25)

Pathfinding is very much not done, but it works enough and I want to get this merged.
TODO: fast replanning, goals that aren't a single node, falling moves (it should be able to play the dropper), parkour moves
This commit is contained in:
mat 2022-11-12 23:54:05 -06:00 committed by GitHub
parent fa57d03627
commit 6eee543a33
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
290 changed files with 3049 additions and 776 deletions

0
.cargo/config.toml Normal file → Executable file
View file

0
.github/workflows/check.yml vendored Normal file → Executable file
View file

0
.gitignore vendored Normal file → Executable file
View file

570
Cargo.lock generated Executable file → Normal file

File diff suppressed because it is too large Load diff

0
Cargo.toml Normal file → Executable file
View file

0
README.md Normal file → Executable file
View file

0
azalea-auth/Cargo.toml Normal file → Executable file
View file

0
azalea-auth/README.md Normal file → Executable file
View file

0
azalea-auth/examples/auth.rs Normal file → Executable file
View file

0
azalea-auth/src/auth.rs Normal file → Executable file
View file

0
azalea-auth/src/cache.rs Normal file → Executable file
View file

0
azalea-auth/src/sessionserver.rs Normal file → Executable file
View file

0
azalea-block/Cargo.toml Normal file → Executable file
View file

0
azalea-block/README.md Normal file → Executable file
View file

0
azalea-block/azalea-block-macros/Cargo.toml Normal file → Executable file
View file

0
azalea-block/azalea-block-macros/src/lib.rs Normal file → Executable file
View file

0
azalea-block/azalea-block-macros/src/utils.rs Normal file → Executable file
View file

0
azalea-block/src/behavior.rs Normal file → Executable file
View file

0
azalea-block/src/blocks.rs Normal file → Executable file
View file

0
azalea-block/src/lib.rs Normal file → Executable file
View file

0
azalea-brigadier/Cargo.toml Normal file → Executable file
View file

0
azalea-brigadier/src/suggestion/mod.rs Normal file → Executable file
View file

0
azalea-brigadier/src/suggestion/suggestions.rs Normal file → Executable file
View file

0
azalea-buf/Cargo.toml Normal file → Executable file
View file

0
azalea-buf/README.md Normal file → Executable file
View file

0
azalea-buf/azalea-buf-macros/Cargo.toml Normal file → Executable file
View file

0
azalea-buf/azalea-buf-macros/README.md Normal file → Executable file
View file

0
azalea-buf/azalea-buf-macros/src/lib.rs Normal file → Executable file
View file

0
azalea-buf/src/definitions.rs Normal file → Executable file
View file

0
azalea-buf/src/lib.rs Normal file → Executable file
View file

0
azalea-buf/src/read.rs Normal file → Executable file
View file

0
azalea-buf/src/serializable_uuid.rs Normal file → Executable file
View file

0
azalea-buf/src/write.rs Normal file → Executable file
View file

0
azalea-chat/Cargo.toml Normal file → Executable file
View file

0
azalea-chat/src/text_component.rs Normal file → Executable file
View file

0
azalea-chat/src/translatable_component.rs Normal file → Executable file
View file

View file

@ -3,23 +3,26 @@ description = "A headless Minecraft client."
edition = "2021"
license = "MIT"
name = "azalea-client"
version = "0.3.0"
repository = "https://github.com/mat-1/azalea/tree/main/azalea-client"
version = "0.3.0"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
anyhow = "1.0.59"
azalea-auth = { path = "../azalea-auth", version = "0.3.0" }
azalea-block = { path = "../azalea-block", version = "0.3.0" }
azalea-chat = { path = "../azalea-chat", version = "0.3.0" }
azalea-core = { path = "../azalea-core", version = "0.3.0" }
azalea-crypto = { path = "../azalea-crypto", version = "0.3.0" }
azalea-physics = { path = "../azalea-physics", version = "0.3.0" }
azalea-protocol = { path = "../azalea-protocol", version = "0.3.0" }
azalea-world = { path = "../azalea-world", version = "0.3.0" }
async-trait = "0.1.58"
azalea-auth = {path = "../azalea-auth", version = "0.3.0"}
azalea-block = {path = "../azalea-block", version = "0.3.0"}
azalea-chat = {path = "../azalea-chat", version = "0.3.0"}
azalea-core = {path = "../azalea-core", version = "0.3.0"}
azalea-crypto = {path = "../azalea-crypto", version = "0.3.0"}
azalea-physics = {path = "../azalea-physics", version = "0.3.0"}
azalea-protocol = {path = "../azalea-protocol", version = "0.3.0"}
azalea-world = {path = "../azalea-world", version = "0.3.0"}
log = "0.4.17"
parking_lot = "0.12.1"
nohash-hasher = "0.2.0"
parking_lot = {version = "^0.12.1", features = ["deadlock_detection"]}
thiserror = "^1.0.34"
tokio = { version = "^1.21.2", features = ["sync"] }
tokio = {version = "^1.21.2", features = ["sync"]}
typemap_rev = "0.2.0"
uuid = "^1.1.2"

0
azalea-client/src/account.rs Normal file → Executable file
View file

2
azalea-client/src/chat.rs Normal file → Executable file
View file

@ -70,7 +70,7 @@ impl Client {
/// # account,
/// # address: "localhost",
/// # state: State::default(),
/// # plugins: vec![],
/// # plugins: plugins![],
/// # handle,
/// # })
/// # .await

View file

@ -1,4 +1,4 @@
use crate::{movement::MoveDirection, Account, Player};
use crate::{movement::WalkDirection, plugins::Plugins, Account, Player};
use azalea_auth::game_profile::GameProfile;
use azalea_chat::Component;
use azalea_core::{ChunkPos, ResourceLocation, Vec3};
@ -28,11 +28,11 @@ use azalea_protocol::{
resolver, ServerAddress,
};
use azalea_world::{
entity::{metadata, EntityData, EntityMetadata, EntityMut, EntityRef},
entity::{metadata, Entity, EntityData, EntityMetadata},
Dimension,
};
use log::{debug, error, info, warn};
use parking_lot::{Mutex, RwLock};
use parking_lot::{Mutex, RwLock, RwLockReadGuard, RwLockWriteGuard};
use std::{
fmt::Debug,
io::{self, Cursor},
@ -86,10 +86,14 @@ pub struct Client {
game_profile: GameProfile,
pub read_conn: Arc<tokio::sync::Mutex<ReadConnection<ClientboundGamePacket>>>,
pub write_conn: Arc<tokio::sync::Mutex<WriteConnection<ServerboundGamePacket>>>,
pub player: Arc<Mutex<Player>>,
pub dimension: Arc<Mutex<Dimension>>,
pub player: Arc<RwLock<Player>>,
pub dimension: Arc<RwLock<Dimension>>,
pub physics_state: Arc<Mutex<PhysicsState>>,
pub client_information: Arc<RwLock<ClientInformation>>,
/// Plugins are a way for other crates to add custom functionality to the
/// client and keep state. If you're not making a plugin and you're using
/// the `azalea` crate. you can ignore this field.
pub plugins: Arc<Plugins>,
tasks: Arc<Mutex<Vec<JoinHandle<()>>>>,
}
@ -97,8 +101,12 @@ pub struct Client {
pub struct PhysicsState {
/// Minecraft only sends a movement packet either after 20 ticks or if the player moved enough. This is that tick counter.
pub position_remainder: u32,
pub was_sprinting: bool,
// Whether we're going to try to start sprinting this tick. Equivalent to
// holding down ctrl for a tick.
pub trying_to_sprint: bool,
pub move_direction: MoveDirection,
pub move_direction: WalkDirection,
pub forward_impulse: f32,
pub left_impulse: f32,
}
@ -253,11 +261,14 @@ impl Client {
game_profile,
read_conn,
write_conn,
player: Arc::new(Mutex::new(Player::default())),
dimension: Arc::new(Mutex::new(Dimension::default())),
player: Arc::new(RwLock::new(Player::default())),
dimension: Arc::new(RwLock::new(Dimension::default())),
physics_state: Arc::new(Mutex::new(PhysicsState::default())),
tasks: Arc::new(Mutex::new(Vec::new())),
client_information: Arc::new(RwLock::new(ClientInformation::default())),
// The plugins can be modified by the user by replacing the plugins
// field right after this. No Mutex so the user doesn't need to .lock().
plugins: Arc::new(Plugins::new()),
tasks: Arc::new(Mutex::new(Vec::new())),
};
tx.send(Event::Initialize).unwrap();
@ -403,7 +414,7 @@ impl Client {
.as_int()
.expect("min_y tag is not an int");
let mut dimension_lock = client.dimension.lock();
let mut dimension_lock = client.dimension.write();
// the 16 here is our render distance
// i'll make this an actual setting later
*dimension_lock = Dimension::new(16, height, min_y);
@ -415,7 +426,7 @@ impl Client {
);
dimension_lock.add_entity(p.player_id, entity);
let mut player_lock = client.player.lock();
let mut player_lock = client.player.write();
player_lock.set_entity_id(p.player_id);
}
@ -482,11 +493,11 @@ impl Client {
let (new_pos, y_rot, x_rot) = {
let player_entity_id = {
let player_lock = client.player.lock();
let player_lock = client.player.write();
player_lock.entity_id
};
let mut dimension_lock = client.dimension.lock();
let mut dimension_lock = client.dimension.write();
let mut player_entity = dimension_lock
.entity_mut(player_entity_id)
@ -574,7 +585,7 @@ impl Client {
debug!("Got chunk cache center packet {:?}", p);
client
.dimension
.lock()
.write()
.update_view_center(&ChunkPos::new(p.x, p.z));
}
ClientboundGamePacket::LevelChunkWithLight(p) => {
@ -584,7 +595,7 @@ impl Client {
// debug("chunk {:?}")
if let Err(e) = client
.dimension
.lock()
.write()
.replace_with_packet_data(&pos, &mut Cursor::new(&p.chunk_data.data))
{
error!("Couldn't set chunk data: {}", e);
@ -596,11 +607,11 @@ impl Client {
ClientboundGamePacket::AddEntity(p) => {
debug!("Got add entity packet {:?}", p);
let entity = EntityData::from(p);
client.dimension.lock().add_entity(p.id, entity);
client.dimension.write().add_entity(p.id, entity);
}
ClientboundGamePacket::SetEntityData(p) => {
debug!("Got set entity data packet {:?}", p);
let mut dimension = client.dimension.lock();
let mut dimension = client.dimension.write();
if let Some(mut entity) = dimension.entity_mut(p.id) {
entity.apply_metadata(&p.packed_items.0);
} else {
@ -619,7 +630,7 @@ impl Client {
ClientboundGamePacket::AddPlayer(p) => {
debug!("Got add player packet {:?}", p);
let entity = EntityData::from(p);
client.dimension.lock().add_entity(p.id, entity);
client.dimension.write().add_entity(p.id, entity);
}
ClientboundGamePacket::InitializeBorder(p) => {
debug!("Got initialize border packet {:?}", p);
@ -640,7 +651,7 @@ impl Client {
debug!("Got set experience packet {:?}", p);
}
ClientboundGamePacket::TeleportEntity(p) => {
let mut dimension_lock = client.dimension.lock();
let mut dimension_lock = client.dimension.write();
dimension_lock
.set_entity_pos(
@ -660,14 +671,14 @@ impl Client {
// debug!("Got rotate head packet {:?}", p);
}
ClientboundGamePacket::MoveEntityPos(p) => {
let mut dimension_lock = client.dimension.lock();
let mut dimension_lock = client.dimension.write();
dimension_lock
.move_entity_with_delta(p.entity_id, &p.delta)
.map_err(|e| HandleError::Other(e.into()))?;
}
ClientboundGamePacket::MoveEntityPosRot(p) => {
let mut dimension_lock = client.dimension.lock();
let mut dimension_lock = client.dimension.write();
dimension_lock
.move_entity_with_delta(p.entity_id, &p.delta)
@ -702,7 +713,7 @@ impl Client {
}
ClientboundGamePacket::BlockUpdate(p) => {
debug!("Got block update packet {:?}", p);
let mut dimension = client.dimension.lock();
let mut dimension = client.dimension.write();
dimension.set_block_state(&p.pos, p.block_state);
}
ClientboundGamePacket::Animate(p) => {
@ -710,7 +721,7 @@ impl Client {
}
ClientboundGamePacket::SectionBlocksUpdate(p) => {
debug!("Got section blocks update packet {:?}", p);
let mut dimension = client.dimension.lock();
let mut dimension = client.dimension.write();
for state in &p.states {
dimension.set_block_state(&(p.section_pos + state.pos.clone()), state.state);
}
@ -808,8 +819,8 @@ impl Client {
async fn game_tick(client: &mut Client, tx: &UnboundedSender<Event>) {
// return if there's no chunk at the player's position
{
let dimension_lock = client.dimension.lock();
let player_lock = client.player.lock();
let dimension_lock = client.dimension.write();
let player_lock = client.player.write();
let player_entity = player_lock.entity(&dimension_lock);
let player_entity = if let Some(player_entity) = player_entity {
player_entity
@ -835,30 +846,42 @@ impl Client {
}
/// Returns the entity associated to the player.
pub fn entity_mut<'d>(&self, dimension: &'d mut Dimension) -> EntityMut<'d> {
pub fn entity_mut(&self) -> Entity<RwLockWriteGuard<Dimension>> {
let entity_id = {
let player_lock = self.player.lock();
let player_lock = self.player.write();
player_lock.entity_id
};
dimension
.entity_mut(entity_id)
.expect("Player entity should be in the given dimension")
let mut dimension = self.dimension.write();
let entity_data = dimension
.entity_storage
.get_mut_by_id(entity_id)
.expect("Player entity should exist");
let entity_ptr = unsafe { entity_data.as_ptr() };
Entity::new(dimension, entity_id, entity_ptr)
}
/// Returns the entity associated to the player.
pub fn entity<'d>(&self, dimension: &'d Dimension) -> EntityRef<'d> {
pub fn entity(&self) -> Entity<RwLockReadGuard<Dimension>> {
let entity_id = {
let player_lock = self.player.lock();
let player_lock = self.player.read();
player_lock.entity_id
};
dimension
.entity(entity_id)
.expect("Player entity should be in the given dimension")
let dimension = self.dimension.read();
let entity_data = dimension
.entity_storage
.get_by_id(entity_id)
.expect("Player entity should be in the given dimension");
let entity_ptr = unsafe { entity_data.as_const_ptr() };
Entity::new(dimension, entity_id, entity_ptr)
}
/// Returns whether we have a received the login packet yet.
pub fn logged_in(&self) -> bool {
let dimension = self.dimension.lock();
let player = self.player.lock();
let dimension = self.dimension.read();
let player = self.player.write();
player.entity(&dimension).is_some()
}

0
azalea-client/src/get_mc_dir.rs Normal file → Executable file
View file

View file

@ -5,6 +5,9 @@
//! [`azalea_protocol`]: https://crates.io/crates/azalea-protocol
//! [`azalea`]: https://crates.io/crates/azalea
#![allow(incomplete_features)]
#![feature(trait_upcasting)]
mod account;
mod chat;
mod client;
@ -12,11 +15,13 @@ mod get_mc_dir;
mod movement;
pub mod ping;
mod player;
mod plugins;
pub use account::Account;
pub use client::{ChatPacket, Client, ClientInformation, Event, JoinError};
pub use movement::MoveDirection;
pub use movement::{SprintDirection, WalkDirection};
pub use player::Player;
pub use plugins::{Plugin, Plugins};
#[cfg(test)]
mod tests {

198
azalea-client/src/movement.rs Normal file → Executable file
View file

@ -2,6 +2,7 @@ use crate::Client;
use azalea_core::Vec3;
use azalea_physics::collision::{MovableEntity, MoverType};
use azalea_physics::HasPhysics;
use azalea_protocol::packets::game::serverbound_player_command_packet::ServerboundPlayerCommandPacket;
use azalea_protocol::packets::game::{
serverbound_move_player_pos_packet::ServerboundMovePlayerPosPacket,
serverbound_move_player_pos_rot_packet::ServerboundMovePlayerPosRotPacket,
@ -28,24 +29,19 @@ impl From<MoveEntityError> for MovePlayerError {
}
impl Client {
/// This gets called every tick.
pub async fn send_position(&mut self) -> Result<(), MovePlayerError> {
/// This gets called automatically every tick.
pub(crate) async fn send_position(&mut self) -> Result<(), MovePlayerError> {
let packet = {
let player_lock = self.player.lock();
let mut physics_state = self.physics_state.lock();
let mut dimension_lock = self.dimension.lock();
let mut player_entity = player_lock
.entity_mut(&mut dimension_lock)
.expect("Player must exist");
let player_pos = player_entity.pos();
let player_old_pos = player_entity.last_pos;
// TODO: send sprinting and sneaking packets here if they changed
self.send_sprinting_if_needed().await?;
// TODO: the camera being able to be controlled by other entities isn't implemented yet
// if !self.is_controlled_camera() { return };
let mut physics_state = self.physics_state.lock();
let player_entity = self.entity();
let player_pos = player_entity.pos();
let player_old_pos = player_entity.last_pos;
let x_delta = player_pos.x - player_old_pos.x;
let y_delta = player_pos.y - player_old_pos.y;
let z_delta = player_pos.z - player_old_pos.z;
@ -105,6 +101,9 @@ impl Client {
None
};
drop(player_entity);
let mut player_entity = self.entity_mut();
if sending_position {
player_entity.last_pos = *player_entity.pos();
physics_state.position_remainder = 0;
@ -127,10 +126,35 @@ impl Client {
Ok(())
}
async fn send_sprinting_if_needed(&mut self) -> Result<(), MovePlayerError> {
let is_sprinting = self.entity().metadata.sprinting;
let was_sprinting = self.physics_state.lock().was_sprinting;
if is_sprinting != was_sprinting {
let sprinting_action = if is_sprinting {
azalea_protocol::packets::game::serverbound_player_command_packet::Action::StartSprinting
} else {
azalea_protocol::packets::game::serverbound_player_command_packet::Action::StopSprinting
};
let player_entity_id = self.entity().id;
self.write_packet(
ServerboundPlayerCommandPacket {
id: player_entity_id,
action: sprinting_action,
data: 0,
}
.get(),
)
.await?;
self.physics_state.lock().was_sprinting = is_sprinting;
}
Ok(())
}
// Set our current position to the provided Vec3, potentially clipping through blocks.
pub async fn set_pos(&mut self, new_pos: Vec3) -> Result<(), MovePlayerError> {
let player_lock = self.player.lock();
let mut dimension_lock = self.dimension.lock();
let player_lock = self.player.write();
let mut dimension_lock = self.dimension.write();
dimension_lock.set_entity_pos(player_lock.entity_id, new_pos)?;
@ -138,8 +162,8 @@ impl Client {
}
pub async fn move_entity(&mut self, movement: &Vec3) -> Result<(), MovePlayerError> {
let mut dimension_lock = self.dimension.lock();
let player = self.player.lock();
let mut dimension_lock = self.dimension.write();
let player = self.player.write();
let mut entity = player
.entity_mut(&mut dimension_lock)
@ -160,19 +184,38 @@ impl Client {
pub fn ai_step(&mut self) {
self.tick_controls(None);
let player_lock = self.player.lock();
let mut dimension_lock = self.dimension.lock();
let mut player_entity = player_lock
.entity_mut(&mut dimension_lock)
.expect("Player must exist");
// server ai step
{
let mut player_entity = self.entity_mut();
let physics_state = self.physics_state.lock();
player_entity.xxa = physics_state.left_impulse;
player_entity.zza = physics_state.forward_impulse;
}
// TODO: food data and abilities
// let has_enough_food_to_sprint = self.food_data().food_level || self.abilities().may_fly;
let has_enough_food_to_sprint = true;
// TODO: double tapping w to sprint i think
let trying_to_sprint = self.physics_state.lock().trying_to_sprint;
if !self.sprinting()
&& (
// !self.is_in_water()
// || self.is_underwater() &&
self.has_enough_impulse_to_start_sprinting()
&& has_enough_food_to_sprint
// && !self.using_item()
// && !self.has_effect(MobEffects.BLINDNESS)
&& trying_to_sprint
)
{
self.set_sprinting(true);
}
let mut player_entity = self.entity_mut();
player_entity.ai_step();
}
@ -184,21 +227,21 @@ impl Client {
let mut left_impulse: f32 = 0.;
let move_direction = physics_state.move_direction;
match move_direction {
MoveDirection::Forward | MoveDirection::ForwardRight | MoveDirection::ForwardLeft => {
WalkDirection::Forward | WalkDirection::ForwardRight | WalkDirection::ForwardLeft => {
forward_impulse += 1.;
}
MoveDirection::Backward
| MoveDirection::BackwardRight
| MoveDirection::BackwardLeft => {
WalkDirection::Backward
| WalkDirection::BackwardRight
| WalkDirection::BackwardLeft => {
forward_impulse -= 1.;
}
_ => {}
};
match move_direction {
MoveDirection::Right | MoveDirection::ForwardRight | MoveDirection::BackwardRight => {
WalkDirection::Right | WalkDirection::ForwardRight | WalkDirection::BackwardRight => {
left_impulse += 1.;
}
MoveDirection::Left | MoveDirection::ForwardLeft | MoveDirection::BackwardLeft => {
WalkDirection::Left | WalkDirection::ForwardLeft | WalkDirection::BackwardLeft => {
left_impulse -= 1.;
}
_ => {}
@ -212,35 +255,90 @@ impl Client {
}
}
/// Start walking in the given direction.
pub fn walk(&mut self, direction: MoveDirection) {
let mut physics_state = self.physics_state.lock();
physics_state.move_direction = direction;
/// Start walking in the given direction. To sprint, use
/// [`Client::sprint`]. To stop walking, call walk with
/// `WalkDirection::None`.
pub fn walk(&mut self, direction: WalkDirection) {
{
let mut physics_state = self.physics_state.lock();
physics_state.move_direction = direction;
}
self.set_sprinting(false);
}
/// Toggle whether we're jumping. This acts as if you held space in
/// Start sprinting in the given direction. To stop moving, call
/// [`Client::walk(WalkDirection::None)`]
pub fn sprint(&mut self, direction: SprintDirection) {
let mut physics_state = self.physics_state.lock();
physics_state.move_direction = WalkDirection::from(direction);
physics_state.trying_to_sprint = true;
}
// Whether we're currently sprinting.
pub fn sprinting(&self) -> bool {
self.entity().metadata.sprinting
}
/// Change whether we're sprinting by adding an attribute modifier to the
/// player. You should use the [`walk`] and [`sprint`] methods instead.
/// Returns if the operation was successful.
fn set_sprinting(&mut self, sprinting: bool) -> bool {
let mut player_entity = self.entity_mut();
player_entity.metadata.sprinting = sprinting;
if sprinting {
player_entity
.attributes
.speed
.insert(azalea_world::entity::attributes::sprinting_modifier())
.is_ok()
} else {
player_entity
.attributes
.speed
.remove(&azalea_world::entity::attributes::sprinting_modifier().uuid)
.is_none()
}
}
/// Set whether we're jumping. This acts as if you held space in
/// vanilla. If you want to jump once, use the `jump` function.
///
/// If you're making a realistic client, calling this function every tick is
/// recommended.
pub fn set_jumping(&mut self, jumping: bool) {
let mut dimension = self.dimension.lock();
let mut player_entity = self.entity_mut(&mut dimension);
let mut player_entity = self.entity_mut();
player_entity.jumping = jumping;
}
/// Returns whether the player will try to jump next tick.
pub fn jumping(&self) -> bool {
let dimension = self.dimension.lock();
let player_entity = self.entity(&dimension);
let player_entity = self.entity();
player_entity.jumping
}
/// Sets your rotation. `y_rot` is yaw (looking to the side), `x_rot` is
/// pitch (looking up and down). You can get these numbers from the vanilla
/// f3 screen.
pub fn set_rotation(&mut self, y_rot: f32, x_rot: f32) {
let mut player_entity = self.entity_mut();
player_entity.set_rotation(y_rot, x_rot);
}
// Whether the player is moving fast enough to be able to start sprinting.
fn has_enough_impulse_to_start_sprinting(&self) -> bool {
// if self.underwater() {
// self.has_forward_impulse()
// } else {
let physics_state = self.physics_state.lock();
physics_state.forward_impulse > 0.8
// }
}
}
#[derive(Clone, Copy, Debug, Default)]
pub enum MoveDirection {
pub enum WalkDirection {
#[default]
None,
Forward,
@ -252,3 +350,21 @@ pub enum MoveDirection {
BackwardRight,
BackwardLeft,
}
/// The directions that we can sprint in. It's a subset of [`WalkDirection`].
#[derive(Clone, Copy, Debug)]
pub enum SprintDirection {
Forward,
ForwardRight,
ForwardLeft,
}
impl From<SprintDirection> for WalkDirection {
fn from(d: SprintDirection) -> Self {
match d {
SprintDirection::Forward => WalkDirection::Forward,
SprintDirection::ForwardRight => WalkDirection::ForwardRight,
SprintDirection::ForwardLeft => WalkDirection::ForwardLeft,
}
}
}

6
azalea-client/src/player.rs Normal file → Executable file
View file

@ -1,4 +1,4 @@
use azalea_world::entity::{EntityMut, EntityRef};
use azalea_world::entity::Entity;
use azalea_world::Dimension;
use uuid::Uuid;
@ -18,12 +18,12 @@ pub struct Player {
impl Player {
/// Get a reference to the entity of the player in the world.
pub fn entity<'d>(&'d self, dimension: &'d Dimension) -> Option<EntityRef> {
pub fn entity<'d>(&'d self, dimension: &'d Dimension) -> Option<Entity<&Dimension>> {
dimension.entity(self.entity_id)
}
/// Get a mutable reference to the entity of the player in the world.
pub fn entity_mut<'d>(&'d self, dimension: &'d mut Dimension) -> Option<EntityMut> {
pub fn entity_mut<'d>(&'d self, dimension: &'d mut Dimension) -> Option<Entity> {
dimension.entity_mut(self.entity_id)
}

View file

@ -0,0 +1,78 @@
use crate::{Client, Event};
use async_trait::async_trait;
use nohash_hasher::NoHashHasher;
use std::{
any::{Any, TypeId},
collections::HashMap,
hash::BuildHasherDefault,
};
// kind of based on https://docs.rs/http/latest/src/http/extensions.rs.html
/// A map of plugin ids to Plugin trait objects. The client stores this so we
/// can keep the state for our plugins.
///
/// If you're using azalea, you should generate this from the `plugins!` macro.
#[derive(Clone)]
pub struct Plugins {
map: Option<HashMap<TypeId, Box<dyn Plugin>, BuildHasherDefault<NoHashHasher<u64>>>>,
}
impl Plugins {
pub fn new() -> Self {
Self { map: None }
}
pub fn add<T: Plugin>(&mut self, plugin: T) {
if self.map.is_none() {
self.map = Some(HashMap::with_hasher(BuildHasherDefault::default()));
}
self.map
.as_mut()
.unwrap()
.insert(TypeId::of::<T>(), Box::new(plugin));
}
pub fn get<T: Plugin>(&self) -> Option<&T> {
self.map
.as_ref()
.and_then(|map| map.get(&TypeId::of::<T>()))
.and_then(|boxed| (boxed.as_ref() as &dyn Any).downcast_ref::<T>())
}
}
impl IntoIterator for Plugins {
type Item = Box<dyn Plugin>;
type IntoIter = std::vec::IntoIter<Self::Item>;
fn into_iter(self) -> Self::IntoIter {
self.map
.map(|map| map.into_iter().map(|(_, v)| v).collect::<Vec<_>>())
.unwrap_or_default()
.into_iter()
}
}
/// Plugins can keep their own personal state, listen to events, and add new functions to Client.
#[async_trait]
pub trait Plugin: Send + Sync + PluginClone + Any + 'static {
async fn handle(self: Box<Self>, event: Event, bot: Client);
}
/// An internal trait that allows Plugin to be cloned.
#[doc(hidden)]
pub trait PluginClone {
fn clone_box(&self) -> Box<dyn Plugin>;
}
impl<T> PluginClone for T
where
T: 'static + Plugin + Clone,
{
fn clone_box(&self) -> Box<dyn Plugin> {
Box::new(self.clone())
}
}
impl Clone for Box<dyn Plugin> {
fn clone(&self) -> Self {
self.clone_box()
}
}

0
azalea-core/Cargo.toml Normal file → Executable file
View file

0
azalea-core/README.md Normal file → Executable file
View file

42
azalea-core/src/aabb.rs Normal file → Executable file
View file

@ -1,4 +1,4 @@
use crate::{Axis, BlockHitResult, BlockPos, Direction, PositionXYZ, Vec3};
use crate::{Axis, BlockHitResult, BlockPos, Direction, Vec3};
pub const EPSILON: f64 = 1.0E-7;
@ -15,7 +15,7 @@ pub struct AABB {
}
pub struct ClipPointOpts<'a> {
pub t: &'a mut [f64],
pub t: &'a mut f64,
pub approach_dir: Option<Direction>,
pub delta: &'a Vec3,
pub begin: f64,
@ -225,13 +225,10 @@ impl AABB {
}
pub fn clip(&self, min: &Vec3, max: &Vec3) -> Option<Vec3> {
let mut t = [1.0];
let x = max.x - min.x;
let y = max.y - min.y;
let z = max.z - min.z;
let _dir = self.get_direction(self, min, &mut t, None, &Vec3 { x, y, z })?;
let t = t[0];
Some(min.add(t * x, t * y, t * z))
let mut t = 1.0;
let delta = max - min;
let _dir = self.get_direction(self, min, &mut t, None, &delta)?;
Some(min + &(delta * t))
}
pub fn clip_iterable(
@ -241,19 +238,16 @@ impl AABB {
to: &Vec3,
pos: &BlockPos,
) -> Option<BlockHitResult> {
let mut t = [1.0];
let mut t = 1.0;
let mut dir = None;
let x = to.x - from.x;
let y = to.y - from.y;
let z = to.z - from.z;
let delta = to - from;
for aabb in boxes {
dir = self.get_direction(aabb, from, &mut t, dir, &Vec3 { x, y, z });
dir = self.get_direction(aabb, from, &mut t, dir, &delta);
}
let dir = dir?;
let t = t[0];
Some(BlockHitResult {
location: from.add(t * x, t * y, t * z),
location: from + &(delta * t),
direction: dir,
block_pos: *pos,
inside: false,
@ -265,7 +259,7 @@ impl AABB {
&self,
aabb: &AABB,
from: &Vec3,
t: &mut [f64],
t: &mut f64,
dir: Option<Direction>,
delta: &Vec3,
) -> Option<Direction> {
@ -393,13 +387,13 @@ impl AABB {
let t_y = (opts.start.y + t_x) / opts.delta.y;
let t_z = (opts.start.z + t_x) / opts.delta.z;
if 0.0 < t_x
&& t_x < opts.t[0]
&& t_x < *opts.t
&& opts.min_x - EPSILON < t_y
&& t_y < opts.max_x + EPSILON
&& opts.min_z - EPSILON < t_z
&& t_z < opts.max_z + EPSILON
{
opts.t[0] = t_x;
*opts.t = t_x;
Some(opts.result_dir)
} else {
opts.approach_dir
@ -416,11 +410,11 @@ impl AABB {
}
pub fn get_center(&self) -> Vec3 {
Vec3 {
x: (self.min_x + self.max_x) / 2.0,
y: (self.min_y + self.max_y) / 2.0,
z: (self.min_z + self.max_z) / 2.0,
}
Vec3::new(
(self.min_x + self.max_x) / 2.0,
(self.min_y + self.max_y) / 2.0,
(self.min_z + self.max_z) / 2.0,
)
}
pub fn of_size(center: Vec3, dx: f64, dy: f64, dz: f64) -> AABB {

0
azalea-core/src/bitset.rs Normal file → Executable file
View file

0
azalea-core/src/block_hit_result.rs Normal file → Executable file
View file

0
azalea-core/src/cursor3d.rs Normal file → Executable file
View file

23
azalea-core/src/delta.rs Normal file → Executable file
View file

@ -1,5 +1,3 @@
use std::ops::{Add, AddAssign};
use crate::Vec3;
pub use azalea_buf::McBuf;
@ -76,24 +74,3 @@ impl Vec3 {
self.multiply(amount, amount, amount)
}
}
// impl + and +=
impl Add for Vec3 {
type Output = Vec3;
fn add(self, other: Vec3) -> Vec3 {
Vec3 {
x: self.x + other.x,
y: self.y + other.y,
z: self.z + other.z,
}
}
}
impl AddAssign for Vec3 {
fn add_assign(&mut self, other: Vec3) {
self.x += other.x;
self.y += other.y;
self.z += other.z;
}
}

58
azalea-core/src/direction.rs Normal file → Executable file
View file

@ -13,6 +13,15 @@ pub enum Direction {
East,
}
// TODO: make azalea_block use this instead of FacingCardinal
#[derive(Clone, Copy, Debug, McBuf)]
pub enum CardinalDirection {
North,
South,
West,
East,
}
#[derive(Clone, Copy, Debug)]
pub enum Axis {
X = 0,
@ -27,6 +36,55 @@ pub enum AxisCycle {
Backward = 2,
}
impl CardinalDirection {
#[inline]
pub fn x(self) -> i32 {
match self {
CardinalDirection::East => 1,
CardinalDirection::West => -1,
_ => 0,
}
}
#[inline]
pub fn z(self) -> i32 {
match self {
CardinalDirection::South => 1,
CardinalDirection::North => -1,
_ => 0,
}
}
pub fn iter() -> impl Iterator<Item = CardinalDirection> {
[
CardinalDirection::North,
CardinalDirection::South,
CardinalDirection::West,
CardinalDirection::East,
]
.iter()
.copied()
}
#[inline]
pub fn right(self) -> CardinalDirection {
match self {
CardinalDirection::North => CardinalDirection::East,
CardinalDirection::South => CardinalDirection::West,
CardinalDirection::West => CardinalDirection::North,
CardinalDirection::East => CardinalDirection::South,
}
}
#[inline]
pub fn left(self) -> CardinalDirection {
match self {
CardinalDirection::North => CardinalDirection::West,
CardinalDirection::South => CardinalDirection::East,
CardinalDirection::West => CardinalDirection::South,
CardinalDirection::East => CardinalDirection::North,
}
}
}
impl Axis {
/// Pick x, y, or z from the arguments depending on the axis.
#[inline]

0
azalea-core/src/particle/mod.rs Normal file → Executable file
View file

301
azalea-core/src/position.rs Normal file → Executable file
View file

@ -2,114 +2,136 @@ use crate::ResourceLocation;
use azalea_buf::{BufReadError, McBufReadable, McBufWritable};
use std::{
io::{Cursor, Write},
ops::{Add, Mul, Rem},
ops::{Add, AddAssign, Mul, Rem, Sub},
};
pub trait PositionXYZ<T>
where
T: Add<T, Output = T> + Mul<T, Output = T>,
{
fn x(&self) -> T;
fn y(&self) -> T;
fn z(&self) -> T;
macro_rules! vec3_impl {
($name:ident, $type:ty) => {
impl $name {
pub fn new(x: $type, y: $type, z: $type) -> Self {
Self { x, y, z }
}
fn set_x(&self, n: T) -> Self;
fn set_y(&self, n: T) -> Self;
fn set_z(&self, n: T) -> Self;
pub fn length_sqr(&self) -> $type {
self.x * self.x + self.y * self.y + self.z * self.z
}
// hopefully these get optimized
fn add_x(&self, n: T) -> Self
where
Self: Sized,
{
self.set_x(self.x() + n)
}
fn add_y(&self, n: T) -> Self
where
Self: Sized,
{
self.set_y(self.y() + n)
}
fn add_z(&self, n: T) -> Self
where
Self: Sized,
{
self.set_z(self.z() + n)
}
/// Return a new instance of this position with the y coordinate
/// decreased by the given number.
pub fn down(&self, y: $type) -> Self {
Self {
x: self.x,
y: self.y - y,
z: self.z,
}
}
/// Return a new instance of this position with the y coordinate
/// increased by the given number.
pub fn up(&self, y: $type) -> Self {
Self {
x: self.x,
y: self.y + y,
z: self.z,
}
}
}
fn add(&self, x: T, y: T, z: T) -> Self
where
Self: Sized,
{
self.add_x(x).add_y(y).add_z(z)
}
impl Add for &$name {
type Output = $name;
fn length_sqr(&self) -> T
where
Self: Sized,
{
self.x() * self.x() + self.y() * self.y() + self.z() * self.z()
}
fn add(self, rhs: Self) -> Self::Output {
$name {
x: self.x + rhs.x,
y: self.y + rhs.y,
z: self.z + rhs.z,
}
}
}
impl Add for $name {
type Output = $name;
fn add(self, rhs: Self) -> Self::Output {
(&self).add(&rhs)
}
}
impl AddAssign for $name {
fn add_assign(&mut self, rhs: Self) {
self.x += rhs.x;
self.y += rhs.y;
self.z += rhs.z;
}
}
impl Rem<$type> for $name {
type Output = Self;
fn rem(self, rhs: $type) -> Self::Output {
Self {
x: self.x % rhs,
y: self.y % rhs,
z: self.z % rhs,
}
}
}
impl Sub for &$name {
type Output = $name;
/// Find the difference between two positions.
fn sub(self, other: Self) -> Self::Output {
Self::Output {
x: self.x - other.x,
y: self.y - other.y,
z: self.z - other.z,
}
}
}
impl Sub for $name {
type Output = Self;
fn sub(self, other: Self) -> Self::Output {
(&self).sub(&other)
}
}
impl Mul<$type> for $name {
type Output = Self;
fn mul(self, multiplier: $type) -> Self::Output {
Self {
x: self.x * multiplier,
y: self.y * multiplier,
z: self.z * multiplier,
}
}
}
};
}
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
#[derive(Clone, Copy, Debug, Default, PartialEq)]
pub struct Vec3 {
pub x: f64,
pub y: f64,
pub z: f64,
}
vec3_impl!(Vec3, f64);
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
pub struct BlockPos {
pub x: i32,
pub y: i32,
pub z: i32,
}
vec3_impl!(BlockPos, i32);
impl BlockPos {
pub fn new(x: i32, y: i32, z: i32) -> Self {
BlockPos { x, y, z }
}
pub fn below(&self) -> Self {
self.add(0, -1, 0)
}
}
impl Rem<i32> for BlockPos {
type Output = Self;
fn rem(self, rhs: i32) -> Self {
BlockPos {
x: self.x % rhs,
y: self.y % rhs,
z: self.z % rhs,
}
}
}
impl PositionXYZ<i32> for BlockPos {
fn x(&self) -> i32 {
self.x
}
fn y(&self) -> i32 {
self.y
}
fn z(&self) -> i32 {
self.z
}
fn set_x(&self, n: i32) -> Self {
BlockPos {
x: n,
y: self.y,
z: self.z,
}
}
fn set_y(&self, n: i32) -> Self {
BlockPos {
x: self.x,
y: n,
z: self.z,
}
}
fn set_z(&self, n: i32) -> Self {
BlockPos {
x: self.x,
y: self.y,
z: n,
/// Get the absolute center of a block position by adding 0.5 to each coordinate.
pub fn center(&self) -> Vec3 {
Vec3 {
x: self.x as f64 + 0.5,
y: self.y as f64 + 0.5,
z: self.z as f64 + 0.5,
}
}
}
@ -127,17 +149,15 @@ impl ChunkPos {
}
/// The coordinates of a chunk section in the world.
#[derive(Clone, Copy, Debug, Default)]
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct ChunkSectionPos {
pub x: i32,
pub y: i32,
pub z: i32,
}
vec3_impl!(ChunkSectionPos, i32);
impl ChunkSectionPos {
pub fn new(x: i32, y: i32, z: i32) -> Self {
ChunkSectionPos { x, y, z }
}
pub fn block_to_section_coord(block: i32) -> i32 {
block >> 4
}
@ -155,32 +175,25 @@ impl ChunkBlockPos {
ChunkBlockPos { x, y, z }
}
}
/// The coordinates of a block inside a chunk section.
#[derive(Clone, Debug, Default)]
/// The coordinates of a block inside a chunk section. Each coordinate must be in the range [0, 15].
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct ChunkSectionBlockPos {
/// A number between 0 and 16.
pub x: u8,
/// A number between 0 and 16.
pub y: u8,
/// A number between 0 and 16.
pub z: u8,
}
impl ChunkSectionBlockPos {
pub fn new(x: u8, y: u8, z: u8) -> Self {
ChunkSectionBlockPos { x, y, z }
}
}
vec3_impl!(ChunkSectionBlockPos, u8);
impl Add<ChunkSectionBlockPos> for ChunkSectionPos {
type Output = BlockPos;
fn add(self, rhs: ChunkSectionBlockPos) -> Self::Output {
BlockPos {
x: self.x * 16 + rhs.x as i32,
y: self.y * 16 + rhs.y as i32,
z: self.z * 16 + rhs.z as i32,
}
BlockPos::new(
self.x * 16 + rhs.x as i32,
self.y * 16 + rhs.y as i32,
self.z * 16 + rhs.z as i32,
)
}
}
@ -192,47 +205,6 @@ pub struct GlobalPos {
pub dimension: ResourceLocation,
}
/// An exact point in the world.
#[derive(Debug, Clone, Copy, Default, PartialEq)]
pub struct Vec3 {
pub x: f64,
pub y: f64,
pub z: f64,
}
impl PositionXYZ<f64> for Vec3 {
fn x(&self) -> f64 {
self.x
}
fn y(&self) -> f64 {
self.y
}
fn z(&self) -> f64 {
self.z
}
fn set_x(&self, n: f64) -> Self {
Vec3 {
x: n,
y: self.y,
z: self.z,
}
}
fn set_y(&self, n: f64) -> Self {
Vec3 {
x: self.x,
y: n,
z: self.z,
}
}
fn set_z(&self, n: f64) -> Self {
Vec3 {
x: self.x,
y: self.y,
z: n,
}
}
}
impl From<&BlockPos> for ChunkPos {
fn from(pos: &BlockPos) -> Self {
ChunkPos {
@ -261,9 +233,9 @@ impl From<ChunkSectionPos> for ChunkPos {
impl From<&BlockPos> for ChunkBlockPos {
fn from(pos: &BlockPos) -> Self {
ChunkBlockPos {
x: pos.x.rem_euclid(16).unsigned_abs() as u8,
x: pos.x.rem_euclid(16) as u8,
y: pos.y,
z: pos.z.rem_euclid(16).unsigned_abs() as u8,
z: pos.z.rem_euclid(16) as u8,
}
}
}
@ -271,9 +243,9 @@ impl From<&BlockPos> for ChunkBlockPos {
impl From<&BlockPos> for ChunkSectionBlockPos {
fn from(pos: &BlockPos) -> Self {
ChunkSectionBlockPos {
x: pos.x.rem(16).unsigned_abs() as u8,
y: pos.y.rem(16).unsigned_abs() as u8,
z: pos.z.rem(16).unsigned_abs() as u8,
x: pos.x.rem_euclid(16) as u8,
y: pos.y.rem_euclid(16) as u8,
z: pos.z.rem_euclid(16) as u8,
}
}
}
@ -282,7 +254,7 @@ impl From<&ChunkBlockPos> for ChunkSectionBlockPos {
fn from(pos: &ChunkBlockPos) -> Self {
ChunkSectionBlockPos {
x: pos.x,
y: pos.y.rem(16).unsigned_abs() as u8,
y: pos.y.rem_euclid(16) as u8,
z: pos.z,
}
}
@ -419,4 +391,13 @@ mod tests {
let block_pos = BlockPos::read_from(&mut buf).unwrap();
assert_eq!(block_pos, BlockPos::new(49, -43, -3));
}
#[test]
fn test_into_chunk_section_block_pos() {
let block_pos = BlockPos::new(0, -60, 0);
assert_eq!(
ChunkSectionBlockPos::from(&block_pos),
ChunkSectionBlockPos::new(0, 4, 0)
);
}
}

0
azalea-core/src/slot.rs Normal file → Executable file
View file

0
azalea-crypto/Cargo.toml Normal file → Executable file
View file

0
azalea-crypto/README.md Normal file → Executable file
View file

0
azalea-crypto/benches/my_benchmark.rs Normal file → Executable file
View file

0
azalea-crypto/src/lib.rs Normal file → Executable file
View file

0
azalea-crypto/src/signing.rs Normal file → Executable file
View file

0
azalea-language/Cargo.toml Normal file → Executable file
View file

0
azalea-language/README.md Normal file → Executable file
View file

0
azalea-language/src/en_us.json Normal file → Executable file
View file

0
azalea-language/src/lib.rs Normal file → Executable file
View file

0
azalea-nbt/Cargo.toml Normal file → Executable file
View file

9
azalea-physics/Cargo.toml Normal file → Executable file
View file

@ -3,16 +3,17 @@ description = "Physics for Minecraft entities."
edition = "2021"
license = "MIT"
name = "azalea-physics"
version = "0.3.0"
repository = "https://github.com/mat-1/azalea/tree/main/azalea-physics"
version = "0.3.0"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
azalea-block = { path = "../azalea-block", version = "^0.3.0" }
azalea-core = { path = "../azalea-core", version = "^0.3.0" }
azalea-world = { path = "../azalea-world", version = "^0.3.0" }
azalea-block = {path = "../azalea-block", version = "^0.3.0"}
azalea-core = {path = "../azalea-core", version = "^0.3.0"}
azalea-world = {path = "../azalea-world", version = "^0.3.0"}
lazy_static = "1.4.0"
parking_lot = "^0.12.1"
[dev-dependencies]
uuid = "^1.1.2"

0
azalea-physics/README.md Normal file → Executable file
View file

0
azalea-physics/src/collision/blocks.rs Normal file → Executable file
View file

7
azalea-physics/src/collision/dimension_collisions.rs Normal file → Executable file
View file

@ -3,7 +3,8 @@ use azalea_block::BlockState;
use azalea_core::{ChunkPos, ChunkSectionPos, Cursor3d, CursorIterationType, EPSILON};
use azalea_world::entity::EntityData;
use azalea_world::{Chunk, Dimension};
use std::sync::{Arc, Mutex};
use parking_lot::Mutex;
use std::sync::Arc;
use super::Shapes;
@ -92,10 +93,10 @@ impl<'a> Iterator for BlockCollisions<'a> {
Some(chunk) => chunk,
None => continue,
};
let chunk_lock = chunk.lock().unwrap();
let pos = item.pos;
let block_state: BlockState = chunk_lock
let block_state: BlockState = chunk
.lock()
.get(&(&pos).into(), self.dimension.min_y())
.unwrap_or(BlockState::Air);

0
azalea-physics/src/collision/discrete_voxel_shape.rs Normal file → Executable file
View file

0
azalea-physics/src/collision/mergers.rs Normal file → Executable file
View file

10
azalea-physics/src/collision/mod.rs Normal file → Executable file
View file

@ -4,8 +4,10 @@ mod discrete_voxel_shape;
mod mergers;
mod shape;
use azalea_core::{Axis, PositionXYZ, Vec3, AABB, EPSILON};
use azalea_world::entity::{EntityData, EntityMut};
use std::ops::DerefMut;
use azalea_core::{Axis, Vec3, AABB, EPSILON};
use azalea_world::entity::{Entity, EntityData};
use azalea_world::{Dimension, MoveEntityError};
pub use blocks::BlockWithShape;
use dimension_collisions::CollisionGetter;
@ -81,7 +83,7 @@ impl HasCollision for Dimension {
}
}
impl MovableEntity for EntityMut<'_> {
impl<D: DerefMut<Target = Dimension>> MovableEntity for Entity<'_, D> {
/// Move an entity by a given delta, checking for collisions.
fn move_colliding(
&mut self,
@ -158,6 +160,8 @@ impl MovableEntity for EntityMut<'_> {
if vertical_collision {
// blockBelow.updateEntityAfterFallOn(this.level, this);
// the default implementation of updateEntityAfterFallOn sets the y movement to 0
self.delta.y = 0.;
}
if on_ground {

0
azalea-physics/src/collision/shape.rs Normal file → Executable file
View file

49
azalea-physics/src/lib.rs Normal file → Executable file
View file

@ -2,9 +2,14 @@
pub mod collision;
use std::ops::DerefMut;
use azalea_block::{Block, BlockState};
use azalea_core::{BlockPos, Vec3};
use azalea_world::entity::{EntityData, EntityMut};
use azalea_world::{
entity::{Entity, EntityData},
Dimension,
};
use collision::{MovableEntity, MoverType};
pub trait HasPhysics {
@ -14,7 +19,7 @@ pub trait HasPhysics {
fn jump_from_ground(&mut self);
}
impl HasPhysics for EntityMut<'_> {
impl<D: DerefMut<Target = Dimension>> HasPhysics for Entity<'_, D> {
/// Move the entity with the given acceleration while handling friction,
/// gravity, collisions, and some other stuff.
fn travel(&mut self, acceleration: &Vec3) {
@ -115,17 +120,17 @@ impl HasPhysics for EntityMut<'_> {
y: jump_power,
z: old_delta_movement.z,
};
// if self.sprinting {
// let y_rot = self.y_rot * 0.017453292;
// self.delta = self.delta
// + Vec3 {
// x: (-f32::sin(y_rot) * 0.2) as f64,
// y: 0.,
// z: (f32::cos(y_rot) * 0.2) as f64,
// };
// }
if self.metadata.sprinting {
let y_rot = self.y_rot * 0.017453292;
self.delta = self.delta
+ Vec3 {
x: (-f32::sin(y_rot) * 0.2) as f64,
y: 0.,
z: (f32::cos(y_rot) * 0.2) as f64,
};
}
// self.has_impulse = true;
self.has_impulse = true;
}
}
@ -138,17 +143,21 @@ fn get_block_pos_below_that_affects_movement(entity: &EntityData) -> BlockPos {
)
}
fn handle_relative_friction_and_calculate_movement(
entity: &mut EntityMut,
fn handle_relative_friction_and_calculate_movement<D: DerefMut<Target = Dimension>>(
entity: &mut Entity<D>,
acceleration: &Vec3,
block_friction: f32,
) -> Vec3 {
entity.move_relative(get_speed(&*entity, block_friction), acceleration);
entity.move_relative(
get_friction_influenced_speed(&*entity, block_friction),
acceleration,
);
// entity.delta = entity.handle_on_climbable(entity.delta);
entity
.move_colliding(&MoverType::Own, &entity.delta.clone())
.expect("Entity should exist.");
// let delta_movement = entity.delta;
// ladders
// if ((entity.horizontalCollision || entity.jumping) && (entity.onClimbable() || entity.getFeetBlockState().is(Blocks.POWDER_SNOW) && PowderSnowBlock.canEntityWalkOnPowderSnow(entity))) {
// var3 = new Vec3(var3.x, 0.2D, var3.z);
// }
@ -160,10 +169,10 @@ fn handle_relative_friction_and_calculate_movement(
// private float getFrictionInfluencedSpeed(float friction) {
// return this.onGround ? this.getSpeed() * (0.21600002F / (friction * friction * friction)) : this.flyingSpeed;
// }
fn get_speed(entity: &EntityData, friction: f32) -> f32 {
fn get_friction_influenced_speed(entity: &EntityData, friction: f32) -> f32 {
// TODO: have speed & flying_speed fields in entity
if entity.on_ground {
let speed: f32 = 0.7;
let speed: f32 = entity.attributes.speed.calculate() as f32;
speed * (0.216f32 / (friction * friction * friction))
} else {
// entity.flying_speed
@ -173,7 +182,7 @@ fn get_speed(entity: &EntityData, friction: f32) -> f32 {
/// Returns the what the entity's jump should be multiplied by based on the
/// block they're standing on.
fn block_jump_factor(entity: &EntityMut) -> f32 {
fn block_jump_factor<D: DerefMut<Target = Dimension>>(entity: &Entity<D>) -> f32 {
let block_at_pos = entity.dimension.get_block_state(&entity.pos().into());
let block_below = entity
.dimension
@ -201,11 +210,11 @@ fn block_jump_factor(entity: &EntityMut) -> f32 {
// public double getJumpBoostPower() {
// return this.hasEffect(MobEffects.JUMP) ? (double)(0.1F * (float)(this.getEffect(MobEffects.JUMP).getAmplifier() + 1)) : 0.0D;
// }
fn jump_power(entity: &EntityMut) -> f32 {
fn jump_power<D: DerefMut<Target = Dimension>>(entity: &Entity<D>) -> f32 {
0.42 * block_jump_factor(entity)
}
fn jump_boost_power(_entity: &EntityMut) -> f64 {
fn jump_boost_power<D: DerefMut<Target = Dimension>>(_entity: &Entity<D>) -> f64 {
// TODO: potion effects
// if let Some(effects) = entity.effects() {
// if let Some(jump_effect) = effects.get(&Effect::Jump) {

0
azalea-protocol/Cargo.toml Normal file → Executable file
View file

0
azalea-protocol/README.md Normal file → Executable file
View file

0
azalea-protocol/azalea-protocol-macros/Cargo.toml Normal file → Executable file
View file

0
azalea-protocol/azalea-protocol-macros/src/lib.rs Normal file → Executable file
View file

0
azalea-protocol/src/connect.rs Normal file → Executable file
View file

View file

View file

View file

View file

View file

View file

View file

View file

View file

View file

View file

Some files were not shown because too many files have changed in this diff Show more