aboutsummaryrefslogtreecommitdiff
path: root/azalea-client/src
diff options
context:
space:
mode:
authormat <27899617+mat-1@users.noreply.github.com>2022-08-29 20:41:01 -0500
committerGitHub <noreply@github.com>2022-08-29 20:41:01 -0500
commitf42d630544165d11a544224ac273d6aaf89d8095 (patch)
tree94bd73771ecb582d89a87cdca8e21b2d6573ef12 /azalea-client/src
parent2ea804401f54a45765860201d10d0569d07862ec (diff)
downloadazalea-drasl-f42d630544165d11a544224ac273d6aaf89d8095.tar.xz
Physics (#11)
* Put physics module in azalea-entity * port aabb * add more stuff to PositionXYZ * azalea-physics * important collision things * more physics stuff * backup because i'm about to delete shapes * more shape stuff * CubeVoxelShape * no compile errors??? insane * impl VoxelShape for ArrayVoxelShape * Shapes stuff * collide_x but it doesn't work yet * binary_search * it compiles * Entity has bounding box * Update discrete_voxel_shape.rs * Entity::make_bounding_box * ok i'm about to merge az-entity and az-world might be a terrible idea which is why i'm committing first * ok so i moved entity to world * on_pos and move_entity compiles * add send_position * move collision stuff to collision module in az-physics * dimension is no longer an Option * start trying to do collision for the client * collision works :tada: * start adding palette resizing * get_and_set (pain) * it compiles but probably won't work * add a test * remove printlns * add more tests for palette stuff * ClientboundMoveVec3Packet -> ClientboundMoveEntityPosPacket i think i changed this on accident once * palette resizing works todo: remove the printlns * Remove printlns in palette.rs * fix issues from merge * fixes + work a bit more on physics * Better entities (#19) * well it compiles * add tests to entity storage * add suggestions in azalea-brigadier * this probably causes ub * fix brigadiersuggestions * get rid of entityid * test From<EntityMut> for EntityRef * don't mention other libraries since there's too many * fix warnings * do todos in brigadier suggestions * work on physics * more physics stuff * remove trait feature on az-block i think rust gets confused and compiles the macro without the feature * bump ahash * aes tests in az-crypto * optimize aes's deps * fix crashes * fix section_index for negative numbers and test * fix BlockPos protocol implementation * remove some debug prints * prepare to add ai_step * make ai step work * clippy
Diffstat (limited to 'azalea-client/src')
-rw-r--r--azalea-client/src/client.rs213
-rw-r--r--azalea-client/src/movement.rs173
-rw-r--r--azalea-client/src/player.rs15
3 files changed, 294 insertions, 107 deletions
diff --git a/azalea-client/src/client.rs b/azalea-client/src/client.rs
index fcb624b4..c495bc5c 100644
--- a/azalea-client/src/client.rs
+++ b/azalea-client/src/client.rs
@@ -1,7 +1,7 @@
use crate::{Account, Player};
use azalea_auth::game_profile::GameProfile;
-use azalea_core::{ChunkPos, EntityPos, PositionDelta, PositionDeltaTrait, ResourceLocation};
-use azalea_entity::Entity;
+use azalea_block::BlockState;
+use azalea_core::{ChunkPos, ResourceLocation, Vec3};
use azalea_protocol::{
connect::{Connection, ConnectionError},
packets::{
@@ -11,7 +11,7 @@ use azalea_protocol::{
serverbound_accept_teleportation_packet::ServerboundAcceptTeleportationPacket,
serverbound_custom_payload_packet::ServerboundCustomPayloadPacket,
serverbound_keep_alive_packet::ServerboundKeepAlivePacket,
- serverbound_move_player_pos_rot_packet::ServerboundMovePlayerPacketPosRot,
+ serverbound_move_player_pos_rot_packet::ServerboundMovePlayerPosRotPacket,
ClientboundGamePacket, ServerboundGamePacket,
},
handshake::client_intention_packet::ClientIntentionPacket,
@@ -25,6 +25,7 @@ use azalea_protocol::{
read::ReadPacketError,
resolver, ServerAddress,
};
+use azalea_world::entity::EntityData;
use azalea_world::Dimension;
use std::{
fmt::Debug,
@@ -66,8 +67,10 @@ pub struct Client {
game_profile: GameProfile,
pub conn: Arc<tokio::sync::Mutex<Connection<ClientboundGamePacket, ServerboundGamePacket>>>,
pub player: Arc<Mutex<Player>>,
- pub dimension: Arc<Mutex<Option<Dimension>>>,
- // game_loop
+ pub dimension: Arc<Mutex<Dimension>>,
+
+ /// 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,
}
/// Whether we should ignore errors when decoding packets.
@@ -181,7 +184,9 @@ impl Client {
game_profile,
conn,
player: Arc::new(Mutex::new(Player::default())),
- dimension: Arc::new(Mutex::new(None)),
+ dimension: Arc::new(Mutex::new(Dimension::default())),
+
+ position_remainder: 0,
};
// just start up the game loop and we're ready!
@@ -298,16 +303,10 @@ impl Client {
let mut dimension_lock = client.dimension.lock().unwrap();
// the 16 here is our render distance
// i'll make this an actual setting later
- *dimension_lock = Some(Dimension::new(16, height, min_y));
+ *dimension_lock = Dimension::new(16, height, min_y);
- let entity =
- Entity::new(p.player_id, client.game_profile.uuid, EntityPos::default());
- dimension_lock
- .as_mut()
- .expect(
- "Dimension doesn't exist! We should've gotten a login packet by now.",
- )
- .add_entity(entity);
+ let entity = EntityData::new(client.game_profile.uuid, Vec3::default());
+ dimension_lock.add_entity(p.player_id, entity);
let mut player_lock = client.player.lock().unwrap();
@@ -368,42 +367,42 @@ impl Client {
println!("Got player position packet {:?}", p);
let (new_pos, y_rot, x_rot) = {
- let player_lock = client.player.lock().unwrap();
- let player_entity_id = player_lock.entity_id;
- drop(player_lock);
+ let player_entity_id = {
+ let player_lock = client.player.lock().unwrap();
+ player_lock.entity_id
+ };
let mut dimension_lock = client.dimension.lock().unwrap();
- let dimension = dimension_lock.as_mut().unwrap();
- let player_entity = dimension
- .mut_entity_by_id(player_entity_id)
+ let mut player_entity = dimension_lock
+ .entity_mut(player_entity_id)
.expect("Player entity doesn't exist");
- let delta_movement = &player_entity.delta;
+ let delta_movement = player_entity.delta;
let is_x_relative = p.relative_arguments.x;
let is_y_relative = p.relative_arguments.y;
let is_z_relative = p.relative_arguments.z;
let (delta_x, new_pos_x) = if is_x_relative {
- player_entity.old_pos.x += p.x;
- (delta_movement.x(), player_entity.pos().x + p.x)
+ player_entity.last_pos.x += p.x;
+ (delta_movement.x, player_entity.pos().x + p.x)
} else {
- player_entity.old_pos.x = p.x;
+ player_entity.last_pos.x = p.x;
(0.0, p.x)
};
let (delta_y, new_pos_y) = if is_y_relative {
- player_entity.old_pos.y += p.y;
- (delta_movement.y(), player_entity.pos().y + p.y)
+ player_entity.last_pos.y += p.y;
+ (delta_movement.y, player_entity.pos().y + p.y)
} else {
- player_entity.old_pos.y = p.y;
+ player_entity.last_pos.y = p.y;
(0.0, p.y)
};
let (delta_z, new_pos_z) = if is_z_relative {
- player_entity.old_pos.z += p.z;
- (delta_movement.z(), player_entity.pos().z + p.z)
+ player_entity.last_pos.z += p.z;
+ (delta_movement.z, player_entity.pos().z + p.z)
} else {
- player_entity.old_pos.z = p.z;
+ player_entity.last_pos.z = p.z;
(0.0, p.z)
};
@@ -416,21 +415,21 @@ impl Client {
x_rot += player_entity.y_rot;
}
- player_entity.delta = PositionDelta {
- xa: delta_x,
- ya: delta_y,
- za: delta_z,
+ player_entity.delta = Vec3 {
+ x: delta_x,
+ y: delta_y,
+ z: delta_z,
};
player_entity.set_rotation(y_rot, x_rot);
// TODO: minecraft sets "xo", "yo", and "zo" here but idk what that means
// so investigate that ig
- let new_pos = EntityPos {
+ let new_pos = Vec3 {
x: new_pos_x,
y: new_pos_y,
z: new_pos_z,
};
- dimension
- .move_entity(player_entity_id, new_pos)
+ dimension_lock
+ .set_entity_pos(player_entity_id, new_pos)
.expect("The player entity should always exist");
(new_pos, y_rot, x_rot)
@@ -442,7 +441,7 @@ impl Client {
.await?;
conn_lock
.write(
- ServerboundMovePlayerPacketPosRot {
+ ServerboundMovePlayerPosRotPacket {
x: new_pos.x,
y: new_pos.y,
z: new_pos.z,
@@ -463,8 +462,6 @@ impl Client {
client
.dimension
.lock()?
- .as_mut()
- .unwrap()
.update_view_center(&ChunkPos::new(p.x, p.z));
}
ClientboundGamePacket::ClientboundLevelChunkWithLightPacket(p) => {
@@ -475,8 +472,6 @@ impl Client {
client
.dimension
.lock()?
- .as_mut()
- .expect("Dimension doesn't exist! We should've gotten a login packet by now.")
.replace_with_packet_data(&pos, &mut p.chunk_data.data.as_slice())
.unwrap();
}
@@ -485,13 +480,8 @@ impl Client {
}
ClientboundGamePacket::ClientboundAddEntityPacket(p) => {
println!("Got add entity packet {:?}", p);
- let entity = Entity::from(p);
- client
- .dimension
- .lock()?
- .as_mut()
- .expect("Dimension doesn't exist! We should've gotten a login packet by now.")
- .add_entity(entity);
+ let entity = EntityData::from(p);
+ client.dimension.lock()?.add_entity(p.id, entity);
}
ClientboundGamePacket::ClientboundSetEntityDataPacket(_p) => {
// println!("Got set entity data packet {:?}", p);
@@ -507,13 +497,8 @@ impl Client {
}
ClientboundGamePacket::ClientboundAddPlayerPacket(p) => {
println!("Got add player packet {:?}", p);
- let entity = Entity::from(p);
- client
- .dimension
- .lock()?
- .as_mut()
- .expect("Dimension doesn't exist! We should've gotten a login packet by now.")
- .add_entity(entity);
+ let entity = EntityData::from(p);
+ client.dimension.lock()?.add_entity(p.id, entity);
}
ClientboundGamePacket::ClientboundInitializeBorderPacket(p) => {
println!("Got initialize border packet {:?}", p);
@@ -535,12 +520,11 @@ impl Client {
}
ClientboundGamePacket::ClientboundTeleportEntityPacket(p) => {
let mut dimension_lock = client.dimension.lock()?;
- let dimension = dimension_lock.as_mut().unwrap();
- dimension
- .move_entity(
+ dimension_lock
+ .set_entity_pos(
p.id,
- EntityPos {
+ Vec3 {
x: p.x,
y: p.y,
z: p.z,
@@ -556,17 +540,15 @@ impl Client {
}
ClientboundGamePacket::ClientboundMoveEntityPosPacket(p) => {
let mut dimension_lock = client.dimension.lock()?;
- let dimension = dimension_lock.as_mut().unwrap();
- dimension
+ dimension_lock
.move_entity_with_delta(p.entity_id, &p.delta)
.map_err(|e| HandleError::Other(e.into()))?;
}
ClientboundGamePacket::ClientboundMoveEntityPosRotPacket(p) => {
let mut dimension_lock = client.dimension.lock()?;
- let dimension = dimension_lock.as_mut().unwrap();
- dimension
+ dimension_lock
.move_entity_with_delta(p.entity_id, &p.delta)
.map_err(|e| HandleError::Other(e.into()))?;
}
@@ -603,6 +585,16 @@ impl Client {
ClientboundGamePacket::ClientboundBlockUpdatePacket(p) => {
println!("Got block update packet {:?}", p);
// TODO: update world
+ let mut dimension = client.dimension.lock()?;
+ // dimension.get_block_state(pos)
+ if let Ok(block_state) = BlockState::try_from(p.block_state) {
+ dimension.set_block_state(&p.pos, block_state);
+ } else {
+ eprintln!(
+ "Non-existent block state for block update packet {}",
+ p.block_state
+ );
+ }
}
ClientboundGamePacket::ClientboundAnimatePacket(p) => {
println!("Got animate packet {:?}", p);
@@ -626,28 +618,107 @@ impl Client {
ClientboundGamePacket::ClientboundUpdateMobEffectPacket(p) => {
println!("Got update mob effect packet {:?}", p);
}
- _ => panic!("Unexpected packet {:?}", packet),
+ ClientboundGamePacket::ClientboundAddExperienceOrbPacket(_) => {}
+ ClientboundGamePacket::ClientboundAwardStatsPacket(_) => {}
+ ClientboundGamePacket::ClientboundBlockChangedAckPacket(_) => {}
+ ClientboundGamePacket::ClientboundBlockDestructionPacket(_) => {}
+ ClientboundGamePacket::ClientboundBlockEntityDataPacket(_) => {}
+ ClientboundGamePacket::ClientboundBlockEventPacket(_) => {}
+ ClientboundGamePacket::ClientboundBossEventPacket(_) => {}
+ ClientboundGamePacket::ClientboundChatPreviewPacket(_) => {}
+ ClientboundGamePacket::ClientboundCommandSuggestionsPacket(_) => {}
+ ClientboundGamePacket::ClientboundContainerSetDataPacket(_) => {}
+ ClientboundGamePacket::ClientboundContainerSetSlotPacket(_) => {}
+ ClientboundGamePacket::ClientboundCooldownPacket(_) => {}
+ ClientboundGamePacket::ClientboundCustomChatCompletionsPacket(_) => {}
+ ClientboundGamePacket::ClientboundCustomSoundPacket(_) => {}
+ ClientboundGamePacket::ClientboundDeleteChatPacket(_) => {}
+ ClientboundGamePacket::ClientboundExplodePacket(_) => {}
+ ClientboundGamePacket::ClientboundForgetLevelChunkPacket(_) => {}
+ ClientboundGamePacket::ClientboundHorseScreenOpenPacket(_) => {}
+ ClientboundGamePacket::ClientboundMapItemDataPacket(_) => {}
+ ClientboundGamePacket::ClientboundMerchantOffersPacket(_) => {}
+ ClientboundGamePacket::ClientboundMoveVehiclePacket(_) => {}
+ ClientboundGamePacket::ClientboundOpenBookPacket(_) => {}
+ ClientboundGamePacket::ClientboundOpenScreenPacket(_) => {}
+ ClientboundGamePacket::ClientboundOpenSignEditorPacket(_) => {}
+ ClientboundGamePacket::ClientboundPingPacket(_) => {}
+ ClientboundGamePacket::ClientboundPlaceGhostRecipePacket(_) => {}
+ ClientboundGamePacket::ClientboundPlayerChatHeaderPacket(_) => {}
+ ClientboundGamePacket::ClientboundPlayerCombatEndPacket(_) => {}
+ ClientboundGamePacket::ClientboundPlayerCombatEnterPacket(_) => {}
+ ClientboundGamePacket::ClientboundPlayerCombatKillPacket(_) => {}
+ ClientboundGamePacket::ClientboundPlayerLookAtPacket(_) => {}
+ ClientboundGamePacket::ClientboundRemoveMobEffectPacket(_) => {}
+ ClientboundGamePacket::ClientboundResourcePackPacket(_) => {}
+ ClientboundGamePacket::ClientboundRespawnPacket(_) => {}
+ ClientboundGamePacket::ClientboundSelectAdvancementsTabPacket(_) => {}
+ ClientboundGamePacket::ClientboundSetActionBarTextPacket(_) => {}
+ ClientboundGamePacket::ClientboundSetBorderCenterPacket(_) => {}
+ ClientboundGamePacket::ClientboundSetBorderLerpSizePacket(_) => {}
+ ClientboundGamePacket::ClientboundSetBorderSizePacket(_) => {}
+ ClientboundGamePacket::ClientboundSetBorderWarningDelayPacket(_) => {}
+ ClientboundGamePacket::ClientboundSetBorderWarningDistancePacket(_) => {}
+ ClientboundGamePacket::ClientboundSetCameraPacket(_) => {}
+ ClientboundGamePacket::ClientboundSetChunkCacheRadiusPacket(_) => {}
+ ClientboundGamePacket::ClientboundSetDisplayChatPreviewPacket(_) => {}
+ ClientboundGamePacket::ClientboundSetDisplayObjectivePacket(_) => {}
+ ClientboundGamePacket::ClientboundSetEntityMotionPacket(_) => {}
+ ClientboundGamePacket::ClientboundSetObjectivePacket(_) => {}
+ ClientboundGamePacket::ClientboundSetPassengersPacket(_) => {}
+ ClientboundGamePacket::ClientboundSetPlayerTeamPacket(_) => {}
+ ClientboundGamePacket::ClientboundSetScorePacket(_) => {}
+ ClientboundGamePacket::ClientboundSetSimulationDistancePacket(_) => {}
+ ClientboundGamePacket::ClientboundSetSubtitleTextPacket(_) => {}
+ ClientboundGamePacket::ClientboundSetTitleTextPacket(_) => {}
+ ClientboundGamePacket::ClientboundSetTitlesAnimationPacket(_) => {}
+ ClientboundGamePacket::ClientboundSoundEntityPacket(_) => {}
+ ClientboundGamePacket::ClientboundStopSoundPacket(_) => {}
+ ClientboundGamePacket::ClientboundTabListPacket(_) => {}
+ ClientboundGamePacket::ClientboundTagQueryPacket(_) => {}
+ ClientboundGamePacket::ClientboundTakeItemEntityPacket(_) => {}
}
Ok(())
}
/// Runs game_tick every 50 milliseconds.
- async fn game_tick_loop(client: Client, tx: UnboundedSender<Event>) {
+ async fn game_tick_loop(mut client: Client, tx: UnboundedSender<Event>) {
let mut game_tick_interval = time::interval(time::Duration::from_millis(50));
// TODO: Minecraft bursts up to 10 ticks and then skips, we should too
game_tick_interval.set_missed_tick_behavior(time::MissedTickBehavior::Burst);
loop {
game_tick_interval.tick().await;
- Self::game_tick(&client, &tx).await;
+ Self::game_tick(&mut client, &tx).await;
}
}
/// Runs every 50 milliseconds.
- async fn game_tick(client: &Client, tx: &UnboundedSender<Event>) {
- if client.dimension.lock().unwrap().is_none() {
- return;
+ 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().unwrap();
+ let player_lock = client.player.lock().unwrap();
+ let player_entity = player_lock.entity(&dimension_lock);
+ let player_entity = if let Some(player_entity) = player_entity {
+ player_entity
+ } else {
+ return;
+ };
+ let player_chunk_pos: ChunkPos = player_entity.pos().into();
+ if dimension_lock[&player_chunk_pos].is_none() {
+ return;
+ }
}
+
+ // TODO: if we're a passenger, send the required packets
+
+ if let Err(e) = client.send_position().await {
+ println!("Error sending position: {:?}", e);
+ }
+
+ // TODO: minecraft does ambient sounds here
+
tx.send(Event::GameTick).unwrap();
}
}
diff --git a/azalea-client/src/movement.rs b/azalea-client/src/movement.rs
index 5f9533be..df2af9d8 100644
--- a/azalea-client/src/movement.rs
+++ b/azalea-client/src/movement.rs
@@ -1,6 +1,13 @@
use crate::Client;
-use azalea_core::EntityPos;
-use azalea_protocol::packets::game::serverbound_move_player_pos_rot_packet::ServerboundMovePlayerPacketPosRot;
+use azalea_core::Vec3;
+use azalea_physics::collision::{MovableEntity, MoverType};
+use azalea_physics::HasPhysics;
+use azalea_protocol::packets::game::{
+ serverbound_move_player_pos_packet::ServerboundMovePlayerPosPacket,
+ serverbound_move_player_pos_rot_packet::ServerboundMovePlayerPosRotPacket,
+ serverbound_move_player_rot_packet::ServerboundMovePlayerRotPacket,
+ serverbound_move_player_status_only_packet::ServerboundMovePlayerStatusOnlyPacket,
+};
use azalea_world::MoveEntityError;
use thiserror::Error;
@@ -12,45 +19,149 @@ pub enum MovePlayerError {
Io(#[from] std::io::Error),
}
+impl From<MoveEntityError> for MovePlayerError {
+ fn from(err: MoveEntityError) -> Self {
+ match err {
+ MoveEntityError::EntityDoesNotExist => MovePlayerError::PlayerNotInWorld,
+ }
+ }
+}
+
impl Client {
- /// Set the client's position to the given coordinates.
- pub async fn move_to(&mut self, new_pos: EntityPos) -> Result<(), MovePlayerError> {
- {
+ /// This gets called every tick.
+ pub async fn send_position(&mut self) -> Result<(), MovePlayerError> {
+ let packet = {
+ let player_lock = self.player.lock().unwrap();
+
let mut dimension_lock = self.dimension.lock().unwrap();
- let dimension = dimension_lock.as_mut().unwrap();
- let player_lock = self.player.lock().unwrap();
+ 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
- let player_id = if let Some(player_lock) = player_lock.entity(dimension) {
- player_lock.id
+ // TODO: the camera being able to be controlled by other entities isn't implemented yet
+ // if !self.is_controlled_camera() { return };
+
+ 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;
+ let y_rot_delta = (player_entity.y_rot - player_entity.y_rot_last) as f64;
+ let x_rot_delta = (player_entity.x_rot - player_entity.x_rot_last) as f64;
+
+ self.position_remainder += 1;
+
+ // boolean sendingPosition = Mth.lengthSquared(xDelta, yDelta, zDelta) > Mth.square(2.0E-4D) || this.positionReminder >= 20;
+ let sending_position = ((x_delta.powi(2) + y_delta.powi(2) + z_delta.powi(2))
+ > 2.0e-4f64.powi(2))
+ || self.position_remainder >= 20;
+ let sending_rotation = y_rot_delta != 0.0 || x_rot_delta != 0.0;
+
+ // if self.is_passenger() {
+ // TODO: posrot packet for being a passenger
+ // }
+ let packet = if sending_position && sending_rotation {
+ Some(
+ ServerboundMovePlayerPosRotPacket {
+ x: player_pos.x,
+ y: player_pos.y,
+ z: player_pos.z,
+ x_rot: player_entity.x_rot,
+ y_rot: player_entity.y_rot,
+ on_ground: player_entity.on_ground,
+ }
+ .get(),
+ )
+ } else if sending_position {
+ Some(
+ ServerboundMovePlayerPosPacket {
+ x: player_pos.x,
+ y: player_pos.y,
+ z: player_pos.z,
+ on_ground: player_entity.on_ground,
+ }
+ .get(),
+ )
+ } else if sending_rotation {
+ Some(
+ ServerboundMovePlayerRotPacket {
+ x_rot: player_entity.x_rot,
+ y_rot: player_entity.y_rot,
+ on_ground: player_entity.on_ground,
+ }
+ .get(),
+ )
+ } else if player_entity.last_on_ground != player_entity.on_ground {
+ Some(
+ ServerboundMovePlayerStatusOnlyPacket {
+ on_ground: player_entity.on_ground,
+ }
+ .get(),
+ )
} else {
- return Err(MovePlayerError::PlayerNotInWorld);
+ None
};
- match dimension.move_entity(player_id, new_pos) {
- Ok(_) => Ok(()),
- Err(e) => match e {
- MoveEntityError::EntityDoesNotExist => Err(MovePlayerError::PlayerNotInWorld),
- },
- }?;
+ if sending_position {
+ player_entity.last_pos = *player_entity.pos();
+ self.position_remainder = 0;
+ }
+ if sending_rotation {
+ player_entity.y_rot_last = player_entity.y_rot;
+ player_entity.x_rot_last = player_entity.x_rot;
+ }
+
+ player_entity.last_on_ground = player_entity.on_ground;
+ // minecraft checks for autojump here, but also autojump is bad so
+
+ packet
+ };
+
+ if let Some(packet) = packet {
+ self.conn.lock().await.write(packet).await?;
}
- self.conn
- .lock()
- .await
- .write(
- ServerboundMovePlayerPacketPosRot {
- x: new_pos.x,
- y: new_pos.y,
- z: new_pos.z,
- x_rot: 0.0,
- y_rot: 0.0,
- on_ground: false,
- }
- .get(),
- )
- .await?;
+ 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().unwrap();
+ let mut dimension_lock = self.dimension.lock().unwrap();
+
+ dimension_lock.set_entity_pos(player_lock.entity_id, new_pos)?;
+
+ Ok(())
+ }
+
+ pub async fn move_entity(&mut self, movement: &Vec3) -> Result<(), MovePlayerError> {
+ let mut dimension_lock = self.dimension.lock().unwrap();
+ let player = self.player.lock().unwrap();
+
+ let mut entity = player
+ .entity_mut(&mut dimension_lock)
+ .ok_or(MovePlayerError::PlayerNotInWorld)?;
+ println!(
+ "move entity bounding box: {} {:?}",
+ entity.id, entity.bounding_box
+ );
+
+ entity.move_colliding(&MoverType::Own, movement)?;
Ok(())
}
+
+ pub fn ai_step(&mut self) {
+ let player_lock = self.player.lock().unwrap();
+ let mut dimension_lock = self.dimension.lock().unwrap();
+
+ let mut player_entity = player_lock
+ .entity_mut(&mut dimension_lock)
+ .expect("Player must exist");
+
+ player_entity.ai_step();
+ }
}
diff --git a/azalea-client/src/player.rs b/azalea-client/src/player.rs
index 6c093517..11651b9c 100644
--- a/azalea-client/src/player.rs
+++ b/azalea-client/src/player.rs
@@ -1,4 +1,4 @@
-use azalea_entity::Entity;
+use azalea_world::entity::{EntityMut, EntityRef};
use azalea_world::Dimension;
use uuid::Uuid;
@@ -7,6 +7,7 @@ pub trait DimensionHaver {
fn dimension(&self) -> &Dimension;
}
+/// A player in the dimension or tab list.
#[derive(Default, Debug)]
pub struct Player {
/// The player's uuid.
@@ -16,10 +17,14 @@ pub struct Player {
}
impl Player {
- /// Get the entity of the player in the world.
- pub fn entity<'a>(&self, world: &'a Dimension) -> Option<&'a Entity> {
- // world.entity_by_uuid(&self.uuid)
- world.entity_by_id(self.entity_id)
+ /// Get a reference to the entity of the player in the world.
+ pub fn entity<'d>(&'d self, dimension: &'d Dimension) -> Option<EntityRef> {
+ 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> {
+ dimension.entity_mut(self.entity_id)
}
pub fn set_uuid(&mut self, uuid: Uuid) {