aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--azalea-client/src/plugins/packet/game/mod.rs123
-rw-r--r--azalea-client/src/test_utils/simulation.rs3
-rw-r--r--azalea-client/tests/move_and_despawn_entity.rs57
-rw-r--r--azalea-entity/src/plugin/indexing.rs36
-rw-r--r--azalea-entity/src/plugin/mod.rs5
-rw-r--r--azalea-entity/src/vec_delta_codec.rs8
-rw-r--r--azalea-protocol/src/common/movements.rs63
-rw-r--r--azalea-protocol/src/packets/game/c_teleport_entity.rs2
8 files changed, 184 insertions, 113 deletions
diff --git a/azalea-client/src/plugins/packet/game/mod.rs b/azalea-client/src/plugins/packet/game/mod.rs
index 2fb9e1cd..1307473d 100644
--- a/azalea-client/src/plugins/packet/game/mod.rs
+++ b/azalea-client/src/plugins/packet/game/mod.rs
@@ -1,10 +1,9 @@
mod events;
-use std::{collections::HashSet, ops::Add, sync::Arc};
+use std::{collections::HashSet, sync::Arc};
use azalea_core::{
game_type::GameMode,
- math,
position::{ChunkPos, Vec3},
};
use azalea_entity::{
@@ -425,68 +424,12 @@ impl GamePacketHandler<'_> {
**last_sent_position = **position;
- fn apply_change<T: Add<Output = T>>(base: T, condition: bool, change: T) -> T {
- if condition { base + change } else { change }
- }
-
- let new_x = apply_change(position.x, p.relative.x, p.change.pos.x);
- let new_y = apply_change(position.y, p.relative.y, p.change.pos.y);
- let new_z = apply_change(position.z, p.relative.z, p.change.pos.z);
-
- let new_y_rot = apply_change(
- direction.y_rot,
- p.relative.y_rot,
- p.change.look_direction.y_rot,
- );
- let new_x_rot = apply_change(
- direction.x_rot,
- p.relative.x_rot,
- p.change.look_direction.x_rot,
- );
-
- let mut new_delta_from_rotations = physics.velocity;
- if p.relative.rotate_delta {
- let y_rot_delta = direction.y_rot - new_y_rot;
- let x_rot_delta = direction.x_rot - new_x_rot;
- new_delta_from_rotations = new_delta_from_rotations
- .x_rot(math::to_radians(x_rot_delta as f64) as f32)
- .y_rot(math::to_radians(y_rot_delta as f64) as f32);
- }
-
- let new_delta = Vec3::new(
- apply_change(
- new_delta_from_rotations.x,
- p.relative.delta_x,
- p.change.delta.x,
- ),
- apply_change(
- new_delta_from_rotations.y,
- p.relative.delta_y,
- p.change.delta.y,
- ),
- apply_change(
- new_delta_from_rotations.z,
- p.relative.delta_z,
- p.change.delta.z,
- ),
- );
-
- // apply the updates
-
- physics.velocity = new_delta;
-
- (direction.y_rot, direction.x_rot) = (new_y_rot, new_x_rot);
-
- let new_pos = Vec3::new(new_x, new_y, new_z);
- if new_pos != **position {
- **position = new_pos;
- }
-
+ p.relative
+ .apply(&p.change, &mut position, &mut direction, &mut physics);
// old_pos is set to the current position when we're teleported
physics.set_old_pos(&position);
// send the relevant packets
-
commands.trigger(SendPacketEvent::new(
self.player,
ServerboundAcceptTeleportation { id: p.id },
@@ -494,8 +437,8 @@ impl GamePacketHandler<'_> {
commands.trigger(SendPacketEvent::new(
self.player,
ServerboundMovePlayerPosRot {
- pos: new_pos,
- look_direction: LookDirection::new(new_y_rot, new_x_rot),
+ pos: **position,
+ look_direction: *direction,
// this is always false
on_ground: false,
},
@@ -852,6 +795,8 @@ impl GamePacketHandler<'_> {
}
pub fn teleport_entity(&mut self, p: &ClientboundTeleportEntity) {
+ debug!("Got teleport entity packet {p:?}");
+
as_system::<(Commands, Query<(&EntityIdIndex, &InstanceHolder)>)>(
self.ecs,
|(mut commands, mut query)| {
@@ -862,26 +807,28 @@ impl GamePacketHandler<'_> {
return;
};
- let new_pos = p.change.pos;
- let new_look_direction = LookDirection {
- x_rot: (p.change.look_direction.x_rot as i32 * 360) as f32 / 256.,
- y_rot: (p.change.look_direction.y_rot as i32 * 360) as f32 / 256.,
- };
+ let relative = p.relative.clone();
+ let change = p.change.clone();
+
commands.entity(entity).queue(RelativeEntityUpdate::new(
instance_holder.partial_instance.clone(),
move |entity| {
- let mut position = entity.get_mut::<Position>().unwrap();
- if new_pos != **position {
- **position = new_pos;
- }
- let position = *position;
- let mut look_direction = entity.get_mut::<LookDirection>().unwrap();
- if new_look_direction != *look_direction {
- *look_direction = new_look_direction;
- }
- // old_pos is set to the current position when we're teleported
- let mut physics = entity.get_mut::<Physics>().unwrap();
- physics.set_old_pos(&position);
+ let entity_id = entity.id();
+ entity.world_scope(move |world| {
+ let mut query =
+ world.query::<(&mut Physics, &mut LookDirection, &mut Position)>();
+ let (mut physics, mut look_direction, mut position) =
+ query.get_mut(world, entity_id).unwrap();
+ let old_position = *position;
+ relative.apply(
+ &change,
+ &mut position,
+ &mut look_direction,
+ &mut physics,
+ );
+ // old_pos is set to the current position when we're teleported
+ physics.set_old_pos(&old_position);
+ });
},
));
},
@@ -914,11 +861,7 @@ impl GamePacketHandler<'_> {
instance_holder.partial_instance.clone(),
move |entity_mut| {
let mut physics = entity_mut.get_mut::<Physics>().unwrap();
- let new_pos = physics.vec_delta_codec.decode(
- new_delta.xa as i64,
- new_delta.ya as i64,
- new_delta.za as i64,
- );
+ let new_pos = physics.vec_delta_codec.decode(&new_delta);
physics.vec_delta_codec.set_base(new_pos);
physics.set_on_ground(new_on_ground);
@@ -968,17 +911,13 @@ impl GamePacketHandler<'_> {
instance_holder.partial_instance.clone(),
move |entity_mut| {
let mut physics = entity_mut.get_mut::<Physics>().unwrap();
- let new_pos = physics.vec_delta_codec.decode(
- new_delta.xa as i64,
- new_delta.ya as i64,
- new_delta.za as i64,
- );
- physics.vec_delta_codec.set_base(new_pos);
+ let new_position = physics.vec_delta_codec.decode(&new_delta);
+ physics.vec_delta_codec.set_base(new_position);
physics.set_on_ground(new_on_ground);
let mut position = entity_mut.get_mut::<Position>().unwrap();
- if new_pos != **position {
- **position = new_pos;
+ if new_position != **position {
+ **position = new_position;
}
let mut look_direction = entity_mut.get_mut::<LookDirection>().unwrap();
diff --git a/azalea-client/src/test_utils/simulation.rs b/azalea-client/src/test_utils/simulation.rs
index 00b35dee..caf63113 100644
--- a/azalea-client/src/test_utils/simulation.rs
+++ b/azalea-client/src/test_utils/simulation.rs
@@ -106,6 +106,9 @@ impl Simulation {
pub fn tick(&mut self) {
tick_app(&mut self.app);
}
+ pub fn update(&mut self) {
+ self.app.update();
+ }
pub fn minecraft_entity_id(&self) -> MinecraftEntityId {
self.component::<MinecraftEntityId>()
diff --git a/azalea-client/tests/move_and_despawn_entity.rs b/azalea-client/tests/move_and_despawn_entity.rs
new file mode 100644
index 00000000..080ca903
--- /dev/null
+++ b/azalea-client/tests/move_and_despawn_entity.rs
@@ -0,0 +1,57 @@
+use azalea_client::test_utils::prelude::*;
+use azalea_core::{
+ position::{ChunkPos, Vec3},
+ resource_location::ResourceLocation,
+};
+use azalea_entity::metadata::Cow;
+use azalea_protocol::{
+ common::movements::{PositionMoveRotation, RelativeMovements},
+ packets::{
+ ConnectionProtocol,
+ game::{ClientboundRemoveEntities, ClientboundTeleportEntity},
+ },
+};
+use azalea_registry::{DataRegistry, DimensionType, EntityKind};
+use azalea_world::MinecraftEntityId;
+use bevy_ecs::query::With;
+
+#[test]
+fn test_move_and_despawn_entity() {
+ init_tracing();
+
+ let mut simulation = Simulation::new(ConnectionProtocol::Game);
+ simulation.receive_packet(make_basic_login_packet(
+ DimensionType::new_raw(0),
+ ResourceLocation::new("azalea:overworld"),
+ ));
+
+ for x in 0..=10 {
+ simulation.receive_packet(make_basic_empty_chunk(ChunkPos::new(x, 0), (384 + 64) / 16));
+ }
+ simulation.tick();
+
+ simulation.receive_packet(make_basic_add_entity(EntityKind::Cow, 123, (0.5, 64., 0.5)));
+ simulation.tick();
+
+ simulation.receive_packet(ClientboundTeleportEntity {
+ id: MinecraftEntityId(123),
+ change: PositionMoveRotation {
+ pos: Vec3::new(16., 0., 0.),
+ delta: Vec3::ZERO,
+ look_direction: Default::default(),
+ },
+ relative: RelativeMovements::all_relative(),
+ on_ground: true,
+ });
+ simulation.receive_packet(ClientboundRemoveEntities {
+ entity_ids: vec![MinecraftEntityId(123)],
+ });
+ simulation.tick();
+
+ // make sure it's despawned
+ let mut cow_query = simulation.app.world_mut().query_filtered::<(), With<Cow>>();
+ let cow_iter = cow_query.iter(simulation.app.world());
+ assert_eq!(cow_iter.count(), 0, "cow should be despawned");
+
+ simulation.tick();
+}
diff --git a/azalea-entity/src/plugin/indexing.rs b/azalea-entity/src/plugin/indexing.rs
index 2fc89e84..f1926286 100644
--- a/azalea-entity/src/plugin/indexing.rs
+++ b/azalea-entity/src/plugin/indexing.rs
@@ -132,16 +132,17 @@ pub fn update_entity_chunk_positions(
instance_container: Res<InstanceContainer>,
) {
for (entity, pos, instance_name, mut entity_chunk_pos) in query.iter_mut() {
- // TODO: move this inside of the if statement so it's not called as often
- let instance_lock = instance_container.get(instance_name).unwrap();
- let mut instance = instance_lock.write();
-
let old_chunk = **entity_chunk_pos;
let new_chunk = ChunkPos::from(*pos);
if old_chunk != new_chunk {
**entity_chunk_pos = new_chunk;
if old_chunk != new_chunk {
+ let Some(instance_lock) = instance_container.get(instance_name) else {
+ continue;
+ };
+ let mut instance = instance_lock.write();
+
// move the entity from the old chunk to the new one
if let Some(entities) = instance.entities_by_chunk.get_mut(&old_chunk) {
entities.remove(&entity);
@@ -163,7 +164,10 @@ pub fn insert_entity_chunk_position(
instance_container: Res<InstanceContainer>,
) {
for (entity, pos, world_name) in query.iter() {
- let instance_lock = instance_container.get(world_name).unwrap();
+ let Some(instance_lock) = instance_container.get(world_name) else {
+ // entity must've been despawned already
+ continue;
+ };
let mut instance = instance_lock.write();
let chunk = ChunkPos::from(*pos);
@@ -213,13 +217,13 @@ pub fn remove_despawned_entities_from_indexes(
let mut instance = instance_lock.write();
- // if the entity has no references left, despawn it
+ // if the entity is being loaded by any of our clients, don't despawn it
if !loaded_by.is_empty() {
continue;
}
// remove the entity from the chunk index
- let chunk = ChunkPos::from(*position);
+ let chunk = ChunkPos::from(position);
match instance.entities_by_chunk.get_mut(&chunk) {
Some(entities_in_chunk) => {
if entities_in_chunk.remove(&entity) {
@@ -247,9 +251,21 @@ pub fn remove_despawned_entities_from_indexes(
}
}
_ => {
- debug!(
- "Tried to remove entity {entity:?} from chunk {chunk:?} but the chunk was not found."
- );
+ let mut found_in_other_chunks = HashSet::new();
+ for (other_chunk, entities_in_other_chunk) in &mut instance.entities_by_chunk {
+ if entities_in_other_chunk.remove(&entity) {
+ found_in_other_chunks.insert(other_chunk);
+ }
+ }
+ if found_in_other_chunks.is_empty() {
+ warn!(
+ "Tried to remove entity {entity:?} from chunk {chunk:?} but the chunk was not found and the entity wasn't in any other chunks."
+ );
+ } else {
+ warn!(
+ "Tried to remove entity {entity:?} from chunk {chunk:?} but the chunk was not found. Entity found in and removed from other chunk(s): {found_in_other_chunks:?}"
+ );
+ }
}
}
// remove it from the uuid index
diff --git a/azalea-entity/src/plugin/mod.rs b/azalea-entity/src/plugin/mod.rs
index 6a6c9615..03afe7cd 100644
--- a/azalea-entity/src/plugin/mod.rs
+++ b/azalea-entity/src/plugin/mod.rs
@@ -46,11 +46,6 @@ impl Plugin for EntityPlugin {
Update,
(
(
- // remove_despawned_entities_from_indexes is done again here to correctly
- // handle the case where an entity is spawned and then the world is removed at
- // the same time (like with ClientboundStartConfiguration).
- indexing::remove_despawned_entities_from_indexes
- .in_set(EntityUpdateSet::Deindex),
indexing::update_entity_chunk_positions,
indexing::insert_entity_chunk_position,
)
diff --git a/azalea-entity/src/vec_delta_codec.rs b/azalea-entity/src/vec_delta_codec.rs
index 51aa7cea..270daff2 100644
--- a/azalea-entity/src/vec_delta_codec.rs
+++ b/azalea-entity/src/vec_delta_codec.rs
@@ -1,4 +1,4 @@
-use azalea_core::position::Vec3;
+use azalea_core::{delta::PositionDelta8, position::Vec3};
#[derive(Debug, Clone, Default)]
pub struct VecDeltaCodec {
@@ -10,7 +10,11 @@ impl VecDeltaCodec {
Self { base }
}
- pub fn decode(&self, x: i64, y: i64, z: i64) -> Vec3 {
+ pub fn decode(&self, delta: &PositionDelta8) -> Vec3 {
+ let x = delta.xa as i64;
+ let y = delta.ya as i64;
+ let z = delta.za as i64;
+
if x == 0 && y == 0 && z == 0 {
return self.base;
}
diff --git a/azalea-protocol/src/common/movements.rs b/azalea-protocol/src/common/movements.rs
index ffc3452f..a70342b3 100644
--- a/azalea-protocol/src/common/movements.rs
+++ b/azalea-protocol/src/common/movements.rs
@@ -1,8 +1,11 @@
-use std::io::{self, Cursor, Write};
+use std::{
+ io::{self, Cursor, Write},
+ ops::Add,
+};
use azalea_buf::{AzBuf, AzaleaRead, AzaleaWrite, BufReadError};
-use azalea_core::{bitset::FixedBitSet, position::Vec3};
-use azalea_entity::LookDirection;
+use azalea_core::{bitset::FixedBitSet, math, position::Vec3};
+use azalea_entity::{LookDirection, Physics, Position};
/// The updated position, velocity, and rotations for an entity.
///
@@ -32,6 +35,60 @@ impl RelativeMovements {
pub fn all_absolute() -> Self {
RelativeMovements::default()
}
+ pub fn all_relative() -> Self {
+ RelativeMovements {
+ x: true,
+ y: true,
+ z: true,
+ y_rot: true,
+ x_rot: true,
+ delta_x: true,
+ delta_y: true,
+ delta_z: true,
+ rotate_delta: true,
+ }
+ }
+
+ pub fn apply(
+ &self,
+ change: &PositionMoveRotation,
+ position: &mut Position,
+ direction: &mut LookDirection,
+ physics: &mut Physics,
+ ) {
+ let new_position = Vec3::new(
+ apply_change(position.x, self.x, change.pos.x),
+ apply_change(position.y, self.y, change.pos.y),
+ apply_change(position.z, self.z, change.pos.z),
+ );
+
+ let new_look_direction = LookDirection::new(
+ apply_change(direction.y_rot, self.y_rot, change.look_direction.y_rot),
+ apply_change(direction.x_rot, self.x_rot, change.look_direction.x_rot),
+ );
+
+ let mut new_delta = physics.velocity;
+ if self.rotate_delta {
+ let y_rot_delta = direction.y_rot - new_look_direction.y_rot;
+ let x_rot_delta = direction.x_rot - new_look_direction.x_rot;
+ new_delta = new_delta
+ .x_rot(math::to_radians(x_rot_delta as f64) as f32)
+ .y_rot(math::to_radians(y_rot_delta as f64) as f32);
+ }
+ let new_delta = Vec3::new(
+ apply_change(new_delta.x, self.delta_x, change.delta.x),
+ apply_change(new_delta.y, self.delta_y, change.delta.y),
+ apply_change(new_delta.z, self.delta_z, change.delta.z),
+ );
+
+ **position = new_position;
+ *direction = new_look_direction;
+ physics.velocity = new_delta;
+ }
+}
+
+fn apply_change<T: Add<Output = T>>(base: T, condition: bool, change: T) -> T {
+ if condition { base + change } else { change }
}
impl AzaleaRead for RelativeMovements {
diff --git a/azalea-protocol/src/packets/game/c_teleport_entity.rs b/azalea-protocol/src/packets/game/c_teleport_entity.rs
index 92b8f1eb..e19ded58 100644
--- a/azalea-protocol/src/packets/game/c_teleport_entity.rs
+++ b/azalea-protocol/src/packets/game/c_teleport_entity.rs
@@ -9,6 +9,6 @@ pub struct ClientboundTeleportEntity {
#[var]
pub id: MinecraftEntityId,
pub change: PositionMoveRotation,
- pub relatives: RelativeMovements,
+ pub relative: RelativeMovements,
pub on_ground: bool,
}