From 83cce236145cdab1872a472a70943b669a880965 Mon Sep 17 00:00:00 2001 From: mat Date: Tue, 19 Sep 2023 02:01:39 -0500 Subject: [PATCH] add Loaded component and fix clamping look direction --- azalea-client/src/client.rs | 7 +- azalea-client/src/movement.rs | 2 +- azalea-entity/src/plugin/indexing.rs | 113 +++++++++++++++------------ azalea-entity/src/plugin/mod.rs | 2 +- azalea/examples/testbot.rs | 28 +++---- 5 files changed, 86 insertions(+), 66 deletions(-) diff --git a/azalea-client/src/client.rs b/azalea-client/src/client.rs index d5f40678..8424bf39 100644 --- a/azalea-client/src/client.rs +++ b/azalea-client/src/client.rs @@ -22,8 +22,9 @@ use azalea_auth::{game_profile::GameProfile, sessionserver::ClientSessionServerE use azalea_chat::FormattedText; use azalea_core::Vec3; use azalea_entity::{ - indexing::EntityIdIndex, metadata::Health, EntityPlugin, EntityUpdateSet, EyeHeight, - LocalEntity, Position, + indexing::{EntityIdIndex, Loaded}, + metadata::Health, + EntityPlugin, EntityUpdateSet, EyeHeight, LocalEntity, Position, }; use azalea_physics::PhysicsPlugin; use azalea_protocol::{ @@ -316,6 +317,7 @@ impl Client { attack: attack::AttackBundle::default(), _local: LocalEntity, + _loaded: Loaded, }); let client = Client::new( @@ -634,6 +636,7 @@ pub struct JoinedClientBundle { pub attack: attack::AttackBundle, pub _local: LocalEntity, + pub _loaded: Loaded, } pub struct AzaleaPlugin; diff --git a/azalea-client/src/movement.rs b/azalea-client/src/movement.rs index 47cbd0ac..3bdcdeac 100644 --- a/azalea-client/src/movement.rs +++ b/azalea-client/src/movement.rs @@ -388,7 +388,7 @@ impl Client { /// An event sent when the client starts walking. This does not get sent for /// non-local entities. -#[derive(Event)] +#[derive(Event, Debug)] pub struct StartWalkEvent { pub entity: Entity, pub direction: WalkDirection, diff --git a/azalea-entity/src/plugin/indexing.rs b/azalea-entity/src/plugin/indexing.rs index d2049b6e..e3b1fccb 100644 --- a/azalea-entity/src/plugin/indexing.rs +++ b/azalea-entity/src/plugin/indexing.rs @@ -76,6 +76,14 @@ impl Debug for EntityUuidIndex { } } +/// A marker component for entities that are in the world and aren't temporary +/// duplicates of other ones. This is meant to be used as a query filter +/// `Added` (since if you do `Added` with another component it might +/// trigger multiple times when in a swarm due to how entities are handled for +/// swarms). +#[derive(Component)] +pub struct Loaded; + /// Remove new entities that have the same id as an existing entity, and /// increase the reference counts. /// @@ -86,7 +94,7 @@ impl Debug for EntityUuidIndex { pub fn deduplicate_entities( mut commands: Commands, mut query: Query< - (Entity, &MinecraftEntityId, &InstanceName), + (Entity, &MinecraftEntityId, &InstanceName, Option<&Loaded>), (Changed, Without), >, mut loaded_by_query: Query<&mut LoadedBy>, @@ -94,43 +102,51 @@ pub fn deduplicate_entities( instance_container: Res, ) { // if this entity already exists, remove it and keep the old one - for (new_entity, id, world_name) in query.iter_mut() { - if let Some(world_lock) = instance_container.get(world_name) { - let world = world_lock.write(); - if let Some(old_entity) = world.entity_by_id.get(id) { - if old_entity == &new_entity { - continue; - } + for (new_entity, id, world_name, loaded) in query.iter_mut() { + let Some(world_lock) = instance_container.get(world_name) else { + error!("Entity was inserted into a world that doesn't exist."); + continue; + }; + let world = world_lock.write(); + let Some(old_entity) = world.entity_by_id.get(id) else { + // not in index yet, so it's good + if loaded.is_none() { + commands.entity(new_entity).insert(Loaded); + } + continue; + }; + if old_entity == &new_entity { + if loaded.is_none() { + commands.entity(new_entity).insert(Loaded); + } + continue; + } - // this entity already exists!!! remove the one we just added but increase - // the reference count - let new_loaded_by = loaded_by_query - .get(new_entity) - .expect("Entities should always have the LoadedBy component ({new_entity:?} did not)") - .clone(); + // this entity already exists!!! remove the one we just added but increase + // the reference count + let new_loaded_by = loaded_by_query + .get(new_entity) + .expect("Entities should always have the LoadedBy component ({new_entity:?} did not)") + .clone(); - // update the `EntityIdIndex`s of the local players that have this entity loaded - for local_player in new_loaded_by.iter() { - let mut entity_id_index = entity_id_index_query + // update the `EntityIdIndex`s of the local players that have this entity loaded + for local_player in new_loaded_by.iter() { + let mut entity_id_index = entity_id_index_query .get_mut(*local_player) .expect("Local players should always have the EntityIdIndex component ({local_player:?} did not)"); - entity_id_index.insert(*id, *old_entity); - } - - let old_loaded_by = loaded_by_query.get_mut(*old_entity); - // merge them if possible - if let Ok(mut old_loaded_by) = old_loaded_by { - old_loaded_by.extend(new_loaded_by.iter()); - } - commands.entity(new_entity).despawn(); - info!( - "Entity with id {id:?} / {new_entity:?} already existed in the world, merging it with {old_entity:?}" - ); - continue; - } - } else { - error!("Entity was inserted into a world that doesn't exist."); + entity_id_index.insert(*id, *old_entity); } + + let old_loaded_by = loaded_by_query.get_mut(*old_entity); + // merge them if possible + if let Ok(mut old_loaded_by) = old_loaded_by { + old_loaded_by.extend(new_loaded_by.iter()); + } + commands.entity(new_entity).despawn(); + info!( + "Entity with id {id:?} / {new_entity:?} already existed in the world, merging it with {old_entity:?}" + ); + continue; } } @@ -147,23 +163,24 @@ pub fn deduplicate_local_entities( ) { // if this entity already exists, remove the old one for (new_entity, id, world_name) in query.iter_mut() { - if let Some(world_lock) = instance_container.get(world_name) { - let world = world_lock.write(); - if let Some(old_entity) = world.entity_by_id.get(id) { - if old_entity == &new_entity { - // lol - continue; - } - - commands.entity(*old_entity).despawn(); - debug!( - "Added local entity {id:?} / {new_entity:?} but already existed in world as {old_entity:?}, despawning {old_entity:?}" - ); - break; - } - } else { + let Some(world_lock) = instance_container.get(world_name) else { error!("Entity was inserted into a world that doesn't exist."); + continue; + }; + let world = world_lock.write(); + let Some(old_entity) = world.entity_by_id.get(id) else { + continue; + }; + if old_entity == &new_entity { + // lol + continue; } + + commands.entity(*old_entity).despawn(); + debug!( + "Added local entity {id:?} / {new_entity:?} but already existed in world as {old_entity:?}, despawning {old_entity:?}" + ); + break; } } diff --git a/azalea-entity/src/plugin/mod.rs b/azalea-entity/src/plugin/mod.rs index a75673ab..1f5fd3bd 100644 --- a/azalea-entity/src/plugin/mod.rs +++ b/azalea-entity/src/plugin/mod.rs @@ -129,7 +129,7 @@ pub struct LoadedBy(pub HashSet); pub fn clamp_look_direction(mut query: Query<&mut LookDirection>) { for mut look_direction in &mut query { - look_direction.y_rot %= 360.0; + look_direction.y_rot = look_direction.y_rot.rem_euclid(360.0); look_direction.x_rot = look_direction.x_rot.clamp(-90.0, 90.0) % 360.0; } } diff --git a/azalea/examples/testbot.rs b/azalea/examples/testbot.rs index c178a1d8..49243aa1 100644 --- a/azalea/examples/testbot.rs +++ b/azalea/examples/testbot.rs @@ -164,17 +164,17 @@ async fn handle(mut bot: Client, event: Event, _state: State) -> anyhow::Result< println!("inventory: {:?}", bot.menu()); } "findblock" => { - let target_pos = bot - .world() - .read() - .find_block(bot.position(), &azalea::Block::DiamondBlock.into()); + let target_pos = bot.world().read().find_block( + bot.position(), + &azalea::registry::Block::DiamondBlock.into(), + ); bot.chat(&format!("target_pos: {target_pos:?}",)); } "gotoblock" => { - let target_pos = bot - .world() - .read() - .find_block(bot.position(), &azalea::Block::DiamondBlock.into()); + let target_pos = bot.world().read().find_block( + bot.position(), + &azalea::registry::Block::DiamondBlock.into(), + ); if let Some(target_pos) = target_pos { // +1 to stand on top of the block bot.goto(BlockPosGoal::from(target_pos.up(1))); @@ -183,10 +183,10 @@ async fn handle(mut bot: Client, event: Event, _state: State) -> anyhow::Result< } } "mineblock" => { - let target_pos = bot - .world() - .read() - .find_block(bot.position(), &azalea::Block::DiamondBlock.into()); + let target_pos = bot.world().read().find_block( + bot.position(), + &azalea::registry::Block::DiamondBlock.into(), + ); if let Some(target_pos) = target_pos { // +1 to stand on top of the block bot.chat("ok mining diamond block"); @@ -201,7 +201,7 @@ async fn handle(mut bot: Client, event: Event, _state: State) -> anyhow::Result< let target_pos = bot .world() .read() - .find_block(bot.position(), &azalea::Block::Lever.into()); + .find_block(bot.position(), &azalea::registry::Block::Lever.into()); let Some(target_pos) = target_pos else { bot.chat("no lever found"); return Ok(()); @@ -218,7 +218,7 @@ async fn handle(mut bot: Client, event: Event, _state: State) -> anyhow::Result< let target_pos = bot .world() .read() - .find_block(bot.position(), &azalea::Block::Chest.into()); + .find_block(bot.position(), &azalea::registry::Block::Chest.into()); let Some(target_pos) = target_pos else { bot.chat("no chest found"); return Ok(());