use std::sync::Arc;
use azalea_core::entity_id::MinecraftEntityId;
use azalea_entity::LocalEntity;
use azalea_world::PartialWorld;
use bevy_ecs::prelude::*;
use derive_more::{Deref, DerefMut};
use parking_lot::RwLock;
use tracing::{debug, warn};
use crate::packet::as_system;
pub struct RelativeEntityUpdate {
pub partial_world: Arc<RwLock<PartialWorld>>,
pub update: Box<dyn FnOnce(&mut EntityWorldMut) + Send + Sync>,
}
impl RelativeEntityUpdate {
pub fn new(
partial_world: Arc<RwLock<PartialWorld>>,
update: impl FnOnce(&mut EntityWorldMut) + Send + Sync + 'static,
) -> Self {
Self {
partial_world,
update: Box::new(update),
}
}
}
#[derive(Component, Debug, Deref, DerefMut)]
pub struct UpdatesReceived(u32);
pub type EntityUpdateQuery<'world, 'state, 'a> = Query<
'world,
'state,
(
&'a MinecraftEntityId,
Option<&'a UpdatesReceived>,
Option<&'a LocalEntity>,
),
>;
pub fn should_apply_entity_update(
commands: &mut Commands,
partial_world: &mut PartialWorld,
entity: Entity,
entity_update_query: EntityUpdateQuery,
) -> bool {
let partial_entity_infos = &mut partial_world.entity_infos;
if Some(entity) == partial_entity_infos.owner_entity {
return true;
};
let Ok((minecraft_entity_id, updates_received, local_entity)) = entity_update_query.get(entity)
else {
debug!("called should_apply_entity_update on an entity with missing components {entity:?}");
return false;
};
if local_entity.is_some() {
return false;
}
let this_client_updates_received = partial_entity_infos
.updates_received
.get(minecraft_entity_id)
.copied();
let can_update = if let Some(updates_received) = updates_received {
this_client_updates_received.unwrap_or(1) == **updates_received
} else {
true
};
if can_update {
let new_updates_received = this_client_updates_received.unwrap_or(0) + 1;
partial_entity_infos
.updates_received
.insert(*minecraft_entity_id, new_updates_received);
commands
.entity(entity)
.insert(UpdatesReceived(new_updates_received));
return true;
}
false
}
impl EntityCommand for RelativeEntityUpdate {
fn apply(self, mut entity_mut: EntityWorldMut) {
let partial_world = self.partial_world.clone();
let mut should_update = false;
let entity = entity_mut.id();
entity_mut.world_scope(|ecs| {
as_system::<(Commands, EntityUpdateQuery)>(ecs, |(mut commands, query)| {
should_update = should_apply_entity_update(
&mut commands,
&mut partial_world.write(),
entity,
query,
);
});
});
if should_update {
(self.update)(&mut entity_mut);
}
}
}
pub fn debug_detect_updates_received_on_local_entities(
query: Query<Entity, (With<LocalEntity>, With<UpdatesReceived>)>,
) {
for entity in &query {
warn!("Entity {entity:?} has both LocalEntity and UpdatesReceived");
}
}