aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--azalea-client/src/client.rs7
-rw-r--r--azalea-client/src/movement.rs2
-rw-r--r--azalea-entity/src/plugin/indexing.rs111
-rw-r--r--azalea-entity/src/plugin/mod.rs2
-rw-r--r--azalea/examples/testbot.rs28
5 files changed, 85 insertions, 65 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<Loaded>` (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<MinecraftEntityId>, Without<LocalEntity>),
>,
mut loaded_by_query: Query<&mut LoadedBy>,
@@ -94,43 +102,51 @@ pub fn deduplicate_entities(
instance_container: Res<InstanceContainer>,
) {
// 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);
- }
+ 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.");
+ 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<Entity>);
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(());