diff --git a/Cargo.lock b/Cargo.lock index 8f36b6bf..72d30ab7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -280,6 +280,7 @@ dependencies = [ "azalea-world", "bevy_app", "bevy_ecs", + "derive_more", "futures", "iyes_loopless", "log", diff --git a/azalea-client/Cargo.toml b/azalea-client/Cargo.toml index d794a80d..41074c63 100644 --- a/azalea-client/Cargo.toml +++ b/azalea-client/Cargo.toml @@ -11,25 +11,26 @@ version = "0.5.0" [dependencies] anyhow = "1.0.59" async-trait = "0.1.58" -azalea-auth = {path = "../azalea-auth", version = "0.5.0"} -azalea-block = {path = "../azalea-block", version = "0.5.0"} -azalea-chat = {path = "../azalea-chat", version = "0.5.0"} -azalea-core = {path = "../azalea-core", version = "0.5.0"} -azalea-crypto = {path = "../azalea-crypto", version = "0.5.0"} -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_app = {version = "0.9.1", default-features = false} -bevy_ecs = {version = "0.9.1", default-features = false} +azalea-auth = { path = "../azalea-auth", version = "0.5.0" } +azalea-block = { path = "../azalea-block", version = "0.5.0" } +azalea-chat = { path = "../azalea-chat", version = "0.5.0" } +azalea-core = { path = "../azalea-core", version = "0.5.0" } +azalea-crypto = { path = "../azalea-crypto", version = "0.5.0" } +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_app = { version = "0.9.1", default-features = false } +bevy_ecs = { version = "0.9.1", default-features = false } futures = "0.3.25" iyes_loopless = "0.9.1" log = "0.4.17" nohash-hasher = "0.2.0" once_cell = "1.16.0" -parking_lot = {version = "^0.12.1", features = ["deadlock_detection"]} +parking_lot = { version = "^0.12.1", features = ["deadlock_detection"] } regex = "1.7.0" 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" +derive_more = { version = "0.99.17", features = ["deref", "deref_mut"] } diff --git a/azalea-client/src/local_player.rs b/azalea-client/src/local_player.rs index cc1c201e..a59c5e2f 100644 --- a/azalea-client/src/local_player.rs +++ b/azalea-client/src/local_player.rs @@ -1,4 +1,9 @@ -use std::{collections::HashMap, io, sync::Arc}; +use std::{ + collections::HashMap, + io, + ops::{Deref, DerefMut}, + sync::Arc, +}; use azalea_auth::game_profile::GameProfile; use azalea_core::{ChunkPos, ResourceLocation}; @@ -8,6 +13,7 @@ use azalea_world::{ EntityInfos, PartialWorld, World, }; use bevy_ecs::{component::Component, system::Query}; +use derive_more::{Deref, DerefMut}; use parking_lot::RwLock; use thiserror::Error; use tokio::sync::mpsc; @@ -25,7 +31,6 @@ pub struct LocalPlayer { // pub world: Arc>, pub physics_state: PhysicsState, pub client_information: ClientInformation, - pub dead: bool, /// A map of player uuids to their information in the tab list pub players: HashMap, @@ -36,6 +41,10 @@ pub struct LocalPlayer { pub tx: mpsc::UnboundedSender, } +/// Present if the player can be dead. +#[derive(Component, Copy, Clone, Default, Deref, DerefMut)] +pub struct Dead(bool); + #[derive(Default)] pub struct PhysicsState { /// Minecraft only sends a movement packet either after 20 ticks or if the diff --git a/azalea-client/src/packet_handling.rs b/azalea-client/src/packet_handling.rs index 5561a30f..8a9a4bc5 100644 --- a/azalea-client/src/packet_handling.rs +++ b/azalea-client/src/packet_handling.rs @@ -4,11 +4,14 @@ use azalea_protocol::{ connect::{ReadConnection, WriteConnection}, packets::game::{ClientboundGamePacket, ServerboundGamePacket}, }; +use azalea_world::{entity::{MinecraftEntityId, Position}, EntityInfos}; use bevy_ecs::{component::Component, prelude::Entity, query::Changed, system::Query}; use log::{error, debug}; use parking_lot::Mutex; use tokio::sync::mpsc; +use crate::{local_player::{Dead, LocalPlayer}, Event}; + /// Something that receives packets from the server. #[derive(Component, Clone)] pub struct PacketReceiver { @@ -24,12 +27,12 @@ pub fn handle_packets( for (entity, packet_events) in query.iter_mut(ecs) { for packet in packet_events.packets.lock().iter() { - handle_packet(entity, packet); + handle_packet(ecs, entity, packet); } } } -pub fn handle_packet(entity: Entity, packet: &ClientboundGamePacket) { +pub fn handle_packet(ecs: &mut bevy_ecs::world::World, entity: Entity, packet: &ClientboundGamePacket) { match packet { ClientboundGamePacket::Login(p) => { debug!("Got login packet"); @@ -458,13 +461,18 @@ pub fn handle_packet(entity: Entity, packet: &ClientboundGamePacket) { // debug!("Got rotate head packet {:?}", p); } ClientboundGamePacket::MoveEntityPos(p) => { - let mut world = client.world.write(); - let _ = world.move_entity_with_delta(EntityId(p.entity_id), - &p.delta); } + let mut local_player = ecs.query::<&mut LocalPlayer>().get_mut(ecs, entity).unwrap(); + let mut partial_entity_infos = local_player.partial_world.write().entity_infos; + let entity = partial_entity_infos.entity_by_id(p.entity_id); + let mut position = ecs.query::<&mut Position>().get_mut(ecs, entity).unwrap(); + **position = position.with_delta(&p.delta); + }, ClientboundGamePacket::MoveEntityPosRot(p) => { - let mut world = client.world.write(); - let _ = world.move_entity_with_delta(EntityId(p.entity_id), - &p.delta); } + let entity_infos = ecs.resource::(); + let entity = entity_infos.entity_by_id(p.entity_id); + let mut position = ecs.query::<&mut Position>().get_mut(ecs, entity).unwrap(); + **position += p.delta; + } ClientboundGamePacket::MoveEntityRot(_p) => { // debug!("Got move entity rot packet {:?}", p); } @@ -551,13 +559,11 @@ pub fn handle_packet(entity: Entity, packet: &ClientboundGamePacket) { ClientboundGamePacket::PlayerCombatEnter(_) => {} ClientboundGamePacket::PlayerCombatKill(p) => { debug!("Got player kill packet {:?}", p); - let entity_id = - if client.entity() == 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() { - *client.dead.lock() = true; - tx.send(Event::Death(Some(Arc::new(p.clone())))).await?; + let (entity_id, mut dead, mut local_player) = ecs.query::<(&MinecraftEntityId, &mut Dead, &mut LocalPlayer)>().get(ecs, entity).unwrap(); + if **entity_id == p.player_id { + if !**dead { + **dead = true; + local_player.tx.send(Event::Death(Some(Arc::new(p.clone())))); } } } @@ -567,8 +573,8 @@ pub fn handle_packet(entity: Entity, packet: &ClientboundGamePacket) { ClientboundGamePacket::Respawn(p) => { debug!("Got respawn packet {:?}", p); // Sets clients dead state to false. - let mut dead_lock = client.dead.lock(); - *dead_lock = false; + let mut dead = ecs.query::<&mut Dead>().get(ecs, entity).unwrap(); + **dead = false; } ClientboundGamePacket::SelectAdvancementsTab(_) => {} ClientboundGamePacket::SetActionBarText(_) => {} diff --git a/azalea-world/src/entity/mod.rs b/azalea-world/src/entity/mod.rs index 21b247a9..813f5139 100644 --- a/azalea-world/src/entity/mod.rs +++ b/azalea-world/src/entity/mod.rs @@ -149,8 +149,9 @@ pub fn on_pos( pub struct EntityUuid(Uuid); /// The position of the entity right now. -/// This can be changed with unsafe_move, but the correct way is with -/// world.move_entity +/// +/// You are free to change this; there's systems that update the indexes +/// automatically. #[derive(Component, Clone, Copy, Debug, Default, PartialEq, Deref, DerefMut)] pub struct Position(Vec3); impl From for ChunkPos { diff --git a/azalea-world/src/entity_info.rs b/azalea-world/src/entity_info.rs index ebc5fc1a..ad548345 100644 --- a/azalea-world/src/entity_info.rs +++ b/azalea-world/src/entity_info.rs @@ -11,12 +11,7 @@ use bevy_ecs::{ }; use log::warn; use nohash_hasher::{IntMap, IntSet}; -use once_cell::sync::Lazy; -use std::{ - collections::{HashMap, HashSet}, - fmt::Debug, - ops::DerefMut, -}; +use std::{collections::HashMap, fmt::Debug, ops::DerefMut}; use uuid::Uuid; /// Plugin handling some basic entity functionality. @@ -74,6 +69,9 @@ pub struct PartialEntityInfos { pub updates_received: IntMap, /// A set of all the entity ids in render distance. pub(crate) loaded_entity_ids: IntSet, + + /// A map of Minecraft entity ids to Azalea ECS entities. + pub(crate) entity_by_id: IntMap, } impl PartialEntityInfos { @@ -85,33 +83,22 @@ impl PartialEntityInfos { owner_entity, updates_received: IntMap::default(), loaded_entity_ids: IntSet::default(), + entity_by_id: IntMap::default(), } } - /// Whether the entity with the given id is being loaded by this storage. - /// If you want to check whether the entity is in the shared storage, use - /// [`WeakEntityStorage::contains_id`]. + /// Whether the entity with the given protocol ID is being loaded by this + /// storage. #[inline] - pub fn limited_contains_id(&self, id: MinecraftEntityId) -> bool { + pub fn contains_id(&self, id: MinecraftEntityId) -> bool { self.loaded_entity_ids.contains(&id) } /// Get an [`Entity`] from the given [`MinecraftEntityId`] (which is just a /// u32 internally) if the entity is being loaded by this storage. #[inline] - pub fn limited_get_by_id( - &self, - id: MinecraftEntityId, - entity_infos: &mut EntityInfos, - ) -> Option { - if self.limited_contains_id(id) { - entity_infos - .minecraft_entity_ids_to_azalea_entities - .get(&id) - .copied() - } else { - None - } + pub fn get_by_id(&self, id: MinecraftEntityId) -> Option { + self.entity_by_id.get(&id).copied() } /// Returns whether we're allowed to update this entity (to prevent two @@ -163,9 +150,6 @@ pub struct EntityInfos { /// The canonical number of updates we've gotten for every entity. pub updates_received: HashMap, - - /// The map of Minecraft entity ids to Azalea ECS entities. - pub minecraft_entity_ids_to_azalea_entities: HashMap, } impl EntityInfos { @@ -174,8 +158,6 @@ impl EntityInfos { entity_reference_count: HashMap::default(), entity_by_uuid: HashMap::default(), updates_received: HashMap::default(), - - minecraft_entity_ids_to_azalea_entities: HashMap::default(), } } @@ -224,6 +206,14 @@ impl EntityInfos { self.entity_reference_count.contains_key(&id) } + /// Get an [`Entity`] by its UUID. + /// + /// If you want to get an entity by its protocol ID, use + /// [`PartialEntityInfos::entity_by_id`]. + /// + /// Also note that if you're using a shared world (i.e. a client swarm), + /// this function might return the wrong entity if there's multiple + /// entities with the same uuid in different worlds. pub fn entity_by_uuid(&self, uuid: &Uuid) -> Option<&Entity> { self.entity_by_uuid.get(uuid) } diff --git a/azalea-world/src/world.rs b/azalea-world/src/world.rs index ae67510e..973fba38 100644 --- a/azalea-world/src/world.rs +++ b/azalea-world/src/world.rs @@ -95,11 +95,6 @@ impl PartialWorld { } } -pub unsafe fn move_entity_with_delta(delta: &PositionDelta8, position: &mut entity::Position) { - let new_pos = position.with_delta(delta); - **position = new_pos; -} - /// A component marker signifying that the entity may have been removed from the /// world, but we're not entirely sure. #[derive(Component)]