mirror of
https://github.com/mat-1/azalea.git
synced 2025-08-02 14:26:04 +00:00
ok well it compiles but
it definitely doesn't work
This commit is contained in:
parent
f6689e62fe
commit
eff339661f
9 changed files with 708 additions and 778 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -424,6 +424,7 @@ dependencies = [
|
||||||
"enum-as-inner",
|
"enum-as-inner",
|
||||||
"log",
|
"log",
|
||||||
"nohash-hasher",
|
"nohash-hasher",
|
||||||
|
"once_cell",
|
||||||
"parking_lot",
|
"parking_lot",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"uuid",
|
"uuid",
|
||||||
|
|
|
@ -20,6 +20,7 @@ derive_more = {version = "0.99.17", features = ["deref", "deref_mut"]}
|
||||||
enum-as-inner = "0.5.1"
|
enum-as-inner = "0.5.1"
|
||||||
log = "0.4.17"
|
log = "0.4.17"
|
||||||
nohash-hasher = "0.2.0"
|
nohash-hasher = "0.2.0"
|
||||||
|
once_cell = "1.16.0"
|
||||||
parking_lot = "^0.12.1"
|
parking_lot = "^0.12.1"
|
||||||
thiserror = "1.0.34"
|
thiserror = "1.0.34"
|
||||||
uuid = "1.1.2"
|
uuid = "1.1.2"
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -3,30 +3,30 @@ mod data;
|
||||||
mod dimensions;
|
mod dimensions;
|
||||||
pub mod metadata;
|
pub mod metadata;
|
||||||
|
|
||||||
use self::{
|
use self::metadata::UpdateMetadataError;
|
||||||
attributes::{AttributeInstance, AttributeModifiers},
|
|
||||||
metadata::UpdateMetadataError,
|
|
||||||
};
|
|
||||||
use crate::WeakWorld;
|
use crate::WeakWorld;
|
||||||
use azalea_block::BlockState;
|
use azalea_block::BlockState;
|
||||||
use azalea_core::{BlockPos, ChunkPos, Vec3, AABB};
|
use azalea_core::{BlockPos, ChunkPos, Vec3, AABB};
|
||||||
use azalea_registry::EntityKind;
|
use bevy_ecs::{
|
||||||
use bevy_ecs::{component::Component, world::EntityMut};
|
component::Component,
|
||||||
|
world::{EntityMut, Mut},
|
||||||
|
};
|
||||||
pub use data::*;
|
pub use data::*;
|
||||||
use derive_more::{Deref, DerefMut};
|
use derive_more::{Deref, DerefMut};
|
||||||
pub use dimensions::*;
|
pub use dimensions::*;
|
||||||
use std::{
|
use std::{
|
||||||
fmt::{Debug, Display, Formatter},
|
fmt::{Debug, Display, Formatter},
|
||||||
marker::PhantomData,
|
ops::Deref,
|
||||||
ops::{Deref, DerefMut},
|
|
||||||
ptr::NonNull,
|
|
||||||
};
|
};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
/// An entity ID that's used by ECS library.
|
||||||
|
pub type EcsEntityId = bevy_ecs::entity::Entity;
|
||||||
|
|
||||||
/// The unique 32-bit unsigned id of an entity.
|
/// The unique 32-bit unsigned id of an entity.
|
||||||
#[derive(Deref, Eq, PartialEq, DerefMut)]
|
#[derive(Deref, Eq, PartialEq, DerefMut, Copy, Clone)]
|
||||||
pub struct EntityId(pub u32);
|
pub struct EntityId(pub u32);
|
||||||
impl From<EntityId> for bevy_ecs::entity::Entity {
|
impl From<EntityId> for EcsEntityId {
|
||||||
// bevy_ecs `Entity`s also store the "generation" which adds another 32 bits,
|
// bevy_ecs `Entity`s also store the "generation" which adds another 32 bits,
|
||||||
// but we don't care about the generation
|
// but we don't care about the generation
|
||||||
fn from(id: EntityId) -> Self {
|
fn from(id: EntityId) -> Self {
|
||||||
|
@ -50,53 +50,56 @@ impl std::hash::Hash for EntityId {
|
||||||
}
|
}
|
||||||
impl nohash_hasher::IsEnabled for EntityId {}
|
impl nohash_hasher::IsEnabled for EntityId {}
|
||||||
|
|
||||||
/// A mutable reference to an entity in a world.
|
// /// A mutable reference to an entity in a world.
|
||||||
pub struct Entity<'w, W = &'w WeakWorld> {
|
// pub struct Entity<'w, W = &'w WeakWorld> {
|
||||||
/// The [`WeakWorld`] this entity is in.
|
// /// The [`WeakWorld`] this entity is in.
|
||||||
pub world: W,
|
// pub world: W,
|
||||||
/// The container for the incrementing numerical id of the entity.
|
// /// The incrementing numerical id of the entity.
|
||||||
pub id: EntityId,
|
// pub id: u32,
|
||||||
/// The ECS data for the entity.
|
// /// The ECS data for the entity.
|
||||||
pub data: bevy_ecs::world::EntityMut<'w>,
|
// pub data: bevy_ecs::world::EntityMut<'w>,
|
||||||
}
|
// }
|
||||||
|
|
||||||
/// Create an entity if you only have a [`bevy_ecs::world::World`].
|
/// Create an entity if you only have a [`bevy_ecs::world::World`].
|
||||||
///
|
///
|
||||||
/// If you do have access to a [`PartialEntityStorage`] though then just call
|
/// If you do have access to a [`PartialEntityStorage`] though then just call
|
||||||
/// [`PartialEntityStorage::insert`].
|
/// [`PartialEntityStorage::insert`].
|
||||||
|
///
|
||||||
|
/// This doesn't return anything since you should be using the [`EntityId`] to
|
||||||
|
/// get the entity data.
|
||||||
pub(crate) fn new_entity<'w>(
|
pub(crate) fn new_entity<'w>(
|
||||||
ecs: &mut bevy_ecs::world::World,
|
ecs: &'w mut bevy_ecs::world::World,
|
||||||
id: EntityId,
|
id: EntityId,
|
||||||
bundle: impl bevy_ecs::bundle::Bundle,
|
bundle: impl bevy_ecs::bundle::Bundle,
|
||||||
) -> EntityMut<'w> {
|
) {
|
||||||
// bevy_ecs only returns None if the entity only exists with a different
|
// bevy_ecs only returns None if the entity only exists with a different
|
||||||
// generation, which shouldn't be possible here
|
// generation, which shouldn't be possible here
|
||||||
ecs.get_or_spawn(id.into())
|
let mut entity = ecs
|
||||||
.expect("Entities should always be generation 0 if we're manually spawning from ids")
|
.get_or_spawn(id.into())
|
||||||
|
.expect("Entities should always be generation 0 if we're manually spawning from ids");
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'d, D: Deref<Target = WeakWorld>> Entity<'d, D> {
|
// impl<'d, D: Deref<Target = WeakWorld>> Entity<'d, D> {
|
||||||
/// Create an Entity when we already know its id and data.
|
// /// Create an Entity when we already know its id and data.
|
||||||
pub(crate) fn new(world: D, id: u32, bundle: impl bevy_ecs::bundle::Bundle) -> Self {
|
// pub(crate) fn new(world: D, id: u32, bundle: impl
|
||||||
let ecs = world.entity_storage.write().ecs;
|
// bevy_ecs::bundle::Bundle) -> Self { let ecs =
|
||||||
let id = EntityId(id);
|
// world.entity_storage.write().ecs; let data = new_entity(&mut ecs, id,
|
||||||
let data = new_entity(&mut ecs, id, bundle);
|
// bundle); Self { world, id, data }
|
||||||
Self { world, id, data }
|
// }
|
||||||
}
|
// }
|
||||||
}
|
|
||||||
|
|
||||||
impl<'d, D: Deref<Target = WeakWorld>> Entity<'d, D> {
|
// impl<'d, D: Deref<Target = WeakWorld>> Entity<'d, D> {
|
||||||
// todo: write more here and add an example too
|
// // todo: write more here and add an example too
|
||||||
/// Get data from the entity.
|
// /// Get data from the entity.
|
||||||
pub fn get<T: bevy_ecs::component::Component>(&self) -> Option<&T> {
|
// pub fn get<T: bevy_ecs::component::Component>(&self) -> Option<&T> {
|
||||||
self.data.get()
|
// self.data.get()
|
||||||
}
|
// }
|
||||||
pub fn get_mut<T: bevy_ecs::component::Component>(
|
// pub fn get_mut<T: bevy_ecs::component::Component>(
|
||||||
&mut self,
|
// &mut self,
|
||||||
) -> Option<bevy_ecs::world::Mut<T>> {
|
// ) -> Option<bevy_ecs::world::Mut<T>> {
|
||||||
self.data.get_mut()
|
// self.data.get_mut()
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
/// Sets the position of the entity. This doesn't update the cache in
|
/// Sets the position of the entity. This doesn't update the cache in
|
||||||
/// azalea-world, and should only be used within azalea-world!
|
/// azalea-world, and should only be used within azalea-world!
|
||||||
|
@ -105,7 +108,7 @@ impl<'d, D: Deref<Target = WeakWorld>> Entity<'d, D> {
|
||||||
/// Cached position in the world must be updated.
|
/// Cached position in the world must be updated.
|
||||||
pub unsafe fn move_unchecked(pos: &mut Position, physics: &mut Physics, new_pos: Vec3) {
|
pub unsafe fn move_unchecked(pos: &mut Position, physics: &mut Physics, new_pos: Vec3) {
|
||||||
*pos = Position(new_pos);
|
*pos = Position(new_pos);
|
||||||
let bounding_box = make_bounding_box(pos, physics);
|
let bounding_box = make_bounding_box(&pos, &physics);
|
||||||
physics.bounding_box = bounding_box;
|
physics.bounding_box = bounding_box;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -146,9 +149,9 @@ pub fn input_vector(physics: &mut Physics, speed: f32, acceleration: &Vec3) -> V
|
||||||
pub fn update_metadatas(
|
pub fn update_metadatas(
|
||||||
ecs: bevy_ecs::world::World,
|
ecs: bevy_ecs::world::World,
|
||||||
entity: bevy_ecs::world::EntityMut,
|
entity: bevy_ecs::world::EntityMut,
|
||||||
items: &Vec<EntityDataItem>,
|
items: Vec<EntityDataItem>,
|
||||||
) -> Result<(), UpdateMetadataError> {
|
) -> Result<(), UpdateMetadataError> {
|
||||||
metadata::update_metadatas(ecs, entity, items)
|
metadata::update_metadatas(entity, items)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn make_bounding_box(pos: &Position, physics: &Physics) -> AABB {
|
pub fn make_bounding_box(pos: &Position, physics: &Physics) -> AABB {
|
||||||
|
@ -206,7 +209,7 @@ pub fn on_pos<W: Deref<Target = WeakWorld>>(
|
||||||
pos
|
pos
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Component, Deref, DerefMut)]
|
#[derive(Component, Deref, DerefMut, Clone, Copy)]
|
||||||
pub struct EntityUuid(Uuid);
|
pub struct EntityUuid(Uuid);
|
||||||
|
|
||||||
/// The position of the entity right now.
|
/// The position of the entity right now.
|
||||||
|
@ -331,11 +334,11 @@ pub struct Physics {
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
|
||||||
impl<W: Deref<Target = WeakWorld>> Debug for Entity<'_, W> {
|
// impl<W: Deref<Target = WeakWorld>> Debug for Entity<'_, W> {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
// fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
f.debug_struct("Entity").field("id", &self.id).finish()
|
// f.debug_struct("Entity").field("id", &self.id).finish()
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
|
@ -1,8 +1,12 @@
|
||||||
use crate::entity::{new_entity, Entity, EntityId, EntityUuid, Position};
|
use crate::entity::{new_entity, EcsEntityId, EntityId, EntityUuid, Position};
|
||||||
use azalea_core::ChunkPos;
|
use azalea_core::ChunkPos;
|
||||||
use bevy_ecs::world::{EntityMut, EntityRef};
|
use bevy_ecs::{
|
||||||
|
query::{QueryEntityError, QueryState, ReadOnlyWorldQuery, WorldQuery},
|
||||||
|
world::{EntityMut, EntityRef},
|
||||||
|
};
|
||||||
use log::warn;
|
use log::warn;
|
||||||
use nohash_hasher::{IntMap, IntSet};
|
use nohash_hasher::{IntMap, IntSet};
|
||||||
|
use once_cell::sync::Lazy;
|
||||||
use parking_lot::RwLock;
|
use parking_lot::RwLock;
|
||||||
use std::{
|
use std::{
|
||||||
collections::HashMap,
|
collections::HashMap,
|
||||||
|
@ -88,31 +92,26 @@ impl PartialEntityStorage {
|
||||||
|
|
||||||
/// Add an entity to the storage.
|
/// Add an entity to the storage.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn insert(&mut self, id: u32, bundle: impl bevy_ecs::bundle::Bundle) {
|
pub fn insert(&mut self, id: EntityId, bundle: impl bevy_ecs::bundle::Bundle) {
|
||||||
// if you're trying to optimize this, see if checking if the id is in
|
let mut shared = self.shared.write();
|
||||||
// self.loaded_entity_ids would help with performance
|
|
||||||
// i didn't put the check here just in case it doesn't actually help
|
|
||||||
|
|
||||||
// if the entity is already in the shared world, we don't need to do
|
// if the entity is already in the shared world, we don't need to do
|
||||||
// anything
|
// anything
|
||||||
let id = EntityId(id);
|
if shared.contains_id(id) {
|
||||||
|
|
||||||
if self.shared.read().contains_id(id) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let entity = new_entity(&mut self.shared.read().ecs, id, bundle);
|
new_entity(&mut shared.ecs, id, bundle);
|
||||||
|
|
||||||
|
let mut query = shared.ecs.query::<(&EntityUuid, &Position)>();
|
||||||
|
let (&uuid, &pos) = query.get(&mut shared.ecs, id.into()).unwrap();
|
||||||
|
|
||||||
// add the entity to the indexes
|
// add the entity to the indexes
|
||||||
let mut shared = self.shared.write();
|
|
||||||
shared
|
shared
|
||||||
.ids_by_chunk
|
.ids_by_chunk
|
||||||
.entry(ChunkPos::from(entity.get::<Position>().unwrap()))
|
.entry(ChunkPos::from(&pos))
|
||||||
.or_default()
|
.or_default()
|
||||||
.insert(id);
|
.insert(id);
|
||||||
shared
|
shared.id_by_uuid.insert(*uuid, id);
|
||||||
.id_by_uuid
|
|
||||||
.insert(**entity.get::<EntityUuid>().unwrap(), id);
|
|
||||||
self.loaded_entity_ids.insert(id);
|
self.loaded_entity_ids.insert(id);
|
||||||
// set our updates_received to the shared updates_received, unless it's
|
// set our updates_received to the shared updates_received, unless it's
|
||||||
// not there in which case set both to 1
|
// not there in which case set both to 1
|
||||||
|
@ -132,15 +131,22 @@ impl PartialEntityStorage {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn remove_by_id(&mut self, id: EntityId) {
|
pub fn remove_by_id(&mut self, id: EntityId) {
|
||||||
if self.loaded_entity_ids.remove(&id) {
|
if self.loaded_entity_ids.remove(&id) {
|
||||||
let entity = self.shared.read().get_by_id(id).expect(
|
let mut shared = self.shared.write();
|
||||||
"If the entity was being loaded by this storage, it must be in the shared storage.",
|
|
||||||
|
let mut query = shared.query::<(&Position, &EntityUuid)>();
|
||||||
|
let (pos, uuid) = query.get(&mut shared.ecs, id.into()).expect(
|
||||||
|
"If the entity was being loaded by this storage, it must be in the shared
|
||||||
|
storage.",
|
||||||
);
|
);
|
||||||
let pos = entity.get::<Position>().unwrap();
|
|
||||||
let chunk = ChunkPos::from(pos);
|
let chunk = ChunkPos::from(pos);
|
||||||
let uuid = **entity.get::<EntityUuid>().unwrap();
|
let uuid = **uuid;
|
||||||
|
|
||||||
|
// TODO: is this line actually necessary? test if it doesn't immediately
|
||||||
|
// panic/deadlock without this line
|
||||||
|
drop(query);
|
||||||
|
|
||||||
self.updates_received.remove(&id);
|
self.updates_received.remove(&id);
|
||||||
drop(entity);
|
shared.remove_entity_if_unused(id, uuid, chunk);
|
||||||
self.shared.write().remove_entity_if_unused(id, uuid, chunk);
|
|
||||||
} else {
|
} else {
|
||||||
warn!("Tried to remove entity with id {id} but it was not found.")
|
warn!("Tried to remove entity with id {id} but it was not found.")
|
||||||
}
|
}
|
||||||
|
@ -154,12 +160,15 @@ impl PartialEntityStorage {
|
||||||
self.loaded_entity_ids.contains(id)
|
self.loaded_entity_ids.contains(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a reference to an entity by its id, if it's being loaded by this
|
/// Get an [`EntityId`] from this u32 entity ID if the entity is being
|
||||||
/// storage.
|
/// loaded by this storage.
|
||||||
|
///
|
||||||
|
/// Note that you can just create an `EntityId` directly if you want, and
|
||||||
|
/// it'll work if the entity is being loaded by any storage.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn limited_get_by_id(&self, id: EntityId) -> Option<EntityMut> {
|
pub fn limited_get_by_id(&self, id: u32) -> Option<EntityId> {
|
||||||
if self.limited_contains_id(&id) {
|
if self.limited_contains_id(&EntityId(id)) {
|
||||||
self.shared.read().get_by_id(id)
|
Some(EntityId(id))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
@ -191,23 +200,24 @@ impl PartialEntityStorage {
|
||||||
/// Get a reference to an entity by its UUID, if it's being loaded by this
|
/// Get a reference to an entity by its UUID, if it's being loaded by this
|
||||||
/// storage.
|
/// storage.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn limited_get_by_uuid(&self, uuid: &Uuid) -> Option<EntityMut> {
|
pub fn limited_get_by_uuid(&self, uuid: &Uuid) -> Option<EntityId> {
|
||||||
let entity_id = self.shared.read().id_by_uuid.get(uuid)?;
|
let shared = self.shared.read();
|
||||||
self.limited_get_by_id(*entity_id)
|
let entity_id = shared.id_by_uuid.get(uuid)?;
|
||||||
|
self.limited_get_by_id(entity_id.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Clear all entities in a chunk. This will not clear them from the
|
/// Clear all entities in a chunk. This will not clear them from the
|
||||||
/// shared storage, unless there are no other references to them.
|
/// shared storage, unless there are no other references to them.
|
||||||
pub fn clear_chunk(&mut self, chunk: &ChunkPos) {
|
pub fn clear_chunk(&mut self, chunk: &ChunkPos) {
|
||||||
let shared = self.shared.write();
|
let mut shared = self.shared.write();
|
||||||
if let Some(entities) = shared.ids_by_chunk.get(chunk) {
|
let mut query = shared.query::<&EntityUuid>();
|
||||||
for id in entities.iter() {
|
|
||||||
if self.loaded_entity_ids.remove(id) {
|
if let Some(entities) = shared.ids_by_chunk.get(chunk).cloned() {
|
||||||
let mut entity = shared.get_by_id(*id).unwrap();
|
for &id in entities.iter() {
|
||||||
let uuid = **entity.get::<EntityUuid>().unwrap();
|
if self.loaded_entity_ids.remove(&id) {
|
||||||
drop(entity);
|
let uuid = **query.get(&shared.ecs, id.into()).unwrap();
|
||||||
// maybe remove it from the storage
|
// maybe remove it from the storage
|
||||||
shared.remove_entity_if_unused(*id, uuid, *chunk);
|
shared.remove_entity_if_unused(id, uuid, *chunk);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -217,7 +227,7 @@ impl PartialEntityStorage {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn update_entity_chunk(
|
pub fn update_entity_chunk(
|
||||||
&mut self,
|
&mut self,
|
||||||
entity_id: u32,
|
entity_id: EntityId,
|
||||||
old_chunk: &ChunkPos,
|
old_chunk: &ChunkPos,
|
||||||
new_chunk: &ChunkPos,
|
new_chunk: &ChunkPos,
|
||||||
) {
|
) {
|
||||||
|
@ -227,6 +237,10 @@ impl PartialEntityStorage {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// This is useful so functions that return an IntSet of entity ids can return a
|
||||||
|
/// reference to nothing.
|
||||||
|
static EMPTY_ENTITY_ID_INTSET: Lazy<IntSet<EntityId>> = Lazy::new(|| IntSet::default());
|
||||||
|
|
||||||
impl WeakEntityStorage {
|
impl WeakEntityStorage {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
@ -246,9 +260,9 @@ impl WeakEntityStorage {
|
||||||
///
|
///
|
||||||
/// Returns whether the entity was removed.
|
/// Returns whether the entity was removed.
|
||||||
pub fn remove_entity_if_unused(&mut self, id: EntityId, uuid: Uuid, chunk: ChunkPos) -> bool {
|
pub fn remove_entity_if_unused(&mut self, id: EntityId, uuid: Uuid, chunk: ChunkPos) -> bool {
|
||||||
if let Some(&mut count) = self.entity_reference_count.get_mut(&id) {
|
if let Some(count) = self.entity_reference_count.get_mut(&id) {
|
||||||
count -= 1;
|
*count -= 1;
|
||||||
if count == 0 {
|
if *count == 0 {
|
||||||
self.entity_reference_count.remove(&id);
|
self.entity_reference_count.remove(&id);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -282,106 +296,32 @@ impl WeakEntityStorage {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get an iterator over all entities in the shared storage. The iterator
|
pub fn query<Q: WorldQuery>(&mut self) -> QueryState<Q, ()> {
|
||||||
/// is over `Weak<EntityData>`s, so you'll have to manually try to upgrade.
|
self.ecs.query::<Q>()
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```rust
|
|
||||||
/// # use std::sync::Arc;
|
|
||||||
/// # use azalea_world::{PartialEntityStorage, entity::{EntityData, EntityMetadata, metadata}};
|
|
||||||
/// # use azalea_core::Vec3;
|
|
||||||
/// # use uuid::Uuid;
|
|
||||||
/// #
|
|
||||||
/// let mut storage = PartialEntityStorage::default();
|
|
||||||
/// storage.insert(
|
|
||||||
/// 0,
|
|
||||||
/// EntityData::new(
|
|
||||||
/// Uuid::nil(),
|
|
||||||
/// Vec3::default(),
|
|
||||||
/// EntityMetadata::Player(metadata::Player::default()),
|
|
||||||
/// ),
|
|
||||||
/// );
|
|
||||||
/// for entity in storage.shared.read().entities() {
|
|
||||||
/// if let Some(entity) = entity.upgrade() {
|
|
||||||
/// println!("Entity: {:?}", entity);
|
|
||||||
/// }
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
pub fn entities(&self) -> std::collections::hash_map::Values<'_, u32, Weak<EntityData>> {
|
|
||||||
self.data_by_id.values()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Whether the entity with the given id is in the shared storage.
|
/// Whether the entity with the given id is in the shared storage.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn contains_id(&self, id: EntityId) -> bool {
|
pub fn contains_id(&self, id: EntityId) -> bool {
|
||||||
self.ecs.get_entity(*id).is_some()
|
self.ecs.get_entity(id.into()).is_some()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get an entity by its id, if it exists.
|
/// Returns a set of entities in the given chunk.
|
||||||
#[inline]
|
pub fn entities_in_chunk<F>(&self, chunk: &ChunkPos) -> &IntSet<EntityId> {
|
||||||
pub fn get_by_id(&self, id: EntityId) -> Option<EntityMut> {
|
self.ids_by_chunk
|
||||||
self.ecs.get_entity_mut(*id)
|
.get(chunk)
|
||||||
|
.unwrap_or(&EMPTY_ENTITY_ID_INTSET)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get an entity in the shared storage by its UUID, if it exists.
|
pub fn id_by_uuid(&self, uuid: &Uuid) -> Option<&EntityId> {
|
||||||
#[inline]
|
self.id_by_uuid.get(uuid)
|
||||||
pub fn get_by_uuid(&self, uuid: &Uuid) -> Option<Arc<EntityData>> {
|
|
||||||
self.id_by_uuid
|
|
||||||
.get(uuid)
|
|
||||||
.and_then(|id| self.data_by_id.get(id).and_then(|e| e.upgrade()))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_by<F>(&self, mut f: F) -> Option<Arc<EntityData>>
|
|
||||||
where
|
|
||||||
F: FnMut(&Arc<EntityData>) -> bool,
|
|
||||||
{
|
|
||||||
for entity in self.entities() {
|
|
||||||
if let Some(entity) = entity.upgrade() {
|
|
||||||
if f(&entity) {
|
|
||||||
return Some(entity);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn entities_by<F>(&self, mut f: F) -> Vec<Arc<EntityData>>
|
|
||||||
where
|
|
||||||
F: FnMut(&Arc<EntityData>) -> bool,
|
|
||||||
{
|
|
||||||
let mut entities = Vec::new();
|
|
||||||
for entity in self.entities() {
|
|
||||||
if let Some(entity) = entity.upgrade() {
|
|
||||||
if f(&entity) {
|
|
||||||
entities.push(entity);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
entities
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn entity_by_in_chunk<F>(&self, chunk: &ChunkPos, mut f: F) -> Option<Arc<EntityData>>
|
|
||||||
where
|
|
||||||
F: FnMut(&EntityData) -> bool,
|
|
||||||
{
|
|
||||||
if let Some(entities) = self.ids_by_chunk.get(chunk) {
|
|
||||||
for entity_id in entities {
|
|
||||||
if let Some(entity) = self.data_by_id.get(entity_id).and_then(|e| e.upgrade()) {
|
|
||||||
if f(&entity) {
|
|
||||||
return Some(entity);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Move an entity from its old chunk to a new chunk.
|
/// Move an entity from its old chunk to a new chunk.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn update_entity_chunk(
|
pub fn update_entity_chunk(
|
||||||
&mut self,
|
&mut self,
|
||||||
entity_id: u32,
|
entity_id: EntityId,
|
||||||
old_chunk: &ChunkPos,
|
old_chunk: &ChunkPos,
|
||||||
new_chunk: &ChunkPos,
|
new_chunk: &ChunkPos,
|
||||||
) {
|
) {
|
||||||
|
@ -409,7 +349,7 @@ impl Debug for WeakEntityStorage {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::entity::{metadata, EntityMetadata};
|
use crate::entity::metadata;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use azalea_core::Vec3;
|
use azalea_core::Vec3;
|
||||||
|
|
|
@ -1,11 +1,16 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
entity::{move_unchecked, Entity, Physics, Position},
|
entity::{move_unchecked, EcsEntityId, EntityId, Physics, Position},
|
||||||
Chunk, MoveEntityError, PartialChunkStorage, PartialEntityStorage, WeakChunkStorage,
|
Chunk, MoveEntityError, PartialChunkStorage, PartialEntityStorage, WeakChunkStorage,
|
||||||
WeakEntityStorage,
|
WeakEntityStorage,
|
||||||
};
|
};
|
||||||
use azalea_block::BlockState;
|
use azalea_block::BlockState;
|
||||||
use azalea_buf::BufReadError;
|
use azalea_buf::BufReadError;
|
||||||
use azalea_core::{BlockPos, ChunkPos, PositionDelta8, Vec3};
|
use azalea_core::{BlockPos, ChunkPos, PositionDelta8, Vec3};
|
||||||
|
use bevy_ecs::{
|
||||||
|
query::{QueryEntityError, QueryState, ReadOnlyWorldQuery, WorldQuery},
|
||||||
|
system::Query,
|
||||||
|
world::EntityMut,
|
||||||
|
};
|
||||||
use parking_lot::RwLock;
|
use parking_lot::RwLock;
|
||||||
use std::{backtrace::Backtrace, fmt::Debug};
|
use std::{backtrace::Backtrace, fmt::Debug};
|
||||||
use std::{fmt::Formatter, io::Cursor, sync::Arc};
|
use std::{fmt::Formatter, io::Cursor, sync::Arc};
|
||||||
|
@ -37,7 +42,11 @@ pub struct WeakWorld {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PartialWorld {
|
impl PartialWorld {
|
||||||
pub fn new(chunk_radius: u32, shared: Arc<WeakWorld>, owner_entity_id: Option<u32>) -> Self {
|
pub fn new(
|
||||||
|
chunk_radius: u32,
|
||||||
|
shared: Arc<WeakWorld>,
|
||||||
|
owner_entity_id: Option<EntityId>,
|
||||||
|
) -> Self {
|
||||||
PartialWorld {
|
PartialWorld {
|
||||||
shared: shared.clone(),
|
shared: shared.clone(),
|
||||||
chunk_storage: PartialChunkStorage::new(chunk_radius, shared.chunk_storage.clone()),
|
chunk_storage: PartialChunkStorage::new(chunk_radius, shared.chunk_storage.clone()),
|
||||||
|
@ -74,65 +83,61 @@ impl PartialWorld {
|
||||||
self.chunk_storage.set_block_state(pos, state)
|
self.chunk_storage.set_block_state(pos, state)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a mutable reference to the entity with the given ID.
|
/// Whether we're allowed to update the entity with the given ID.
|
||||||
pub fn entity_mut(&mut self, id: u32) -> Option<Entity<'_, &WeakWorld>> {
|
///
|
||||||
|
/// Only call this if you're actually updating the entity, otherwise it'll
|
||||||
|
/// cause the update tracker to get out of sync.
|
||||||
|
fn maybe_update_entity(&mut self, id: EntityId) -> bool {
|
||||||
// no entity for you (we're processing this entity somewhere else)
|
// no entity for you (we're processing this entity somewhere else)
|
||||||
if Some(id) != self.entity_storage.owner_entity_id && !self.entity_storage.maybe_update(id)
|
if Some(id) != self.entity_storage.owner_entity_id && !self.entity_storage.maybe_update(id)
|
||||||
{
|
{
|
||||||
return None;
|
false
|
||||||
|
} else {
|
||||||
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
self.shared.entity(id)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_entity(&mut self, id: u32, bundle: impl bevy_ecs::prelude::Bundle) {
|
/// Makes sure we can modify this EntityId, and returns it if we can.
|
||||||
|
///
|
||||||
|
/// Only call this if you're actually updating the entity, otherwise it'll
|
||||||
|
/// cause the update tracker to get out of sync.
|
||||||
|
pub fn entity_mut(&mut self, id: EntityId) -> Option<EntityId> {
|
||||||
|
if self.maybe_update_entity(id) {
|
||||||
|
Some(id)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_entity(&mut self, id: EntityId, bundle: impl bevy_ecs::prelude::Bundle) {
|
||||||
self.entity_storage.insert(id, bundle);
|
self.entity_storage.insert(id, bundle);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_entity_pos(&mut self, entity_id: u32, new_pos: Vec3) -> Result<(), MoveEntityError> {
|
pub fn set_entity_pos(
|
||||||
let mut entity = self
|
&mut self,
|
||||||
.entity_mut(entity_id)
|
entity_id: EntityId,
|
||||||
.ok_or_else(|| MoveEntityError::EntityDoesNotExist(Backtrace::capture()))?;
|
new_pos: Vec3,
|
||||||
|
) -> Result<(), MoveEntityError> {
|
||||||
let pos = entity.get_mut::<Position>().unwrap();
|
if self.maybe_update_entity(entity_id) {
|
||||||
let physics = entity.get_mut::<Physics>().unwrap();
|
self.shared.set_entity_pos(entity_id, new_pos)
|
||||||
|
} else {
|
||||||
let old_chunk = ChunkPos::from(pos.as_ref());
|
Err(MoveEntityError::EntityDoesNotExist(Backtrace::capture()))
|
||||||
let new_chunk = ChunkPos::from(&new_pos);
|
|
||||||
// this is fine because we update the chunk below
|
|
||||||
unsafe { move_unchecked(&mut pos, &mut physics, new_pos) };
|
|
||||||
|
|
||||||
if old_chunk != new_chunk {
|
|
||||||
self.entity_storage
|
|
||||||
.update_entity_chunk(entity_id, &old_chunk, &new_chunk);
|
|
||||||
}
|
}
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn move_entity_with_delta(
|
pub fn move_entity_with_delta(
|
||||||
&mut self,
|
&mut self,
|
||||||
entity_id: u32,
|
entity_id: EntityId,
|
||||||
delta: &PositionDelta8,
|
delta: &PositionDelta8,
|
||||||
) -> Result<(), MoveEntityError> {
|
) -> Result<(), MoveEntityError> {
|
||||||
let mut entity = self
|
let mut query = self.shared.query_entities::<&Position>();
|
||||||
.entity_mut(entity_id)
|
let pos = **query
|
||||||
.ok_or_else(|| MoveEntityError::EntityDoesNotExist(Backtrace::capture()))?;
|
.get(&self.shared.entity_storage.read().ecs, entity_id.into())
|
||||||
|
.map_err(|_| MoveEntityError::EntityDoesNotExist(Backtrace::capture()))?;
|
||||||
let pos = entity.get_mut::<Position>().unwrap();
|
|
||||||
let physics = entity.get_mut::<Physics>().unwrap();
|
|
||||||
|
|
||||||
let new_pos = pos.with_delta(delta);
|
let new_pos = pos.with_delta(delta);
|
||||||
|
|
||||||
let old_chunk = ChunkPos::from(pos.as_ref());
|
self.set_entity_pos(entity_id, new_pos)
|
||||||
let new_chunk = ChunkPos::from(&new_pos);
|
|
||||||
// this is fine because we update the chunk below
|
|
||||||
|
|
||||||
unsafe { move_unchecked(&mut pos, &mut physics, new_pos) };
|
|
||||||
if old_chunk != new_chunk {
|
|
||||||
self.entity_storage
|
|
||||||
.update_entity_chunk(entity_id, &old_chunk, &new_chunk);
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -155,47 +160,29 @@ impl WeakWorld {
|
||||||
self.chunk_storage.read().min_y
|
self.chunk_storage.read().min_y
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a entity with the given ID.
|
pub fn id_by_uuid(&self, uuid: &Uuid) -> Option<EntityId> {
|
||||||
///
|
self.entity_storage.read().id_by_uuid(uuid).copied()
|
||||||
/// The returned Entity can technically be mutated, but you should avoid
|
|
||||||
/// doing any relative mutations (i.e. getting the current position and then
|
|
||||||
/// setting the new position relative to that position).
|
|
||||||
pub fn entity(&self, id: u32) -> Option<Entity<&WeakWorld>> {
|
|
||||||
let entity_data = self.entity_storage.read().get_by_id(id)?;
|
|
||||||
let entity_ptr = unsafe { entity_data.as_ptr() };
|
|
||||||
Some(Entity::new(self, id, entity_ptr))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn entity_by_uuid(&self, uuid: &Uuid) -> Option<Arc<EntityData>> {
|
pub fn query_entities<Q: WorldQuery>(&self) -> QueryState<Q, ()> {
|
||||||
self.entity_storage.read().get_by_uuid(uuid)
|
self.entity_storage.write().query::<Q>()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn entity_by<F>(&self, mut f: F) -> Option<Arc<EntityData>>
|
pub fn set_entity_pos(
|
||||||
where
|
&self,
|
||||||
F: FnMut(&EntityData) -> bool,
|
entity_id: EntityId,
|
||||||
{
|
new_pos: Vec3,
|
||||||
self.entity_storage.read().get_by(|e| f(e))
|
) -> Result<(), MoveEntityError> {
|
||||||
}
|
let mut entity_storage = self.entity_storage.write();
|
||||||
|
let mut query = entity_storage.query::<(&mut Position, &mut Physics)>();
|
||||||
pub fn entities_by<F>(&self, mut f: F) -> Vec<Arc<EntityData>>
|
let (pos, physics) = query
|
||||||
where
|
.get_mut(&mut entity_storage.ecs, entity_id.into())
|
||||||
F: FnMut(&EntityData) -> bool,
|
.unwrap();
|
||||||
{
|
|
||||||
self.entity_storage.read().entities_by(|e| f(e))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_entity_pos(&self, entity_id: u32, new_pos: Vec3) -> Result<(), MoveEntityError> {
|
|
||||||
let mut entity = self
|
|
||||||
.entity(entity_id)
|
|
||||||
.ok_or_else(|| MoveEntityError::EntityDoesNotExist(Backtrace::capture()))?;
|
|
||||||
|
|
||||||
let pos = entity.get_mut::<Position>().unwrap();
|
|
||||||
let physics = entity.get_mut::<Physics>().unwrap();
|
|
||||||
|
|
||||||
let old_chunk = ChunkPos::from(pos.as_ref());
|
let old_chunk = ChunkPos::from(pos.as_ref());
|
||||||
let new_chunk = ChunkPos::from(&new_pos);
|
let new_chunk = ChunkPos::from(&new_pos);
|
||||||
// this is fine because we update the chunk below
|
// this is fine because we update the chunk below
|
||||||
unsafe { move_unchecked(&mut pos, &mut physics, new_pos) };
|
unsafe { move_unchecked(pos.into_inner(), physics.into_inner(), new_pos) };
|
||||||
if old_chunk != new_chunk {
|
if old_chunk != new_chunk {
|
||||||
self.entity_storage
|
self.entity_storage
|
||||||
.write()
|
.write()
|
||||||
|
|
|
@ -64,8 +64,12 @@ async fn handle(bot: Client, event: Event, state: State) -> anyhow::Result<()> {
|
||||||
crafting_table.close().await;
|
crafting_table.close().await;
|
||||||
|
|
||||||
bot.hold(&pickaxe);
|
bot.hold(&pickaxe);
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
if let Err(e) = bot.dig(bot.entity().feet_pos().down(1)).await {
|
if let Err(e) = bot
|
||||||
|
.dig(azalea::entity::feet_pos(bot.entity()).down(1))
|
||||||
|
.await
|
||||||
|
{
|
||||||
println!("{:?}", e);
|
println!("{:?}", e);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,15 +47,11 @@ async fn swarm_handle(
|
||||||
match event {
|
match event {
|
||||||
SwarmEvent::Tick => {
|
SwarmEvent::Tick => {
|
||||||
// choose an arbitrary player within render distance to target
|
// choose an arbitrary player within render distance to target
|
||||||
if let Some(target) = swarm
|
if let Some(target) = swarm.worlds.read().entity_by(|_: &Player| true) {
|
||||||
.worlds
|
|
||||||
.read()
|
|
||||||
.entity_by(|e| e.id == "minecraft:player")
|
|
||||||
{
|
|
||||||
for (bot, bot_state) in swarm {
|
for (bot, bot_state) in swarm {
|
||||||
bot.tick_goto_goal(pathfinder::Goals::Reach(target.bounding_box));
|
bot.tick_goto_goal(pathfinder::Goals::Reach(target.bounding_box));
|
||||||
// if target.bounding_box.distance(bot.eyes) < bot.reach_distance() {
|
// if target.bounding_box.distance(bot.eyes) < bot.reach_distance() {
|
||||||
if bot.entity().can_reach(target.bounding_box) {
|
if azalea::entities::can_reach(bot.entity(), target.bounding_box) {
|
||||||
bot.swing();
|
bot.swing();
|
||||||
}
|
}
|
||||||
if !bot.using_held_item() && bot.hunger() <= 17 {
|
if !bot.using_held_item() && bot.hunger() <= 17 {
|
||||||
|
|
|
@ -183,12 +183,11 @@ impl From<EntityDataValue> for UpdateMetadataError {
|
||||||
|
|
||||||
# impl Allay {
|
# impl Allay {
|
||||||
# pub fn update_metadata(
|
# pub fn update_metadata(
|
||||||
# ecs: bevy_ecs::world::World,
|
|
||||||
# entity: &mut bevy_ecs::world::EntityMut,
|
# entity: &mut bevy_ecs::world::EntityMut,
|
||||||
# d: EntityDataItem,
|
# d: EntityDataItem,
|
||||||
# ) -> Result<(), UpdateMetadataError> {
|
# ) -> Result<(), UpdateMetadataError> {
|
||||||
# match d.index {
|
# match d.index {
|
||||||
# 0..=15 => AbstractCreatureBundle::update_metadata(ecs, entity, d)?,
|
# 0..=15 => AbstractCreatureBundle::update_metadata(entity, d)?,
|
||||||
# 16 => entity.insert(Dancing(d.value.into_boolean()?)),
|
# 16 => entity.insert(Dancing(d.value.into_boolean()?)),
|
||||||
# 17 => entity.insert(CanDuplicate(d.value.into_boolean()?)),
|
# 17 => entity.insert(CanDuplicate(d.value.into_boolean()?)),
|
||||||
# }
|
# }
|
||||||
|
@ -197,7 +196,7 @@ impl From<EntityDataValue> for UpdateMetadataError {
|
||||||
# }
|
# }
|
||||||
code.append(f'impl {struct_name} {{')
|
code.append(f'impl {struct_name} {{')
|
||||||
code.append(
|
code.append(
|
||||||
f' pub fn update_metadata(ecs: bevy_ecs::world::World, entity: &mut bevy_ecs::world::EntityMut, d: &EntityDataItem) -> Result<(), UpdateMetadataError> {{')
|
f' pub fn update_metadata(entity: &mut bevy_ecs::world::EntityMut, d: EntityDataItem) -> Result<(), UpdateMetadataError> {{')
|
||||||
code.append(f' match d.index {{')
|
code.append(f' match d.index {{')
|
||||||
|
|
||||||
parent_last_index = -1
|
parent_last_index = -1
|
||||||
|
@ -207,7 +206,7 @@ impl From<EntityDataValue> for UpdateMetadataError {
|
||||||
parent_last_index = index
|
parent_last_index = index
|
||||||
if parent_last_index != -1:
|
if parent_last_index != -1:
|
||||||
code.append(
|
code.append(
|
||||||
f' 0..={parent_last_index} => {parent_struct_name}::update_metadata(ecs, entity, d)?,')
|
f' 0..={parent_last_index} => {parent_struct_name}::update_metadata(entity, d)?,')
|
||||||
|
|
||||||
for index, name_or_bitfield in enumerate(all_field_names_or_bitfields):
|
for index, name_or_bitfield in enumerate(all_field_names_or_bitfields):
|
||||||
if index <= parent_last_index:
|
if index <= parent_last_index:
|
||||||
|
@ -242,6 +241,7 @@ impl From<EntityDataValue> for UpdateMetadataError {
|
||||||
code.append(
|
code.append(
|
||||||
f'entity.insert({field_struct_name}(bitfield & {mask} != 0));')
|
f'entity.insert({field_struct_name}(bitfield & {mask} != 0));')
|
||||||
code.append(' },')
|
code.append(' },')
|
||||||
|
code.append(' _ => {}')
|
||||||
code.append(' }')
|
code.append(' }')
|
||||||
code.append(' Ok(())')
|
code.append(' Ok(())')
|
||||||
code.append(' }')
|
code.append(' }')
|
||||||
|
@ -390,13 +390,12 @@ impl From<EntityDataValue> for UpdateMetadataError {
|
||||||
|
|
||||||
# and now make the main update_metadatas
|
# and now make the main update_metadatas
|
||||||
# pub fn update_metadatas(
|
# pub fn update_metadatas(
|
||||||
# ecs: bevy_ecs::world::World,
|
|
||||||
# entity: bevy_ecs::world::EntityMut,
|
# entity: bevy_ecs::world::EntityMut,
|
||||||
# items: &Vec<EntityDataItem>,
|
# items: Vec<EntityDataItem>,
|
||||||
# ) -> Result<(), UpdateMetadataError> {
|
# ) -> Result<(), UpdateMetadataError> {
|
||||||
# if entity.contains::<Allay>() {
|
# if entity.contains::<Allay>() {
|
||||||
# for d in items {
|
# for d in items {
|
||||||
# Allay::update_metadata(ecs, entity, d)?;
|
# Allay::update_metadata(entity, d)?;
|
||||||
# }
|
# }
|
||||||
# return Ok(());
|
# return Ok(());
|
||||||
# }
|
# }
|
||||||
|
@ -404,7 +403,7 @@ impl From<EntityDataValue> for UpdateMetadataError {
|
||||||
# Ok(())
|
# Ok(())
|
||||||
# }
|
# }
|
||||||
code.append(
|
code.append(
|
||||||
f'pub fn update_metadatas(ecs: bevy_ecs::world::World, entity: bevy_ecs::world::EntityMut, items: &Vec<EntityDataItem>) -> Result<(), UpdateMetadataError> {{')
|
f'pub fn update_metadatas(mut entity: bevy_ecs::world::EntityMut, items: Vec<EntityDataItem>) -> Result<(), UpdateMetadataError> {{')
|
||||||
for entity_id in burger_entity_data:
|
for entity_id in burger_entity_data:
|
||||||
if entity_id.startswith('~'):
|
if entity_id.startswith('~'):
|
||||||
# not actually an entity
|
# not actually an entity
|
||||||
|
@ -414,7 +413,7 @@ impl From<EntityDataValue> for UpdateMetadataError {
|
||||||
f' if entity.contains::<{struct_name}>() {{')
|
f' if entity.contains::<{struct_name}>() {{')
|
||||||
code.append(' for d in items {')
|
code.append(' for d in items {')
|
||||||
code.append(
|
code.append(
|
||||||
f' {struct_name}::update_metadata(ecs, &mut entity, d)?;')
|
f' {struct_name}::update_metadata(&mut entity, d)?;')
|
||||||
code.append(' }')
|
code.append(' }')
|
||||||
code.append(f' return Ok(());')
|
code.append(f' return Ok(());')
|
||||||
code.append(' }')
|
code.append(' }')
|
||||||
|
|
Loading…
Add table
Reference in a new issue