aboutsummaryrefslogtreecommitdiff
path: root/azalea-entity/src/info.rs
diff options
context:
space:
mode:
Diffstat (limited to 'azalea-entity/src/info.rs')
-rw-r--r--azalea-entity/src/info.rs306
1 files changed, 0 insertions, 306 deletions
diff --git a/azalea-entity/src/info.rs b/azalea-entity/src/info.rs
deleted file mode 100644
index 0c5fd3d3..00000000
--- a/azalea-entity/src/info.rs
+++ /dev/null
@@ -1,306 +0,0 @@
-//! Implement things relating to entity datas, like an index of uuids to
-//! entities.
-
-use azalea_core::ChunkPos;
-use azalea_world::{InstanceContainer, InstanceName, MinecraftEntityId, PartialInstance};
-use bevy_app::{App, Plugin, PostUpdate, PreUpdate, Update};
-use bevy_ecs::{
- component::Component,
- entity::Entity,
- query::{Added, Changed, With, Without},
- schedule::{IntoSystemConfigs, SystemSet},
- system::{Commands, EntityCommand, Query, Res, ResMut, Resource},
- world::{EntityMut, World},
-};
-use derive_more::{Deref, DerefMut};
-use log::{debug, warn};
-use parking_lot::RwLock;
-use std::{
- collections::{HashMap, HashSet},
- fmt::Debug,
- sync::Arc,
-};
-use uuid::Uuid;
-
-use crate::{
- add_dead,
- systems::{
- deduplicate_entities, deduplicate_local_entities, update_entity_by_id_index,
- update_fluid_on_eyes, update_uuid_index,
- },
- update_bounding_box, EntityUuid, LastSentPosition, Position,
-};
-
-use super::{Local, LookDirection};
-
-/// A Bevy [`SystemSet`] for various types of entity updates.
-#[derive(SystemSet, Debug, Hash, Eq, PartialEq, Clone)]
-pub enum EntityUpdateSet {
- /// Remove ECS entities that refer to an entity that was already in the ECS
- /// before.
- Deduplicate,
- /// Create search indexes for entities.
- Index,
- /// Remove despawned entities from search indexes.
- Deindex,
-}
-
-/// Plugin handling some basic entity functionality.
-pub struct EntityPlugin;
-impl Plugin for EntityPlugin {
- fn build(&self, app: &mut App) {
- // entities get added pre-update
- // added to indexes during update (done by this plugin)
- // modified during update
- // despawned post-update (done by this plugin)
- app.add_systems(
- PreUpdate,
- remove_despawned_entities_from_indexes.in_set(EntityUpdateSet::Deindex),
- )
- .add_systems(
- PostUpdate,
- (deduplicate_entities, deduplicate_local_entities).in_set(EntityUpdateSet::Deduplicate),
- )
- .add_systems(
- Update,
- (
- (
- update_entity_chunk_positions,
- update_uuid_index,
- update_entity_by_id_index,
- )
- .in_set(EntityUpdateSet::Index),
- (
- add_updates_received,
- debug_new_entity,
- debug_detect_updates_received_on_local_entities,
- add_dead,
- update_bounding_box,
- clamp_look_direction,
- update_fluid_on_eyes,
- ),
- ),
- )
- .init_resource::<EntityInfos>();
- }
-}
-
-fn debug_new_entity(query: Query<(Entity, Option<&Local>), Added<MinecraftEntityId>>) {
- for (entity, local) in query.iter() {
- if local.is_some() {
- debug!("new local entity: {:?}", entity);
- } else {
- debug!("new entity: {:?}", entity);
- }
- }
-}
-
-// How entity updates are processed (to avoid issues with shared worlds)
-// - each bot contains a map of { entity id: updates received }
-// - the shared world also contains a canonical "true" updates received for each
-// entity
-// - when a client loads an entity, its "updates received" is set to the same as
-// the global "updates received"
-// - when the shared world sees an entity for the first time, the "updates
-// received" is set to 1.
-// - clients can force the shared "updates received" to 0 to make it so certain
-// entities (i.e. other bots in our swarm) don't get confused and updated by
-// other bots
-// - when a client gets an update to an entity, we check if our "updates
-// received" is the same as the shared world's "updates received": if it is,
-// then process the update and increment the client's and shared world's
-// "updates received" if not, then we simply increment our local "updates
-// received" and do nothing else
-
-/// An [`EntityCommand`] that applies a "relative update" to an entity, which
-/// means this update won't be run multiple times by different clients in the
-/// same world.
-///
-/// This is used to avoid a bug where when there's multiple clients in the same
-/// world and an entity sends a relative move packet to all clients, its
-/// position gets desynced since the relative move is applied multiple times.
-///
-/// Don't use this unless you actually got an entity update packet that all
-/// other clients within render distance will get too. You usually don't need
-/// this when the change isn't relative either.
-pub struct RelativeEntityUpdate {
- pub partial_world: Arc<RwLock<PartialInstance>>,
- // a function that takes the entity and updates it
- pub update: Box<dyn FnOnce(&mut EntityMut) + Send + Sync>,
-}
-impl EntityCommand for RelativeEntityUpdate {
- fn apply(self, entity: Entity, world: &mut World) {
- let partial_entity_infos = &mut self.partial_world.write().entity_infos;
-
- let mut entity_mut = world.entity_mut(entity);
-
- if Some(entity) == partial_entity_infos.owner_entity {
- // if the entity owns this partial world, it's always allowed to update itself
- (self.update)(&mut entity_mut);
- return;
- };
-
- let entity_id = *entity_mut.get::<MinecraftEntityId>().unwrap();
- let Some(updates_received) = entity_mut.get_mut::<UpdatesReceived>() else {
- // a client tried to update another client, which isn't allowed
- return;
- };
-
- let this_client_updates_received = partial_entity_infos
- .updates_received
- .get(&entity_id)
- .copied();
-
- let can_update = this_client_updates_received.unwrap_or(1) == **updates_received;
- if can_update {
- let new_updates_received = this_client_updates_received.unwrap_or(0) + 1;
- partial_entity_infos
- .updates_received
- .insert(entity_id, new_updates_received);
-
- **entity_mut.get_mut::<UpdatesReceived>().unwrap() = new_updates_received;
-
- let mut entity = world.entity_mut(entity);
- (self.update)(&mut entity);
- }
- }
-}
-
-/// Things that are shared between all the partial worlds.
-#[derive(Resource, Default)]
-pub struct EntityInfos {
- /// An index of entities by their UUIDs
- pub entity_by_uuid: HashMap<Uuid, Entity>,
-}
-
-impl EntityInfos {
- pub fn new() -> Self {
- Self {
- entity_by_uuid: HashMap::default(),
- }
- }
-
- pub fn get_entity_by_uuid(&self, uuid: &Uuid) -> Option<Entity> {
- self.entity_by_uuid.get(uuid).copied()
- }
-}
-
-/// Update the chunk position indexes in [`EntityInfos`].
-fn update_entity_chunk_positions(
- mut query: Query<(Entity, &Position, &mut LastSentPosition, &InstanceName), Changed<Position>>,
- instance_container: Res<InstanceContainer>,
-) {
- for (entity, pos, last_pos, world_name) in query.iter_mut() {
- let world_lock = instance_container.get(world_name).unwrap();
- let mut world = world_lock.write();
-
- let old_chunk = ChunkPos::from(*last_pos);
- let new_chunk = ChunkPos::from(*pos);
-
- if old_chunk != new_chunk {
- // move the entity from the old chunk to the new one
- if let Some(entities) = world.entities_by_chunk.get_mut(&old_chunk) {
- entities.remove(&entity);
- }
- world
- .entities_by_chunk
- .entry(new_chunk)
- .or_default()
- .insert(entity);
- }
- }
-}
-/// A component that lists all the local player entities that have this entity
-/// loaded. If this is empty, the entity will be removed from the ECS.
-#[derive(Component, Clone, Deref, DerefMut)]
-pub struct LoadedBy(pub HashSet<Entity>);
-
-/// A component that counts the number of times this entity has been modified.
-/// This is used for making sure two clients don't do the same relative update
-/// on an entity.
-///
-/// If an entity is local (i.e. it's a client/localplayer), this component
-/// should NOT be present in the entity.
-#[derive(Component, Debug, Deref, DerefMut)]
-pub struct UpdatesReceived(u32);
-
-#[allow(clippy::type_complexity)]
-pub fn add_updates_received(
- mut commands: Commands,
- query: Query<
- Entity,
- (
- Changed<MinecraftEntityId>,
- (Without<UpdatesReceived>, Without<Local>),
- ),
- >,
-) {
- for entity in query.iter() {
- // entities always start with 1 update received
- commands.entity(entity).insert(UpdatesReceived(1));
- }
-}
-
-/// The [`UpdatesReceived`] component should never be on [`Local`] entities.
-/// This warns if an entity has both components.
-fn debug_detect_updates_received_on_local_entities(
- query: Query<Entity, (With<Local>, With<UpdatesReceived>)>,
-) {
- for entity in &query {
- warn!("Entity {:?} has both Local and UpdatesReceived", entity);
- }
-}
-
-/// Despawn entities that aren't being loaded by anything.
-fn remove_despawned_entities_from_indexes(
- mut commands: Commands,
- mut entity_infos: ResMut<EntityInfos>,
- instance_container: Res<InstanceContainer>,
- query: Query<(Entity, &EntityUuid, &Position, &InstanceName, &LoadedBy), Changed<LoadedBy>>,
-) {
- for (entity, uuid, position, world_name, loaded_by) in &query {
- let world_lock = instance_container.get(world_name).unwrap();
- let mut world = world_lock.write();
-
- // if the entity has no references left, despawn it
- if !loaded_by.is_empty() {
- continue;
- }
-
- // remove the entity from the chunk index
- let chunk = ChunkPos::from(*position);
- if let Some(entities_in_chunk) = world.entities_by_chunk.get_mut(&chunk) {
- if entities_in_chunk.remove(&entity) {
- // remove the chunk if there's no entities in it anymore
- if entities_in_chunk.is_empty() {
- world.entities_by_chunk.remove(&chunk);
- }
- } else {
- warn!("Tried to remove entity from chunk {chunk:?} but the entity was not there.");
- }
- } else {
- warn!("Tried to remove entity from chunk {chunk:?} but the chunk was not found.");
- }
- // remove it from the uuid index
- if entity_infos.entity_by_uuid.remove(uuid).is_none() {
- warn!("Tried to remove entity {entity:?} from the uuid index but it was not there.");
- }
- // and now remove the entity from the ecs
- commands.entity(entity).despawn();
- debug!("Despawned entity {entity:?} because it was not loaded by anything.");
- return;
- }
-}
-
-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.x_rot = look_direction.x_rot.clamp(-90.0, 90.0) % 360.0;
- }
-}
-
-impl Debug for EntityInfos {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- f.debug_struct("EntityInfos").finish()
- }
-}