From 24fc8dc1887cfa1d70c0917c5d9776458ee46faa Mon Sep 17 00:00:00 2001 From: mat Date: Sun, 25 Dec 2022 17:12:14 -0600 Subject: [PATCH] packet handler uses the ecs now and other fun changes i still need to make ticking use the ecs but that's tricker, i'm considering using bevy_ecs systems for those bevy_ecs systems can't be async but the only async things in ticking is just sending packets which can just be done as a tokio task so that's not a big deal --- Cargo.lock | 1 + azalea-client/Cargo.toml | 1 + azalea-client/src/client.rs | 87 +++++++++++-------- azalea-client/src/movement.rs | 57 ++++++------ .../game/clientbound_add_player_packet.rs | 4 +- azalea-world/src/entity_storage.rs | 4 +- azalea-world/src/world.rs | 72 +++++++-------- bot/src/main.rs | 2 +- 8 files changed, 119 insertions(+), 109 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 958e5dcb..af4f94dc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -278,6 +278,7 @@ dependencies = [ "azalea-protocol", "azalea-registry", "azalea-world", + "bevy_ecs", "log", "nohash-hasher", "once_cell", diff --git a/azalea-client/Cargo.toml b/azalea-client/Cargo.toml index f218c46b..3357f430 100644 --- a/azalea-client/Cargo.toml +++ b/azalea-client/Cargo.toml @@ -20,6 +20,7 @@ azalea-physics = {path = "../azalea-physics", version = "0.5.0"} azalea-protocol = {path = "../azalea-protocol", version = "0.5.0"} azalea-registry = {path = "../azalea-registry", version = "0.5.0"} azalea-world = {path = "../azalea-world", version = "0.5.0"} +bevy_ecs = {version = "0.9.1", default-features = false} log = "0.4.17" nohash-hasher = "0.2.0" once_cell = "1.16.0" diff --git a/azalea-client/src/client.rs b/azalea-client/src/client.rs index 287d6c23..a0053637 100644 --- a/azalea-client/src/client.rs +++ b/azalea-client/src/client.rs @@ -614,15 +614,13 @@ impl Client { let (new_pos, y_rot, x_rot) = { let player_entity_id = *client.entity_id.read(); - let mut world_lock = client.world(); - // let mut player_entity = world_lock.entity_mut(player_entity_id).unwrap(); - let (physics, position) = - world_lock - .entity_storage - .read() - .query_entity_mut::<(&mut entity::Physics, &entity::Position)>( - player_entity_id, - ); + let mut world = client.world(); + // let mut player_entity = world.entity_mut(player_entity_id).unwrap(); + let mut entities = world.entities.write(); + let (mut physics, mut position) = + entities.query_entity_mut::<(&mut entity::Physics, &mut entity::Position)>( + player_entity_id, + ); let delta_movement = physics.delta; @@ -674,8 +672,13 @@ impl Client { y: new_pos_y, z: new_pos_z, }; - world_lock - .set_entity_pos(player_entity_id, new_pos) + world + .set_entity_pos_from_refs( + player_entity_id, + new_pos, + position.into_inner(), + physics.into_inner(), + ) .expect("The player entity should always exist"); (new_pos, y_rot, x_rot) @@ -773,12 +776,7 @@ impl Client { // world, since we check that the chunk isn't currently owned // by this client. let shared_has_chunk = client.world.read().get_chunk(&pos).is_some(); - let this_client_has_chunk = client - .world - .read() - .chunk_storage - .limited_get(&pos) - .is_some(); + let this_client_has_chunk = client.world.read().chunks.limited_get(&pos).is_some(); if shared_has_chunk && !this_client_has_chunk { trace!( "Skipping parsing chunk {:?} because we already know about it", @@ -802,18 +800,22 @@ impl Client { } ClientboundGamePacket::AddEntity(p) => { debug!("Got add entity packet {:?}", p); - // let entity = EntityData::from(p); let bundle = p.as_entity_bundle(); - let world = client.world(); - world.add_entity(p.id, bundle); - let entity = world.entity_storage.write().ecs_entity(p.id); - p.apply_metadata(entity); + let mut world = client.world.write(); + world.add_entity(EntityId(p.id), bundle); + // the bundle doesn't include the default entity metadata so we add that + // separately + let mut entities = world.entities.shared.write(); + let mut entity = entities.ecs_entity_mut(EntityId(p.id)).unwrap(); + p.apply_metadata(&mut entity); } ClientboundGamePacket::SetEntityData(p) => { debug!("Got set entity data packet {:?}", p); - let mut world = client.world.write(); - if let Some(mut entity) = world.entity_mut(p.id) { - entity.apply_metadata(&p.packed_items.0); + let world = client.world.write(); + let mut entities = world.entities.shared.write(); + let entity = entities.ecs_entity_mut(EntityId(p.id)); + if let Some(mut entity) = entity { + entity::metadata::apply_metadata(&mut entity, p.packed_items.0.clone()); } else { // warn!("Server sent an entity data packet for an entity id // ({}) that we don't know about", p.id); @@ -830,8 +832,11 @@ impl Client { } ClientboundGamePacket::AddPlayer(p) => { debug!("Got add player packet {:?}", p); - let entity = EntityData::from(p); - client.world.write().add_entity(p.id, entity); + let bundle = p.as_player_bundle(); + let mut world = client.world.write(); + world.add_entity(EntityId(p.id), bundle); + // the default metadata was already included in the bundle for + // us } ClientboundGamePacket::InitializeBorder(p) => { debug!("Got initialize border packet {:?}", p); @@ -860,9 +865,9 @@ impl Client { debug!("Got set experience packet {:?}", p); } ClientboundGamePacket::TeleportEntity(p) => { - let mut world_lock = client.world.write(); - let _ = world_lock.set_entity_pos( - p.id, + let mut world = client.world.write(); + let _ = world.set_entity_pos( + EntityId(p.id), Vec3 { x: p.x, y: p.y, @@ -877,14 +882,12 @@ impl Client { // debug!("Got rotate head packet {:?}", p); } ClientboundGamePacket::MoveEntityPos(p) => { - let mut world_lock = client.world.write(); - - let _ = world_lock.move_entity_with_delta(p.entity_id, &p.delta); + let mut world = client.world.write(); + let _ = world.move_entity_with_delta(EntityId(p.entity_id), &p.delta); } ClientboundGamePacket::MoveEntityPosRot(p) => { - let mut world_lock = client.world.write(); - - let _ = world_lock.move_entity_with_delta(p.entity_id, &p.delta); + let mut world = client.world.write(); + let _ = world.move_entity_with_delta(EntityId(p.entity_id), &p.delta); } ClientboundGamePacket::MoveEntityRot(_p) => { // debug!("Got move entity rot packet {:?}", p); @@ -972,7 +975,7 @@ impl Client { ClientboundGamePacket::PlayerCombatEnter(_) => {} ClientboundGamePacket::PlayerCombatKill(p) => { debug!("Got player kill packet {:?}", p); - if *client.entity_id.read() == p.player_id { + if *client.entity_id.read() == EntityId(p.player_id) { // we can't define a variable here with client.dead.lock() // because of https://github.com/rust-lang/rust/issues/57478 if !*client.dead.lock() { @@ -1086,6 +1089,16 @@ impl Client { // Entity::new(world, entity_id, entity_ptr) // } + // pub fn query_entity<'w, Q: bevy_ecs::query::WorldQuery>( + // &'w self, + // ) -> bevy_ecs::query::ROQueryItem<'w, Q> { + // let e = parking_lot::RwLockReadGuard::map( + // self.world().entity_storage.read(), + // |entity_storage| entity_storage.query_entity::<'w, + // Q>(*self.entity_id.read()), ); + // e + // } + /// Returns whether we have a received the login packet yet. pub fn logged_in(&self) -> bool { // the login packet tells us the world name diff --git a/azalea-client/src/movement.rs b/azalea-client/src/movement.rs index d9cab1d4..3c8be08c 100644 --- a/azalea-client/src/movement.rs +++ b/azalea-client/src/movement.rs @@ -2,8 +2,7 @@ use std::backtrace::Backtrace; use crate::Client; use azalea_core::Vec3; -use azalea_physics::collision::{MovableEntity, MoverType}; -use azalea_physics::HasPhysics; +use azalea_physics::collision::MoverType; use azalea_protocol::packets::game::serverbound_player_command_packet::ServerboundPlayerCommandPacket; use azalea_protocol::packets::game::{ serverbound_move_player_pos_packet::ServerboundMovePlayerPosPacket, @@ -11,7 +10,7 @@ use azalea_protocol::packets::game::{ serverbound_move_player_rot_packet::ServerboundMovePlayerRotPacket, serverbound_move_player_status_only_packet::ServerboundMovePlayerStatusOnlyPacket, }; -use azalea_world::MoveEntityError; +use azalea_world::{entity, MoveEntityError}; use thiserror::Error; #[derive(Error, Debug)] @@ -35,22 +34,29 @@ impl From for MovePlayerError { impl Client { /// This gets called automatically every tick. pub(crate) async fn send_position(&mut self) -> Result<(), MovePlayerError> { + self.send_sprinting_if_needed().await?; + let packet = { - 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; + // i don't like this + let entity_storage_lock = self.world().entities.clone(); + let mut entity_storage = entity_storage_lock.write(); + let (player_pos, mut physics) = entity_storage + .query_entity_mut::<(&entity::Position, &mut entity::Physics)>( + *self.entity_id.read(), + ); + + let player_old_pos = physics.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; - 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; + let y_rot_delta = (physics.y_rot - physics.y_rot_last) as f64; + let x_rot_delta = (physics.x_rot - physics.x_rot_last) as f64; physics_state.position_remainder += 1; @@ -70,9 +76,9 @@ impl Client { 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, + x_rot: physics.x_rot, + y_rot: physics.y_rot, + on_ground: physics.on_ground, } .get(), ) @@ -82,23 +88,23 @@ impl Client { x: player_pos.x, y: player_pos.y, z: player_pos.z, - on_ground: player_entity.on_ground, + on_ground: physics.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, + x_rot: physics.x_rot, + y_rot: physics.y_rot, + on_ground: physics.on_ground, } .get(), ) - } else if player_entity.last_on_ground != player_entity.on_ground { + } else if physics.last_on_ground != physics.on_ground { Some( ServerboundMovePlayerStatusOnlyPacket { - on_ground: player_entity.on_ground, + on_ground: physics.on_ground, } .get(), ) @@ -106,19 +112,16 @@ impl Client { None }; - drop(player_entity); - let mut player_entity = self.entity(); - if sending_position { - player_entity.last_pos = *player_entity.pos(); + physics.last_pos = **player_pos; physics_state.position_remainder = 0; } if sending_rotation { - player_entity.y_rot_last = player_entity.y_rot; - player_entity.x_rot_last = player_entity.x_rot; + physics.y_rot_last = physics.y_rot; + physics.x_rot_last = physics.x_rot; } - player_entity.last_on_ground = player_entity.on_ground; + physics.last_on_ground = physics.on_ground; // minecraft checks for autojump here, but also autojump is bad so packet @@ -140,10 +143,10 @@ impl Client { } else { azalea_protocol::packets::game::serverbound_player_command_packet::Action::StopSprinting }; - let player_entity_id = self.entity().id; + let player_entity_id = *self.entity_id.read(); self.write_packet( ServerboundPlayerCommandPacket { - id: player_entity_id, + id: *player_entity_id, action: sprinting_action, data: 0, } diff --git a/azalea-protocol/src/packets/game/clientbound_add_player_packet.rs b/azalea-protocol/src/packets/game/clientbound_add_player_packet.rs index 5d5d3c19..09b1e16c 100755 --- a/azalea-protocol/src/packets/game/clientbound_add_player_packet.rs +++ b/azalea-protocol/src/packets/game/clientbound_add_player_packet.rs @@ -18,9 +18,9 @@ pub struct ClientboundAddPlayerPacket { } impl ClientboundAddPlayerPacket { - fn as_bundle(p: &ClientboundAddPlayerPacket) -> PlayerBundle { + pub fn as_player_bundle(&self) -> PlayerBundle { PlayerBundle { - entity: EntityBundle::new(p.uuid, p.position, EntityKind::Player), + entity: EntityBundle::new(self.uuid, self.position, EntityKind::Player), metadata: PlayerMetadataBundle::default(), } } diff --git a/azalea-world/src/entity_storage.rs b/azalea-world/src/entity_storage.rs index 69cca026..35fddda7 100755 --- a/azalea-world/src/entity_storage.rs +++ b/azalea-world/src/entity_storage.rs @@ -355,8 +355,8 @@ impl WeakEntityStorage { /// /// You only need this if you're going to be adding new components to the /// entity. Otherwise, use [`Self::query_entity_mut`]. - pub fn ecs_entity_mut(&mut self, entity_id: EntityId) -> EntityMut { - self.ecs.get_entity_mut(entity_id.into()).unwrap() + pub fn ecs_entity_mut(&mut self, entity_id: EntityId) -> Option { + self.ecs.get_entity_mut(entity_id.into()) } } diff --git a/azalea-world/src/world.rs b/azalea-world/src/world.rs index ab44c495..9fc60bac 100644 --- a/azalea-world/src/world.rs +++ b/azalea-world/src/world.rs @@ -1,16 +1,12 @@ use crate::{ - entity::{move_unchecked, EcsEntityId, EntityId, Physics, Position}, + entity::{move_unchecked, EntityId, Physics, Position}, Chunk, MoveEntityError, PartialChunkStorage, PartialEntityStorage, WeakChunkStorage, WeakEntityStorage, }; use azalea_block::BlockState; use azalea_buf::BufReadError; use azalea_core::{BlockPos, ChunkPos, PositionDelta8, Vec3}; -use bevy_ecs::{ - query::{QueryEntityError, QueryState, ReadOnlyWorldQuery, WorldQuery}, - system::Query, - world::EntityMut, -}; +use bevy_ecs::query::{QueryState, WorldQuery}; use parking_lot::RwLock; use std::{backtrace::Backtrace, fmt::Debug}; use std::{fmt::Formatter, io::Cursor, sync::Arc}; @@ -29,16 +25,16 @@ pub struct PartialWorld { // dropped, we don't need to do anything with it pub shared: Arc, - pub chunk_storage: PartialChunkStorage, - pub entity_storage: PartialEntityStorage, + pub chunks: PartialChunkStorage, + pub entities: PartialEntityStorage, } /// A world where the chunks are stored as weak pointers. This is used for /// shared worlds. #[derive(Default, Debug)] pub struct WeakWorld { - pub chunk_storage: Arc>, - pub entity_storage: Arc>, + pub chunks: Arc>, + pub entities: Arc>, } impl PartialWorld { @@ -49,11 +45,8 @@ impl PartialWorld { ) -> Self { PartialWorld { shared: shared.clone(), - chunk_storage: PartialChunkStorage::new(chunk_radius, shared.chunk_storage.clone()), - entity_storage: PartialEntityStorage::new( - shared.entity_storage.clone(), - owner_entity_id, - ), + chunks: PartialChunkStorage::new(chunk_radius, shared.chunks.clone()), + entities: PartialEntityStorage::new(shared.entities.clone(), owner_entity_id), } } @@ -62,25 +55,25 @@ impl PartialWorld { pos: &ChunkPos, data: &mut Cursor<&[u8]>, ) -> Result<(), BufReadError> { - self.chunk_storage.replace_with_packet_data(pos, data) + self.chunks.replace_with_packet_data(pos, data) } pub fn get_chunk(&self, pos: &ChunkPos) -> Option>> { - self.chunk_storage.get(pos) + self.chunks.get(pos) } pub fn set_chunk(&mut self, pos: &ChunkPos, chunk: Option) -> Result<(), BufReadError> { - self.chunk_storage + self.chunks .set(pos, chunk.map(|c| Arc::new(RwLock::new(c)))); Ok(()) } pub fn update_view_center(&mut self, pos: &ChunkPos) { - self.chunk_storage.view_center = *pos; + self.chunks.view_center = *pos; } pub fn set_block_state(&mut self, pos: &BlockPos, state: BlockState) -> Option { - self.chunk_storage.set_block_state(pos, state) + self.chunks.set_block_state(pos, state) } /// Whether we're allowed to update the entity with the given ID. @@ -89,8 +82,7 @@ impl PartialWorld { /// cause the update tracker to get out of sync. fn maybe_update_entity(&mut self, id: EntityId) -> bool { // no entity for you (we're processing this entity somewhere else) - if Some(id) != self.entity_storage.owner_entity_id && !self.entity_storage.maybe_update(id) - { + if Some(id) != self.entities.owner_entity_id && !self.entities.maybe_update(id) { false } else { true @@ -110,7 +102,7 @@ impl PartialWorld { } pub fn add_entity(&mut self, id: EntityId, bundle: impl bevy_ecs::prelude::Bundle) { - self.entity_storage.insert(id, bundle); + self.entities.insert(id, bundle); } pub fn set_entity_pos( @@ -132,7 +124,7 @@ impl PartialWorld { ) -> Result<(), MoveEntityError> { let mut query = self.shared.query_entities::<&Position>(); let pos = **query - .get(&self.shared.entity_storage.read().ecs, entity_id.into()) + .get(&self.shared.entities.read().ecs, entity_id.into()) .map_err(|_| MoveEntityError::EntityDoesNotExist(Backtrace::capture()))?; let new_pos = pos.with_delta(delta); @@ -144,28 +136,28 @@ impl PartialWorld { impl WeakWorld { pub fn new(height: u32, min_y: i32) -> Self { WeakWorld { - chunk_storage: Arc::new(RwLock::new(WeakChunkStorage::new(height, min_y))), - entity_storage: Arc::new(RwLock::new(WeakEntityStorage::new())), + chunks: Arc::new(RwLock::new(WeakChunkStorage::new(height, min_y))), + entities: Arc::new(RwLock::new(WeakEntityStorage::new())), } } /// Read the total height of the world. You can add this to [`Self::min_y`] /// to get the highest possible y coordinate a block can be placed at. pub fn height(&self) -> u32 { - self.chunk_storage.read().height + self.chunks.read().height } /// Get the lowest possible y coordinate a block can be placed at. pub fn min_y(&self) -> i32 { - self.chunk_storage.read().min_y + self.chunks.read().min_y } pub fn id_by_uuid(&self, uuid: &Uuid) -> Option { - self.entity_storage.read().id_by_uuid(uuid).copied() + self.entities.read().id_by_uuid(uuid).copied() } pub fn query_entities(&self) -> QueryState { - self.entity_storage.write().query_to_state::() + self.entities.write().query_to_state::() } /// Set an entity's position in the world. @@ -178,7 +170,7 @@ impl WeakWorld { entity_id: EntityId, new_pos: Vec3, ) -> Result<(), MoveEntityError> { - let mut entity_storage = self.entity_storage.write(); + let mut entity_storage = self.entities.write(); let (pos, physics) = entity_storage.query_entity_mut::<(&mut Position, &mut Physics)>(entity_id); @@ -199,7 +191,7 @@ impl WeakWorld { // this is fine because we update the chunk below unsafe { move_unchecked(pos, physics, new_pos) }; if old_chunk != new_chunk { - self.entity_storage + self.entities .write() .update_entity_chunk(entity_id, &old_chunk, &new_chunk); } @@ -207,19 +199,19 @@ impl WeakWorld { } pub fn get_block_state(&self, pos: &BlockPos) -> Option { - self.chunk_storage.read().get_block_state(pos) + self.chunks.read().get_block_state(pos) } pub fn get_chunk(&self, pos: &ChunkPos) -> Option>> { - self.chunk_storage.read().get(pos) + self.chunks.read().get(pos) } } impl Debug for PartialWorld { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { f.debug_struct("World") - .field("chunk_storage", &self.chunk_storage) - .field("entity_storage", &self.entity_storage) + .field("chunk_storage", &self.chunks) + .field("entity_storage", &self.entities) .field("shared", &self.shared) .finish() } @@ -231,11 +223,11 @@ impl Default for PartialWorld { let entity_storage = PartialEntityStorage::default(); Self { shared: Arc::new(WeakWorld { - chunk_storage: chunk_storage.shared.clone(), - entity_storage: entity_storage.shared.clone(), + chunks: chunk_storage.shared.clone(), + entities: entity_storage.shared.clone(), }), - chunk_storage, - entity_storage, + chunks: chunk_storage, + entities: entity_storage, } } } diff --git a/bot/src/main.rs b/bot/src/main.rs index fd03203c..d8332f6d 100644 --- a/bot/src/main.rs +++ b/bot/src/main.rs @@ -144,7 +144,7 @@ async fn swarm_handle( for (name, world) in &swarm.worlds.read().worlds { println!("world name: {}", name); if let Some(w) = world.upgrade() { - for chunk_pos in w.chunk_storage.read().chunks.values() { + for chunk_pos in w.chunks.read().chunks.values() { println!("chunk: {:?}", chunk_pos); } } else {