mirror of
https://github.com/mat-1/azalea.git
synced 2025-08-02 06:16:04 +00:00
fix despawning entities on dimension change
This commit is contained in:
parent
c9022e8f67
commit
cf66c4be10
5 changed files with 141 additions and 17 deletions
|
@ -207,18 +207,22 @@ impl GamePacketHandler<'_> {
|
||||||
|
|
||||||
as_system::<(
|
as_system::<(
|
||||||
Commands,
|
Commands,
|
||||||
Query<(
|
Query<
|
||||||
&GameProfileComponent,
|
(
|
||||||
&ClientInformation,
|
&GameProfileComponent,
|
||||||
Option<&mut InstanceName>,
|
&ClientInformation,
|
||||||
Option<&mut LoadedBy>,
|
Option<&mut InstanceName>,
|
||||||
&mut EntityIdIndex,
|
Option<&mut LoadedBy>,
|
||||||
&mut InstanceHolder,
|
&mut EntityIdIndex,
|
||||||
)>,
|
&mut InstanceHolder,
|
||||||
|
),
|
||||||
|
With<LocalEntity>,
|
||||||
|
>,
|
||||||
EventWriter<InstanceLoadedEvent>,
|
EventWriter<InstanceLoadedEvent>,
|
||||||
ResMut<InstanceContainer>,
|
ResMut<InstanceContainer>,
|
||||||
ResMut<EntityUuidIndex>,
|
ResMut<EntityUuidIndex>,
|
||||||
EventWriter<SendPacketEvent>,
|
EventWriter<SendPacketEvent>,
|
||||||
|
Query<&mut LoadedBy, Without<LocalEntity>>,
|
||||||
)>(
|
)>(
|
||||||
self.ecs,
|
self.ecs,
|
||||||
|(
|
|(
|
||||||
|
@ -228,6 +232,7 @@ impl GamePacketHandler<'_> {
|
||||||
mut instance_container,
|
mut instance_container,
|
||||||
mut entity_uuid_index,
|
mut entity_uuid_index,
|
||||||
mut send_packet_events,
|
mut send_packet_events,
|
||||||
|
mut loaded_by_query,
|
||||||
)| {
|
)| {
|
||||||
let (
|
let (
|
||||||
game_profile,
|
game_profile,
|
||||||
|
@ -317,6 +322,11 @@ impl GamePacketHandler<'_> {
|
||||||
&mut instance_holder.instance.write(),
|
&mut instance_holder.instance.write(),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// every entity is now unloaded by this player
|
||||||
|
for mut loaded_by in &mut loaded_by_query.iter_mut() {
|
||||||
|
loaded_by.remove(&self.player);
|
||||||
|
}
|
||||||
|
|
||||||
// update or insert loaded_by
|
// update or insert loaded_by
|
||||||
if let Some(mut loaded_by) = loaded_by {
|
if let Some(mut loaded_by) = loaded_by {
|
||||||
loaded_by.insert(self.player);
|
loaded_by.insert(self.player);
|
||||||
|
@ -1413,16 +1423,20 @@ impl GamePacketHandler<'_> {
|
||||||
|
|
||||||
as_system::<(
|
as_system::<(
|
||||||
Commands,
|
Commands,
|
||||||
Query<(
|
Query<
|
||||||
&mut InstanceHolder,
|
(
|
||||||
&GameProfileComponent,
|
&mut InstanceHolder,
|
||||||
&ClientInformation,
|
&GameProfileComponent,
|
||||||
)>,
|
&ClientInformation,
|
||||||
|
),
|
||||||
|
With<LocalEntity>,
|
||||||
|
>,
|
||||||
EventWriter<_>,
|
EventWriter<_>,
|
||||||
ResMut<InstanceContainer>,
|
ResMut<InstanceContainer>,
|
||||||
|
Query<&mut LoadedBy, Without<LocalEntity>>,
|
||||||
)>(
|
)>(
|
||||||
self.ecs,
|
self.ecs,
|
||||||
|(mut commands, mut query, mut events, mut instance_container)| {
|
|(mut commands, mut query, mut events, mut instance_container, mut loaded_by_query)| {
|
||||||
let (mut instance_holder, game_profile, client_information) =
|
let (mut instance_holder, game_profile, client_information) =
|
||||||
query.get_mut(self.player).unwrap();
|
query.get_mut(self.player).unwrap();
|
||||||
|
|
||||||
|
@ -1461,6 +1475,11 @@ impl GamePacketHandler<'_> {
|
||||||
);
|
);
|
||||||
instance_holder.instance = weak_instance;
|
instance_holder.instance = weak_instance;
|
||||||
|
|
||||||
|
// every entity is now unloaded by this player
|
||||||
|
for mut loaded_by in &mut loaded_by_query.iter_mut() {
|
||||||
|
loaded_by.remove(&self.player);
|
||||||
|
}
|
||||||
|
|
||||||
// this resets a bunch of our components like physics and stuff
|
// this resets a bunch of our components like physics and stuff
|
||||||
let entity_bundle = EntityBundle::new(
|
let entity_bundle = EntityBundle::new(
|
||||||
game_profile.uuid,
|
game_profile.uuid,
|
||||||
|
|
|
@ -0,0 +1,97 @@
|
||||||
|
use azalea_client::{InConfigState, InGameState, test_simulation::*};
|
||||||
|
use azalea_core::{
|
||||||
|
delta::PositionDelta8,
|
||||||
|
position::{ChunkPos, Vec3},
|
||||||
|
resource_location::ResourceLocation,
|
||||||
|
};
|
||||||
|
use azalea_entity::{LocalEntity, metadata::Cow};
|
||||||
|
use azalea_protocol::packets::{
|
||||||
|
ConnectionProtocol,
|
||||||
|
config::{ClientboundFinishConfiguration, ClientboundRegistryData},
|
||||||
|
game::ClientboundAddEntity,
|
||||||
|
};
|
||||||
|
use azalea_registry::DimensionType;
|
||||||
|
use azalea_world::InstanceName;
|
||||||
|
use bevy_ecs::{entity::Entity, query::With};
|
||||||
|
use bevy_log::tracing_subscriber;
|
||||||
|
use simdnbt::owned::{NbtCompound, NbtTag};
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_despawn_entities_when_changing_dimension() {
|
||||||
|
let _ = tracing_subscriber::fmt::try_init();
|
||||||
|
|
||||||
|
let mut simulation = Simulation::new(ConnectionProtocol::Configuration);
|
||||||
|
simulation.receive_packet(ClientboundRegistryData {
|
||||||
|
registry_id: ResourceLocation::new("minecraft:dimension_type"),
|
||||||
|
entries: vec![
|
||||||
|
(
|
||||||
|
ResourceLocation::new("minecraft:overworld"),
|
||||||
|
Some(NbtCompound::from_values(vec![
|
||||||
|
("height".into(), NbtTag::Int(384)),
|
||||||
|
("min_y".into(), NbtTag::Int(-64)),
|
||||||
|
])),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
ResourceLocation::new("minecraft:nether"),
|
||||||
|
Some(NbtCompound::from_values(vec![
|
||||||
|
("height".into(), NbtTag::Int(256)),
|
||||||
|
("min_y".into(), NbtTag::Int(0)),
|
||||||
|
])),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
.into_iter()
|
||||||
|
.collect(),
|
||||||
|
});
|
||||||
|
simulation.tick();
|
||||||
|
simulation.receive_packet(ClientboundFinishConfiguration);
|
||||||
|
simulation.tick();
|
||||||
|
|
||||||
|
//
|
||||||
|
// OVERWORLD
|
||||||
|
//
|
||||||
|
|
||||||
|
simulation.receive_packet(make_basic_login_packet(
|
||||||
|
DimensionType::new_raw(0), // overworld
|
||||||
|
ResourceLocation::new("azalea:a"),
|
||||||
|
));
|
||||||
|
simulation.tick();
|
||||||
|
|
||||||
|
simulation.receive_packet(make_basic_empty_chunk(ChunkPos::new(0, 0), (384 + 64) / 16));
|
||||||
|
simulation.tick();
|
||||||
|
// spawn a cow
|
||||||
|
simulation.receive_packet(ClientboundAddEntity {
|
||||||
|
id: 123.into(),
|
||||||
|
uuid: Uuid::from_u128(1234),
|
||||||
|
entity_type: azalea_registry::EntityKind::Cow,
|
||||||
|
position: Vec3::new(0., 64., 0.),
|
||||||
|
x_rot: 0,
|
||||||
|
y_rot: 0,
|
||||||
|
y_head_rot: 0,
|
||||||
|
data: 0,
|
||||||
|
velocity: PositionDelta8::default(),
|
||||||
|
});
|
||||||
|
simulation.tick();
|
||||||
|
// make sure it's spawned
|
||||||
|
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(), 1, "cow should be spawned");
|
||||||
|
|
||||||
|
//
|
||||||
|
// NETHER
|
||||||
|
//
|
||||||
|
|
||||||
|
simulation.receive_packet(make_basic_respawn_packet(
|
||||||
|
DimensionType::new_raw(1), // nether
|
||||||
|
ResourceLocation::new("azalea:b"),
|
||||||
|
));
|
||||||
|
simulation.tick();
|
||||||
|
|
||||||
|
// cow should be completely deleted from the ecs
|
||||||
|
let cow_iter = cow_query.iter(simulation.app.world());
|
||||||
|
assert_eq!(
|
||||||
|
cow_iter.count(),
|
||||||
|
0,
|
||||||
|
"cow should be despawned after switching dimensions"
|
||||||
|
);
|
||||||
|
}
|
|
@ -7,7 +7,7 @@ use azalea_world::{Instance, InstanceContainer, InstanceName, MinecraftEntityId}
|
||||||
use bevy_ecs::{
|
use bevy_ecs::{
|
||||||
component::Component,
|
component::Component,
|
||||||
entity::Entity,
|
entity::Entity,
|
||||||
query::{Added, Changed},
|
query::{Added, Changed, Without},
|
||||||
system::{Commands, Query, Res, ResMut, Resource},
|
system::{Commands, Query, Res, ResMut, Resource},
|
||||||
};
|
};
|
||||||
use derive_more::{Deref, DerefMut};
|
use derive_more::{Deref, DerefMut};
|
||||||
|
@ -16,7 +16,7 @@ use tracing::{debug, warn};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
use super::LoadedBy;
|
use super::LoadedBy;
|
||||||
use crate::{EntityUuid, Position};
|
use crate::{EntityUuid, LocalEntity, Position};
|
||||||
|
|
||||||
#[derive(Resource, Default)]
|
#[derive(Resource, Default)]
|
||||||
pub struct EntityUuidIndex {
|
pub struct EntityUuidIndex {
|
||||||
|
@ -152,7 +152,7 @@ pub fn remove_despawned_entities_from_indexes(
|
||||||
&InstanceName,
|
&InstanceName,
|
||||||
&LoadedBy,
|
&LoadedBy,
|
||||||
),
|
),
|
||||||
Changed<LoadedBy>,
|
(Changed<LoadedBy>, Without<LocalEntity>),
|
||||||
>,
|
>,
|
||||||
) {
|
) {
|
||||||
for (entity, uuid, minecraft_id, position, instance_name, loaded_by) in &query {
|
for (entity, uuid, minecraft_id, position, instance_name, loaded_by) in &query {
|
||||||
|
|
|
@ -206,6 +206,9 @@ pub fn update_bounding_box(mut query: Query<(&Position, &mut Physics), Changed<P
|
||||||
|
|
||||||
/// Marks an entity that's in a loaded chunk. This is updated at the beginning
|
/// Marks an entity that's in a loaded chunk. This is updated at the beginning
|
||||||
/// of every tick.
|
/// of every tick.
|
||||||
|
///
|
||||||
|
/// Internally, this is only used for player physics. Not to be confused with
|
||||||
|
/// the somewhat similarly named [`LoadedBy`].
|
||||||
#[derive(Component, Clone, Debug, Copy)]
|
#[derive(Component, Clone, Debug, Copy)]
|
||||||
pub struct InLoadedChunk;
|
pub struct InLoadedChunk;
|
||||||
|
|
||||||
|
|
|
@ -97,6 +97,11 @@ impl Display for MinecraftEntityId {
|
||||||
write!(f, "eid({})", self.0)
|
write!(f, "eid({})", self.0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
impl From<i32> for MinecraftEntityId {
|
||||||
|
fn from(id: i32) -> Self {
|
||||||
|
Self(id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Keep track of certain metadatas that are only relevant for this partial
|
/// Keep track of certain metadatas that are only relevant for this partial
|
||||||
/// world.
|
/// world.
|
||||||
|
|
Loading…
Add table
Reference in a new issue