mirror of
https://github.com/mat-1/azalea.git
synced 2025-08-02 14:26:04 +00:00
more ecs stuff for entity storage
This commit is contained in:
parent
1436a88ca2
commit
f6689e62fe
8 changed files with 594 additions and 503 deletions
35
Cargo.lock
generated
35
Cargo.lock
generated
|
@ -420,6 +420,7 @@ dependencies = [
|
|||
"azalea-nbt",
|
||||
"azalea-registry",
|
||||
"bevy_ecs",
|
||||
"derive_more",
|
||||
"enum-as-inner",
|
||||
"log",
|
||||
"nohash-hasher",
|
||||
|
@ -667,6 +668,12 @@ dependencies = [
|
|||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "convert_case"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e"
|
||||
|
||||
[[package]]
|
||||
name = "core-foundation"
|
||||
version = "0.9.3"
|
||||
|
@ -818,6 +825,19 @@ version = "2.3.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3ee2393c4a91429dffb4bedf19f4d6abf27d8a732c8ce4980305d782e5426d57"
|
||||
|
||||
[[package]]
|
||||
name = "derive_more"
|
||||
version = "0.99.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321"
|
||||
dependencies = [
|
||||
"convert_case",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"rustc_version",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "digest"
|
||||
version = "0.10.6"
|
||||
|
@ -1858,6 +1878,15 @@ version = "0.1.21"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342"
|
||||
|
||||
[[package]]
|
||||
name = "rustc_version"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366"
|
||||
dependencies = [
|
||||
"semver",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.11"
|
||||
|
@ -1912,6 +1941,12 @@ dependencies = [
|
|||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "1.0.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "58bc9567378fc7690d6b2addae4e60ac2eeea07becb2c64b9f218b53865cba2a"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.149"
|
||||
|
|
|
@ -270,12 +270,22 @@ impl From<&Vec3> for BlockPos {
|
|||
}
|
||||
}
|
||||
}
|
||||
impl From<Vec3> for BlockPos {
|
||||
fn from(pos: Vec3) -> Self {
|
||||
BlockPos::from(&pos)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&Vec3> for ChunkPos {
|
||||
fn from(pos: &Vec3) -> Self {
|
||||
ChunkPos::from(&BlockPos::from(pos))
|
||||
}
|
||||
}
|
||||
impl From<Vec3> for ChunkPos {
|
||||
fn from(pos: Vec3) -> Self {
|
||||
ChunkPos::from(&pos)
|
||||
}
|
||||
}
|
||||
|
||||
const PACKED_X_LENGTH: u64 = 1 + 25; // minecraft does something a bit more complicated to get this 25
|
||||
const PACKED_Z_LENGTH: u64 = PACKED_X_LENGTH;
|
||||
|
|
|
@ -16,6 +16,7 @@ azalea-core = {path = "../azalea-core", version = "^0.5.0", features = ["bevy_ec
|
|||
azalea-nbt = {path = "../azalea-nbt", version = "^0.5.0"}
|
||||
azalea-registry = {path = "../azalea-registry", version = "^0.5.0"}
|
||||
bevy_ecs = {version = "0.9.1", default-features = false}
|
||||
derive_more = {version = "0.99.17", features = ["deref", "deref_mut"]}
|
||||
enum-as-inner = "0.5.1"
|
||||
log = "0.4.17"
|
||||
nohash-hasher = "0.2.0"
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -3,47 +3,84 @@ mod data;
|
|||
mod dimensions;
|
||||
pub mod metadata;
|
||||
|
||||
use self::attributes::{AttributeInstance, AttributeModifiers};
|
||||
use self::{
|
||||
attributes::{AttributeInstance, AttributeModifiers},
|
||||
metadata::UpdateMetadataError,
|
||||
};
|
||||
use crate::WeakWorld;
|
||||
use azalea_block::BlockState;
|
||||
use azalea_core::{BlockPos, Vec3, AABB};
|
||||
use azalea_core::{BlockPos, ChunkPos, Vec3, AABB};
|
||||
use azalea_registry::EntityKind;
|
||||
use bevy_ecs::component::Component;
|
||||
use bevy_ecs::{component::Component, world::EntityMut};
|
||||
pub use data::*;
|
||||
use derive_more::{Deref, DerefMut};
|
||||
pub use dimensions::*;
|
||||
use std::{
|
||||
fmt::Debug,
|
||||
fmt::{Debug, Display, Formatter},
|
||||
marker::PhantomData,
|
||||
ops::{Deref, DerefMut},
|
||||
ptr::NonNull,
|
||||
};
|
||||
use uuid::Uuid;
|
||||
|
||||
/// Note: EntityId internally uses twice the memory as just a u32, so if a u32
|
||||
/// would work just as well then use that.
|
||||
pub type EntityId = bevy_ecs::entity::Entity;
|
||||
/// The unique 32-bit unsigned id of an entity.
|
||||
#[derive(Deref, Eq, PartialEq, DerefMut)]
|
||||
pub struct EntityId(pub u32);
|
||||
impl From<EntityId> for bevy_ecs::entity::Entity {
|
||||
// bevy_ecs `Entity`s also store the "generation" which adds another 32 bits,
|
||||
// but we don't care about the generation
|
||||
fn from(id: EntityId) -> Self {
|
||||
Self::from_raw(*id)
|
||||
}
|
||||
}
|
||||
impl Debug for EntityId {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "EntityId({})", **self)
|
||||
}
|
||||
}
|
||||
impl Display for EntityId {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", **self)
|
||||
}
|
||||
}
|
||||
impl std::hash::Hash for EntityId {
|
||||
fn hash<H: std::hash::Hasher>(&self, hasher: &mut H) {
|
||||
hasher.write_u32(self.0)
|
||||
}
|
||||
}
|
||||
impl nohash_hasher::IsEnabled for EntityId {}
|
||||
|
||||
/// A mutable reference to an entity in a world.
|
||||
pub struct Entity<'w, W = &'w WeakWorld> {
|
||||
/// The world this entity is in.
|
||||
/// The [`WeakWorld`] this entity is in.
|
||||
pub world: W,
|
||||
/// The container for the incrementing numerical id of the entity.
|
||||
pub id: EntityId,
|
||||
/// The ECS data for the entity.
|
||||
pub data: bevy_ecs::world::EntityMut<'w>,
|
||||
}
|
||||
|
||||
/// Create an entity if you only have a [`bevy_ecs::world::World`].
|
||||
///
|
||||
/// If you do have access to a [`PartialEntityStorage`] though then just call
|
||||
/// [`PartialEntityStorage::insert`].
|
||||
pub(crate) fn new_entity<'w>(
|
||||
ecs: &mut bevy_ecs::world::World,
|
||||
id: EntityId,
|
||||
bundle: impl bevy_ecs::bundle::Bundle,
|
||||
) -> EntityMut<'w> {
|
||||
// bevy_ecs only returns None if the entity only exists with a different
|
||||
// generation, which shouldn't be possible here
|
||||
ecs.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> {
|
||||
/// Create an Entity when we already know its id and data.
|
||||
pub fn new(world: D, id: u32, bundle: impl bevy_ecs::bundle::Bundle) -> Self {
|
||||
let id = EntityId::from_raw(id);
|
||||
pub(crate) fn new(world: D, id: u32, bundle: impl bevy_ecs::bundle::Bundle) -> Self {
|
||||
let ecs = world.entity_storage.write().ecs;
|
||||
|
||||
// bevy_ecs only returns None if the entity only exists with a different
|
||||
// generation, which shouldn't be possible here
|
||||
let mut data =
|
||||
world.entity_storage.write().ecs.get_or_spawn(id).expect(
|
||||
"Entities should always be generation 0 if we're manually spawning from ids",
|
||||
);
|
||||
let id = EntityId(id);
|
||||
let data = new_entity(&mut ecs, id, bundle);
|
||||
Self { world, id, data }
|
||||
}
|
||||
}
|
||||
|
@ -61,70 +98,70 @@ impl<'d, D: Deref<Target = WeakWorld>> Entity<'d, D> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'w, W: Deref<Target = WeakWorld>> Entity<'w, W> {
|
||||
/// Sets the position of the entity. This doesn't update the cache in
|
||||
/// azalea-world, and should only be used within azalea-world!
|
||||
///
|
||||
/// # Safety
|
||||
/// Cached position in the world must be updated.
|
||||
pub unsafe fn move_unchecked(&mut self, new_pos: Vec3) {
|
||||
self.pos = new_pos;
|
||||
let bounding_box = self.make_bounding_box();
|
||||
self.bounding_box = bounding_box;
|
||||
}
|
||||
/// Sets the position of the entity. This doesn't update the cache in
|
||||
/// azalea-world, and should only be used within azalea-world!
|
||||
///
|
||||
/// # Safety
|
||||
/// Cached position in the world must be updated.
|
||||
pub unsafe fn move_unchecked(pos: &mut Position, physics: &mut Physics, new_pos: Vec3) {
|
||||
*pos = Position(new_pos);
|
||||
let bounding_box = make_bounding_box(pos, physics);
|
||||
physics.bounding_box = bounding_box;
|
||||
}
|
||||
|
||||
pub fn set_rotation(&mut self, y_rot: f32, x_rot: f32) {
|
||||
self.y_rot = y_rot % 360.0;
|
||||
self.x_rot = x_rot.clamp(-90.0, 90.0) % 360.0;
|
||||
// TODO: minecraft also sets yRotO and xRotO to xRot and yRot ... but
|
||||
// idk what they're used for so
|
||||
}
|
||||
pub fn set_rotation(physics: &mut Physics, y_rot: f32, x_rot: f32) {
|
||||
physics.y_rot = y_rot % 360.0;
|
||||
physics.x_rot = x_rot.clamp(-90.0, 90.0) % 360.0;
|
||||
// TODO: minecraft also sets yRotO and xRotO to xRot and yRot ... but
|
||||
// idk what they're used for so
|
||||
}
|
||||
|
||||
pub fn move_relative(&mut self, speed: f32, acceleration: &Vec3) {
|
||||
let input_vector = self.input_vector(speed, acceleration);
|
||||
self.delta += input_vector;
|
||||
}
|
||||
pub fn move_relative(physics: &mut Physics, speed: f32, acceleration: &Vec3) {
|
||||
let input_vector = input_vector(physics, speed, acceleration);
|
||||
physics.delta += input_vector;
|
||||
}
|
||||
|
||||
pub fn input_vector(&self, speed: f32, acceleration: &Vec3) -> Vec3 {
|
||||
let distance = acceleration.length_squared();
|
||||
if distance < 1.0E-7 {
|
||||
return Vec3::default();
|
||||
}
|
||||
let acceleration = if distance > 1.0 {
|
||||
acceleration.normalize()
|
||||
} else {
|
||||
*acceleration
|
||||
}
|
||||
.scale(speed as f64);
|
||||
let y_rot = f32::sin(self.y_rot * 0.017453292f32);
|
||||
let x_rot = f32::cos(self.y_rot * 0.017453292f32);
|
||||
Vec3 {
|
||||
x: acceleration.x * (x_rot as f64) - acceleration.z * (y_rot as f64),
|
||||
y: acceleration.y,
|
||||
z: acceleration.z * (x_rot as f64) + acceleration.x * (y_rot as f64),
|
||||
}
|
||||
pub fn input_vector(physics: &mut Physics, speed: f32, acceleration: &Vec3) -> Vec3 {
|
||||
let distance = acceleration.length_squared();
|
||||
if distance < 1.0E-7 {
|
||||
return Vec3::default();
|
||||
}
|
||||
|
||||
/// Apply the given metadata items to the entity. Everything that isn't
|
||||
/// included in items will be left unchanged. If an error occured, None
|
||||
/// will be returned.
|
||||
///
|
||||
/// TODO: this should be changed to have a proper error.
|
||||
pub fn apply_metadata(&mut self, items: &Vec<EntityDataItem>) -> Option<()> {
|
||||
// for item in items {
|
||||
// self.metadata.set_index(item.index, item.value.clone())?;
|
||||
// }
|
||||
Some(())
|
||||
let acceleration = if distance > 1.0 {
|
||||
acceleration.normalize()
|
||||
} else {
|
||||
*acceleration
|
||||
}
|
||||
.scale(speed as f64);
|
||||
let y_rot = f32::sin(physics.y_rot * 0.017453292f32);
|
||||
let x_rot = f32::cos(physics.y_rot * 0.017453292f32);
|
||||
Vec3 {
|
||||
x: acceleration.x * (x_rot as f64) - acceleration.z * (y_rot as f64),
|
||||
y: acceleration.y,
|
||||
z: acceleration.z * (x_rot as f64) + acceleration.x * (y_rot as f64),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn make_bounding_box(physics: &EntityPhysics) -> AABB {
|
||||
physics.dimensions.make_bounding_box(&physics.pos)
|
||||
/// Apply the given metadata items to the entity. Everything that isn't
|
||||
/// included in items will be left unchanged.
|
||||
pub fn update_metadatas(
|
||||
ecs: bevy_ecs::world::World,
|
||||
entity: bevy_ecs::world::EntityMut,
|
||||
items: &Vec<EntityDataItem>,
|
||||
) -> Result<(), UpdateMetadataError> {
|
||||
metadata::update_metadatas(ecs, entity, items)
|
||||
}
|
||||
|
||||
pub fn make_bounding_box(pos: &Position, physics: &Physics) -> AABB {
|
||||
physics.dimensions.make_bounding_box(&pos)
|
||||
}
|
||||
|
||||
/// Get the position of the block below the entity, but a little lower.
|
||||
pub fn on_pos_legacy<W: Deref<Target = WeakWorld>>(world: &W, physics: &EntityPhysics) -> BlockPos {
|
||||
on_pos(world, physics, 0.2)
|
||||
pub fn on_pos_legacy<W: Deref<Target = WeakWorld>>(
|
||||
world: &W,
|
||||
pos: &Position,
|
||||
physics: &Physics,
|
||||
) -> BlockPos {
|
||||
on_pos(world, pos, physics, 0.2)
|
||||
}
|
||||
|
||||
// int x = Mth.floor(this.position.x);
|
||||
|
@ -141,12 +178,13 @@ pub fn on_pos_legacy<W: Deref<Target = WeakWorld>>(world: &W, physics: &EntityPh
|
|||
// return var5;
|
||||
pub fn on_pos<W: Deref<Target = WeakWorld>>(
|
||||
world: &W,
|
||||
physics: &EntityPhysics,
|
||||
pos: &Position,
|
||||
physics: &Physics,
|
||||
offset: f32,
|
||||
) -> BlockPos {
|
||||
let x = physics.pos.x.floor() as i32;
|
||||
let y = (physics.pos.y - offset as f64).floor() as i32;
|
||||
let z = physics.pos.z.floor() as i32;
|
||||
let x = pos.x.floor() as i32;
|
||||
let y = (pos.y - offset as f64).floor() as i32;
|
||||
let z = pos.z.floor() as i32;
|
||||
let pos = BlockPos { x, y, z };
|
||||
|
||||
// TODO: check if block below is a fence, wall, or fence gate
|
||||
|
@ -168,18 +206,24 @@ pub fn on_pos<W: Deref<Target = WeakWorld>>(
|
|||
pos
|
||||
}
|
||||
|
||||
#[derive(Component)]
|
||||
pub struct EntityUuid(pub Uuid);
|
||||
#[derive(Component, Deref, DerefMut)]
|
||||
pub struct EntityUuid(Uuid);
|
||||
|
||||
/// The position of the entity right now.
|
||||
/// This can be changed with unsafe_move, but the correct way is with
|
||||
/// world.move_entity
|
||||
#[derive(Component, Clone, Copy, Debug, Default, PartialEq, Deref, DerefMut)]
|
||||
pub struct Position(Vec3);
|
||||
impl From<&Position> for ChunkPos {
|
||||
fn from(value: &Position) -> Self {
|
||||
ChunkPos::from(&value.0)
|
||||
}
|
||||
}
|
||||
|
||||
/// The physics data relating to the entity, such as position, velocity, and
|
||||
/// bounding box.
|
||||
#[derive(Debug, Component)]
|
||||
pub struct EntityPhysics {
|
||||
/// The position of the entity right now.
|
||||
/// This can be changde with unsafe_move, but the correct way is with
|
||||
/// world.move_entity
|
||||
pub pos: Vec3,
|
||||
|
||||
pub struct Physics {
|
||||
/// The position of the entity last tick.
|
||||
pub last_pos: Vec3,
|
||||
pub delta: Vec3,
|
||||
|
@ -213,79 +257,79 @@ pub struct EntityPhysics {
|
|||
pub has_impulse: bool,
|
||||
}
|
||||
|
||||
impl EntityData {
|
||||
pub fn new(uuid: Uuid, pos: Vec3, metadata: EntityMetadata) -> Self {
|
||||
let dimensions = EntityDimensions {
|
||||
width: 0.6,
|
||||
height: 1.8,
|
||||
};
|
||||
// impl EntityData {
|
||||
// pub fn new(uuid: Uuid, pos: Vec3, metadata: EntityMetadata) -> Self {
|
||||
// let dimensions = EntityDimensions {
|
||||
// width: 0.6,
|
||||
// height: 1.8,
|
||||
// };
|
||||
|
||||
Self {
|
||||
uuid,
|
||||
pos,
|
||||
last_pos: pos,
|
||||
delta: Vec3::default(),
|
||||
// Self {
|
||||
// uuid,
|
||||
// pos,
|
||||
// last_pos: pos,
|
||||
// delta: Vec3::default(),
|
||||
|
||||
xxa: 0.,
|
||||
yya: 0.,
|
||||
zza: 0.,
|
||||
// xxa: 0.,
|
||||
// yya: 0.,
|
||||
// zza: 0.,
|
||||
|
||||
x_rot: 0.,
|
||||
y_rot: 0.,
|
||||
// x_rot: 0.,
|
||||
// y_rot: 0.,
|
||||
|
||||
y_rot_last: 0.,
|
||||
x_rot_last: 0.,
|
||||
// y_rot_last: 0.,
|
||||
// x_rot_last: 0.,
|
||||
|
||||
on_ground: false,
|
||||
last_on_ground: false,
|
||||
// on_ground: false,
|
||||
// last_on_ground: false,
|
||||
|
||||
// TODO: have this be based on the entity type
|
||||
bounding_box: dimensions.make_bounding_box(&pos),
|
||||
dimensions,
|
||||
// // TODO: have this be based on the entity type
|
||||
// bounding_box: dimensions.make_bounding_box(&pos),
|
||||
// dimensions,
|
||||
|
||||
has_impulse: false,
|
||||
// has_impulse: false,
|
||||
|
||||
jumping: false,
|
||||
// jumping: false,
|
||||
|
||||
metadata,
|
||||
// metadata,
|
||||
|
||||
attributes: AttributeModifiers {
|
||||
// TODO: do the correct defaults for everything, some entities have different
|
||||
// defaults
|
||||
speed: AttributeInstance::new(0.1),
|
||||
},
|
||||
}
|
||||
}
|
||||
// attributes: AttributeModifiers {
|
||||
// // TODO: do the correct defaults for everything, some
|
||||
// entities have different // defaults
|
||||
// speed: AttributeInstance::new(0.1),
|
||||
// },
|
||||
// }
|
||||
// }
|
||||
|
||||
/// Get the position of the entity in the world.
|
||||
#[inline]
|
||||
pub fn pos(&self) -> &Vec3 {
|
||||
&self.pos
|
||||
}
|
||||
// /// Get the position of the entity in the world.
|
||||
// #[inline]
|
||||
// pub fn pos(&self) -> &Vec3 {
|
||||
// &self.pos
|
||||
// }
|
||||
|
||||
/// Convert this &self into a (mutable) pointer.
|
||||
///
|
||||
/// # Safety
|
||||
/// The entity MUST exist for at least as long as this pointer exists.
|
||||
pub unsafe fn as_ptr(&self) -> NonNull<EntityData> {
|
||||
// this is cursed
|
||||
NonNull::new_unchecked(self as *const EntityData as *mut EntityData)
|
||||
}
|
||||
// /// Convert this &self into a (mutable) pointer.
|
||||
// ///
|
||||
// /// # Safety
|
||||
// /// The entity MUST exist for at least as long as this pointer exists.
|
||||
// pub unsafe fn as_ptr(&self) -> NonNull<EntityData> {
|
||||
// // this is cursed
|
||||
// NonNull::new_unchecked(self as *const EntityData as *mut EntityData)
|
||||
// }
|
||||
|
||||
/// Returns the type of entity this is.
|
||||
///
|
||||
/// ```rust
|
||||
/// let entity = EntityData::new(
|
||||
/// Uuid::nil(),
|
||||
/// Vec3::default(),
|
||||
/// EntityMetadata::Player(metadata::Player::default()),
|
||||
/// );
|
||||
/// assert_eq!(entity.kind(), EntityKind::Player);
|
||||
/// ```
|
||||
pub fn kind(&self) -> EntityKind {
|
||||
EntityKind::from(&self.metadata)
|
||||
}
|
||||
}
|
||||
// /// Returns the type of entity this is.
|
||||
// ///
|
||||
// /// ```rust
|
||||
// /// let entity = EntityData::new(
|
||||
// /// Uuid::nil(),
|
||||
// /// Vec3::default(),
|
||||
// /// EntityMetadata::Player(metadata::Player::default()),
|
||||
// /// );
|
||||
// /// assert_eq!(entity.kind(), EntityKind::Player);
|
||||
// /// ```
|
||||
// pub fn kind(&self) -> EntityKind {
|
||||
// EntityKind::from(&self.metadata)
|
||||
// }
|
||||
// }
|
||||
|
||||
impl<W: Deref<Target = WeakWorld>> Debug for Entity<'_, W> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
use crate::entity::{EntityData, EntityId};
|
||||
use crate::entity::{new_entity, Entity, EntityId, EntityUuid, Position};
|
||||
use azalea_core::ChunkPos;
|
||||
use bevy_ecs::world::{EntityMut, EntityRef};
|
||||
use log::warn;
|
||||
use nohash_hasher::{IntMap, IntSet};
|
||||
use parking_lot::RwLock;
|
||||
|
@ -42,15 +43,15 @@ pub struct PartialEntityStorage {
|
|||
/// it doesn't get modified from outside sources.
|
||||
///
|
||||
/// [`PartialWorld::entity_mut`]: crate::PartialWorld::entity_mut
|
||||
pub owner_entity_id: Option<u32>,
|
||||
pub owner_entity_id: Option<EntityId>,
|
||||
/// A counter for each entity that tracks how many updates we've observed
|
||||
/// for it.
|
||||
///
|
||||
/// This is used for shared worlds (i.e. swarms), to make sure we don't
|
||||
/// update entities twice on accident.
|
||||
pub updates_received: IntMap<u32, u32>,
|
||||
pub updates_received: IntMap<EntityId, u32>,
|
||||
/// A set of all the entity ids in render distance.
|
||||
loaded_entity_ids: IntSet<u32>,
|
||||
loaded_entity_ids: IntSet<EntityId>,
|
||||
}
|
||||
|
||||
/// Weakly store entities in a world. If the entities aren't being referenced
|
||||
|
@ -66,14 +67,14 @@ pub struct WeakEntityStorage {
|
|||
/// An index of all the entity ids we know are in a chunk
|
||||
ids_by_chunk: HashMap<ChunkPos, IntSet<EntityId>>,
|
||||
/// An index of entity ids by their UUIDs
|
||||
id_by_uuid: HashMap<Uuid, u32>,
|
||||
id_by_uuid: HashMap<Uuid, EntityId>,
|
||||
|
||||
/// The canonical number of updates we've gotten for every entity.
|
||||
pub updates_received: IntMap<u32, u32>,
|
||||
pub updates_received: IntMap<EntityId, u32>,
|
||||
}
|
||||
|
||||
impl PartialEntityStorage {
|
||||
pub fn new(shared: Arc<RwLock<WeakEntityStorage>>, owner_entity_id: Option<u32>) -> Self {
|
||||
pub fn new(shared: Arc<RwLock<WeakEntityStorage>>, owner_entity_id: Option<EntityId>) -> Self {
|
||||
if let Some(owner_entity_id) = owner_entity_id {
|
||||
shared.write().updates_received.insert(owner_entity_id, 0);
|
||||
}
|
||||
|
@ -94,23 +95,25 @@ impl PartialEntityStorage {
|
|||
|
||||
// if the entity is already in the shared world, we don't need to do
|
||||
// anything
|
||||
if self.shared.read().contains_id(&id) {
|
||||
let id = EntityId(id);
|
||||
|
||||
if self.shared.read().contains_id(id) {
|
||||
return;
|
||||
}
|
||||
|
||||
let entity = new_entity(&mut self.shared.read().ecs, id, bundle);
|
||||
|
||||
// add the entity to the indexes
|
||||
let mut shared = self.shared.write();
|
||||
shared
|
||||
.ids_by_chunk
|
||||
.entry(ChunkPos::from(entity.pos()))
|
||||
.entry(ChunkPos::from(entity.get::<Position>().unwrap()))
|
||||
.or_default()
|
||||
.insert(id);
|
||||
shared.id_by_uuid.insert(entity.uuid, id);
|
||||
|
||||
// now store the actual entity data
|
||||
let entity = Arc::new(entity);
|
||||
shared.data_by_id.insert(id, Arc::downgrade(&entity));
|
||||
self.data_by_id.insert(id, entity);
|
||||
shared
|
||||
.id_by_uuid
|
||||
.insert(**entity.get::<EntityUuid>().unwrap(), id);
|
||||
self.loaded_entity_ids.insert(id);
|
||||
// set our updates_received to the shared updates_received, unless it's
|
||||
// not there in which case set both to 1
|
||||
if let Some(&shared_updates_received) = shared.updates_received.get(&id) {
|
||||
|
@ -127,13 +130,16 @@ impl PartialEntityStorage {
|
|||
/// Remove an entity from this storage by its id. It will only be removed
|
||||
/// from the shared storage if there are no other references to it.
|
||||
#[inline]
|
||||
pub fn remove_by_id(&mut self, id: u32) {
|
||||
if let Some(entity) = self.data_by_id.remove(&id) {
|
||||
let chunk = ChunkPos::from(entity.pos());
|
||||
let uuid = entity.uuid;
|
||||
pub fn remove_by_id(&mut self, id: EntityId) {
|
||||
if self.loaded_entity_ids.remove(&id) {
|
||||
let entity = self.shared.read().get_by_id(id).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 uuid = **entity.get::<EntityUuid>().unwrap();
|
||||
self.updates_received.remove(&id);
|
||||
drop(entity);
|
||||
// maybe remove it from the storage
|
||||
self.shared.write().remove_entity_if_unused(id, uuid, chunk);
|
||||
} else {
|
||||
warn!("Tried to remove entity with id {id} but it was not found.")
|
||||
|
@ -144,22 +150,19 @@ impl PartialEntityStorage {
|
|||
/// If you want to check whether the entity is in the shared storage, use
|
||||
/// [`WeakEntityStorage::contains_id`].
|
||||
#[inline]
|
||||
pub fn limited_contains_id(&self, id: &u32) -> bool {
|
||||
self.data_by_id.contains_key(id)
|
||||
pub fn limited_contains_id(&self, id: &EntityId) -> bool {
|
||||
self.loaded_entity_ids.contains(id)
|
||||
}
|
||||
|
||||
/// Get a reference to an entity by its id, if it's being loaded by this
|
||||
/// storage.
|
||||
#[inline]
|
||||
pub fn limited_get_by_id(&self, id: u32) -> Option<&Arc<EntityData>> {
|
||||
self.data_by_id.get(&id)
|
||||
}
|
||||
|
||||
/// Get a mutable reference to an entity by its id, if it's being loaded by
|
||||
/// this storage.
|
||||
#[inline]
|
||||
pub fn limited_get_mut_by_id(&mut self, id: u32) -> Option<&mut Arc<EntityData>> {
|
||||
self.data_by_id.get_mut(&id)
|
||||
pub fn limited_get_by_id(&self, id: EntityId) -> Option<EntityMut> {
|
||||
if self.limited_contains_id(&id) {
|
||||
self.shared.read().get_by_id(id)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns whether we're allowed to update this entity (to prevent two
|
||||
|
@ -167,7 +170,7 @@ impl PartialEntityStorage {
|
|||
/// we WILL update it if it's true. Don't call this unless you actually
|
||||
/// got an entity update that all other clients within render distance
|
||||
/// will get too.
|
||||
pub fn maybe_update(&mut self, id: u32) -> bool {
|
||||
pub fn maybe_update(&mut self, id: EntityId) -> bool {
|
||||
let this_client_updates_received = self.updates_received.get(&id).copied();
|
||||
let shared_updates_received = self.shared.read().updates_received.get(&id).copied();
|
||||
|
||||
|
@ -188,42 +191,25 @@ impl PartialEntityStorage {
|
|||
/// Get a reference to an entity by its UUID, if it's being loaded by this
|
||||
/// storage.
|
||||
#[inline]
|
||||
pub fn limited_get_by_uuid(&self, uuid: &Uuid) -> Option<&Arc<EntityData>> {
|
||||
self.shared
|
||||
.read()
|
||||
.id_by_uuid
|
||||
.get(uuid)
|
||||
.and_then(|id| self.data_by_id.get(id))
|
||||
}
|
||||
|
||||
/// Get a mutable reference to an entity by its UUID, if it's being loaded
|
||||
/// by this storage.
|
||||
#[inline]
|
||||
pub fn limited_get_mut_by_uuid(&mut self, uuid: &Uuid) -> Option<&mut Arc<EntityData>> {
|
||||
self.shared
|
||||
.read()
|
||||
.id_by_uuid
|
||||
.get(uuid)
|
||||
.and_then(|id| self.data_by_id.get_mut(id))
|
||||
pub fn limited_get_by_uuid(&self, uuid: &Uuid) -> Option<EntityMut> {
|
||||
let entity_id = self.shared.read().id_by_uuid.get(uuid)?;
|
||||
self.limited_get_by_id(*entity_id)
|
||||
}
|
||||
|
||||
/// Clear all entities in a chunk. This will not clear them from the
|
||||
/// shared storage, unless there are no other references to them.
|
||||
pub fn clear_chunk(&mut self, chunk: &ChunkPos) {
|
||||
if let Some(entities) = self.shared.read().ids_by_chunk.get(chunk) {
|
||||
let shared = self.shared.write();
|
||||
if let Some(entities) = shared.ids_by_chunk.get(chunk) {
|
||||
for id in entities.iter() {
|
||||
if let Some(entity) = self.data_by_id.remove(id) {
|
||||
let uuid = entity.uuid;
|
||||
if self.loaded_entity_ids.remove(id) {
|
||||
let mut entity = shared.get_by_id(*id).unwrap();
|
||||
let uuid = **entity.get::<EntityUuid>().unwrap();
|
||||
drop(entity);
|
||||
// maybe remove it from the storage
|
||||
self.shared
|
||||
.write()
|
||||
.remove_entity_if_unused(*id, uuid, *chunk);
|
||||
shared.remove_entity_if_unused(*id, uuid, *chunk);
|
||||
}
|
||||
}
|
||||
// for entity_id in entities {
|
||||
// self.remove_by_id(entity_id);
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -244,38 +230,46 @@ impl PartialEntityStorage {
|
|||
impl WeakEntityStorage {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
data_by_id: IntMap::default(),
|
||||
ecs: bevy_ecs::world::World::new(),
|
||||
entity_reference_count: IntMap::default(),
|
||||
ids_by_chunk: HashMap::default(),
|
||||
id_by_uuid: HashMap::default(),
|
||||
updates_received: IntMap::default(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Remove an entity from the storage if it has no strong references left.
|
||||
/// Call this if a [`PartialEntityStorage`] just removed an entity.
|
||||
///
|
||||
/// It'll
|
||||
/// decrease the reference count and remove the entity from the storage if
|
||||
/// there's no more references to it.
|
||||
///
|
||||
/// Returns whether the entity was removed.
|
||||
pub fn remove_entity_if_unused(&mut self, id: u32, uuid: Uuid, chunk: ChunkPos) -> bool {
|
||||
if self.data_by_id.get(&id).and_then(|e| e.upgrade()).is_some() {
|
||||
// if we could get the entity, that means there are still strong
|
||||
// references to it
|
||||
false
|
||||
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) {
|
||||
count -= 1;
|
||||
if count == 0 {
|
||||
self.entity_reference_count.remove(&id);
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
if self.ids_by_chunk.remove(&chunk).is_none() {
|
||||
warn!("Tried to remove entity with id {id} from chunk {chunk:?} but it was not found.");
|
||||
}
|
||||
if self.id_by_uuid.remove(&uuid).is_none() {
|
||||
warn!(
|
||||
"Tried to remove entity with id {id} from uuid {uuid:?} but it was not found."
|
||||
);
|
||||
}
|
||||
if self.updates_received.remove(&id).is_none() {
|
||||
// if this happens it means we weren't tracking the updates_received for the
|
||||
// client (bad)
|
||||
warn!(
|
||||
"Tried to remove entity with id {id} from updates_received but it was not found."
|
||||
);
|
||||
}
|
||||
true
|
||||
warn!("Tried to remove entity with id {id} but it was not found.");
|
||||
return false;
|
||||
}
|
||||
if self.ids_by_chunk.remove(&chunk).is_none() {
|
||||
warn!("Tried to remove entity with id {id} from chunk {chunk:?} but it was not found.");
|
||||
}
|
||||
if self.id_by_uuid.remove(&uuid).is_none() {
|
||||
warn!("Tried to remove entity with id {id} from uuid {uuid:?} but it was not found.");
|
||||
}
|
||||
if self.updates_received.remove(&id).is_none() {
|
||||
// if this happens it means we weren't tracking the updates_received for the
|
||||
// client (bad)
|
||||
warn!(
|
||||
"Tried to remove entity with id {id} from updates_received but it was not found."
|
||||
);
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
/// Remove a chunk from the storage if the entities in it have no strong
|
||||
|
@ -320,14 +314,14 @@ impl WeakEntityStorage {
|
|||
|
||||
/// Whether the entity with the given id is in the shared storage.
|
||||
#[inline]
|
||||
pub fn contains_id(&self, id: &u32) -> bool {
|
||||
self.data_by_id.contains_key(id)
|
||||
pub fn contains_id(&self, id: EntityId) -> bool {
|
||||
self.ecs.get_entity(*id).is_some()
|
||||
}
|
||||
|
||||
/// Get an entity by its id, if it exists.
|
||||
#[inline]
|
||||
pub fn get_by_id(&self, id: u32) -> Option<Arc<EntityData>> {
|
||||
self.data_by_id.get(&id).and_then(|e| e.upgrade())
|
||||
pub fn get_by_id(&self, id: EntityId) -> Option<EntityMut> {
|
||||
self.ecs.get_entity_mut(*id)
|
||||
}
|
||||
|
||||
/// Get an entity in the shared storage by its UUID, if it exists.
|
||||
|
@ -338,7 +332,7 @@ impl WeakEntityStorage {
|
|||
.and_then(|id| self.data_by_id.get(id).and_then(|e| e.upgrade()))
|
||||
}
|
||||
|
||||
pub fn entity_by<F>(&self, mut f: F) -> Option<Arc<EntityData>>
|
||||
pub fn get_by<F>(&self, mut f: F) -> Option<Arc<EntityData>>
|
||||
where
|
||||
F: FnMut(&Arc<EntityData>) -> bool,
|
||||
{
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use crate::{
|
||||
entity::{Entity, EntityData},
|
||||
entity::{move_unchecked, Entity, Physics, Position},
|
||||
Chunk, MoveEntityError, PartialChunkStorage, PartialEntityStorage, WeakChunkStorage,
|
||||
WeakEntityStorage,
|
||||
};
|
||||
|
@ -93,10 +93,15 @@ impl PartialWorld {
|
|||
let mut entity = self
|
||||
.entity_mut(entity_id)
|
||||
.ok_or_else(|| MoveEntityError::EntityDoesNotExist(Backtrace::capture()))?;
|
||||
let old_chunk = ChunkPos::from(entity.pos());
|
||||
|
||||
let pos = entity.get_mut::<Position>().unwrap();
|
||||
let physics = entity.get_mut::<Physics>().unwrap();
|
||||
|
||||
let old_chunk = ChunkPos::from(pos.as_ref());
|
||||
let new_chunk = ChunkPos::from(&new_pos);
|
||||
// this is fine because we update the chunk below
|
||||
unsafe { entity.move_unchecked(new_pos) };
|
||||
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);
|
||||
|
@ -112,13 +117,17 @@ impl PartialWorld {
|
|||
let mut entity = self
|
||||
.entity_mut(entity_id)
|
||||
.ok_or_else(|| MoveEntityError::EntityDoesNotExist(Backtrace::capture()))?;
|
||||
let new_pos = entity.pos().with_delta(delta);
|
||||
|
||||
let old_chunk = ChunkPos::from(entity.pos());
|
||||
let pos = entity.get_mut::<Position>().unwrap();
|
||||
let physics = entity.get_mut::<Physics>().unwrap();
|
||||
|
||||
let new_pos = pos.with_delta(delta);
|
||||
|
||||
let old_chunk = ChunkPos::from(pos.as_ref());
|
||||
let new_chunk = ChunkPos::from(&new_pos);
|
||||
// this is fine because we update the chunk below
|
||||
|
||||
unsafe { entity.move_unchecked(new_pos) };
|
||||
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);
|
||||
|
@ -146,14 +155,11 @@ impl WeakWorld {
|
|||
self.chunk_storage.read().min_y
|
||||
}
|
||||
|
||||
pub fn entity_data_by_id(&self, id: u32) -> Option<Arc<EntityData>> {
|
||||
self.entity_storage.read().get_by_id(id)
|
||||
}
|
||||
|
||||
/// Returns a entity with the given ID.
|
||||
///
|
||||
/// The returned Entity can technically be mutated, but you should avoid
|
||||
/// doing any relative mutations.
|
||||
/// 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() };
|
||||
|
@ -168,7 +174,7 @@ impl WeakWorld {
|
|||
where
|
||||
F: FnMut(&EntityData) -> bool,
|
||||
{
|
||||
self.entity_storage.read().entity_by(|e| f(e))
|
||||
self.entity_storage.read().get_by(|e| f(e))
|
||||
}
|
||||
|
||||
pub fn entities_by<F>(&self, mut f: F) -> Vec<Arc<EntityData>>
|
||||
|
@ -182,10 +188,14 @@ impl WeakWorld {
|
|||
let mut entity = self
|
||||
.entity(entity_id)
|
||||
.ok_or_else(|| MoveEntityError::EntityDoesNotExist(Backtrace::capture()))?;
|
||||
let old_chunk = ChunkPos::from(entity.pos());
|
||||
|
||||
let pos = entity.get_mut::<Position>().unwrap();
|
||||
let physics = entity.get_mut::<Physics>().unwrap();
|
||||
|
||||
let old_chunk = ChunkPos::from(pos.as_ref());
|
||||
let new_chunk = ChunkPos::from(&new_pos);
|
||||
// this is fine because we update the chunk below
|
||||
unsafe { entity.move_unchecked(new_pos) };
|
||||
unsafe { move_unchecked(&mut pos, &mut physics, new_pos) };
|
||||
if old_chunk != new_chunk {
|
||||
self.entity_storage
|
||||
.write()
|
||||
|
|
|
@ -42,7 +42,7 @@ def generate_entity_metadata(burger_entity_data: dict, mappings: Mappings):
|
|||
|
||||
#![allow(clippy::clone_on_copy, clippy::derivable_impls)]
|
||||
use super::{
|
||||
EntityDataValue, EntityMetadataItems, OptionalUnsignedInt, Pose, Rotations, VillagerData, EntityDataItem,
|
||||
EntityDataValue, OptionalUnsignedInt, Pose, Rotations, VillagerData, EntityDataItem,
|
||||
};
|
||||
use azalea_block::BlockState;
|
||||
use azalea_chat::FormattedText;
|
||||
|
@ -197,7 +197,7 @@ impl From<EntityDataValue> for UpdateMetadataError {
|
|||
# }
|
||||
code.append(f'impl {struct_name} {{')
|
||||
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(ecs: bevy_ecs::world::World, entity: &mut bevy_ecs::world::EntityMut, d: &EntityDataItem) -> Result<(), UpdateMetadataError> {{')
|
||||
code.append(f' match d.index {{')
|
||||
|
||||
parent_last_index = -1
|
||||
|
@ -389,13 +389,13 @@ impl From<EntityDataValue> for UpdateMetadataError {
|
|||
new_entity(entity_id)
|
||||
|
||||
# and now make the main update_metadatas
|
||||
# fn update_metadatas(
|
||||
# pub fn update_metadatas(
|
||||
# ecs: bevy_ecs::world::World,
|
||||
# entity: bevy_ecs::world::EntityMut,
|
||||
# data: EntityMetadataItems,
|
||||
# items: &Vec<EntityDataItem>,
|
||||
# ) -> Result<(), UpdateMetadataError> {
|
||||
# if entity.contains::<Allay>() {
|
||||
# for d in data.0 {
|
||||
# for d in items {
|
||||
# Allay::update_metadata(ecs, entity, d)?;
|
||||
# }
|
||||
# return Ok(());
|
||||
|
@ -404,7 +404,7 @@ impl From<EntityDataValue> for UpdateMetadataError {
|
|||
# Ok(())
|
||||
# }
|
||||
code.append(
|
||||
f'fn update_metadatas(ecs: bevy_ecs::world::World, entity: bevy_ecs::world::EntityMut, data: EntityMetadataItems) -> Result<(), UpdateMetadataError> {{')
|
||||
f'pub fn update_metadatas(ecs: bevy_ecs::world::World, entity: bevy_ecs::world::EntityMut, items: &Vec<EntityDataItem>) -> Result<(), UpdateMetadataError> {{')
|
||||
for entity_id in burger_entity_data:
|
||||
if entity_id.startswith('~'):
|
||||
# not actually an entity
|
||||
|
@ -412,7 +412,7 @@ impl From<EntityDataValue> for UpdateMetadataError {
|
|||
struct_name: str = upper_first_letter(to_camel_case(entity_id))
|
||||
code.append(
|
||||
f' if entity.contains::<{struct_name}>() {{')
|
||||
code.append(' for d in data.0 {')
|
||||
code.append(' for d in items {')
|
||||
code.append(
|
||||
f' {struct_name}::update_metadata(ecs, &mut entity, d)?;')
|
||||
code.append(' }')
|
||||
|
|
Loading…
Add table
Reference in a new issue