diff --git a/azalea-client/src/client.rs b/azalea-client/src/client.rs index 0b1fccc1..301d9197 100644 --- a/azalea-client/src/client.rs +++ b/azalea-client/src/client.rs @@ -20,7 +20,7 @@ use crate::{ use azalea_auth::{game_profile::GameProfile, sessionserver::ClientSessionServerError}; use azalea_chat::FormattedText; use azalea_core::Vec3; -use azalea_entity::{EntityPlugin, EntityUpdateSet, Local, Position}; +use azalea_entity::{metadata::Health, EntityPlugin, EntityUpdateSet, Local, Position}; use azalea_physics::{PhysicsPlugin, PhysicsSet}; use azalea_protocol::{ connect::{Connection, ConnectionError}, @@ -548,6 +548,12 @@ impl Client { pub fn position(&self) -> Vec3 { Vec3::from(&self.component::()) } + /// Get the health of this client. + /// + /// This is a shortcut for `*bot.component::()`. + pub fn health(&self) -> f32 { + *self.component::() + } } /// A bundle for the components that are present on a local player that received diff --git a/azalea-client/src/packet_handling.rs b/azalea-client/src/packet_handling.rs index fd8d77e5..4ceb3999 100644 --- a/azalea-client/src/packet_handling.rs +++ b/azalea-client/src/packet_handling.rs @@ -3,8 +3,8 @@ use std::{collections::HashSet, io::Cursor, sync::Arc}; use azalea_core::{ChunkPos, GameMode, ResourceLocation, Vec3}; use azalea_entity::{ metadata::{apply_metadata, Health, PlayerMetadataBundle}, - Dead, EntityBundle, EntityKind, EntityUpdateSet, LastSentPosition, LoadedBy, LookDirection, - Physics, PlayerBundle, Position, RelativeEntityUpdate, + Dead, EntityBundle, EntityInfos, EntityKind, EntityUpdateSet, LastSentPosition, LoadedBy, + LookDirection, Physics, PlayerBundle, Position, RelativeEntityUpdate, }; use azalea_protocol::{ connect::{ReadConnection, WriteConnection}, @@ -21,14 +21,15 @@ use azalea_protocol::{ read::ReadPacketError, }; use azalea_world::{InstanceContainer, InstanceName, MinecraftEntityId, PartialInstance}; -use bevy_app::{App, First, Plugin, PreUpdate}; +use bevy_app::{App, First, Plugin, PreUpdate, Update}; use bevy_ecs::{ component::Component, entity::Entity, event::{EventReader, EventWriter, Events}, prelude::Event, + query::Changed, schedule::IntoSystemConfigs, - system::{Commands, Query, ResMut, SystemState}, + system::{Commands, Query, Res, ResMut, SystemState}, world::World, }; use log::{debug, error, trace, warn}; @@ -86,6 +87,7 @@ impl Plugin for PacketHandlerPlugin { // we want to index and deindex right after .before(EntityUpdateSet::Deindex), ) + .add_systems(Update, death_event_on_0_health) .init_resource::>() .add_event::() .add_event::() @@ -130,6 +132,20 @@ pub struct DeathEvent { pub packet: Option, } +pub fn death_event_on_0_health( + query: Query<(Entity, &Health), Changed>, + mut death_events: EventWriter, +) { + for (entity, health) in query.iter() { + if **health == 0. { + death_events.send(DeathEvent { + entity, + packet: None, + }); + } + } +} + /// A KeepAlive packet is sent from the server to verify that the client is /// still connected. #[derive(Event, Debug, Clone)] @@ -558,18 +574,37 @@ fn process_packet_events(ecs: &mut World) { ClientboundGamePacket::AddEntity(p) => { debug!("Got add entity packet {:?}", p); - let mut system_state: SystemState<(Commands, Query>)> = - SystemState::new(ecs); - let (mut commands, mut query) = system_state.get_mut(ecs); - let world_name = query.get_mut(player_entity).unwrap(); + let mut system_state: SystemState<( + Commands, + Query>, + ResMut, + ResMut, + )> = SystemState::new(ecs); + let (mut commands, mut query, mut instance_container, mut entity_infos) = + system_state.get_mut(ecs); + let instance_name = query.get_mut(player_entity).unwrap(); - if let Some(InstanceName(world_name)) = world_name { - let bundle = p.as_entity_bundle(world_name.clone()); + if let Some(instance_name) = instance_name { + let bundle = p.as_entity_bundle((**instance_name).clone()); let mut entity_commands = commands.spawn(( MinecraftEntityId(p.id), LoadedBy(HashSet::from([player_entity])), bundle, )); + + { + // add it to the indexes immediately so if there's a packet that references + // it immediately after it still works + let instance = instance_container.get(instance_name).unwrap(); + instance + .write() + .entity_by_id + .insert(MinecraftEntityId(p.id), entity_commands.id()); + entity_infos + .entity_by_uuid + .insert(p.uuid, entity_commands.id()); + } + // the bundle doesn't include the default entity metadata so we add that // separately p.apply_metadata(&mut entity_commands); @@ -664,13 +699,6 @@ fn process_packet_events(ecs: &mut World) { let (mut query, mut death_events) = system_state.get_mut(ecs); let mut health = query.get_mut(player_entity).unwrap(); - if p.health == 0. && **health != 0. { - death_events.send(DeathEvent { - entity: player_entity, - packet: None, - }); - } - **health = p.health; // the `Dead` component is added by the `update_dead` system diff --git a/azalea-entity/src/info.rs b/azalea-entity/src/info.rs index 18021d36..0c5fd3d3 100644 --- a/azalea-entity/src/info.rs +++ b/azalea-entity/src/info.rs @@ -170,7 +170,7 @@ impl EntityCommand for RelativeEntityUpdate { #[derive(Resource, Default)] pub struct EntityInfos { /// An index of entities by their UUIDs - pub(crate) entity_by_uuid: HashMap, + pub entity_by_uuid: HashMap, } impl EntityInfos { diff --git a/azalea/examples/testbot.rs b/azalea/examples/testbot.rs index d9bd8681..337ac6ec 100644 --- a/azalea/examples/testbot.rs +++ b/azalea/examples/testbot.rs @@ -45,11 +45,9 @@ async fn main() -> anyhow::Result<()> { } let mut accounts = Vec::new(); - let mut states = Vec::new(); for i in 0..1 { accounts.push(Account::offline(&format!("bot{i}"))); - states.push(State::default()); } loop { diff --git a/azalea/src/auto_respawn.rs b/azalea/src/auto_respawn.rs index 77a75b4b..6d9c5954 100644 --- a/azalea/src/auto_respawn.rs +++ b/azalea/src/auto_respawn.rs @@ -21,5 +21,6 @@ fn auto_respawn( perform_respawn_events.send(PerformRespawnEvent { entity: event.entity, }); + println!("auto respawning"); } }