diff options
| author | mat <27899617+mat-1@users.noreply.github.com> | 2022-08-29 20:41:01 -0500 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2022-08-29 20:41:01 -0500 |
| commit | f42d630544165d11a544224ac273d6aaf89d8095 (patch) | |
| tree | 94bd73771ecb582d89a87cdca8e21b2d6573ef12 /azalea-client/src | |
| parent | 2ea804401f54a45765860201d10d0569d07862ec (diff) | |
| download | azalea-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.rs | 213 | ||||
| -rw-r--r-- | azalea-client/src/movement.rs | 173 | ||||
| -rw-r--r-- | azalea-client/src/player.rs | 15 |
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) { |
