mirror of
https://github.com/bevyengine/bevy
synced 2024-11-21 20:23:28 +00:00
ecs: use generational entity ids and other optimizations (#504)
ecs: use generational entity ids and other optimizations
This commit is contained in:
parent
34c6f5f41b
commit
70ad6671db
35 changed files with 1232 additions and 776 deletions
|
@ -1,6 +1,24 @@
|
|||
[
|
||||
(
|
||||
entity: 328997855,
|
||||
entity: 0,
|
||||
components: [
|
||||
{
|
||||
"type": "ComponentB",
|
||||
"map": {
|
||||
"value": "hello",
|
||||
},
|
||||
},
|
||||
{
|
||||
"type": "ComponentA",
|
||||
"map": {
|
||||
"x": 1.0,
|
||||
"y": 2.0,
|
||||
},
|
||||
},
|
||||
],
|
||||
),
|
||||
(
|
||||
entity: 1,
|
||||
components: [
|
||||
{
|
||||
"type": "ComponentA",
|
||||
|
@ -11,22 +29,4 @@
|
|||
},
|
||||
],
|
||||
),
|
||||
(
|
||||
entity: 404566393,
|
||||
components: [
|
||||
{
|
||||
"type": "ComponentA",
|
||||
"map": {
|
||||
"x": 1.0,
|
||||
"y": 2.0,
|
||||
},
|
||||
},
|
||||
{
|
||||
"type": "ComponentB",
|
||||
"map": {
|
||||
"value": "hello",
|
||||
},
|
||||
},
|
||||
],
|
||||
),
|
||||
]
|
|
@ -57,7 +57,8 @@ impl App {
|
|||
}
|
||||
|
||||
pub fn update(&mut self) {
|
||||
self.schedule.initialize(&mut self.resources);
|
||||
self.schedule
|
||||
.initialize(&mut self.world, &mut self.resources);
|
||||
self.executor
|
||||
.run(&mut self.schedule, &mut self.world, &mut self.resources);
|
||||
}
|
||||
|
@ -69,7 +70,8 @@ impl App {
|
|||
.unwrap_or_else(DefaultTaskPoolOptions::default)
|
||||
.create_default_pools(&mut self.resources);
|
||||
|
||||
self.startup_schedule.initialize(&mut self.resources);
|
||||
self.startup_schedule
|
||||
.initialize(&mut self.world, &mut self.resources);
|
||||
self.startup_executor.run(
|
||||
&mut self.startup_schedule,
|
||||
&mut self.world,
|
||||
|
|
|
@ -68,7 +68,7 @@ fn iterate_100k(b: &mut Bencher) {
|
|||
world.spawn((Position(-(i as f32)), Velocity(i as f32)));
|
||||
}
|
||||
b.iter(|| {
|
||||
for (mut pos, vel) in &mut world.query::<(&mut Position, &Velocity)>() {
|
||||
for (mut pos, vel) in &mut world.query_mut::<(&mut Position, &Velocity)>() {
|
||||
pos.0 += vel.0;
|
||||
}
|
||||
})
|
||||
|
|
|
@ -1,39 +0,0 @@
|
|||
// modified by Bevy contributors
|
||||
|
||||
//! One way to the contents of an entity, as you might do for debugging. A similar pattern could
|
||||
//! also be useful for serialization, or other row-oriented generic operations.
|
||||
|
||||
fn format_entity(entity: bevy_hecs::EntityRef<'_>) -> String {
|
||||
fn fmt<T: bevy_hecs::Component + std::fmt::Display>(
|
||||
entity: bevy_hecs::EntityRef<'_>,
|
||||
) -> Option<String> {
|
||||
Some(entity.get::<T>()?.to_string())
|
||||
}
|
||||
|
||||
const FUNCTIONS: &[&dyn Fn(bevy_hecs::EntityRef<'_>) -> Option<String>] =
|
||||
&[&fmt::<i32>, &fmt::<bool>, &fmt::<f64>];
|
||||
|
||||
let mut out = String::new();
|
||||
for f in FUNCTIONS {
|
||||
if let Some(x) = f(entity) {
|
||||
if out.is_empty() {
|
||||
out.push_str("[");
|
||||
} else {
|
||||
out.push_str(", ");
|
||||
}
|
||||
out.push_str(&x);
|
||||
}
|
||||
}
|
||||
if out.is_empty() {
|
||||
out.push_str(&"[]");
|
||||
} else {
|
||||
out.push(']');
|
||||
}
|
||||
out
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let mut world = bevy_hecs::World::new();
|
||||
let e = world.spawn((42, true));
|
||||
println!("{}", format_entity(world.entity(e).unwrap()));
|
||||
}
|
|
@ -14,11 +14,12 @@
|
|||
|
||||
// modified by Bevy contributors
|
||||
|
||||
use crate::alloc::{
|
||||
alloc::{alloc, dealloc, Layout},
|
||||
boxed::Box,
|
||||
vec,
|
||||
vec::Vec,
|
||||
use crate::{
|
||||
alloc::{
|
||||
alloc::{alloc, dealloc, Layout},
|
||||
vec::Vec,
|
||||
},
|
||||
Entity,
|
||||
};
|
||||
use bevy_utils::{HashMap, HashMapExt};
|
||||
use core::{
|
||||
|
@ -37,13 +38,13 @@ use crate::{borrow::AtomicBorrow, query::Fetch, Access, Component, Query};
|
|||
pub struct Archetype {
|
||||
types: Vec<TypeInfo>,
|
||||
state: HashMap<TypeId, TypeState>,
|
||||
len: u32,
|
||||
entities: Box<[u128]>,
|
||||
len: usize,
|
||||
entities: Vec<Entity>,
|
||||
// UnsafeCell allows unique references into `data` to be constructed while shared references
|
||||
// containing the `Archetype` exist
|
||||
data: UnsafeCell<NonNull<u8>>,
|
||||
data_size: usize,
|
||||
grow_size: u32,
|
||||
grow_size: usize,
|
||||
}
|
||||
|
||||
impl Archetype {
|
||||
|
@ -53,7 +54,7 @@ impl Archetype {
|
|||
}
|
||||
|
||||
#[allow(missing_docs)]
|
||||
pub fn with_grow(types: Vec<TypeInfo>, grow_size: u32) -> Self {
|
||||
pub fn with_grow(types: Vec<TypeInfo>, grow_size: usize) -> Self {
|
||||
debug_assert!(
|
||||
types.windows(2).all(|x| x[0] < x[1]),
|
||||
"type info unsorted or contains duplicates"
|
||||
|
@ -65,7 +66,7 @@ impl Archetype {
|
|||
Self {
|
||||
state,
|
||||
types,
|
||||
entities: Box::new([]),
|
||||
entities: Vec::new(),
|
||||
len: 0,
|
||||
data: UnsafeCell::new(NonNull::dangling()),
|
||||
data_size: 0,
|
||||
|
@ -94,6 +95,12 @@ impl Archetype {
|
|||
self.has_dynamic(TypeId::of::<T>())
|
||||
}
|
||||
|
||||
#[allow(missing_docs)]
|
||||
#[inline]
|
||||
pub fn has_type(&self, ty: TypeId) -> bool {
|
||||
self.has_dynamic(ty)
|
||||
}
|
||||
|
||||
pub(crate) fn has_dynamic(&self, id: TypeId) -> bool {
|
||||
self.state.contains_key(&id)
|
||||
}
|
||||
|
@ -111,61 +118,21 @@ impl Archetype {
|
|||
|
||||
#[allow(missing_docs)]
|
||||
#[inline]
|
||||
pub fn get_with_added<T: Component>(&self) -> Option<(NonNull<T>, NonNull<bool>)> {
|
||||
pub fn get_with_type_state<T: Component>(&self) -> Option<(NonNull<T>, &TypeState)> {
|
||||
let state = self.state.get(&TypeId::of::<T>())?;
|
||||
Some(unsafe {
|
||||
(
|
||||
NonNull::new_unchecked(
|
||||
(*self.data.get()).as_ptr().add(state.offset).cast::<T>() as *mut T
|
||||
),
|
||||
NonNull::new_unchecked(state.added_entities.as_ptr() as *mut bool),
|
||||
state,
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
#[allow(missing_docs)]
|
||||
#[inline]
|
||||
pub fn get_with_mutated<T: Component>(&self) -> Option<(NonNull<T>, NonNull<bool>)> {
|
||||
let state = self.state.get(&TypeId::of::<T>())?;
|
||||
Some(unsafe {
|
||||
(
|
||||
NonNull::new_unchecked(
|
||||
(*self.data.get()).as_ptr().add(state.offset).cast::<T>() as *mut T
|
||||
),
|
||||
NonNull::new_unchecked(state.mutated_entities.as_ptr() as *mut bool),
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
#[allow(missing_docs)]
|
||||
#[inline]
|
||||
pub fn get_with_added_and_mutated<T: Component>(
|
||||
&self,
|
||||
) -> Option<(NonNull<T>, NonNull<bool>, NonNull<bool>)> {
|
||||
let state = self.state.get(&TypeId::of::<T>())?;
|
||||
Some(unsafe {
|
||||
(
|
||||
NonNull::new_unchecked(
|
||||
(*self.data.get()).as_ptr().add(state.offset).cast::<T>() as *mut T
|
||||
),
|
||||
NonNull::new_unchecked(state.added_entities.as_ptr() as *mut bool),
|
||||
NonNull::new_unchecked(state.mutated_entities.as_ptr() as *mut bool),
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
#[allow(missing_docs)]
|
||||
#[inline]
|
||||
pub fn get_mutated<T: Component>(&self) -> Option<NonNull<bool>> {
|
||||
let state = self.state.get(&TypeId::of::<T>())?;
|
||||
Some(unsafe { NonNull::new_unchecked(state.mutated_entities.as_ptr() as *mut bool) })
|
||||
}
|
||||
|
||||
#[allow(missing_docs)]
|
||||
#[inline]
|
||||
pub fn get_added<T: Component>(&self) -> Option<NonNull<bool>> {
|
||||
let state = self.state.get(&TypeId::of::<T>())?;
|
||||
Some(unsafe { NonNull::new_unchecked(state.added_entities.as_ptr() as *mut bool) })
|
||||
pub fn get_type_state(&self, ty: TypeId) -> Option<&TypeState> {
|
||||
self.state.get(&ty)
|
||||
}
|
||||
|
||||
#[allow(missing_docs)]
|
||||
|
@ -215,7 +182,7 @@ impl Archetype {
|
|||
|
||||
#[allow(missing_docs)]
|
||||
#[inline]
|
||||
pub fn len(&self) -> u32 {
|
||||
pub fn len(&self) -> usize {
|
||||
self.len
|
||||
}
|
||||
|
||||
|
@ -226,17 +193,17 @@ impl Archetype {
|
|||
}
|
||||
|
||||
#[allow(missing_docs)]
|
||||
pub fn iter_entities(&self) -> impl Iterator<Item = &u128> {
|
||||
self.entities.iter().take(self.len as usize)
|
||||
pub fn iter_entities(&self) -> impl Iterator<Item = &Entity> {
|
||||
self.entities.iter().take(self.len)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn entities(&self) -> NonNull<u128> {
|
||||
pub(crate) fn entities(&self) -> NonNull<Entity> {
|
||||
unsafe { NonNull::new_unchecked(self.entities.as_ptr() as *mut _) }
|
||||
}
|
||||
|
||||
pub(crate) fn entity_id(&self, index: u32) -> u128 {
|
||||
self.entities[index as usize]
|
||||
pub(crate) fn get_entity(&self, index: usize) -> Entity {
|
||||
self.entities[index]
|
||||
}
|
||||
|
||||
#[allow(missing_docs)]
|
||||
|
@ -250,37 +217,37 @@ impl Archetype {
|
|||
&self,
|
||||
ty: TypeId,
|
||||
size: usize,
|
||||
index: u32,
|
||||
index: usize,
|
||||
) -> Option<NonNull<u8>> {
|
||||
debug_assert!(index < self.len);
|
||||
Some(NonNull::new_unchecked(
|
||||
(*self.data.get())
|
||||
.as_ptr()
|
||||
.add(self.state.get(&ty)?.offset + size * index as usize)
|
||||
.add(self.state.get(&ty)?.offset + size * index)
|
||||
.cast::<u8>(),
|
||||
))
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
/// Every type must be written immediately after this call
|
||||
pub unsafe fn allocate(&mut self, id: u128) -> u32 {
|
||||
if self.len as usize == self.entities.len() {
|
||||
pub unsafe fn allocate(&mut self, id: Entity) -> usize {
|
||||
if self.len == self.entities.len() {
|
||||
self.grow(self.len.max(self.grow_size));
|
||||
}
|
||||
|
||||
self.entities[self.len as usize] = id;
|
||||
self.entities[self.len] = id;
|
||||
self.len += 1;
|
||||
self.len - 1
|
||||
}
|
||||
|
||||
pub(crate) fn reserve(&mut self, additional: u32) {
|
||||
pub(crate) fn reserve(&mut self, additional: usize) {
|
||||
if additional > (self.capacity() - self.len()) {
|
||||
self.grow(additional - (self.capacity() - self.len()));
|
||||
}
|
||||
}
|
||||
|
||||
fn capacity(&self) -> u32 {
|
||||
self.entities.len() as u32
|
||||
fn capacity(&self) -> usize {
|
||||
self.entities.len()
|
||||
}
|
||||
|
||||
#[allow(missing_docs)]
|
||||
|
@ -290,13 +257,17 @@ impl Archetype {
|
|||
}
|
||||
}
|
||||
|
||||
fn grow(&mut self, increment: u32) {
|
||||
fn grow(&mut self, increment: usize) {
|
||||
unsafe {
|
||||
let old_count = self.len as usize;
|
||||
let count = old_count + increment as usize;
|
||||
let mut new_entities = vec![!0; count].into_boxed_slice();
|
||||
new_entities[0..old_count].copy_from_slice(&self.entities[0..old_count]);
|
||||
self.entities = new_entities;
|
||||
let old_count = self.len;
|
||||
let count = old_count + increment;
|
||||
self.entities.resize(
|
||||
self.entities.len() + increment,
|
||||
Entity {
|
||||
id: u32::MAX,
|
||||
generation: u32::MAX,
|
||||
},
|
||||
);
|
||||
|
||||
for type_state in self.state.values_mut() {
|
||||
type_state.mutated_entities.resize_with(count, || false);
|
||||
|
@ -341,7 +312,7 @@ impl Archetype {
|
|||
}
|
||||
|
||||
/// Returns the ID of the entity moved into `index`, if any
|
||||
pub(crate) unsafe fn remove(&mut self, index: u32) -> Option<u128> {
|
||||
pub(crate) unsafe fn remove(&mut self, index: usize) -> Option<Entity> {
|
||||
let last = self.len - 1;
|
||||
for ty in &self.types {
|
||||
let removed = self
|
||||
|
@ -360,16 +331,14 @@ impl Archetype {
|
|||
);
|
||||
|
||||
let type_state = self.state.get_mut(&ty.id).unwrap();
|
||||
type_state.mutated_entities[index as usize] =
|
||||
type_state.mutated_entities[last as usize];
|
||||
type_state.added_entities[index as usize] =
|
||||
type_state.added_entities[last as usize];
|
||||
type_state.mutated_entities[index] = type_state.mutated_entities[last];
|
||||
type_state.added_entities[index] = type_state.added_entities[last];
|
||||
}
|
||||
}
|
||||
self.len = last;
|
||||
if index != last {
|
||||
self.entities[index as usize] = self.entities[last as usize];
|
||||
Some(self.entities[last as usize])
|
||||
self.entities[index] = self.entities[last];
|
||||
Some(self.entities[last])
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
@ -378,9 +347,9 @@ impl Archetype {
|
|||
/// Returns the ID of the entity moved into `index`, if any
|
||||
pub(crate) unsafe fn move_to(
|
||||
&mut self,
|
||||
index: u32,
|
||||
index: usize,
|
||||
mut f: impl FnMut(*mut u8, TypeId, usize, bool, bool),
|
||||
) -> Option<u128> {
|
||||
) -> Option<Entity> {
|
||||
let last = self.len - 1;
|
||||
for ty in &self.types {
|
||||
let moved = self
|
||||
|
@ -388,8 +357,8 @@ impl Archetype {
|
|||
.unwrap()
|
||||
.as_ptr();
|
||||
let type_state = self.state.get(&ty.id).unwrap();
|
||||
let is_added = type_state.added_entities[index as usize];
|
||||
let is_mutated = type_state.mutated_entities[index as usize];
|
||||
let is_added = type_state.added_entities[index];
|
||||
let is_mutated = type_state.mutated_entities[index];
|
||||
f(moved, ty.id(), ty.layout().size(), is_added, is_mutated);
|
||||
if index != last {
|
||||
ptr::copy_nonoverlapping(
|
||||
|
@ -400,16 +369,14 @@ impl Archetype {
|
|||
ty.layout.size(),
|
||||
);
|
||||
let type_state = self.state.get_mut(&ty.id).unwrap();
|
||||
type_state.added_entities[index as usize] =
|
||||
type_state.added_entities[last as usize];
|
||||
type_state.mutated_entities[index as usize] =
|
||||
type_state.mutated_entities[last as usize];
|
||||
type_state.added_entities[index] = type_state.added_entities[last];
|
||||
type_state.mutated_entities[index] = type_state.mutated_entities[last];
|
||||
}
|
||||
}
|
||||
self.len -= 1;
|
||||
if index != last {
|
||||
self.entities[index as usize] = self.entities[last as usize];
|
||||
Some(self.entities[last as usize])
|
||||
self.entities[index] = self.entities[last];
|
||||
Some(self.entities[last])
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
@ -427,16 +394,16 @@ impl Archetype {
|
|||
component: *mut u8,
|
||||
ty: TypeId,
|
||||
size: usize,
|
||||
index: u32,
|
||||
index: usize,
|
||||
added: bool,
|
||||
) {
|
||||
let state = self.state.get_mut(&ty).unwrap();
|
||||
if added {
|
||||
state.added_entities[index as usize] = true;
|
||||
state.added_entities[index] = true;
|
||||
}
|
||||
let ptr = (*self.data.get())
|
||||
.as_ptr()
|
||||
.add(state.offset + size * index as usize)
|
||||
.add(state.offset + size * index)
|
||||
.cast::<u8>();
|
||||
ptr::copy_nonoverlapping(component, ptr, size);
|
||||
}
|
||||
|
@ -464,11 +431,12 @@ impl Drop for Archetype {
|
|||
}
|
||||
}
|
||||
|
||||
/// Metadata about a type stored in an archetype
|
||||
pub struct TypeState {
|
||||
offset: usize,
|
||||
borrow: AtomicBorrow,
|
||||
pub mutated_entities: Vec<bool>,
|
||||
pub added_entities: Vec<bool>,
|
||||
mutated_entities: Vec<bool>,
|
||||
added_entities: Vec<bool>,
|
||||
}
|
||||
|
||||
impl TypeState {
|
||||
|
@ -490,6 +458,18 @@ impl TypeState {
|
|||
*added = false;
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(missing_docs)]
|
||||
#[inline]
|
||||
pub fn mutated(&self) -> NonNull<bool> {
|
||||
unsafe { NonNull::new_unchecked(self.mutated_entities.as_ptr() as *mut bool) }
|
||||
}
|
||||
|
||||
#[allow(missing_docs)]
|
||||
#[inline]
|
||||
pub fn added(&self) -> NonNull<bool> {
|
||||
unsafe { NonNull::new_unchecked(self.added_entities.as_ptr() as *mut bool) }
|
||||
}
|
||||
}
|
||||
|
||||
/// Metadata required to store a component
|
||||
|
|
|
@ -17,7 +17,6 @@
|
|||
use core::{
|
||||
fmt::Debug,
|
||||
ops::{Deref, DerefMut},
|
||||
ptr::NonNull,
|
||||
sync::atomic::{AtomicUsize, Ordering},
|
||||
};
|
||||
|
||||
|
@ -68,7 +67,7 @@ const UNIQUE_BIT: usize = !(usize::max_value() >> 1);
|
|||
#[derive(Clone)]
|
||||
pub struct Ref<'a, T: Component> {
|
||||
archetype: &'a Archetype,
|
||||
target: NonNull<T>,
|
||||
target: &'a T,
|
||||
}
|
||||
|
||||
impl<'a, T: Component> Ref<'a, T> {
|
||||
|
@ -77,16 +76,15 @@ impl<'a, T: Component> Ref<'a, T> {
|
|||
/// # Safety
|
||||
///
|
||||
/// - the index of the component must be valid
|
||||
pub unsafe fn new(archetype: &'a Archetype, index: u32) -> Result<Self, MissingComponent> {
|
||||
let target = NonNull::new_unchecked(
|
||||
archetype
|
||||
.get::<T>()
|
||||
.ok_or_else(MissingComponent::new::<T>)?
|
||||
.as_ptr()
|
||||
.add(index as usize),
|
||||
);
|
||||
pub unsafe fn new(archetype: &'a Archetype, index: usize) -> Result<Self, MissingComponent> {
|
||||
let target = archetype
|
||||
.get::<T>()
|
||||
.ok_or_else(MissingComponent::new::<T>)?;
|
||||
archetype.borrow::<T>();
|
||||
Ok(Self { archetype, target })
|
||||
Ok(Self {
|
||||
archetype,
|
||||
target: &*target.as_ptr().add(index as usize),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -103,7 +101,7 @@ impl<'a, T: Component> Deref for Ref<'a, T> {
|
|||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &T {
|
||||
unsafe { self.target.as_ref() }
|
||||
self.target
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -119,7 +117,7 @@ where
|
|||
/// Unique borrow of an entity's component
|
||||
pub struct RefMut<'a, T: Component> {
|
||||
archetype: &'a Archetype,
|
||||
target: NonNull<T>,
|
||||
target: &'a mut T,
|
||||
modified: &'a mut bool,
|
||||
}
|
||||
|
||||
|
@ -129,24 +127,15 @@ impl<'a, T: Component> RefMut<'a, T> {
|
|||
/// # Safety
|
||||
///
|
||||
/// - the index of the component must be valid
|
||||
pub unsafe fn new(archetype: &'a Archetype, index: u32) -> Result<Self, MissingComponent> {
|
||||
let target = NonNull::new_unchecked(
|
||||
archetype
|
||||
.get::<T>()
|
||||
.ok_or_else(MissingComponent::new::<T>)?
|
||||
.as_ptr()
|
||||
.add(index as usize),
|
||||
);
|
||||
pub unsafe fn new(archetype: &'a Archetype, index: usize) -> Result<Self, MissingComponent> {
|
||||
let (target, type_state) = archetype
|
||||
.get_with_type_state::<T>()
|
||||
.ok_or_else(MissingComponent::new::<T>)?;
|
||||
archetype.borrow_mut::<T>();
|
||||
let modified = archetype
|
||||
.get_mutated::<T>()
|
||||
.unwrap()
|
||||
.as_ptr()
|
||||
.add(index as usize);
|
||||
Ok(Self {
|
||||
archetype,
|
||||
target,
|
||||
modified: &mut *modified,
|
||||
target: &mut *target.as_ptr().add(index),
|
||||
modified: &mut *type_state.mutated().as_ptr().add(index),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -164,14 +153,14 @@ impl<'a, T: Component> Deref for RefMut<'a, T> {
|
|||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &T {
|
||||
unsafe { self.target.as_ref() }
|
||||
self.target
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: Component> DerefMut for RefMut<'a, T> {
|
||||
fn deref_mut(&mut self) -> &mut T {
|
||||
*self.modified = true;
|
||||
unsafe { self.target.as_mut() }
|
||||
self.target
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -188,7 +177,7 @@ where
|
|||
#[derive(Copy, Clone)]
|
||||
pub struct EntityRef<'a> {
|
||||
archetype: Option<&'a Archetype>,
|
||||
index: u32,
|
||||
index: usize,
|
||||
}
|
||||
|
||||
impl<'a> EntityRef<'a> {
|
||||
|
@ -200,7 +189,7 @@ impl<'a> EntityRef<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn new(archetype: &'a Archetype, index: u32) -> Self {
|
||||
pub(crate) unsafe fn new(archetype: &'a Archetype, index: usize) -> Self {
|
||||
Self {
|
||||
archetype: Some(archetype),
|
||||
index,
|
||||
|
|
|
@ -1,27 +1,45 @@
|
|||
// modified by Bevy contributors
|
||||
|
||||
use bevy_utils::HashMap;
|
||||
use core::fmt;
|
||||
use alloc::{boxed::Box, vec::Vec};
|
||||
use core::{
|
||||
convert::TryFrom,
|
||||
fmt, mem,
|
||||
sync::atomic::{AtomicU32, Ordering},
|
||||
};
|
||||
#[cfg(feature = "std")]
|
||||
use std::error::Error;
|
||||
|
||||
/// Lightweight unique ID of an entity
|
||||
///
|
||||
/// Obtained from `World::spawn`. Can be stored to refer to an entity in the future.
|
||||
#[derive(Debug, Clone, Copy, Hash, Eq, Ord, PartialEq, PartialOrd)]
|
||||
pub struct Entity(u128);
|
||||
#[derive(Clone, Copy, Hash, Eq, Ord, PartialEq, PartialOrd)]
|
||||
pub struct Entity {
|
||||
pub(crate) generation: u32,
|
||||
pub(crate) id: u32,
|
||||
}
|
||||
|
||||
#[allow(clippy::new_without_default)]
|
||||
impl Entity {
|
||||
#[allow(missing_docs)]
|
||||
pub fn new() -> Self {
|
||||
Self(rand::random::<u128>())
|
||||
/// Creates a new entity reference with a generation of 0
|
||||
pub fn new(id: u32) -> Entity {
|
||||
Entity { id, generation: 0 }
|
||||
}
|
||||
|
||||
#[allow(missing_docs)]
|
||||
#[inline]
|
||||
pub fn from_id(id: u128) -> Self {
|
||||
Self(id)
|
||||
/// Convert to a form convenient for passing outside of rust
|
||||
///
|
||||
/// Only useful for identifying entities within the same instance of an application. Do not use
|
||||
/// for serialization between runs.
|
||||
///
|
||||
/// No particular structure is guaranteed for the returned bits.
|
||||
pub fn to_bits(self) -> u64 {
|
||||
u64::from(self.generation) << 32 | u64::from(self.id)
|
||||
}
|
||||
|
||||
/// Reconstruct an `Entity` previously destructured with `to_bits`
|
||||
///
|
||||
/// Only useful when applied to results from `to_bits` in the same instance of an application.
|
||||
pub fn from_bits(bits: u64) -> Self {
|
||||
Self {
|
||||
generation: (bits >> 32) as u32,
|
||||
id: bits as u32,
|
||||
}
|
||||
}
|
||||
|
||||
/// Extract a transiently unique identifier
|
||||
|
@ -29,65 +47,306 @@ impl Entity {
|
|||
/// No two simultaneously-live entities share the same ID, but dead entities' IDs may collide
|
||||
/// with both live and dead entities. Useful for compactly representing entities within a
|
||||
/// specific snapshot of the world, such as when serializing.
|
||||
#[inline]
|
||||
pub fn id(self) -> u128 {
|
||||
self.0
|
||||
pub fn id(self) -> u32 {
|
||||
self.id
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Entity {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}v{}", self.id, self.generation)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub(crate) struct Entities {
|
||||
pub entity_locations: HashMap<Entity, Location>,
|
||||
pub meta: Vec<EntityMeta>,
|
||||
// Reserved entities outside the range of `meta`, having implicit generation 0, archetype 0, and
|
||||
// undefined index. Calling `flush` converts these to real entities, which can have a fully
|
||||
// defined location.
|
||||
pending: AtomicU32,
|
||||
// Unused entity IDs below `meta.len()`
|
||||
free: Vec<u32>,
|
||||
free_cursor: AtomicU32,
|
||||
// Reserved IDs within `meta.len()` with implicit archetype 0 and undefined index. Should be
|
||||
// consumed and used to initialize locations to produce real entities after calling `flush`.
|
||||
reserved: Box<[AtomicU32]>,
|
||||
reserved_cursor: AtomicU32,
|
||||
}
|
||||
|
||||
impl Entities {
|
||||
/// Reserve an entity ID concurrently
|
||||
///
|
||||
/// Storage for entity generation and location is lazily allocated by calling `flush`. Locations
|
||||
/// can be determined by the return value of `flush` and by iterating through the `reserved`
|
||||
/// accessors, and should all be written immediately after flushing.
|
||||
pub fn reserve_entity(&self) -> Entity {
|
||||
loop {
|
||||
let index = self.free_cursor.load(Ordering::Relaxed);
|
||||
match index.checked_sub(1) {
|
||||
// The freelist is empty, so increment `pending` to arrange for a new entity with a
|
||||
// predictable ID to be allocated on the next `flush` call
|
||||
None => {
|
||||
let n = self.pending.fetch_add(1, Ordering::Relaxed);
|
||||
return Entity {
|
||||
generation: 0,
|
||||
id: u32::try_from(self.meta.len())
|
||||
.ok()
|
||||
.and_then(|x| x.checked_add(n))
|
||||
.expect("too many entities"),
|
||||
};
|
||||
}
|
||||
// The freelist has entities in it, so move the last entry to the reserved list, to
|
||||
// be consumed by the caller as part of a higher-level flush.
|
||||
Some(next) => {
|
||||
// We don't care about memory ordering here so long as we get our slot.
|
||||
if self
|
||||
.free_cursor
|
||||
.compare_exchange_weak(index, next, Ordering::Relaxed, Ordering::Relaxed)
|
||||
.is_err()
|
||||
{
|
||||
// Another thread already consumed this slot, start over.
|
||||
continue;
|
||||
}
|
||||
let id = self.free[next as usize];
|
||||
let reservation = self.reserved_cursor.fetch_add(1, Ordering::Relaxed);
|
||||
self.reserved[reservation as usize].store(id, Ordering::Relaxed);
|
||||
return Entity {
|
||||
generation: self.meta[id as usize].generation,
|
||||
id,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Allocate an entity ID directly
|
||||
///
|
||||
/// Location should be written immediately.
|
||||
pub fn alloc(&mut self) -> Entity {
|
||||
debug_assert_eq!(
|
||||
self.pending.load(Ordering::Relaxed),
|
||||
0,
|
||||
"allocator must be flushed before potentially growing"
|
||||
);
|
||||
let index = self.free_cursor.load(Ordering::Relaxed);
|
||||
match index.checked_sub(1) {
|
||||
None => {
|
||||
self.grow(0);
|
||||
let cursor = self.free_cursor.fetch_sub(1, Ordering::Relaxed);
|
||||
let id = self.free[(cursor - 1) as usize];
|
||||
Entity {
|
||||
generation: self.meta[id as usize].generation,
|
||||
id,
|
||||
}
|
||||
}
|
||||
Some(next) => {
|
||||
// Not racey due to &mut self
|
||||
self.free_cursor.store(next, Ordering::Relaxed);
|
||||
let id = self.free[next as usize];
|
||||
Entity {
|
||||
generation: self.meta[id as usize].generation,
|
||||
id,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Destroy an entity, allowing it to be reused
|
||||
///
|
||||
/// Must not be called on reserved entities prior to `flush`.
|
||||
pub fn free(&mut self, entity: Entity) -> Result<Location, NoSuchEntity> {
|
||||
if let Some(location) = self.entity_locations.remove(&entity) {
|
||||
Ok(location)
|
||||
let meta = &mut self.meta[entity.id as usize];
|
||||
if meta.generation != entity.generation {
|
||||
return Err(NoSuchEntity);
|
||||
}
|
||||
meta.generation += 1;
|
||||
let loc = mem::replace(
|
||||
&mut meta.location,
|
||||
Location {
|
||||
archetype: 0,
|
||||
// Guard against bugs in reservation handling
|
||||
index: usize::max_value(),
|
||||
},
|
||||
);
|
||||
let index = self.free_cursor.fetch_add(1, Ordering::Relaxed); // Not racey due to &mut self
|
||||
self.free[index as usize] = entity.id;
|
||||
debug_assert!(
|
||||
loc.index != usize::max_value(),
|
||||
"free called on reserved entity without flush"
|
||||
);
|
||||
Ok(loc)
|
||||
}
|
||||
|
||||
/// Ensure `n` at least allocations can succeed without reallocating
|
||||
pub fn reserve(&mut self, additional: u32) {
|
||||
debug_assert_eq!(
|
||||
self.pending.load(Ordering::Relaxed),
|
||||
0,
|
||||
"allocator must be flushed before potentially growing"
|
||||
);
|
||||
let free = self.free_cursor.load(Ordering::Relaxed);
|
||||
if additional > free {
|
||||
self.grow(additional - free);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn contains(&self, entity: Entity) -> bool {
|
||||
if entity.id >= self.meta.len() as u32 {
|
||||
return true;
|
||||
}
|
||||
self.meta[entity.id as usize].generation == entity.generation
|
||||
}
|
||||
|
||||
pub fn clear(&mut self) {
|
||||
// Not racey due to &mut self
|
||||
self.free_cursor
|
||||
.store(self.meta.len() as u32, Ordering::Relaxed);
|
||||
for (i, x) in self.free.iter_mut().enumerate() {
|
||||
*x = i as u32;
|
||||
}
|
||||
self.pending.store(0, Ordering::Relaxed);
|
||||
self.reserved_cursor.store(0, Ordering::Relaxed);
|
||||
}
|
||||
|
||||
/// Access the location storage of an entity
|
||||
///
|
||||
/// Must not be called on pending entities.
|
||||
pub fn get_mut(&mut self, entity: Entity) -> Result<&mut Location, NoSuchEntity> {
|
||||
let meta = &mut self.meta[entity.id as usize];
|
||||
if meta.generation == entity.generation {
|
||||
Ok(&mut meta.location)
|
||||
} else {
|
||||
Err(NoSuchEntity)
|
||||
}
|
||||
}
|
||||
|
||||
/// Ensure `n` at least allocations can succeed without reallocating
|
||||
pub fn reserve(&mut self, additional: u32) {
|
||||
self.entity_locations.reserve(additional as usize)
|
||||
}
|
||||
|
||||
pub fn contains(&self, entity: Entity) -> bool {
|
||||
self.entity_locations.contains_key(&entity)
|
||||
}
|
||||
|
||||
pub fn clear(&mut self) {
|
||||
self.entity_locations.clear();
|
||||
}
|
||||
|
||||
/// Access the location storage of an entity
|
||||
pub fn get_mut(&mut self, entity: Entity) -> Result<&mut Location, NoSuchEntity> {
|
||||
self.entity_locations.get_mut(&entity).ok_or(NoSuchEntity)
|
||||
}
|
||||
|
||||
/// Access the location storage of an entity
|
||||
pub fn insert(&mut self, entity: Entity, location: Location) {
|
||||
self.entity_locations.insert(entity, location);
|
||||
}
|
||||
|
||||
/// Returns `Ok(Location { archetype: 0, index: undefined })` for pending entities
|
||||
pub fn get(&self, entity: Entity) -> Result<Location, NoSuchEntity> {
|
||||
self.entity_locations
|
||||
.get(&entity)
|
||||
.cloned()
|
||||
.ok_or(NoSuchEntity)
|
||||
if self.meta.len() <= entity.id as usize {
|
||||
return Ok(Location {
|
||||
archetype: 0,
|
||||
index: usize::max_value(),
|
||||
});
|
||||
}
|
||||
let meta = &self.meta[entity.id as usize];
|
||||
if meta.generation != entity.generation {
|
||||
return Err(NoSuchEntity);
|
||||
}
|
||||
if meta.location.archetype == 0 {
|
||||
return Ok(Location {
|
||||
archetype: 0,
|
||||
index: usize::max_value(),
|
||||
});
|
||||
}
|
||||
Ok(meta.location)
|
||||
}
|
||||
|
||||
/// Allocate space for and enumerate pending entities
|
||||
#[allow(clippy::reversed_empty_ranges)]
|
||||
pub fn flush(&mut self) -> impl Iterator<Item = u32> {
|
||||
let pending = self.pending.load(Ordering::Relaxed); // Not racey due to &mut self
|
||||
if pending != 0 {
|
||||
let first = self.meta.len() as u32;
|
||||
self.grow(0);
|
||||
first..(first + pending)
|
||||
} else {
|
||||
0..0
|
||||
}
|
||||
}
|
||||
|
||||
// The following three methods allow iteration over `reserved` simultaneous to location
|
||||
// writes. This is a lazy hack, but we only use it in `World::flush` so the complexity and unsafety
|
||||
// involved in producing an `impl Iterator<Item=(u32, &mut Location)>` isn't a clear win.
|
||||
pub fn reserved_len(&self) -> u32 {
|
||||
self.reserved_cursor.load(Ordering::Relaxed)
|
||||
}
|
||||
|
||||
pub fn reserved(&self, i: u32) -> u32 {
|
||||
debug_assert!(i < self.reserved_len());
|
||||
self.reserved[i as usize].load(Ordering::Relaxed)
|
||||
}
|
||||
|
||||
pub fn clear_reserved(&mut self) {
|
||||
self.reserved_cursor.store(0, Ordering::Relaxed);
|
||||
}
|
||||
|
||||
/// Expand storage and mark all but the first `pending` of the new slots as free
|
||||
fn grow(&mut self, increment: u32) {
|
||||
let pending = self.pending.swap(0, Ordering::Relaxed);
|
||||
let new_len = (self.meta.len() + pending as usize + increment as usize)
|
||||
.max(self.meta.len() * 2)
|
||||
.max(1024);
|
||||
let mut new_meta = Vec::with_capacity(new_len);
|
||||
new_meta.extend_from_slice(&self.meta);
|
||||
new_meta.resize(
|
||||
new_len,
|
||||
EntityMeta {
|
||||
generation: 0,
|
||||
location: Location {
|
||||
archetype: 0,
|
||||
index: usize::max_value(), // dummy value, to be filled in
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
let free_cursor = self.free_cursor.load(Ordering::Relaxed); // Not racey due to &mut self
|
||||
let mut new_free = Vec::with_capacity(new_len);
|
||||
new_free.extend_from_slice(&self.free[0..free_cursor as usize]);
|
||||
// Add freshly allocated trailing free slots
|
||||
new_free.extend(((self.meta.len() as u32 + pending)..new_len as u32).rev());
|
||||
debug_assert!(new_free.len() <= new_len);
|
||||
self.free_cursor
|
||||
.store(new_free.len() as u32, Ordering::Relaxed); // Not racey due to &mut self
|
||||
|
||||
// Zero-fill
|
||||
new_free.resize(new_len, 0);
|
||||
|
||||
self.meta = new_meta;
|
||||
self.free = new_free;
|
||||
let mut new_reserved = Vec::with_capacity(new_len);
|
||||
// Not racey due to &mut self
|
||||
let reserved_cursor = self.reserved_cursor.load(Ordering::Relaxed);
|
||||
for x in &self.reserved[..reserved_cursor as usize] {
|
||||
new_reserved.push(AtomicU32::new(x.load(Ordering::Relaxed)));
|
||||
}
|
||||
new_reserved.resize_with(new_len, || AtomicU32::new(0));
|
||||
self.reserved = new_reserved.into();
|
||||
}
|
||||
|
||||
pub fn get_reserver(&self) -> EntityReserver {
|
||||
// SAFE: reservers use atomics for anything write-related
|
||||
let entities: &'static Entities = unsafe { mem::transmute(self) };
|
||||
EntityReserver { entities }
|
||||
}
|
||||
}
|
||||
|
||||
/// Reserves entities in a way that is usable in multi-threaded contexts.
|
||||
pub struct EntityReserver {
|
||||
entities: &'static Entities,
|
||||
}
|
||||
|
||||
impl EntityReserver {
|
||||
/// Reserves an entity
|
||||
pub fn reserve_entity(&self) -> Entity {
|
||||
self.entities.reserve_entity()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
#[allow(missing_docs)]
|
||||
pub(crate) struct EntityMeta {
|
||||
pub generation: u32,
|
||||
pub location: Location,
|
||||
}
|
||||
|
||||
/// A location of an entity in an archetype
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct Location {
|
||||
/// The archetype index
|
||||
pub archetype: u32,
|
||||
pub index: u32,
|
||||
|
||||
/// The index of the entity in the archetype
|
||||
pub index: usize,
|
||||
}
|
||||
|
||||
/// Error indicating that no entity with a particular ID exists
|
||||
|
@ -102,3 +361,17 @@ impl fmt::Display for NoSuchEntity {
|
|||
|
||||
#[cfg(feature = "std")]
|
||||
impl Error for NoSuchEntity {}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn entity_bits_roundtrip() {
|
||||
let e = Entity {
|
||||
generation: 0xDEADBEEF,
|
||||
id: 0xBAADF00D,
|
||||
};
|
||||
assert_eq!(Entity::from_bits(e.to_bits()), e);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,7 +33,7 @@
|
|||
//! let a = world.spawn((123, true, "abc"));
|
||||
//! let b = world.spawn((42, false));
|
||||
//! // Systems can be simple for loops
|
||||
//! for (id, mut number, &flag) in &mut world.query::<(Entity, &mut i32, &bool)>() {
|
||||
//! for (id, mut number, &flag) in &mut world.query_mut::<(Entity, &mut i32, &bool)>() {
|
||||
//! if flag { *number *= 2; }
|
||||
//! }
|
||||
//! assert_eq!(*world.get::<i32>(a).unwrap(), 246);
|
||||
|
@ -75,17 +75,17 @@ mod query_one;
|
|||
mod serde;
|
||||
mod world;
|
||||
|
||||
pub use archetype::Archetype;
|
||||
pub use borrow::{EntityRef, Ref, RefMut};
|
||||
pub use archetype::{Archetype, TypeState};
|
||||
pub use borrow::{Ref, RefMut};
|
||||
pub use bundle::{Bundle, DynamicBundle, MissingComponent};
|
||||
pub use entities::{Entity, Location, NoSuchEntity};
|
||||
pub use entities::{Entity, EntityReserver, Location, NoSuchEntity};
|
||||
pub use entity_builder::{BuiltEntity, EntityBuilder};
|
||||
pub use query::{
|
||||
Access, Added, BatchedIter, Changed, Mut, Mutated, Or, Query, QueryBorrow, QueryIter, With,
|
||||
Without,
|
||||
Access, Added, BatchedIter, Changed, Mut, Mutated, Or, Query, QueryBorrow, QueryIter,
|
||||
ReadOnlyFetch, With, Without,
|
||||
};
|
||||
pub use query_one::QueryOne;
|
||||
pub use world::{ArchetypesGeneration, Component, ComponentError, Iter, SpawnBatchIter, World};
|
||||
pub use world::{ArchetypesGeneration, Component, ComponentError, SpawnBatchIter, World};
|
||||
|
||||
// Unstable implementation details needed by the macros
|
||||
#[doc(hidden)]
|
||||
|
|
|
@ -20,7 +20,7 @@ use core::{
|
|||
ptr::NonNull,
|
||||
};
|
||||
|
||||
use crate::{archetype::Archetype, Component, Entity};
|
||||
use crate::{archetype::Archetype, Component, Entity, MissingComponent};
|
||||
|
||||
/// A collection of component types to fetch from a `World`
|
||||
pub trait Query {
|
||||
|
@ -28,6 +28,9 @@ pub trait Query {
|
|||
type Fetch: for<'a> Fetch<'a>;
|
||||
}
|
||||
|
||||
/// A fetch that is read only. This should only be implemented for read-only fetches.
|
||||
pub unsafe trait ReadOnlyFetch {}
|
||||
|
||||
/// Streaming iterators over contiguous homogeneous ranges of components
|
||||
pub trait Fetch<'a>: Sized {
|
||||
/// Type of value to be fetched
|
||||
|
@ -76,7 +79,8 @@ pub enum Access {
|
|||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct EntityFetch(NonNull<u128>);
|
||||
pub struct EntityFetch(NonNull<Entity>);
|
||||
unsafe impl ReadOnlyFetch for EntityFetch {}
|
||||
|
||||
impl Query for Entity {
|
||||
type Fetch = EntityFetch;
|
||||
|
@ -107,7 +111,7 @@ impl<'a> Fetch<'a> for EntityFetch {
|
|||
unsafe fn next(&mut self) -> Self::Item {
|
||||
let id = self.0.as_ptr();
|
||||
self.0 = NonNull::new_unchecked(id.add(1));
|
||||
Entity::from_id(*id)
|
||||
*id
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -118,6 +122,8 @@ impl<'a, T: Component> Query for &'a T {
|
|||
#[doc(hidden)]
|
||||
pub struct FetchRead<T>(NonNull<T>);
|
||||
|
||||
unsafe impl<T> ReadOnlyFetch for FetchRead<T> {}
|
||||
|
||||
impl<'a, T: Component> Fetch<'a> for FetchRead<T> {
|
||||
type Item = &'a T;
|
||||
|
||||
|
@ -161,8 +167,24 @@ impl<T: Query> Query for Option<T> {
|
|||
|
||||
/// Unique borrow of an entity's component
|
||||
pub struct Mut<'a, T: Component> {
|
||||
value: &'a mut T,
|
||||
mutated: &'a mut bool,
|
||||
pub(crate) value: &'a mut T,
|
||||
pub(crate) mutated: &'a mut bool,
|
||||
}
|
||||
|
||||
impl<'a, T: Component> Mut<'a, T> {
|
||||
/// Creates a new mutable reference to a component. This is unsafe because the index bounds are not checked.
|
||||
///
|
||||
/// # Safety
|
||||
/// This doesn't check the bounds of index in archetype
|
||||
pub unsafe fn new(archetype: &'a Archetype, index: usize) -> Result<Self, MissingComponent> {
|
||||
let (target, type_state) = archetype
|
||||
.get_with_type_state::<T>()
|
||||
.ok_or_else(MissingComponent::new::<T>)?;
|
||||
Ok(Self {
|
||||
value: &mut *target.as_ptr().add(index),
|
||||
mutated: &mut *type_state.mutated().as_ptr().add(index),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<T: Component> Send for Mut<'_, T> {}
|
||||
|
@ -214,11 +236,11 @@ impl<'a, T: Component> Fetch<'a> for FetchMut<T> {
|
|||
|
||||
unsafe fn get(archetype: &'a Archetype, offset: usize) -> Option<Self> {
|
||||
archetype
|
||||
.get_with_mutated::<T>()
|
||||
.map(|(components, mutated)| {
|
||||
.get_with_type_state::<T>()
|
||||
.map(|(components, type_state)| {
|
||||
Self(
|
||||
NonNull::new_unchecked(components.as_ptr().add(offset)),
|
||||
NonNull::new_unchecked(mutated.as_ptr().add(offset)),
|
||||
NonNull::new_unchecked(type_state.mutated().as_ptr().add(offset)),
|
||||
)
|
||||
})
|
||||
}
|
||||
|
@ -306,11 +328,11 @@ impl_or_query!(Q1, Q2, Q3, Q4, Q5, Q6, Q7, Q8, Q9, Q10);
|
|||
/// let mut world = World::new();
|
||||
/// world.spawn((123, true, 1., Some(1)));
|
||||
/// world.spawn((456, false, 2., Some(0)));
|
||||
/// for mut b in world.query::<Mut<i32>>().iter().skip(1).take(1) {
|
||||
/// for mut b in world.query_mut::<Mut<i32>>().iter().skip(1).take(1) {
|
||||
/// *b += 1;
|
||||
/// }
|
||||
/// let components = world
|
||||
/// .query::<Or<(Mutated<bool>, Mutated<i32>, Mutated<f64>, Mutated<Option<i32>>)>>()
|
||||
/// .query_mut::<Or<(Mutated<bool>, Mutated<i32>, Mutated<f64>, Mutated<Option<i32>>)>>()
|
||||
/// .iter()
|
||||
/// .map(|(b, i, f, o)| (*b, *i))
|
||||
/// .collect::<Vec<_>>();
|
||||
|
@ -361,11 +383,11 @@ impl<'a, T: Component> Fetch<'a> for FetchMutated<T> {
|
|||
|
||||
unsafe fn get(archetype: &'a Archetype, offset: usize) -> Option<Self> {
|
||||
archetype
|
||||
.get_with_mutated::<T>()
|
||||
.map(|(components, mutated)| {
|
||||
.get_with_type_state::<T>()
|
||||
.map(|(components, type_state)| {
|
||||
Self(
|
||||
NonNull::new_unchecked(components.as_ptr().add(offset)),
|
||||
NonNull::new_unchecked(mutated.as_ptr().add(offset)),
|
||||
NonNull::new_unchecked(type_state.mutated().as_ptr().add(offset)),
|
||||
)
|
||||
})
|
||||
}
|
||||
|
@ -408,6 +430,7 @@ impl<'a, T: Component> Query for Added<'a, T> {
|
|||
|
||||
#[doc(hidden)]
|
||||
pub struct FetchAdded<T>(NonNull<T>, NonNull<bool>);
|
||||
unsafe impl<T> ReadOnlyFetch for FetchAdded<T> {}
|
||||
|
||||
impl<'a, T: Component> Fetch<'a> for FetchAdded<T> {
|
||||
type Item = Added<'a, T>;
|
||||
|
@ -425,12 +448,14 @@ impl<'a, T: Component> Fetch<'a> for FetchAdded<T> {
|
|||
}
|
||||
|
||||
unsafe fn get(archetype: &'a Archetype, offset: usize) -> Option<Self> {
|
||||
archetype.get_with_added::<T>().map(|(components, added)| {
|
||||
Self(
|
||||
NonNull::new_unchecked(components.as_ptr().add(offset)),
|
||||
NonNull::new_unchecked(added.as_ptr().add(offset)),
|
||||
)
|
||||
})
|
||||
archetype
|
||||
.get_with_type_state::<T>()
|
||||
.map(|(components, type_state)| {
|
||||
Self(
|
||||
NonNull::new_unchecked(components.as_ptr().add(offset)),
|
||||
NonNull::new_unchecked(type_state.added().as_ptr().add(offset)),
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
fn release(archetype: &Archetype) {
|
||||
|
@ -471,6 +496,7 @@ impl<'a, T: Component> Query for Changed<'a, T> {
|
|||
|
||||
#[doc(hidden)]
|
||||
pub struct FetchChanged<T>(NonNull<T>, NonNull<bool>, NonNull<bool>);
|
||||
unsafe impl<T> ReadOnlyFetch for FetchChanged<T> {}
|
||||
|
||||
impl<'a, T: Component> Fetch<'a> for FetchChanged<T> {
|
||||
type Item = Changed<'a, T>;
|
||||
|
@ -489,12 +515,12 @@ impl<'a, T: Component> Fetch<'a> for FetchChanged<T> {
|
|||
|
||||
unsafe fn get(archetype: &'a Archetype, offset: usize) -> Option<Self> {
|
||||
archetype
|
||||
.get_with_added_and_mutated::<T>()
|
||||
.map(|(components, added, mutated)| {
|
||||
.get_with_type_state::<T>()
|
||||
.map(|(components, type_state)| {
|
||||
Self(
|
||||
NonNull::new_unchecked(components.as_ptr().add(offset)),
|
||||
NonNull::new_unchecked(added.as_ptr().add(offset)),
|
||||
NonNull::new_unchecked(mutated.as_ptr().add(offset)),
|
||||
NonNull::new_unchecked(type_state.added().as_ptr().add(offset)),
|
||||
NonNull::new_unchecked(type_state.mutated().as_ptr().add(offset)),
|
||||
)
|
||||
})
|
||||
}
|
||||
|
@ -520,6 +546,7 @@ impl<'a, T: Component> Fetch<'a> for FetchChanged<T> {
|
|||
|
||||
#[doc(hidden)]
|
||||
pub struct TryFetch<T>(Option<T>);
|
||||
unsafe impl<T> ReadOnlyFetch for TryFetch<T> where T: ReadOnlyFetch {}
|
||||
|
||||
impl<'a, T: Fetch<'a>> Fetch<'a> for TryFetch<T> {
|
||||
type Item = Option<T::Item>;
|
||||
|
@ -574,6 +601,10 @@ impl<T: Component, Q: Query> Query for Without<T, Q> {
|
|||
|
||||
#[doc(hidden)]
|
||||
pub struct FetchWithout<T, F>(F, PhantomData<fn(T)>);
|
||||
unsafe impl<'a, T: Component, F: Fetch<'a>> ReadOnlyFetch for FetchWithout<T, F> where
|
||||
F: ReadOnlyFetch
|
||||
{
|
||||
}
|
||||
|
||||
impl<'a, T: Component, F: Fetch<'a>> Fetch<'a> for FetchWithout<T, F> {
|
||||
type Item = F::Item;
|
||||
|
@ -637,6 +668,7 @@ impl<T: Component, Q: Query> Query for With<T, Q> {
|
|||
|
||||
#[doc(hidden)]
|
||||
pub struct FetchWith<T, F>(F, PhantomData<fn(T)>);
|
||||
unsafe impl<'a, T: Component, F: Fetch<'a>> ReadOnlyFetch for FetchWith<T, F> where F: ReadOnlyFetch {}
|
||||
|
||||
impl<'a, T: Component, F: Fetch<'a>> Fetch<'a> for FetchWith<T, F> {
|
||||
type Item = F::Item;
|
||||
|
@ -706,7 +738,7 @@ impl<'w, Q: Query> QueryBorrow<'w, Q> {
|
|||
/// Like `iter`, but returns child iterators of at most `batch_size` elements
|
||||
///
|
||||
/// Useful for distributing work over a threadpool.
|
||||
pub fn iter_batched<'q>(&'q mut self, batch_size: u32) -> BatchedIter<'q, 'w, Q> {
|
||||
pub fn iter_batched<'q>(&'q mut self, batch_size: usize) -> BatchedIter<'q, 'w, Q> {
|
||||
self.borrow();
|
||||
BatchedIter {
|
||||
borrow: self,
|
||||
|
@ -722,12 +754,7 @@ impl<'w, Q: Query> QueryBorrow<'w, Q> {
|
|||
"called QueryBorrow::iter twice on the same borrow; construct a new query instead"
|
||||
);
|
||||
}
|
||||
for x in self.archetypes {
|
||||
// TODO: Release prior borrows on failure?
|
||||
if Q::Fetch::access(x) >= Some(Access::Read) {
|
||||
Q::Fetch::borrow(x);
|
||||
}
|
||||
}
|
||||
|
||||
self.borrowed = true;
|
||||
}
|
||||
|
||||
|
@ -781,32 +808,20 @@ impl<'w, Q: Query> QueryBorrow<'w, Q> {
|
|||
|
||||
/// Helper to change the type of the query
|
||||
fn transform<R: Query>(mut self) -> QueryBorrow<'w, R> {
|
||||
let x = QueryBorrow {
|
||||
let borrow = QueryBorrow {
|
||||
archetypes: self.archetypes,
|
||||
borrowed: self.borrowed,
|
||||
_marker: PhantomData,
|
||||
};
|
||||
// Ensure `Drop` won't fire redundantly
|
||||
|
||||
self.borrowed = false;
|
||||
x
|
||||
borrow
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<'w, Q: Query> Send for QueryBorrow<'w, Q> {}
|
||||
unsafe impl<'w, Q: Query> Sync for QueryBorrow<'w, Q> {}
|
||||
|
||||
impl<'w, Q: Query> Drop for QueryBorrow<'w, Q> {
|
||||
fn drop(&mut self) {
|
||||
if self.borrowed {
|
||||
for x in self.archetypes {
|
||||
if Q::Fetch::access(x) >= Some(Access::Read) {
|
||||
Q::Fetch::release(x);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'q, 'w, Q: Query> IntoIterator for &'q mut QueryBorrow<'w, Q> {
|
||||
type IntoIter = QueryIter<'q, 'w, Q>;
|
||||
type Item = <Q::Fetch as Fetch<'q>>::Item;
|
||||
|
@ -819,7 +834,7 @@ impl<'q, 'w, Q: Query> IntoIterator for &'q mut QueryBorrow<'w, Q> {
|
|||
/// Iterator over the set of entities with the components in `Q`
|
||||
pub struct QueryIter<'q, 'w, Q: Query> {
|
||||
borrow: &'q mut QueryBorrow<'w, Q>,
|
||||
archetype_index: u32,
|
||||
archetype_index: usize,
|
||||
iter: Option<ChunkIter<Q>>,
|
||||
}
|
||||
|
||||
|
@ -834,7 +849,7 @@ impl<'q, 'w, Q: Query> Iterator for QueryIter<'q, 'w, Q> {
|
|||
loop {
|
||||
match self.iter {
|
||||
None => {
|
||||
let archetype = self.borrow.archetypes.get(self.archetype_index as usize)?;
|
||||
let archetype = self.borrow.archetypes.get(self.archetype_index)?;
|
||||
self.archetype_index += 1;
|
||||
unsafe {
|
||||
self.iter = Q::Fetch::get(archetype, 0).map(|fetch| ChunkIter {
|
||||
|
@ -868,14 +883,14 @@ impl<'q, 'w, Q: Query> ExactSizeIterator for QueryIter<'q, 'w, Q> {
|
|||
.archetypes
|
||||
.iter()
|
||||
.filter(|&x| Q::Fetch::access(x).is_some())
|
||||
.map(|x| x.len() as usize)
|
||||
.map(|x| x.len())
|
||||
.sum()
|
||||
}
|
||||
}
|
||||
|
||||
struct ChunkIter<Q: Query> {
|
||||
fetch: Q::Fetch,
|
||||
len: u32,
|
||||
len: usize,
|
||||
}
|
||||
|
||||
impl<Q: Query> ChunkIter<Q> {
|
||||
|
@ -900,9 +915,9 @@ impl<Q: Query> ChunkIter<Q> {
|
|||
/// Batched version of `QueryIter`
|
||||
pub struct BatchedIter<'q, 'w, Q: Query> {
|
||||
borrow: &'q mut QueryBorrow<'w, Q>,
|
||||
archetype_index: u32,
|
||||
batch_size: u32,
|
||||
batch: u32,
|
||||
archetype_index: usize,
|
||||
batch_size: usize,
|
||||
batch: usize,
|
||||
}
|
||||
|
||||
unsafe impl<'q, 'w, Q: Query> Send for BatchedIter<'q, 'w, Q> {}
|
||||
|
@ -913,14 +928,14 @@ impl<'q, 'w, Q: Query> Iterator for BatchedIter<'q, 'w, Q> {
|
|||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
loop {
|
||||
let archetype = self.borrow.archetypes.get(self.archetype_index as usize)?;
|
||||
let archetype = self.borrow.archetypes.get(self.archetype_index)?;
|
||||
let offset = self.batch_size * self.batch;
|
||||
if offset >= archetype.len() {
|
||||
self.archetype_index += 1;
|
||||
self.batch = 0;
|
||||
continue;
|
||||
}
|
||||
if let Some(fetch) = unsafe { Q::Fetch::get(archetype, offset as usize) } {
|
||||
if let Some(fetch) = unsafe { Q::Fetch::get(archetype, offset) } {
|
||||
self.batch += 1;
|
||||
return Some(Batch {
|
||||
_marker: PhantomData,
|
||||
|
@ -1003,10 +1018,11 @@ macro_rules! tuple_impl {
|
|||
impl<$($name: Query),*> Query for ($($name,)*) {
|
||||
type Fetch = ($($name::Fetch,)*);
|
||||
}
|
||||
|
||||
unsafe impl<$($name: ReadOnlyFetch),*> ReadOnlyFetch for ($($name,)*) {}
|
||||
};
|
||||
}
|
||||
|
||||
//smaller_tuples_too!(tuple_impl, B, A);
|
||||
smaller_tuples_too!(tuple_impl, O, N, M, L, K, J, I, H, G, F, E, D, C, B, A);
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -1067,31 +1083,31 @@ mod tests {
|
|||
let e3 = world.spawn((A(0), B(0)));
|
||||
world.spawn((A(0), B));
|
||||
|
||||
for (i, mut a) in world.query::<Mut<A>>().iter().enumerate() {
|
||||
for (i, mut a) in world.query_mut::<Mut<A>>().iter().enumerate() {
|
||||
if i % 2 == 0 {
|
||||
a.0 += 1;
|
||||
}
|
||||
}
|
||||
|
||||
fn get_changed_a(world: &World) -> Vec<Entity> {
|
||||
fn get_changed_a(world: &mut World) -> Vec<Entity> {
|
||||
world
|
||||
.query::<(Mutated<A>, Entity)>()
|
||||
.query_mut::<(Mutated<A>, Entity)>()
|
||||
.iter()
|
||||
.map(|(_a, e)| e)
|
||||
.collect::<Vec<Entity>>()
|
||||
};
|
||||
|
||||
assert_eq!(get_changed_a(&world), vec![e1, e3]);
|
||||
assert_eq!(get_changed_a(&mut world), vec![e1, e3]);
|
||||
|
||||
// ensure changing an entity's archetypes also moves its mutated state
|
||||
world.insert(e1, (C,)).unwrap();
|
||||
|
||||
assert_eq!(get_changed_a(&world), vec![e3, e1], "changed entities list should not change (although the order will due to archetype moves)");
|
||||
assert_eq!(get_changed_a(&mut world), vec![e3, e1], "changed entities list should not change (although the order will due to archetype moves)");
|
||||
|
||||
// spawning a new A entity should not change existing mutated state
|
||||
world.insert(e1, (A(0), B)).unwrap();
|
||||
assert_eq!(
|
||||
get_changed_a(&world),
|
||||
get_changed_a(&mut world),
|
||||
vec![e3, e1],
|
||||
"changed entities list should not change"
|
||||
);
|
||||
|
@ -1099,7 +1115,7 @@ mod tests {
|
|||
// removing an unchanged entity should not change mutated state
|
||||
world.despawn(e2).unwrap();
|
||||
assert_eq!(
|
||||
get_changed_a(&world),
|
||||
get_changed_a(&mut world),
|
||||
vec![e3, e1],
|
||||
"changed entities list should not change"
|
||||
);
|
||||
|
@ -1107,7 +1123,7 @@ mod tests {
|
|||
// removing a changed entity should remove it from enumeration
|
||||
world.despawn(e1).unwrap();
|
||||
assert_eq!(
|
||||
get_changed_a(&world),
|
||||
get_changed_a(&mut world),
|
||||
vec![e3],
|
||||
"e1 should no longer be returned"
|
||||
);
|
||||
|
@ -1115,7 +1131,7 @@ mod tests {
|
|||
world.clear_trackers();
|
||||
|
||||
assert!(world
|
||||
.query::<(Mutated<A>, Entity)>()
|
||||
.query_mut::<(Mutated<A>, Entity)>()
|
||||
.iter()
|
||||
.map(|(_a, e)| e)
|
||||
.collect::<Vec<Entity>>()
|
||||
|
@ -1129,16 +1145,16 @@ mod tests {
|
|||
let e2 = world.spawn((A(0), B(0)));
|
||||
world.spawn((A(0), B(0)));
|
||||
|
||||
for mut a in world.query::<Mut<A>>().iter() {
|
||||
for mut a in world.query_mut::<Mut<A>>().iter() {
|
||||
a.0 += 1;
|
||||
}
|
||||
|
||||
for mut b in world.query::<Mut<B>>().iter().skip(1).take(1) {
|
||||
for mut b in world.query_mut::<Mut<B>>().iter().skip(1).take(1) {
|
||||
b.0 += 1;
|
||||
}
|
||||
|
||||
let a_b_changed = world
|
||||
.query::<(Mutated<A>, Mutated<B>, Entity)>()
|
||||
.query_mut::<(Mutated<A>, Mutated<B>, Entity)>()
|
||||
.iter()
|
||||
.map(|(_a, _b, e)| e)
|
||||
.collect::<Vec<Entity>>();
|
||||
|
@ -1154,16 +1170,16 @@ mod tests {
|
|||
let _e4 = world.spawn((A(0), B(0)));
|
||||
|
||||
// Mutate A in entities e1 and e2
|
||||
for mut a in world.query::<Mut<A>>().iter().take(2) {
|
||||
for mut a in world.query_mut::<Mut<A>>().iter().take(2) {
|
||||
a.0 += 1;
|
||||
}
|
||||
// Mutate B in entities e2 and e3
|
||||
for mut b in world.query::<Mut<B>>().iter().skip(1).take(2) {
|
||||
for mut b in world.query_mut::<Mut<B>>().iter().skip(1).take(2) {
|
||||
b.0 += 1;
|
||||
}
|
||||
|
||||
let a_b_changed = world
|
||||
.query::<(Or<(Mutated<A>, Mutated<B>)>, Entity)>()
|
||||
.query_mut::<(Or<(Mutated<A>, Mutated<B>)>, Entity)>()
|
||||
.iter()
|
||||
.map(|((_a, _b), e)| e)
|
||||
.collect::<Vec<Entity>>();
|
||||
|
|
|
@ -3,15 +3,14 @@
|
|||
use core::marker::PhantomData;
|
||||
|
||||
use crate::{
|
||||
query::{Fetch, With, Without},
|
||||
query::{Fetch, ReadOnlyFetch, With, Without},
|
||||
Archetype, Component, Query,
|
||||
};
|
||||
|
||||
/// A borrow of a `World` sufficient to execute the query `Q` on a single entity
|
||||
pub struct QueryOne<'a, Q: Query> {
|
||||
archetype: &'a Archetype,
|
||||
index: u32,
|
||||
borrowed: bool,
|
||||
index: usize,
|
||||
_marker: PhantomData<Q>,
|
||||
}
|
||||
|
||||
|
@ -21,11 +20,10 @@ impl<'a, Q: Query> QueryOne<'a, Q> {
|
|||
/// # Safety
|
||||
///
|
||||
/// `index` must be in-bounds for `archetype`
|
||||
pub(crate) unsafe fn new(archetype: &'a Archetype, index: u32) -> Self {
|
||||
pub(crate) unsafe fn new(archetype: &'a Archetype, index: usize) -> Self {
|
||||
Self {
|
||||
archetype,
|
||||
index,
|
||||
borrowed: false,
|
||||
_marker: PhantomData,
|
||||
}
|
||||
}
|
||||
|
@ -37,13 +35,8 @@ impl<'a, Q: Query> QueryOne<'a, Q> {
|
|||
/// Panics if called more than once or if it would construct a borrow that clashes with another
|
||||
/// pre-existing borrow.
|
||||
pub fn get(&mut self) -> Option<<Q::Fetch as Fetch<'_>>::Item> {
|
||||
if self.borrowed {
|
||||
panic!("called QueryOnce::get twice; construct a new query instead");
|
||||
}
|
||||
unsafe {
|
||||
let mut fetch = Q::Fetch::get(self.archetype, self.index as usize)?;
|
||||
self.borrowed = true;
|
||||
Q::Fetch::borrow(self.archetype);
|
||||
let mut fetch = Q::Fetch::get(self.archetype, self.index)?;
|
||||
Some(fetch.next())
|
||||
}
|
||||
}
|
||||
|
@ -63,26 +56,81 @@ impl<'a, Q: Query> QueryOne<'a, Q> {
|
|||
}
|
||||
|
||||
/// Helper to change the type of the query
|
||||
fn transform<R: Query>(mut self) -> QueryOne<'a, R> {
|
||||
let x = QueryOne {
|
||||
fn transform<R: Query>(self) -> QueryOne<'a, R> {
|
||||
QueryOne {
|
||||
archetype: self.archetype,
|
||||
index: self.index,
|
||||
borrowed: self.borrowed,
|
||||
_marker: PhantomData,
|
||||
};
|
||||
// Ensure `Drop` won't fire redundantly
|
||||
self.borrowed = false;
|
||||
x
|
||||
}
|
||||
}
|
||||
|
||||
impl<Q: Query> Drop for QueryOne<'_, Q> {
|
||||
fn drop(&mut self) {
|
||||
if self.borrowed {
|
||||
Q::Fetch::release(self.archetype);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<Q: Query> Send for QueryOne<'_, Q> {}
|
||||
unsafe impl<Q: Query> Sync for QueryOne<'_, Q> {}
|
||||
|
||||
/// A read only borrow of a `World` sufficient to execute the query `Q` on a single entity
|
||||
pub struct ReadOnlyQueryOne<'a, Q: Query> {
|
||||
archetype: &'a Archetype,
|
||||
index: usize,
|
||||
_marker: PhantomData<Q>,
|
||||
}
|
||||
|
||||
impl<'a, Q: Query> ReadOnlyQueryOne<'a, Q>
|
||||
where
|
||||
Q::Fetch: ReadOnlyFetch,
|
||||
{
|
||||
/// Construct a query accessing the entity in `archetype` at `index`
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// `index` must be in-bounds for `archetype`
|
||||
pub(crate) unsafe fn new(archetype: &'a Archetype, index: usize) -> Self {
|
||||
Self {
|
||||
archetype,
|
||||
index,
|
||||
_marker: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the query result, or `None` if the entity does not satisfy the query
|
||||
///
|
||||
/// Must be called at most once.
|
||||
///
|
||||
/// Panics if called more than once or if it would construct a borrow that clashes with another
|
||||
/// pre-existing borrow.
|
||||
pub fn get(&self) -> Option<<Q::Fetch as Fetch<'_>>::Item>
|
||||
where
|
||||
Q::Fetch: ReadOnlyFetch,
|
||||
{
|
||||
unsafe {
|
||||
let mut fetch = Q::Fetch::get(self.archetype, self.index)?;
|
||||
Some(fetch.next())
|
||||
}
|
||||
}
|
||||
|
||||
/// Transform the query into one that requires a certain component without borrowing it
|
||||
///
|
||||
/// See `QueryBorrow::with` for details.
|
||||
pub fn with<T: Component>(self) -> QueryOne<'a, With<T, Q>> {
|
||||
self.transform()
|
||||
}
|
||||
|
||||
/// Transform the query into one that skips entities having a certain component
|
||||
///
|
||||
/// See `QueryBorrow::without` for details.
|
||||
pub fn without<T: Component>(self) -> QueryOne<'a, Without<T, Q>> {
|
||||
self.transform()
|
||||
}
|
||||
|
||||
/// Helper to change the type of the query
|
||||
fn transform<R: Query>(self) -> QueryOne<'a, R> {
|
||||
QueryOne {
|
||||
archetype: self.archetype,
|
||||
index: self.index,
|
||||
_marker: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<Q: Query> Send for ReadOnlyQueryOne<'_, Q> {}
|
||||
unsafe impl<Q: Query> Sync for ReadOnlyQueryOne<'_, Q> {}
|
||||
|
|
|
@ -8,6 +8,6 @@ impl Serialize for Entity {
|
|||
where
|
||||
S: Serializer,
|
||||
{
|
||||
serializer.serialize_u128(self.id())
|
||||
serializer.serialize_u32(self.id())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,9 +14,12 @@
|
|||
|
||||
// modified by Bevy contributors
|
||||
|
||||
use crate::alloc::vec::Vec;
|
||||
use crate::{
|
||||
alloc::vec::Vec, borrow::EntityRef, query::ReadOnlyFetch, query_one::ReadOnlyQueryOne,
|
||||
EntityReserver, Mut, RefMut,
|
||||
};
|
||||
use bevy_utils::{HashMap, HashSet};
|
||||
use core::{any::TypeId, convert::TryFrom, fmt, mem, ptr};
|
||||
use core::{any::TypeId, fmt, mem, ptr};
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
use std::error::Error;
|
||||
|
@ -24,8 +27,8 @@ use std::error::Error;
|
|||
use crate::{
|
||||
archetype::Archetype,
|
||||
entities::{Entities, Location},
|
||||
Bundle, DynamicBundle, Entity, EntityRef, MissingComponent, NoSuchEntity, Query, QueryBorrow,
|
||||
QueryOne, Ref, RefMut,
|
||||
Bundle, DynamicBundle, Entity, MissingComponent, NoSuchEntity, Query, QueryBorrow, QueryOne,
|
||||
Ref,
|
||||
};
|
||||
|
||||
/// An unordered collection of entities, each having any number of distinctly typed components
|
||||
|
@ -80,20 +83,11 @@ impl World {
|
|||
/// let b = world.spawn((456, true));
|
||||
/// ```
|
||||
pub fn spawn(&mut self, components: impl DynamicBundle) -> Entity {
|
||||
let entity = Entity::new();
|
||||
self.spawn_as_entity(entity, components);
|
||||
entity
|
||||
}
|
||||
// Ensure all entity allocations are accounted for so `self.entities` can realloc if
|
||||
// necessary
|
||||
self.flush();
|
||||
|
||||
/// Create an entity with the given Entity id and the given components
|
||||
///
|
||||
/// Arguments can be tuples, structs annotated with `#[derive(Bundle)]`, or the result of
|
||||
/// calling `build` on an `EntityBuilder`, which is useful if the set of components isn't
|
||||
/// statically known. To spawn an entity with only one component, use a one-element tuple like
|
||||
/// `(x,)`.
|
||||
///
|
||||
/// Any type that satisfies `Send + Sync + 'static` can be used as a component.
|
||||
pub fn spawn_as_entity(&mut self, entity: Entity, components: impl DynamicBundle) {
|
||||
let entity = self.entities.alloc();
|
||||
let archetype_id = components.with_ids(|ids| {
|
||||
self.index.get(ids).copied().unwrap_or_else(|| {
|
||||
let x = self.archetypes.len() as u32;
|
||||
|
@ -106,19 +100,18 @@ impl World {
|
|||
|
||||
let archetype = &mut self.archetypes[archetype_id as usize];
|
||||
unsafe {
|
||||
let index = archetype.allocate(entity.id());
|
||||
let index = archetype.allocate(entity);
|
||||
components.put(|ptr, ty, size| {
|
||||
archetype.put_dynamic(ptr, ty, size, index, true);
|
||||
true
|
||||
});
|
||||
self.entities.insert(
|
||||
entity,
|
||||
Location {
|
||||
archetype: archetype_id,
|
||||
index,
|
||||
},
|
||||
);
|
||||
self.entities.meta[entity.id as usize].location = Location {
|
||||
archetype: archetype_id,
|
||||
index,
|
||||
};
|
||||
}
|
||||
|
||||
entity
|
||||
}
|
||||
|
||||
/// Efficiently spawn a large number of entities with the same components
|
||||
|
@ -139,11 +132,13 @@ impl World {
|
|||
I: IntoIterator,
|
||||
I::Item: Bundle,
|
||||
{
|
||||
// Ensure all entity allocations are accounted for so `self.entities` can realloc if
|
||||
// necessary
|
||||
self.flush();
|
||||
|
||||
let iter = iter.into_iter();
|
||||
let (lower, upper) = iter.size_hint();
|
||||
let archetype_id = self.reserve_inner::<I::Item>(
|
||||
u32::try_from(upper.unwrap_or(lower)).expect("iterator too large"),
|
||||
);
|
||||
let archetype_id = self.reserve_inner::<I::Item>(upper.unwrap_or(lower) as u32);
|
||||
|
||||
SpawnBatchIter {
|
||||
inner: iter,
|
||||
|
@ -155,10 +150,12 @@ impl World {
|
|||
|
||||
/// Destroy an entity and all its components
|
||||
pub fn despawn(&mut self, entity: Entity) -> Result<(), NoSuchEntity> {
|
||||
self.flush();
|
||||
|
||||
let loc = self.entities.free(entity)?;
|
||||
let archetype = &mut self.archetypes[loc.archetype as usize];
|
||||
if let Some(moved) = unsafe { archetype.remove(loc.index) } {
|
||||
self.entities.get_mut(Entity::from_id(moved)).unwrap().index = loc.index;
|
||||
self.entities.get_mut(moved).unwrap().index = loc.index;
|
||||
}
|
||||
for ty in archetype.types() {
|
||||
let removed_entities = self
|
||||
|
@ -175,7 +172,13 @@ impl World {
|
|||
self.reserve_inner::<T>(additional);
|
||||
}
|
||||
|
||||
/// Reserves an entity.
|
||||
pub fn reserve_entity(&self) -> Entity {
|
||||
self.entities.reserve_entity()
|
||||
}
|
||||
|
||||
fn reserve_inner<T: Bundle>(&mut self, additional: u32) -> u32 {
|
||||
self.flush();
|
||||
self.entities.reserve(additional);
|
||||
|
||||
let archetype_id = T::with_static_ids(|ids| {
|
||||
|
@ -188,7 +191,7 @@ impl World {
|
|||
})
|
||||
});
|
||||
|
||||
self.archetypes[archetype_id as usize].reserve(additional);
|
||||
self.archetypes[archetype_id as usize].reserve(additional as usize);
|
||||
archetype_id
|
||||
}
|
||||
|
||||
|
@ -202,7 +205,7 @@ impl World {
|
|||
.removed_components
|
||||
.entry(ty.id())
|
||||
.or_insert_with(Vec::new);
|
||||
removed_entities.extend(archetype.iter_entities().map(|id| Entity::from_id(*id)));
|
||||
removed_entities.extend(archetype.iter_entities().copied());
|
||||
}
|
||||
archetype.clear();
|
||||
}
|
||||
|
@ -214,6 +217,14 @@ impl World {
|
|||
self.entities.contains(entity)
|
||||
}
|
||||
|
||||
/// Returns true if the given entity has a component with the given type id.
|
||||
pub fn has_component_type(&self, entity: Entity, ty: TypeId) -> bool {
|
||||
self.get_entity_location(entity)
|
||||
.map(|location| &self.archetypes[location.archetype as usize])
|
||||
.map(|archetype| archetype.has_type(ty))
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
/// Efficiently iterate over all entities that have certain components
|
||||
///
|
||||
/// Calling `iter` on the returned value yields `(Entity, Q)` tuples, where `Q` is some query
|
||||
|
@ -227,17 +238,6 @@ impl World {
|
|||
/// The returned `QueryBorrow` can be further transformed with combinator methods; see its
|
||||
/// documentation for details.
|
||||
///
|
||||
/// Iterating a query will panic if it would violate an existing unique reference or construct
|
||||
/// an invalid unique reference. This occurs when two simultaneously-active queries could expose
|
||||
/// the same entity. Simultaneous queries can access the same component type if and only if the
|
||||
/// world contains no entities that have all components required by both queries, assuming no
|
||||
/// other component borrows are outstanding.
|
||||
///
|
||||
/// Iterating a query yields references with lifetimes bound to the `QueryBorrow` returned
|
||||
/// here. To ensure those are invalidated, the return value of this method must be dropped for
|
||||
/// its dynamic borrows from the world to be released. Similarly, lifetime rules ensure that
|
||||
/// references obtained from a query cannot outlive the `QueryBorrow`.
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// # use bevy_hecs::*;
|
||||
|
@ -253,15 +253,86 @@ impl World {
|
|||
/// assert!(entities.contains(&(a, 123, true)));
|
||||
/// assert!(entities.contains(&(b, 456, false)));
|
||||
/// ```
|
||||
pub fn query<Q: Query>(&self) -> QueryBorrow<'_, Q> {
|
||||
pub fn query<Q: Query>(&self) -> QueryBorrow<'_, Q>
|
||||
where
|
||||
Q::Fetch: ReadOnlyFetch,
|
||||
{
|
||||
// SAFE: read-only access to world and read only query prevents mutable access
|
||||
unsafe { self.query_unchecked() }
|
||||
}
|
||||
|
||||
/// Efficiently iterate over all entities that have certain components
|
||||
///
|
||||
/// Calling `iter` on the returned value yields `(Entity, Q)` tuples, where `Q` is some query
|
||||
/// type. A query type is `&T`, `&mut T`, a tuple of query types, or an `Option` wrapping a
|
||||
/// query type, where `T` is any component type. Components queried with `&mut` must only appear
|
||||
/// once. Entities which do not have a component type referenced outside of an `Option` will be
|
||||
/// skipped.
|
||||
///
|
||||
/// Entities are yielded in arbitrary order.
|
||||
///
|
||||
/// The returned `QueryBorrow` can be further transformed with combinator methods; see its
|
||||
/// documentation for details.
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// # use bevy_hecs::*;
|
||||
/// let mut world = World::new();
|
||||
/// let a = world.spawn((123, true, "abc"));
|
||||
/// let b = world.spawn((456, false));
|
||||
/// let c = world.spawn((42, "def"));
|
||||
/// let entities = world.query::<(Entity, &i32, &bool)>()
|
||||
/// .iter()
|
||||
/// .map(|(e, &i, &b)| (e, i, b)) // Copy out of the world
|
||||
/// .collect::<Vec<_>>();
|
||||
/// assert_eq!(entities.len(), 2);
|
||||
/// assert!(entities.contains(&(a, 123, true)));
|
||||
/// assert!(entities.contains(&(b, 456, false)));
|
||||
/// ```
|
||||
pub fn query_mut<Q: Query>(&mut self) -> QueryBorrow<'_, Q> {
|
||||
// SAFE: unique mutable access
|
||||
unsafe { self.query_unchecked() }
|
||||
}
|
||||
|
||||
/// Efficiently iterate over all entities that have certain components
|
||||
///
|
||||
/// Calling `iter` on the returned value yields `(Entity, Q)` tuples, where `Q` is some query
|
||||
/// type. A query type is `&T`, `&mut T`, a tuple of query types, or an `Option` wrapping a
|
||||
/// query type, where `T` is any component type. Components queried with `&mut` must only appear
|
||||
/// once. Entities which do not have a component type referenced outside of an `Option` will be
|
||||
/// skipped.
|
||||
///
|
||||
/// Entities are yielded in arbitrary order.
|
||||
///
|
||||
/// The returned `QueryBorrow` can be further transformed with combinator methods; see its
|
||||
/// documentation for details.
|
||||
///
|
||||
/// # Safety
|
||||
/// This does not check for mutable query correctness. To be safe, make sure mutable queries
|
||||
/// have unique access to the components they query.
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// # use bevy_hecs::*;
|
||||
/// let mut world = World::new();
|
||||
/// let a = world.spawn((123, true, "abc"));
|
||||
/// let b = world.spawn((456, false));
|
||||
/// let c = world.spawn((42, "def"));
|
||||
/// let entities = world.query::<(Entity, &i32, &bool)>()
|
||||
/// .iter()
|
||||
/// .map(|(e, &i, &b)| (e, i, b)) // Copy out of the world
|
||||
/// .collect::<Vec<_>>();
|
||||
/// assert_eq!(entities.len(), 2);
|
||||
/// assert!(entities.contains(&(a, 123, true)));
|
||||
/// assert!(entities.contains(&(b, 456, false)));
|
||||
/// ```
|
||||
pub unsafe fn query_unchecked<Q: Query>(&self) -> QueryBorrow<'_, Q> {
|
||||
QueryBorrow::new(&self.archetypes)
|
||||
}
|
||||
|
||||
/// Prepare a query against a single entity
|
||||
/// Prepare a read only query against a single entity
|
||||
///
|
||||
/// Call `get` on the resulting `QueryOne` to actually execute the query. The `QueryOne` value
|
||||
/// is responsible for releasing the dynamically-checked borrow made by `get`, so it can't be
|
||||
/// dropped while references returned by `get` are live.
|
||||
/// Call `get` on the resulting `QueryOne` to actually execute the query.
|
||||
///
|
||||
/// Handy for accessing multiple components simultaneously.
|
||||
///
|
||||
|
@ -271,49 +342,128 @@ impl World {
|
|||
/// let mut world = World::new();
|
||||
/// let a = world.spawn((123, true, "abc"));
|
||||
/// // The returned query must outlive the borrow made by `get`
|
||||
/// let mut query = world.query_one::<(&mut i32, &bool)>(a).unwrap();
|
||||
/// let mut query = world.query_one::<(&i32, &bool)>(a).unwrap();
|
||||
/// let (number, flag) = query.get().unwrap();
|
||||
/// assert_eq!(*number, 123);
|
||||
/// ```
|
||||
pub fn query_one<Q: Query>(
|
||||
&self,
|
||||
entity: Entity,
|
||||
) -> Result<ReadOnlyQueryOne<'_, Q>, NoSuchEntity>
|
||||
where
|
||||
Q::Fetch: ReadOnlyFetch,
|
||||
{
|
||||
let loc = self.entities.get(entity)?;
|
||||
Ok(unsafe { ReadOnlyQueryOne::new(&self.archetypes[loc.archetype as usize], loc.index) })
|
||||
}
|
||||
|
||||
/// Prepare a query against a single entity
|
||||
///
|
||||
/// Call `get` on the resulting `QueryOne` to actually execute the query.
|
||||
///
|
||||
/// Handy for accessing multiple components simultaneously.
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// # use bevy_hecs::*;
|
||||
/// let mut world = World::new();
|
||||
/// let a = world.spawn((123, true, "abc"));
|
||||
/// // The returned query must outlive the borrow made by `get`
|
||||
/// let mut query = world.query_one_mut::<(&mut i32, &bool)>(a).unwrap();
|
||||
/// let (mut number, flag) = query.get().unwrap();
|
||||
/// if *flag { *number *= 2; }
|
||||
/// assert_eq!(*number, 246);
|
||||
/// ```
|
||||
pub fn query_one<Q: Query>(&self, entity: Entity) -> Result<QueryOne<'_, Q>, NoSuchEntity> {
|
||||
pub fn query_one_mut<Q: Query>(
|
||||
&mut self,
|
||||
entity: Entity,
|
||||
) -> Result<QueryOne<'_, Q>, NoSuchEntity> {
|
||||
// SAFE: unique mutable access to world
|
||||
unsafe { self.query_one_mut_unchecked(entity) }
|
||||
}
|
||||
|
||||
/// Prepare a query against a single entity, without checking the safety of mutable queries
|
||||
///
|
||||
/// Call `get` on the resulting `QueryOne` to actually execute the query.
|
||||
///
|
||||
/// Handy for accessing multiple components simultaneously.
|
||||
///
|
||||
/// # Safety
|
||||
/// This does not check for mutable query correctness. To be safe, make sure mutable queries
|
||||
/// have unique access to the components they query.
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// # use bevy_hecs::*;
|
||||
/// let mut world = World::new();
|
||||
/// let a = world.spawn((123, true, "abc"));
|
||||
/// // The returned query must outlive the borrow made by `get`
|
||||
/// let mut query = world.query_one_mut::<(&mut i32, &bool)>(a).unwrap();
|
||||
/// let (mut number, flag) = query.get().unwrap();
|
||||
/// if *flag { *number *= 2; }
|
||||
/// assert_eq!(*number, 246);
|
||||
/// ```
|
||||
pub unsafe fn query_one_mut_unchecked<Q: Query>(
|
||||
&self,
|
||||
entity: Entity,
|
||||
) -> Result<QueryOne<'_, Q>, NoSuchEntity> {
|
||||
let loc = self.entities.get(entity)?;
|
||||
Ok(unsafe { QueryOne::new(&self.archetypes[loc.archetype as usize], loc.index) })
|
||||
Ok(QueryOne::new(
|
||||
&self.archetypes[loc.archetype as usize],
|
||||
loc.index,
|
||||
))
|
||||
}
|
||||
|
||||
/// Borrow the `T` component of `entity`
|
||||
///
|
||||
/// Panics if the component is already uniquely borrowed from another entity with the same
|
||||
/// components.
|
||||
pub fn get<T: Component>(&self, entity: Entity) -> Result<Ref<'_, T>, ComponentError> {
|
||||
let loc = self.entities.get(entity)?;
|
||||
if loc.archetype == 0 {
|
||||
return Err(MissingComponent::new::<T>().into());
|
||||
pub fn get<T: Component>(&self, entity: Entity) -> Result<&'_ T, ComponentError> {
|
||||
unsafe {
|
||||
let loc = self.entities.get(entity)?;
|
||||
if loc.archetype == 0 {
|
||||
return Err(MissingComponent::new::<T>().into());
|
||||
}
|
||||
Ok(&*self.archetypes[loc.archetype as usize]
|
||||
.get::<T>()
|
||||
.ok_or_else(MissingComponent::new::<T>)?
|
||||
.as_ptr()
|
||||
.add(loc.index as usize))
|
||||
}
|
||||
Ok(unsafe { Ref::new(&self.archetypes[loc.archetype as usize], loc.index)? })
|
||||
}
|
||||
|
||||
/// Uniquely borrow the `T` component of `entity`
|
||||
///
|
||||
/// Panics if the component is already borrowed from another entity with the same components.
|
||||
pub fn get_mut<T: Component>(&self, entity: Entity) -> Result<RefMut<'_, T>, ComponentError> {
|
||||
let loc = self.entities.get(entity)?;
|
||||
if loc.archetype == 0 {
|
||||
return Err(MissingComponent::new::<T>().into());
|
||||
}
|
||||
Ok(unsafe { RefMut::new(&self.archetypes[loc.archetype as usize], loc.index)? })
|
||||
/// Mutably borrow the `T` component of `entity`
|
||||
pub fn get_mut<T: Component>(&mut self, entity: Entity) -> Result<Mut<'_, T>, ComponentError> {
|
||||
// SAFE: uniquely borrows world
|
||||
unsafe { self.get_mut_unchecked(entity) }
|
||||
}
|
||||
|
||||
/// Access an entity regardless of its component types
|
||||
///
|
||||
/// Does not immediately borrow any component.
|
||||
pub fn entity(&self, entity: Entity) -> Result<EntityRef<'_>, NoSuchEntity> {
|
||||
pub fn entity(&mut self, entity: Entity) -> Result<EntityRef<'_>, NoSuchEntity> {
|
||||
Ok(match self.entities.get(entity)? {
|
||||
Location { archetype: 0, .. } => EntityRef::empty(),
|
||||
loc => unsafe { EntityRef::new(&self.archetypes[loc.archetype as usize], loc.index) },
|
||||
})
|
||||
}
|
||||
|
||||
/// Borrow the `T` component of `entity` without checking if it can be mutated
|
||||
///
|
||||
/// # Safety
|
||||
/// This does not check for mutable access correctness. To be safe, make sure this is the only
|
||||
/// thing accessing this entity's T component.
|
||||
pub unsafe fn get_mut_unchecked<T: Component>(
|
||||
&self,
|
||||
entity: Entity,
|
||||
) -> Result<Mut<'_, T>, ComponentError> {
|
||||
let loc = self.entities.get(entity)?;
|
||||
if loc.archetype == 0 {
|
||||
return Err(MissingComponent::new::<T>().into());
|
||||
}
|
||||
Ok(Mut::new(
|
||||
&self.archetypes[loc.archetype as usize],
|
||||
loc.index,
|
||||
)?)
|
||||
}
|
||||
|
||||
/// Iterate over all entities in the world
|
||||
///
|
||||
/// Entities are yielded in arbitrary order. Prefer `World::query` for better performance when
|
||||
|
@ -330,7 +480,7 @@ impl World {
|
|||
/// assert!(ids.contains(&a));
|
||||
/// assert!(ids.contains(&b));
|
||||
/// ```
|
||||
pub fn iter(&self) -> Iter<'_> {
|
||||
pub fn iter(&mut self) -> Iter<'_> {
|
||||
Iter::new(&self.archetypes, &self.entities)
|
||||
}
|
||||
|
||||
|
@ -364,6 +514,7 @@ impl World {
|
|||
) -> Result<(), NoSuchEntity> {
|
||||
use std::collections::hash_map::Entry;
|
||||
|
||||
self.flush();
|
||||
let loc = self.entities.get_mut(entity)?;
|
||||
unsafe {
|
||||
// Assemble Vec<TypeInfo> for the final entity
|
||||
|
@ -407,18 +558,18 @@ impl World {
|
|||
loc.archetype as usize,
|
||||
target as usize,
|
||||
);
|
||||
let target_index = target_arch.allocate(entity.id());
|
||||
let target_index = target_arch.allocate(entity);
|
||||
loc.archetype = target;
|
||||
let old_index = mem::replace(&mut loc.index, target_index);
|
||||
if let Some(moved) =
|
||||
source_arch.move_to(old_index, |ptr, ty, size, is_added, is_mutated| {
|
||||
target_arch.put_dynamic(ptr, ty, size, target_index, false);
|
||||
let type_state = target_arch.get_type_state_mut(ty).unwrap();
|
||||
type_state.added_entities[target_index as usize] = is_added;
|
||||
type_state.mutated_entities[target_index as usize] = is_mutated;
|
||||
*type_state.added().as_ptr().add(target_index) = is_added;
|
||||
*type_state.mutated().as_ptr().add(target_index) = is_mutated;
|
||||
})
|
||||
{
|
||||
self.entities.get_mut(Entity::from_id(moved)).unwrap().index = old_index;
|
||||
self.entities.get_mut(moved).unwrap().index = old_index;
|
||||
}
|
||||
|
||||
components.put(|ptr, ty, size| {
|
||||
|
@ -462,6 +613,7 @@ impl World {
|
|||
pub fn remove<T: Bundle>(&mut self, entity: Entity) -> Result<T, ComponentError> {
|
||||
use std::collections::hash_map::Entry;
|
||||
|
||||
self.flush();
|
||||
let loc = self.entities.get_mut(entity)?;
|
||||
unsafe {
|
||||
let removed = T::with_static_ids(|ids| ids.iter().copied().collect::<HashSet<_>>());
|
||||
|
@ -490,7 +642,7 @@ impl World {
|
|||
loc.archetype as usize,
|
||||
target as usize,
|
||||
);
|
||||
let target_index = target_arch.allocate(entity.id());
|
||||
let target_index = target_arch.allocate(entity);
|
||||
loc.archetype = target;
|
||||
loc.index = target_index;
|
||||
let removed_components = &mut self.removed_components;
|
||||
|
@ -500,8 +652,8 @@ impl World {
|
|||
if let Some(dst) = target_arch.get_dynamic(ty, size, target_index) {
|
||||
ptr::copy_nonoverlapping(src, dst.as_ptr(), size);
|
||||
let state = target_arch.get_type_state_mut(ty).unwrap();
|
||||
state.added_entities[target_index as usize] = is_added;
|
||||
state.mutated_entities[target_index as usize] = is_mutated;
|
||||
*state.added().as_ptr().add(target_index) = is_added;
|
||||
*state.mutated().as_ptr().add(target_index) = is_mutated;
|
||||
} else {
|
||||
let removed_entities =
|
||||
removed_components.entry(ty).or_insert_with(Vec::new);
|
||||
|
@ -509,7 +661,7 @@ impl World {
|
|||
}
|
||||
})
|
||||
{
|
||||
self.entities.get_mut(Entity::from_id(moved)).unwrap().index = old_index;
|
||||
self.entities.get_mut(moved).unwrap().index = old_index;
|
||||
}
|
||||
Ok(bundle)
|
||||
}
|
||||
|
@ -522,24 +674,75 @@ impl World {
|
|||
self.remove::<(T,)>(entity).map(|(x,)| x)
|
||||
}
|
||||
|
||||
/// Borrow the `T` component of `entity` without safety checks
|
||||
///
|
||||
/// Should only be used as a building block for safe abstractions.
|
||||
/// Borrow the `T` component at the given location, without safety checks
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// `entity` must have been previously obtained from this `World`, and no unique borrow of the
|
||||
/// same component of `entity` may be live simultaneous to the returned reference.
|
||||
pub unsafe fn get_unchecked<T: Component>(&self, entity: Entity) -> Result<&T, ComponentError> {
|
||||
let loc = self.entities.get(entity)?;
|
||||
if loc.archetype == 0 {
|
||||
/// This does not check that the location is within bounds of the archetype.
|
||||
pub unsafe fn get_ref_at_location_unchecked<T: Component>(
|
||||
&self,
|
||||
location: Location,
|
||||
) -> Result<Ref<T>, ComponentError> {
|
||||
if location.archetype == 0 {
|
||||
return Err(MissingComponent::new::<T>().into());
|
||||
}
|
||||
Ok(&*self.archetypes[loc.archetype as usize]
|
||||
Ok(Ref::new(
|
||||
&self.archetypes[location.archetype as usize],
|
||||
location.index,
|
||||
)?)
|
||||
}
|
||||
|
||||
/// Borrow the `T` component at the given location, without safety checks
|
||||
///
|
||||
/// # Safety
|
||||
/// This does not check that the location is within bounds of the archetype.
|
||||
/// It also does not check for mutable access correctness. To be safe, make sure this is the only
|
||||
/// thing accessing this entity's T component.
|
||||
pub unsafe fn get_ref_mut_at_location_unchecked<T: Component>(
|
||||
&self,
|
||||
location: Location,
|
||||
) -> Result<RefMut<T>, ComponentError> {
|
||||
if location.archetype == 0 {
|
||||
return Err(MissingComponent::new::<T>().into());
|
||||
}
|
||||
Ok(RefMut::new(
|
||||
&self.archetypes[location.archetype as usize],
|
||||
location.index,
|
||||
)?)
|
||||
}
|
||||
|
||||
/// Borrow the `T` component at the given location, without safety checks
|
||||
/// # Safety
|
||||
/// This does not check that the location is within bounds of the archetype.
|
||||
pub unsafe fn get_at_location_unchecked<T: Component>(
|
||||
&self,
|
||||
location: Location,
|
||||
) -> Result<&T, ComponentError> {
|
||||
if location.archetype == 0 {
|
||||
return Err(MissingComponent::new::<T>().into());
|
||||
}
|
||||
Ok(&*self.archetypes[location.archetype as usize]
|
||||
.get::<T>()
|
||||
.ok_or_else(MissingComponent::new::<T>)?
|
||||
.as_ptr()
|
||||
.add(loc.index as usize))
|
||||
.add(location.index as usize))
|
||||
}
|
||||
|
||||
/// Borrow the `T` component at the given location, without safety checks
|
||||
/// # Safety
|
||||
/// This does not check that the location is within bounds of the archetype.
|
||||
/// It also does not check for mutable access correctness. To be safe, make sure this is the only
|
||||
/// thing accessing this entity's T component.
|
||||
pub unsafe fn get_mut_at_location_unchecked<T: Component>(
|
||||
&self,
|
||||
location: Location,
|
||||
) -> Result<Mut<T>, ComponentError> {
|
||||
if location.archetype == 0 {
|
||||
return Err(MissingComponent::new::<T>().into());
|
||||
}
|
||||
Ok(Mut::new(
|
||||
&self.archetypes[location.archetype as usize],
|
||||
location.index,
|
||||
)?)
|
||||
}
|
||||
|
||||
/// Uniquely borrow the `T` component of `entity` without safety checks
|
||||
|
@ -565,6 +768,31 @@ impl World {
|
|||
.add(loc.index as usize))
|
||||
}
|
||||
|
||||
/// Convert all reserved entities into empty entities that can be iterated and accessed
|
||||
///
|
||||
/// Invoked implicitly by `spawn`, `despawn`, `insert`, and `remove`.
|
||||
pub fn flush(&mut self) {
|
||||
let arch = &mut self.archetypes[0];
|
||||
for entity_id in self.entities.flush() {
|
||||
self.entities.meta[entity_id as usize].location.index = unsafe {
|
||||
arch.allocate(Entity {
|
||||
id: entity_id,
|
||||
generation: self.entities.meta[entity_id as usize].generation,
|
||||
})
|
||||
};
|
||||
}
|
||||
for i in 0..self.entities.reserved_len() {
|
||||
let id = self.entities.reserved(i);
|
||||
self.entities.meta[id as usize].location.index = unsafe {
|
||||
arch.allocate(Entity {
|
||||
id,
|
||||
generation: self.entities.meta[id as usize].generation,
|
||||
})
|
||||
};
|
||||
}
|
||||
self.entities.clear_reserved();
|
||||
}
|
||||
|
||||
/// Inspect the archetypes that entities are organized into
|
||||
///
|
||||
/// Useful for dynamically scheduling concurrent queries by checking borrows in advance. Does
|
||||
|
@ -608,6 +836,11 @@ impl World {
|
|||
|
||||
self.removed_components.clear();
|
||||
}
|
||||
|
||||
/// Gets an entity reserver, which can be used to reserve entity ids in a multi-threaded context.
|
||||
pub fn get_entity_reserver(&self) -> EntityReserver {
|
||||
self.entities.get_reserver()
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Send for World {}
|
||||
|
@ -619,7 +852,7 @@ impl Default for World {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> IntoIterator for &'a World {
|
||||
impl<'a> IntoIterator for &'a mut World {
|
||||
type IntoIter = Iter<'a>;
|
||||
type Item = (Entity, EntityRef<'a>);
|
||||
|
||||
|
@ -682,7 +915,7 @@ pub struct Iter<'a> {
|
|||
archetypes: core::slice::Iter<'a, Archetype>,
|
||||
entities: &'a Entities,
|
||||
current: Option<&'a Archetype>,
|
||||
index: u32,
|
||||
index: usize,
|
||||
}
|
||||
|
||||
impl<'a> Iter<'a> {
|
||||
|
@ -710,23 +943,21 @@ impl<'a> Iterator for Iter<'a> {
|
|||
self.index = 0;
|
||||
}
|
||||
Some(current) => {
|
||||
if self.index == current.len() as u32 {
|
||||
if self.index == current.len() {
|
||||
self.current = None;
|
||||
continue;
|
||||
}
|
||||
let index = self.index;
|
||||
self.index += 1;
|
||||
let id = current.entity_id(index);
|
||||
return Some((Entity::from_id(id), unsafe {
|
||||
EntityRef::new(current, index)
|
||||
}));
|
||||
let id = current.get_entity(index);
|
||||
return Some((id, unsafe { EntityRef::new(current, index) }));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
(0, Some(self.entities.entity_locations.len()))
|
||||
(0, Some(self.entities.meta.len()))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -784,20 +1015,17 @@ where
|
|||
|
||||
fn next(&mut self) -> Option<Entity> {
|
||||
let components = self.inner.next()?;
|
||||
let entity = Entity::new();
|
||||
let entity = self.entities.alloc();
|
||||
unsafe {
|
||||
let index = self.archetype.allocate(entity.id());
|
||||
let index = self.archetype.allocate(entity);
|
||||
components.put(|ptr, ty, size| {
|
||||
self.archetype.put_dynamic(ptr, ty, size, index, true);
|
||||
true
|
||||
});
|
||||
self.entities.insert(
|
||||
entity,
|
||||
Location {
|
||||
archetype: self.archetype_id,
|
||||
index,
|
||||
},
|
||||
);
|
||||
self.entities.meta[entity.id as usize].location = Location {
|
||||
archetype: self.archetype_id,
|
||||
index,
|
||||
};
|
||||
}
|
||||
Some(entity)
|
||||
}
|
||||
|
|
|
@ -164,36 +164,6 @@ fn dynamic_components() {
|
|||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "already borrowed")]
|
||||
fn illegal_borrow() {
|
||||
let mut world = World::new();
|
||||
world.spawn(("abc", 123));
|
||||
world.spawn(("def", 456));
|
||||
|
||||
world.query::<(&mut i32, &i32)>().iter();
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "already borrowed")]
|
||||
fn illegal_borrow_2() {
|
||||
let mut world = World::new();
|
||||
world.spawn(("abc", 123));
|
||||
world.spawn(("def", 456));
|
||||
|
||||
world.query::<(&mut i32, &mut i32)>().iter();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn disjoint_queries() {
|
||||
let mut world = World::new();
|
||||
world.spawn(("abc", true));
|
||||
world.spawn(("def", 456));
|
||||
|
||||
let _a = world.query::<(&mut &str, &bool)>();
|
||||
let _b = world.query::<(&mut &str, &i32)>();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn shared_borrow() {
|
||||
let mut world = World::new();
|
||||
|
@ -203,15 +173,6 @@ fn shared_borrow() {
|
|||
world.query::<(&i32, &i32)>();
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "already borrowed")]
|
||||
fn illegal_random_access() {
|
||||
let mut world = World::new();
|
||||
let e = world.spawn(("abc", 123));
|
||||
let _borrow = world.get_mut::<i32>(e).unwrap();
|
||||
world.get::<i32>(e).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "macros")]
|
||||
fn derived_bundle() {
|
||||
|
@ -267,7 +228,7 @@ fn alias() {
|
|||
let mut world = World::new();
|
||||
world.spawn(("abc", 123));
|
||||
world.spawn(("def", 456, true));
|
||||
let mut q = world.query::<&mut i32>();
|
||||
let mut q = world.query_mut::<Entity>();
|
||||
let _a = q.iter().collect::<Vec<_>>();
|
||||
let _b = q.iter().collect::<Vec<_>>();
|
||||
}
|
||||
|
|
|
@ -266,8 +266,9 @@ impl<'a, T: Resource> FetchResource<'a> for FetchResourceWrite<T> {
|
|||
type Item = ResMut<'a, T>;
|
||||
|
||||
unsafe fn get(resources: &'a Resources, _system_id: Option<SystemId>) -> Self::Item {
|
||||
let (value, mutated) = resources.get_unsafe_ref_with_mutated::<T>(ResourceIndex::Global);
|
||||
ResMut::new(value, mutated)
|
||||
let (value, type_state) =
|
||||
resources.get_unsafe_ref_with_type_state::<T>(ResourceIndex::Global);
|
||||
ResMut::new(value, type_state.mutated())
|
||||
}
|
||||
|
||||
fn borrow(resources: &Resources) {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use super::{FetchResource, ResourceQuery};
|
||||
use crate::system::SystemId;
|
||||
use bevy_hecs::{Archetype, Ref, RefMut, TypeInfo};
|
||||
use bevy_hecs::{Archetype, Entity, Ref, RefMut, TypeInfo, TypeState};
|
||||
use bevy_utils::HashMap;
|
||||
use core::any::TypeId;
|
||||
use std::ptr::NonNull;
|
||||
|
@ -11,8 +11,8 @@ impl<T: Send + Sync + 'static> Resource for T {}
|
|||
|
||||
pub(crate) struct ResourceData {
|
||||
archetype: Archetype,
|
||||
default_index: Option<u32>,
|
||||
system_id_to_archetype_index: HashMap<u32, u32>,
|
||||
default_index: Option<usize>,
|
||||
system_id_to_archetype_index: HashMap<usize, usize>,
|
||||
}
|
||||
|
||||
pub enum ResourceIndex {
|
||||
|
@ -94,7 +94,7 @@ impl Resources {
|
|||
use std::cmp::Ordering;
|
||||
match index.cmp(&archetype.len()) {
|
||||
Ordering::Equal => {
|
||||
unsafe { archetype.allocate(index as u128) };
|
||||
unsafe { archetype.allocate(Entity::new(index as u32)) };
|
||||
}
|
||||
Ordering::Greater => panic!("attempted to access index beyond 'current_capacity + 1'"),
|
||||
Ordering::Less => (),
|
||||
|
@ -177,18 +177,18 @@ impl Resources {
|
|||
|
||||
#[inline]
|
||||
#[allow(clippy::missing_safety_doc)]
|
||||
pub unsafe fn get_unsafe_ref_with_mutated<T: Resource>(
|
||||
pub unsafe fn get_unsafe_ref_with_type_state<T: Resource>(
|
||||
&self,
|
||||
resource_index: ResourceIndex,
|
||||
) -> (NonNull<T>, NonNull<bool>) {
|
||||
) -> (NonNull<T>, &TypeState) {
|
||||
self.get_resource_data_index::<T>(resource_index)
|
||||
.and_then(|(data, index)| {
|
||||
data.archetype
|
||||
.get_with_mutated::<T>()
|
||||
.map(|(resource, mutated)| {
|
||||
.get_with_type_state::<T>()
|
||||
.map(|(resource, type_state)| {
|
||||
(
|
||||
NonNull::new_unchecked(resource.as_ptr().add(index)),
|
||||
NonNull::new_unchecked(mutated.as_ptr().add(index)),
|
||||
type_state,
|
||||
)
|
||||
})
|
||||
})
|
||||
|
@ -203,9 +203,10 @@ impl Resources {
|
|||
) -> (NonNull<bool>, NonNull<bool>) {
|
||||
self.get_resource_data_index::<T>(resource_index)
|
||||
.and_then(|(data, index)| {
|
||||
let type_state = data.archetype.get_type_state(TypeId::of::<T>())?;
|
||||
Some((
|
||||
NonNull::new_unchecked(data.archetype.get_added::<T>()?.as_ptr().add(index)),
|
||||
NonNull::new_unchecked(data.archetype.get_mutated::<T>()?.as_ptr().add(index)),
|
||||
NonNull::new_unchecked(type_state.added().as_ptr().add(index)),
|
||||
NonNull::new_unchecked(type_state.mutated().as_ptr().add(index)),
|
||||
))
|
||||
})
|
||||
.unwrap_or_else(|| panic!("Resource does not exist {}", std::any::type_name::<T>()))
|
||||
|
|
|
@ -6,8 +6,7 @@ use crate::{
|
|||
use bevy_hecs::{ArchetypesGeneration, World};
|
||||
use bevy_tasks::{ComputeTaskPool, CountdownEvent, TaskPool};
|
||||
use fixedbitset::FixedBitSet;
|
||||
use parking_lot::Mutex;
|
||||
use std::{ops::Range, sync::Arc};
|
||||
use std::ops::Range;
|
||||
|
||||
/// Executes each schedule stage in parallel by analyzing system dependencies.
|
||||
/// System execution order is undefined except under the following conditions:
|
||||
|
@ -109,7 +108,7 @@ impl ExecutorStage {
|
|||
pub fn prepare_to_next_thread_local(
|
||||
&mut self,
|
||||
world: &World,
|
||||
systems: &[Arc<Mutex<Box<dyn System>>>],
|
||||
systems: &mut [Box<dyn System>],
|
||||
schedule_changed: bool,
|
||||
next_thread_local_index: usize,
|
||||
) -> Range<usize> {
|
||||
|
@ -143,8 +142,7 @@ impl ExecutorStage {
|
|||
if schedule_changed || archetypes_generation_changed {
|
||||
// update each system's archetype access to latest world archetypes
|
||||
for system_index in prepare_system_index_range.clone() {
|
||||
let mut system = systems[system_index].lock();
|
||||
system.update_archetype_access(world);
|
||||
systems[system_index].update_archetype_access(world);
|
||||
|
||||
// Clear this so that the next block of code that populates it doesn't insert
|
||||
// duplicates
|
||||
|
@ -156,7 +154,7 @@ impl ExecutorStage {
|
|||
let mut current_archetype_access = ArchetypeAccess::default();
|
||||
let mut current_resource_access = TypeAccess::default();
|
||||
for system_index in prepare_system_index_range.clone() {
|
||||
let system = systems[system_index].lock();
|
||||
let system = &systems[system_index];
|
||||
let archetype_access = system.archetype_access();
|
||||
match system.thread_local_execution() {
|
||||
ThreadLocalExecution::NextFlush => {
|
||||
|
@ -169,7 +167,7 @@ impl ExecutorStage {
|
|||
for earlier_system_index in
|
||||
prepare_system_index_range.start..system_index
|
||||
{
|
||||
let earlier_system = systems[earlier_system_index].lock();
|
||||
let earlier_system = &systems[earlier_system_index];
|
||||
|
||||
// due to how prepare ranges work, previous systems should all be "NextFlush"
|
||||
debug_assert_eq!(
|
||||
|
@ -292,7 +290,7 @@ impl ExecutorStage {
|
|||
&self,
|
||||
world: &World,
|
||||
resources: &Resources,
|
||||
systems: &[Arc<Mutex<Box<dyn System>>>],
|
||||
systems: &mut [Box<dyn System>],
|
||||
prepared_system_range: Range<usize>,
|
||||
compute_pool: &TaskPool,
|
||||
) {
|
||||
|
@ -300,25 +298,16 @@ impl ExecutorStage {
|
|||
log::trace!("running systems {:?}", prepared_system_range);
|
||||
compute_pool.scope(|scope| {
|
||||
let start_system_index = prepared_system_range.start;
|
||||
for system_index in prepared_system_range {
|
||||
let system = systems[system_index].clone();
|
||||
|
||||
let mut system_index = start_system_index;
|
||||
for system in &mut systems[prepared_system_range] {
|
||||
log::trace!(
|
||||
"prepare {} {} with {} dependents and {} dependencies",
|
||||
system_index,
|
||||
system.lock().name(),
|
||||
system.name(),
|
||||
self.system_dependents[system_index].len(),
|
||||
self.system_dependencies[system_index].count_ones(..)
|
||||
);
|
||||
|
||||
for dependency in self.system_dependencies[system_index].ones() {
|
||||
log::trace!(
|
||||
" * system ({}) depends on {}",
|
||||
system_index,
|
||||
systems[dependency].lock().name()
|
||||
);
|
||||
}
|
||||
|
||||
// This event will be awaited, preventing the task from starting until all
|
||||
// our dependencies finish running
|
||||
let ready_event = &self.ready_events[system_index];
|
||||
|
@ -375,7 +364,6 @@ impl ExecutorStage {
|
|||
// Execute the system - in a scope to ensure the system lock is dropped before
|
||||
// triggering dependents
|
||||
{
|
||||
let mut system = system.lock();
|
||||
log::trace!("run {}", system.name());
|
||||
#[cfg(feature = "profiler")]
|
||||
crate::profiler_start(resources, system.name().clone());
|
||||
|
@ -389,6 +377,7 @@ impl ExecutorStage {
|
|||
trigger_event.decrement();
|
||||
}
|
||||
});
|
||||
system_index += 1;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -397,7 +386,7 @@ impl ExecutorStage {
|
|||
&mut self,
|
||||
world: &mut World,
|
||||
resources: &mut Resources,
|
||||
systems: &[Arc<Mutex<Box<dyn System>>>],
|
||||
systems: &mut [Box<dyn System>],
|
||||
schedule_changed: bool,
|
||||
) {
|
||||
let start_archetypes_generation = world.archetypes_generation();
|
||||
|
@ -424,7 +413,6 @@ impl ExecutorStage {
|
|||
.resize(systems.len(), Vec::new());
|
||||
|
||||
for (system_index, system) in systems.iter().enumerate() {
|
||||
let system = system.lock();
|
||||
if system.thread_local_execution() == ThreadLocalExecution::Immediate {
|
||||
self.thread_local_system_indices.push(system_index);
|
||||
}
|
||||
|
@ -466,7 +454,7 @@ impl ExecutorStage {
|
|||
self.thread_local_system_indices[next_thread_local_index];
|
||||
{
|
||||
// if a thread local system is ready to run, run it exclusively on the main thread
|
||||
let mut system = systems[thread_local_system_index].lock();
|
||||
let system = systems[thread_local_system_index].as_mut();
|
||||
log::trace!("running thread local system {}", system.name());
|
||||
system.run(world, resources);
|
||||
system.run_thread_local(world, resources);
|
||||
|
@ -495,8 +483,7 @@ impl ExecutorStage {
|
|||
}
|
||||
|
||||
// "flush"
|
||||
for system in systems.iter() {
|
||||
let mut system = system.lock();
|
||||
for system in systems.iter_mut() {
|
||||
match system.thread_local_execution() {
|
||||
ThreadLocalExecution::NextFlush => system.run_thread_local(world, resources),
|
||||
ThreadLocalExecution::Immediate => { /* already ran */ }
|
||||
|
@ -559,6 +546,7 @@ mod tests {
|
|||
schedule.add_system_to_stage("PostArchetypeChange", read.system());
|
||||
|
||||
let mut executor = ParallelExecutor::default();
|
||||
schedule.initialize(&mut world, &mut resources);
|
||||
executor.run(&mut schedule, &mut world, &mut resources);
|
||||
}
|
||||
|
||||
|
|
|
@ -4,15 +4,14 @@ use crate::{
|
|||
};
|
||||
use bevy_hecs::World;
|
||||
use bevy_utils::{HashMap, HashSet};
|
||||
use parking_lot::Mutex;
|
||||
use std::{borrow::Cow, sync::Arc};
|
||||
use std::borrow::Cow;
|
||||
|
||||
/// An ordered collection of stages, which each contain an ordered list of [System]s.
|
||||
/// Schedules are essentially the "execution plan" for an App's systems.
|
||||
/// They are run on a given [World] and [Resources] reference.
|
||||
#[derive(Default)]
|
||||
pub struct Schedule {
|
||||
pub(crate) stages: HashMap<Cow<'static, str>, Vec<Arc<Mutex<Box<dyn System>>>>>,
|
||||
pub(crate) stages: HashMap<Cow<'static, str>, Vec<Box<dyn System>>>,
|
||||
pub(crate) stage_order: Vec<Cow<'static, str>>,
|
||||
pub(crate) system_ids: HashSet<SystemId>,
|
||||
generation: usize,
|
||||
|
@ -94,7 +93,7 @@ impl Schedule {
|
|||
);
|
||||
}
|
||||
self.system_ids.insert(system.id());
|
||||
systems.push(Arc::new(Mutex::new(system)));
|
||||
systems.push(system);
|
||||
|
||||
self.generation += 1;
|
||||
self
|
||||
|
@ -118,7 +117,7 @@ impl Schedule {
|
|||
);
|
||||
}
|
||||
self.system_ids.insert(system.id());
|
||||
systems.insert(0, Arc::new(Mutex::new(system)));
|
||||
systems.insert(0, system);
|
||||
|
||||
self.generation += 1;
|
||||
self
|
||||
|
@ -128,7 +127,6 @@ impl Schedule {
|
|||
for stage_name in self.stage_order.iter() {
|
||||
if let Some(stage_systems) = self.stages.get_mut(stage_name) {
|
||||
for system in stage_systems.iter_mut() {
|
||||
let mut system = system.lock();
|
||||
#[cfg(feature = "profiler")]
|
||||
crate::profiler_start(resources, system.name().clone());
|
||||
system.update_archetype_access(world);
|
||||
|
@ -147,7 +145,6 @@ impl Schedule {
|
|||
// "flush"
|
||||
// NOTE: when this is made parallel a full sync is required here
|
||||
for system in stage_systems.iter_mut() {
|
||||
let mut system = system.lock();
|
||||
match system.thread_local_execution() {
|
||||
ThreadLocalExecution::NextFlush => {
|
||||
system.run_thread_local(world, resources)
|
||||
|
@ -163,15 +160,14 @@ impl Schedule {
|
|||
}
|
||||
|
||||
// TODO: move this code to ParallelExecutor
|
||||
pub fn initialize(&mut self, resources: &mut Resources) {
|
||||
pub fn initialize(&mut self, world: &mut World, resources: &mut Resources) {
|
||||
if self.last_initialize_generation == self.generation {
|
||||
return;
|
||||
}
|
||||
|
||||
for stage in self.stages.values_mut() {
|
||||
for system in stage.iter_mut() {
|
||||
let mut system = system.lock();
|
||||
system.initialize(resources);
|
||||
system.initialize(world, resources);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use super::SystemId;
|
||||
use crate::resource::{Resource, Resources};
|
||||
use bevy_hecs::{Bundle, Component, DynamicBundle, Entity, World};
|
||||
use bevy_hecs::{Bundle, Component, DynamicBundle, Entity, EntityReserver, World};
|
||||
use parking_lot::Mutex;
|
||||
use std::{marker::PhantomData, sync::Arc};
|
||||
|
||||
|
@ -31,23 +31,6 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) struct SpawnAsEntity<T>
|
||||
where
|
||||
T: DynamicBundle + Send + Sync + 'static,
|
||||
{
|
||||
entity: Entity,
|
||||
components: T,
|
||||
}
|
||||
|
||||
impl<T> WorldWriter for SpawnAsEntity<T>
|
||||
where
|
||||
T: DynamicBundle + Send + Sync + 'static,
|
||||
{
|
||||
fn write(self: Box<Self>, world: &mut World) {
|
||||
world.spawn_as_entity(self.entity, self.components);
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct SpawnBatch<I>
|
||||
where
|
||||
I: IntoIterator,
|
||||
|
@ -158,24 +141,19 @@ impl<T: Resource> ResourcesWriter for InsertLocalResource<T> {
|
|||
pub struct CommandsInternal {
|
||||
pub commands: Vec<Command>,
|
||||
pub current_entity: Option<Entity>,
|
||||
pub entity_reserver: Option<EntityReserver>,
|
||||
}
|
||||
|
||||
impl CommandsInternal {
|
||||
pub fn spawn(&mut self, components: impl DynamicBundle + Send + Sync + 'static) -> &mut Self {
|
||||
self.spawn_as_entity(Entity::new(), components)
|
||||
}
|
||||
|
||||
pub fn spawn_as_entity(
|
||||
&mut self,
|
||||
entity: Entity,
|
||||
components: impl DynamicBundle + Send + Sync + 'static,
|
||||
) -> &mut Self {
|
||||
let entity = self
|
||||
.entity_reserver
|
||||
.as_ref()
|
||||
.expect("entity reserver has not been set")
|
||||
.reserve_entity();
|
||||
self.current_entity = Some(entity);
|
||||
self.commands
|
||||
.push(Command::WriteWorld(Box::new(SpawnAsEntity {
|
||||
entity,
|
||||
components,
|
||||
})));
|
||||
.push(Command::WriteWorld(Box::new(Insert { entity, components })));
|
||||
self
|
||||
}
|
||||
|
||||
|
@ -224,17 +202,9 @@ pub struct Commands {
|
|||
|
||||
impl Commands {
|
||||
pub fn spawn(&mut self, components: impl DynamicBundle + Send + Sync + 'static) -> &mut Self {
|
||||
self.spawn_as_entity(Entity::new(), components)
|
||||
}
|
||||
|
||||
pub fn spawn_as_entity(
|
||||
&mut self,
|
||||
entity: Entity,
|
||||
components: impl DynamicBundle + Send + Sync + 'static,
|
||||
) -> &mut Self {
|
||||
{
|
||||
let mut commands = self.commands.lock();
|
||||
commands.spawn_as_entity(entity, components);
|
||||
commands.spawn(components);
|
||||
}
|
||||
self
|
||||
}
|
||||
|
@ -348,6 +318,10 @@ impl Commands {
|
|||
phantom: PhantomData,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn set_entity_reserver(&self, entity_reserver: EntityReserver) {
|
||||
self.commands.lock().entity_reserver = Some(entity_reserver);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -361,6 +335,7 @@ mod tests {
|
|||
let mut world = World::default();
|
||||
let mut resources = Resources::default();
|
||||
let mut command_buffer = Commands::default();
|
||||
command_buffer.set_entity_reserver(world.get_entity_reserver());
|
||||
command_buffer.spawn((1u32, 2u64));
|
||||
command_buffer.insert_resource(3.14f32);
|
||||
command_buffer.apply(&mut world, &mut resources);
|
||||
|
|
|
@ -11,7 +11,7 @@ pub(crate) struct SystemFn<State, F, ThreadLocalF, Init, SetArchetypeAccess>
|
|||
where
|
||||
F: FnMut(&World, &Resources, &ArchetypeAccess, &mut State) + Send + Sync,
|
||||
ThreadLocalF: FnMut(&mut World, &mut Resources, &mut State) + Send + Sync,
|
||||
Init: FnMut(&mut Resources) + Send + Sync,
|
||||
Init: FnMut(&mut World, &mut Resources, &mut State) + Send + Sync,
|
||||
SetArchetypeAccess: FnMut(&World, &mut ArchetypeAccess, &mut State) + Send + Sync,
|
||||
State: Send + Sync,
|
||||
{
|
||||
|
@ -32,7 +32,7 @@ impl<State, F, ThreadLocalF, Init, SetArchetypeAccess> System
|
|||
where
|
||||
F: FnMut(&World, &Resources, &ArchetypeAccess, &mut State) + Send + Sync,
|
||||
ThreadLocalF: FnMut(&mut World, &mut Resources, &mut State) + Send + Sync,
|
||||
Init: FnMut(&mut Resources) + Send + Sync,
|
||||
Init: FnMut(&mut World, &mut Resources, &mut State) + Send + Sync,
|
||||
SetArchetypeAccess: FnMut(&World, &mut ArchetypeAccess, &mut State) + Send + Sync,
|
||||
State: Send + Sync,
|
||||
{
|
||||
|
@ -65,8 +65,8 @@ where
|
|||
(self.thread_local_func)(world, resources, &mut self.state);
|
||||
}
|
||||
|
||||
fn initialize(&mut self, resources: &mut Resources) {
|
||||
(self.init_func)(resources);
|
||||
fn initialize(&mut self, world: &mut World, resources: &mut Resources) {
|
||||
(self.init_func)(world, resources, &mut self.state);
|
||||
}
|
||||
|
||||
fn id(&self) -> SystemId {
|
||||
|
@ -104,21 +104,23 @@ macro_rules! impl_into_foreach_system {
|
|||
name: core::any::type_name::<Self>().into(),
|
||||
id,
|
||||
func: move |world, resources, _archetype_access, state| {
|
||||
<<($($resource,)*) as ResourceQuery>::Fetch as FetchResource>::borrow(&resources);
|
||||
{
|
||||
if let Some(($($resource,)*)) = resources.query_system::<($($resource,)*)>(id) {
|
||||
for ($($component,)*) in world.query::<($($component,)*)>().iter() {
|
||||
fn_call!(self, ($($commands, state)*), ($($resource),*), ($($component),*))
|
||||
// SAFE: the scheduler has ensured that there is no archetype clashing here
|
||||
unsafe {
|
||||
for ($($component,)*) in world.query_unchecked::<($($component,)*)>().iter() {
|
||||
fn_call!(self, ($($commands, state)*), ($($resource),*), ($($component),*))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
<<($($resource,)*) as ResourceQuery>::Fetch as FetchResource>::release(&resources);
|
||||
},
|
||||
thread_local_func: move |world, resources, state| {
|
||||
state.apply(world, resources);
|
||||
},
|
||||
init_func: move |resources| {
|
||||
init_func: move |world, resources, state| {
|
||||
<($($resource,)*)>::initialize(resources, Some(id));
|
||||
state.set_entity_reserver(world.get_entity_reserver())
|
||||
},
|
||||
resource_access: <<($($resource,)*) as ResourceQuery>::Fetch as FetchResource>::access(),
|
||||
archetype_access: ArchetypeAccess::default(),
|
||||
|
@ -174,7 +176,6 @@ macro_rules! impl_into_query_system {
|
|||
id,
|
||||
name: core::any::type_name::<Self>().into(),
|
||||
func: move |world, resources, archetype_access, state| {
|
||||
<<($($resource,)*) as ResourceQuery>::Fetch as FetchResource>::borrow(&resources);
|
||||
{
|
||||
if let Some(($($resource,)*)) = resources.query_system::<($($resource,)*)>(id) {
|
||||
let mut i = 0;
|
||||
|
@ -187,13 +188,14 @@ macro_rules! impl_into_query_system {
|
|||
fn_call!(self, ($($commands, commands)*), ($($resource),*), ($($query),*))
|
||||
}
|
||||
}
|
||||
<<($($resource,)*) as ResourceQuery>::Fetch as FetchResource>::release(&resources);
|
||||
},
|
||||
thread_local_func: move |world, resources, state| {
|
||||
state.commands.apply(world, resources);
|
||||
},
|
||||
init_func: move |resources| {
|
||||
init_func: move |world, resources, state| {
|
||||
<($($resource,)*)>::initialize(resources, Some(id));
|
||||
state.commands.set_entity_reserver(world.get_entity_reserver())
|
||||
|
||||
},
|
||||
resource_access: <<($($resource,)*) as ResourceQuery>::Fetch as FetchResource>::access(),
|
||||
archetype_access: ArchetypeAccess::default(),
|
||||
|
@ -317,7 +319,7 @@ where
|
|||
self.run(world, resources);
|
||||
},
|
||||
func: |_, _, _, _| {},
|
||||
init_func: |_| {},
|
||||
init_func: |_, _, _| {},
|
||||
set_archetype_access: |_, _, _| {},
|
||||
thread_local_execution: ThreadLocalExecution::Immediate,
|
||||
name: core::any::type_name::<F>().into(),
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use crate::ArchetypeAccess;
|
||||
use bevy_hecs::{
|
||||
Archetype, Component, ComponentError, Entity, Fetch, Query as HecsQuery, QueryOne, Ref, RefMut,
|
||||
World,
|
||||
Archetype, Component, ComponentError, Entity, Fetch, Query as HecsQuery, Ref, RefMut, With,
|
||||
Without, World,
|
||||
};
|
||||
use bevy_tasks::ParallelIterator;
|
||||
use std::marker::PhantomData;
|
||||
|
@ -33,13 +33,15 @@ impl<'a, Q: HecsQuery> Query<'a, Q> {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
pub fn iter(&mut self) -> QueryBorrow<'_, Q> {
|
||||
QueryBorrow::new(&self.world.archetypes, self.archetype_access)
|
||||
pub fn iter(&mut self) -> QueryBorrowChecked<'_, Q> {
|
||||
QueryBorrowChecked::new(&self.world.archetypes, self.archetype_access)
|
||||
}
|
||||
|
||||
// TODO: find a way to make `iter`, `get`, `get_mut`, and `entity` safe without using tracking pointers with global locks
|
||||
|
||||
/// Gets a reference to the entity's component of the given type. This will fail if the entity does not have
|
||||
/// the given component type or if the given component type does not match this query.
|
||||
pub fn get<T: Component>(&self, entity: Entity) -> Result<Ref<'_, T>, QueryError> {
|
||||
pub fn get<T: Component>(&self, entity: Entity) -> Result<Ref<T>, QueryError> {
|
||||
if let Some(location) = self.world.get_entity_location(entity) {
|
||||
if self
|
||||
.archetype_access
|
||||
|
@ -50,7 +52,12 @@ impl<'a, Q: HecsQuery> Query<'a, Q> {
|
|||
.mutable
|
||||
.contains(location.archetype as usize)
|
||||
{
|
||||
self.world.get(entity).map_err(QueryError::ComponentError)
|
||||
// SAFE: we have already checked that the entity/component matches our archetype access. and systems are scheduled to run with safe archetype access
|
||||
unsafe {
|
||||
self.world
|
||||
.get_ref_at_location_unchecked(location)
|
||||
.map_err(QueryError::ComponentError)
|
||||
}
|
||||
} else {
|
||||
Err(QueryError::CannotReadArchetype)
|
||||
}
|
||||
|
@ -59,7 +66,7 @@ impl<'a, Q: HecsQuery> Query<'a, Q> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn entity(&self, entity: Entity) -> Result<QueryOne<'_, Q>, QueryError> {
|
||||
pub fn entity(&mut self, entity: Entity) -> Result<QueryOneChecked<'_, Q>, QueryError> {
|
||||
if let Some(location) = self.world.get_entity_location(entity) {
|
||||
if self
|
||||
.archetype_access
|
||||
|
@ -70,7 +77,13 @@ impl<'a, Q: HecsQuery> Query<'a, Q> {
|
|||
.mutable
|
||||
.contains(location.archetype as usize)
|
||||
{
|
||||
Ok(self.world.query_one(entity).unwrap())
|
||||
// SAFE: we have already checked that the entity matches our archetype. and systems are scheduled to run with safe archetype access
|
||||
Ok(unsafe {
|
||||
QueryOneChecked::new(
|
||||
&self.world.archetypes[location.archetype as usize],
|
||||
location.index,
|
||||
)
|
||||
})
|
||||
} else {
|
||||
Err(QueryError::CannotReadArchetype)
|
||||
}
|
||||
|
@ -92,9 +105,12 @@ impl<'a, Q: HecsQuery> Query<'a, Q> {
|
|||
.mutable
|
||||
.contains(location.archetype as usize)
|
||||
{
|
||||
self.world
|
||||
.get_mut(entity)
|
||||
.map_err(QueryError::ComponentError)
|
||||
// SAFE: RefMut does exclusivity checks and we have already validated the entity
|
||||
unsafe {
|
||||
self.world
|
||||
.get_ref_mut_at_location_unchecked(location)
|
||||
.map_err(QueryError::ComponentError)
|
||||
}
|
||||
} else {
|
||||
Err(QueryError::CannotWriteArchetype)
|
||||
}
|
||||
|
@ -106,7 +122,7 @@ impl<'a, Q: HecsQuery> Query<'a, Q> {
|
|||
|
||||
/// Sets the entity's component to the given value. This will fail if the entity does not already have
|
||||
/// the given component type or if the given component type does not match this query.
|
||||
pub fn set<T: Component>(&self, entity: Entity, component: T) -> Result<(), QueryError> {
|
||||
pub fn set<T: Component>(&mut self, entity: Entity, component: T) -> Result<(), QueryError> {
|
||||
let mut current = self.get_mut::<T>(entity)?;
|
||||
*current = component;
|
||||
Ok(())
|
||||
|
@ -116,23 +132,18 @@ impl<'a, Q: HecsQuery> Query<'a, Q> {
|
|||
/// A borrow of a `World` sufficient to execute the query `Q`
|
||||
///
|
||||
/// Note that borrows are not released until this object is dropped.
|
||||
pub struct QueryBorrow<'w, Q: HecsQuery> {
|
||||
pub struct QueryBorrowChecked<'w, Q: HecsQuery> {
|
||||
archetypes: &'w [Archetype],
|
||||
archetype_access: &'w ArchetypeAccess,
|
||||
borrowed: bool,
|
||||
_marker: PhantomData<Q>,
|
||||
}
|
||||
|
||||
impl<'w, Q: HecsQuery> QueryBorrow<'w, Q> {
|
||||
impl<'w, Q: HecsQuery> QueryBorrowChecked<'w, Q> {
|
||||
pub(crate) fn new(archetypes: &'w [Archetype], archetype_access: &'w ArchetypeAccess) -> Self {
|
||||
for index in archetype_access.immutable.ones() {
|
||||
Q::Fetch::borrow(&archetypes[index]);
|
||||
}
|
||||
|
||||
for index in archetype_access.mutable.ones() {
|
||||
Q::Fetch::borrow(&archetypes[index]);
|
||||
}
|
||||
Self {
|
||||
archetypes,
|
||||
borrowed: false,
|
||||
archetype_access,
|
||||
_marker: PhantomData,
|
||||
}
|
||||
|
@ -143,6 +154,7 @@ impl<'w, Q: HecsQuery> QueryBorrow<'w, Q> {
|
|||
/// Must be called only once per query.
|
||||
#[inline]
|
||||
pub fn iter<'q>(&'q mut self) -> QueryIter<'q, 'w, Q> {
|
||||
self.borrow();
|
||||
QueryIter {
|
||||
borrow: self,
|
||||
archetype_index: 0,
|
||||
|
@ -164,7 +176,8 @@ impl<'w, Q: HecsQuery> QueryBorrow<'w, Q> {
|
|||
/// each batch could take longer than running the batch. On the other
|
||||
/// hand, a too large batch size risks that one batch is still running
|
||||
/// long after the rest have finished.
|
||||
pub fn par_iter<'q>(&'q mut self, batch_size: u32) -> ParIter<'q, 'w, Q> {
|
||||
pub fn par_iter<'q>(&'q mut self, batch_size: usize) -> ParIter<'q, 'w, Q> {
|
||||
self.borrow();
|
||||
ParIter {
|
||||
borrow: self,
|
||||
archetype_index: 0,
|
||||
|
@ -172,25 +185,45 @@ impl<'w, Q: HecsQuery> QueryBorrow<'w, Q> {
|
|||
batch: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<'w, Q: HecsQuery> Send for QueryBorrow<'w, Q> {}
|
||||
unsafe impl<'w, Q: HecsQuery> Sync for QueryBorrow<'w, Q> {}
|
||||
fn borrow(&mut self) {
|
||||
if self.borrowed {
|
||||
panic!(
|
||||
"called QueryBorrowChecked::iter twice on the same borrow; construct a new query instead"
|
||||
);
|
||||
}
|
||||
|
||||
impl<'w, Q: HecsQuery> Drop for QueryBorrow<'w, Q> {
|
||||
#[inline]
|
||||
fn drop(&mut self) {
|
||||
for index in self.archetype_access.immutable.ones() {
|
||||
Q::Fetch::release(&self.archetypes[index]);
|
||||
Q::Fetch::borrow(&self.archetypes[index]);
|
||||
}
|
||||
|
||||
for index in self.archetype_access.mutable.ones() {
|
||||
Q::Fetch::release(&self.archetypes[index]);
|
||||
Q::Fetch::borrow(&self.archetypes[index]);
|
||||
}
|
||||
|
||||
self.borrowed = true;
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<'w, Q: HecsQuery> Send for QueryBorrowChecked<'w, Q> {}
|
||||
unsafe impl<'w, Q: HecsQuery> Sync for QueryBorrowChecked<'w, Q> {}
|
||||
|
||||
impl<'w, Q: HecsQuery> Drop for QueryBorrowChecked<'w, Q> {
|
||||
#[inline]
|
||||
fn drop(&mut self) {
|
||||
if self.borrowed {
|
||||
for index in self.archetype_access.immutable.ones() {
|
||||
Q::Fetch::release(&self.archetypes[index]);
|
||||
}
|
||||
|
||||
for index in self.archetype_access.mutable.ones() {
|
||||
Q::Fetch::release(&self.archetypes[index]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'q, 'w, Q: HecsQuery> IntoIterator for &'q mut QueryBorrow<'w, Q> {
|
||||
impl<'q, 'w, Q: HecsQuery> IntoIterator for &'q mut QueryBorrowChecked<'w, Q> {
|
||||
type IntoIter = QueryIter<'q, 'w, Q>;
|
||||
type Item = <Q::Fetch as Fetch<'q>>::Item;
|
||||
|
||||
|
@ -202,8 +235,8 @@ impl<'q, 'w, Q: HecsQuery> IntoIterator for &'q mut QueryBorrow<'w, Q> {
|
|||
|
||||
/// Iterator over the set of entities with the components in `Q`
|
||||
pub struct QueryIter<'q, 'w, Q: HecsQuery> {
|
||||
borrow: &'q mut QueryBorrow<'w, Q>,
|
||||
archetype_index: u32,
|
||||
borrow: &'q mut QueryBorrowChecked<'w, Q>,
|
||||
archetype_index: usize,
|
||||
iter: Option<ChunkIter<Q>>,
|
||||
}
|
||||
|
||||
|
@ -252,14 +285,14 @@ impl<'q, 'w, Q: HecsQuery> ExactSizeIterator for QueryIter<'q, 'w, Q> {
|
|||
.archetypes
|
||||
.iter()
|
||||
.filter(|&x| Q::Fetch::access(x).is_some())
|
||||
.map(|x| x.len() as usize)
|
||||
.map(|x| x.len())
|
||||
.sum()
|
||||
}
|
||||
}
|
||||
|
||||
struct ChunkIter<Q: HecsQuery> {
|
||||
fetch: Q::Fetch,
|
||||
len: u32,
|
||||
len: usize,
|
||||
}
|
||||
|
||||
impl<Q: HecsQuery> ChunkIter<Q> {
|
||||
|
@ -284,10 +317,10 @@ impl<Q: HecsQuery> ChunkIter<Q> {
|
|||
|
||||
/// Batched version of `QueryIter`
|
||||
pub struct ParIter<'q, 'w, Q: HecsQuery> {
|
||||
borrow: &'q mut QueryBorrow<'w, Q>,
|
||||
archetype_index: u32,
|
||||
batch_size: u32,
|
||||
batch: u32,
|
||||
borrow: &'q mut QueryBorrowChecked<'w, Q>,
|
||||
archetype_index: usize,
|
||||
batch_size: usize,
|
||||
batch: usize,
|
||||
}
|
||||
|
||||
impl<'q, 'w, Q: HecsQuery> ParallelIterator<Batch<'q, Q>> for ParIter<'q, 'w, Q> {
|
||||
|
@ -295,7 +328,7 @@ impl<'q, 'w, Q: HecsQuery> ParallelIterator<Batch<'q, Q>> for ParIter<'q, 'w, Q>
|
|||
|
||||
fn next_batch(&mut self) -> Option<Batch<'q, Q>> {
|
||||
loop {
|
||||
let archetype = self.borrow.archetypes.get(self.archetype_index as usize)?;
|
||||
let archetype = self.borrow.archetypes.get(self.archetype_index)?;
|
||||
let offset = self.batch_size * self.batch;
|
||||
if offset >= archetype.len() {
|
||||
self.archetype_index += 1;
|
||||
|
@ -339,3 +372,77 @@ impl<'q, 'w, Q: HecsQuery> Iterator for Batch<'q, Q> {
|
|||
}
|
||||
|
||||
unsafe impl<'q, Q: HecsQuery> Send for Batch<'q, Q> {}
|
||||
|
||||
/// A borrow of a `World` sufficient to execute the query `Q` on a single entity
|
||||
pub struct QueryOneChecked<'a, Q: HecsQuery> {
|
||||
archetype: &'a Archetype,
|
||||
index: usize,
|
||||
borrowed: bool,
|
||||
_marker: PhantomData<Q>,
|
||||
}
|
||||
|
||||
impl<'a, Q: HecsQuery> QueryOneChecked<'a, Q> {
|
||||
/// Construct a query accessing the entity in `archetype` at `index`
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// `index` must be in-bounds for `archetype`
|
||||
pub(crate) unsafe fn new(archetype: &'a Archetype, index: usize) -> Self {
|
||||
Self {
|
||||
archetype,
|
||||
index,
|
||||
borrowed: false,
|
||||
_marker: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the query result, or `None` if the entity does not satisfy the query
|
||||
///
|
||||
/// Must be called at most once.
|
||||
///
|
||||
/// Panics if called more than once or if it would construct a borrow that clashes with another
|
||||
/// pre-existing borrow.
|
||||
pub fn get(&mut self) -> Option<<Q::Fetch as Fetch<'_>>::Item> {
|
||||
unsafe {
|
||||
let mut fetch = Q::Fetch::get(self.archetype, self.index as usize)?;
|
||||
self.borrowed = true;
|
||||
Q::Fetch::borrow(self.archetype);
|
||||
Some(fetch.next())
|
||||
}
|
||||
}
|
||||
|
||||
/// Transform the query into one that requires a certain component without borrowing it
|
||||
///
|
||||
/// See `QueryBorrow::with` for details.
|
||||
pub fn with<T: Component>(self) -> QueryOneChecked<'a, With<T, Q>> {
|
||||
self.transform()
|
||||
}
|
||||
|
||||
/// Transform the query into one that skips entities having a certain component
|
||||
///
|
||||
/// See `QueryBorrow::without` for details.
|
||||
pub fn without<T: Component>(self) -> QueryOneChecked<'a, Without<T, Q>> {
|
||||
self.transform()
|
||||
}
|
||||
|
||||
/// Helper to change the type of the query
|
||||
fn transform<R: HecsQuery>(self) -> QueryOneChecked<'a, R> {
|
||||
QueryOneChecked {
|
||||
archetype: self.archetype,
|
||||
index: self.index,
|
||||
borrowed: self.borrowed,
|
||||
_marker: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<Q: HecsQuery> Drop for QueryOneChecked<'_, Q> {
|
||||
fn drop(&mut self) {
|
||||
if self.borrowed {
|
||||
Q::Fetch::release(self.archetype);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<Q: HecsQuery> Send for QueryOneChecked<'_, Q> {}
|
||||
unsafe impl<Q: HecsQuery> Sync for QueryOneChecked<'_, Q> {}
|
||||
|
|
|
@ -12,12 +12,12 @@ pub enum ThreadLocalExecution {
|
|||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
||||
pub struct SystemId(pub u32);
|
||||
pub struct SystemId(pub usize);
|
||||
|
||||
impl SystemId {
|
||||
#[allow(clippy::new_without_default)]
|
||||
pub fn new() -> Self {
|
||||
SystemId(rand::random::<u32>())
|
||||
SystemId(rand::random::<usize>())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -31,7 +31,7 @@ pub trait System: Send + Sync {
|
|||
fn thread_local_execution(&self) -> ThreadLocalExecution;
|
||||
fn run(&mut self, world: &World, resources: &Resources);
|
||||
fn run_thread_local(&mut self, world: &mut World, resources: &mut Resources);
|
||||
fn initialize(&mut self, _resources: &mut Resources) {}
|
||||
fn initialize(&mut self, _world: &mut World, _resources: &mut Resources) {}
|
||||
}
|
||||
|
||||
/// Provides information about the archetypes a [System] reads and writes
|
||||
|
|
|
@ -22,7 +22,7 @@ pub struct WorldBuilder<'a> {
|
|||
|
||||
impl<'a> WorldBuilder<'a> {
|
||||
pub fn entity(&mut self) -> &mut Self {
|
||||
self.current_entity = Some(Entity::new());
|
||||
self.current_entity = Some(self.world.reserve_entity());
|
||||
self
|
||||
}
|
||||
|
||||
|
@ -61,10 +61,4 @@ impl<'a> WorldBuilder<'a> {
|
|||
self.current_entity = Some(self.world.spawn(components));
|
||||
self
|
||||
}
|
||||
|
||||
pub fn spawn_as_entity(&mut self, entity: Entity, components: impl DynamicBundle) -> &mut Self {
|
||||
self.world.spawn_as_entity(entity, components);
|
||||
self.current_entity = Some(entity);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ impl_property!(Entity, serialize_entity, deserialize_entity);
|
|||
mod private {
|
||||
use serde::{Deserialize, Serialize};
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub(super) struct Entity(pub(super) u128);
|
||||
pub(super) struct Entity(pub(super) u32);
|
||||
}
|
||||
|
||||
fn serialize_entity(entity: &Entity) -> Serializable {
|
||||
|
@ -20,5 +20,5 @@ fn deserialize_entity(
|
|||
_registry: &PropertyTypeRegistry,
|
||||
) -> Result<Box<dyn Property>, erased_serde::Error> {
|
||||
let entity = private::Entity::deserialize(deserializer)?;
|
||||
Ok(Box::new(Entity::from_id(entity.0)))
|
||||
Ok(Box::new(Entity::new(entity.0)))
|
||||
}
|
||||
|
|
|
@ -177,19 +177,21 @@ impl<'a> FetchResource<'a> for FetchDrawContext {
|
|||
|
||||
unsafe fn get(resources: &'a Resources, _system_id: Option<SystemId>) -> Self::Item {
|
||||
let pipelines = {
|
||||
let (value, mutated) = resources
|
||||
.get_unsafe_ref_with_mutated::<Assets<PipelineDescriptor>>(ResourceIndex::Global);
|
||||
ResMut::new(value, mutated)
|
||||
let (value, type_state) = resources
|
||||
.get_unsafe_ref_with_type_state::<Assets<PipelineDescriptor>>(
|
||||
ResourceIndex::Global,
|
||||
);
|
||||
ResMut::new(value, type_state.mutated())
|
||||
};
|
||||
let shaders = {
|
||||
let (value, mutated) =
|
||||
resources.get_unsafe_ref_with_mutated::<Assets<Shader>>(ResourceIndex::Global);
|
||||
ResMut::new(value, mutated)
|
||||
let (value, type_state) =
|
||||
resources.get_unsafe_ref_with_type_state::<Assets<Shader>>(ResourceIndex::Global);
|
||||
ResMut::new(value, type_state.mutated())
|
||||
};
|
||||
let pipeline_compiler = {
|
||||
let (value, mutated) =
|
||||
resources.get_unsafe_ref_with_mutated::<PipelineCompiler>(ResourceIndex::Global);
|
||||
ResMut::new(value, mutated)
|
||||
let (value, type_state) =
|
||||
resources.get_unsafe_ref_with_type_state::<PipelineCompiler>(ResourceIndex::Global);
|
||||
ResMut::new(value, type_state.mutated())
|
||||
};
|
||||
|
||||
DrawContext {
|
||||
|
|
|
@ -12,7 +12,7 @@ use crate::{
|
|||
},
|
||||
};
|
||||
use bevy_asset::{Assets, Handle};
|
||||
use bevy_ecs::{HecsQuery, Resources, World};
|
||||
use bevy_ecs::{HecsQuery, ReadOnlyFetch, Resources, World};
|
||||
use std::marker::PhantomData;
|
||||
|
||||
struct CameraInfo {
|
||||
|
@ -108,7 +108,10 @@ impl<Q: HecsQuery> PassNode<Q> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<Q: HecsQuery + Send + Sync + 'static> Node for PassNode<Q> {
|
||||
impl<Q: HecsQuery + Send + Sync + 'static> Node for PassNode<Q>
|
||||
where
|
||||
Q::Fetch: ReadOnlyFetch,
|
||||
{
|
||||
fn input(&self) -> &[ResourceSlotInfo] {
|
||||
&self.inputs
|
||||
}
|
||||
|
@ -190,7 +193,7 @@ impl<Q: HecsQuery + Send + Sync + 'static> Node for PassNode<Q> {
|
|||
// attempt to draw each visible entity
|
||||
let mut draw_state = DrawState::default();
|
||||
for visible_entity in visible_entities.iter() {
|
||||
if let Ok(mut query_one) = world.query_one::<Q>(visible_entity.entity) {
|
||||
if let Ok(query_one) = world.query_one::<Q>(visible_entity.entity) {
|
||||
if query_one.get().is_none() {
|
||||
// visible entity does not match the Pass query
|
||||
continue;
|
||||
|
|
|
@ -11,7 +11,7 @@ pub struct Scene {
|
|||
}
|
||||
|
||||
pub struct Entity {
|
||||
pub entity: u128,
|
||||
pub entity: u32,
|
||||
pub components: Vec<DynamicProperties>,
|
||||
}
|
||||
|
||||
|
@ -23,7 +23,7 @@ impl Scene {
|
|||
for (index, entity) in archetype.iter_entities().enumerate() {
|
||||
if index == entities.len() {
|
||||
entities.push(Entity {
|
||||
entity: *entity,
|
||||
entity: entity.id(),
|
||||
components: Vec::new(),
|
||||
})
|
||||
}
|
||||
|
|
|
@ -3,12 +3,12 @@ use bevy_app::prelude::*;
|
|||
use bevy_asset::{AssetEvent, Assets, Handle};
|
||||
use bevy_ecs::{Resources, World};
|
||||
use bevy_type_registry::TypeRegistry;
|
||||
use bevy_utils::{HashMap, HashSet};
|
||||
use bevy_utils::HashMap;
|
||||
use thiserror::Error;
|
||||
use uuid::Uuid;
|
||||
|
||||
struct InstanceInfo {
|
||||
entity_map: HashMap<u128, bevy_ecs::Entity>,
|
||||
entity_map: HashMap<u32, bevy_ecs::Entity>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
|
||||
|
@ -22,13 +22,11 @@ impl InstanceId {
|
|||
|
||||
#[derive(Default)]
|
||||
pub struct SceneSpawner {
|
||||
loaded_scenes: HashSet<Handle<Scene>>,
|
||||
spawned_scenes: HashMap<Handle<Scene>, Vec<InstanceId>>,
|
||||
spawned_instances: HashMap<InstanceId, InstanceInfo>,
|
||||
scene_asset_event_reader: EventReader<AssetEvent<Scene>>,
|
||||
scenes_to_spawn: Vec<Handle<Scene>>,
|
||||
scenes_to_load: Vec<Handle<Scene>>,
|
||||
scenes_to_unload: Vec<Handle<Scene>>,
|
||||
scenes_to_instance: Vec<Handle<Scene>>,
|
||||
scenes_to_despawn: Vec<Handle<Scene>>,
|
||||
}
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
|
@ -40,30 +38,15 @@ pub enum SceneSpawnError {
|
|||
}
|
||||
|
||||
impl SceneSpawner {
|
||||
pub fn instance(&mut self, scene_handle: Handle<Scene>) {
|
||||
self.scenes_to_spawn.push(scene_handle);
|
||||
pub fn spawn(&mut self, scene_handle: Handle<Scene>) {
|
||||
self.scenes_to_instance.push(scene_handle);
|
||||
}
|
||||
|
||||
pub fn load(&mut self, scene_handle: Handle<Scene>) {
|
||||
self.scenes_to_load.push(scene_handle);
|
||||
pub fn despawn(&mut self, scene_handle: Handle<Scene>) {
|
||||
self.scenes_to_despawn.push(scene_handle);
|
||||
}
|
||||
|
||||
pub fn unload(&mut self, scene_handle: Handle<Scene>) {
|
||||
self.scenes_to_unload.push(scene_handle);
|
||||
}
|
||||
|
||||
pub fn load_sync(
|
||||
&mut self,
|
||||
world: &mut World,
|
||||
resources: &Resources,
|
||||
scene_handle: Handle<Scene>,
|
||||
) -> Result<(), SceneSpawnError> {
|
||||
Self::load_internal(world, resources, scene_handle, None)?;
|
||||
self.loaded_scenes.insert(scene_handle);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn unload_sync(
|
||||
pub fn despawn_sync(
|
||||
&mut self,
|
||||
world: &mut World,
|
||||
scene_handle: Handle<Scene>,
|
||||
|
@ -72,12 +55,11 @@ impl SceneSpawner {
|
|||
for instance_id in instance_ids {
|
||||
if let Some(instance) = self.spawned_instances.get(&instance_id) {
|
||||
for entity in instance.entity_map.values() {
|
||||
let _ = world.despawn(*entity); // Ignore the result, unload only cares if it exists.
|
||||
let _ = world.despawn(*entity); // Ignore the result, despawn only cares if it exists.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.loaded_scenes.remove(&scene_handle);
|
||||
self.spawned_scenes.remove(&scene_handle);
|
||||
}
|
||||
Ok(())
|
||||
|
@ -93,7 +75,7 @@ impl SceneSpawner {
|
|||
let mut instance_info = InstanceInfo {
|
||||
entity_map: HashMap::default(),
|
||||
};
|
||||
Self::load_internal(world, resources, scene_handle, Some(&mut instance_info))?;
|
||||
Self::spawn_internal(world, resources, scene_handle, &mut instance_info)?;
|
||||
self.spawned_instances.insert(instance_id, instance_info);
|
||||
let spawned = self
|
||||
.spawned_scenes
|
||||
|
@ -103,11 +85,11 @@ impl SceneSpawner {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn load_internal(
|
||||
fn spawn_internal(
|
||||
world: &mut World,
|
||||
resources: &Resources,
|
||||
scene_handle: Handle<Scene>,
|
||||
mut instance_info: Option<&mut InstanceInfo>,
|
||||
instance_info: &mut InstanceInfo,
|
||||
) -> Result<(), SceneSpawnError> {
|
||||
let type_registry = resources.get::<TypeRegistry>().unwrap();
|
||||
let component_registry = type_registry.component.read();
|
||||
|
@ -119,33 +101,21 @@ impl SceneSpawner {
|
|||
})?;
|
||||
|
||||
for scene_entity in scene.entities.iter() {
|
||||
let entity = if let Some(ref mut instance_info) = instance_info {
|
||||
*instance_info
|
||||
.entity_map
|
||||
.entry(scene_entity.entity)
|
||||
.or_insert_with(bevy_ecs::Entity::new)
|
||||
} else {
|
||||
bevy_ecs::Entity::from_id(scene_entity.entity)
|
||||
};
|
||||
if world.contains(entity) {
|
||||
for component in scene_entity.components.iter() {
|
||||
let component_registration = component_registry
|
||||
.get_with_name(&component.type_name)
|
||||
.ok_or_else(|| SceneSpawnError::UnregisteredComponent {
|
||||
type_name: component.type_name.to_string(),
|
||||
})?;
|
||||
let entity = *instance_info
|
||||
.entity_map
|
||||
.entry(scene_entity.entity)
|
||||
.or_insert_with(|| world.reserve_entity());
|
||||
for component in scene_entity.components.iter() {
|
||||
let component_registration = component_registry
|
||||
.get_with_name(&component.type_name)
|
||||
.ok_or_else(|| SceneSpawnError::UnregisteredComponent {
|
||||
type_name: component.type_name.to_string(),
|
||||
})?;
|
||||
if world.has_component_type(entity, component_registration.ty) {
|
||||
if component.type_name != "Camera" {
|
||||
component_registration.apply_component_to_entity(world, entity, component);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
world.spawn_as_entity(entity, (1,));
|
||||
for component in scene_entity.components.iter() {
|
||||
let component_registration = component_registry
|
||||
.get_with_name(&component.type_name)
|
||||
.ok_or_else(|| SceneSpawnError::UnregisteredComponent {
|
||||
type_name: component.type_name.to_string(),
|
||||
})?;
|
||||
} else {
|
||||
component_registration
|
||||
.add_component_to_entity(world, resources, entity, component);
|
||||
}
|
||||
|
@ -164,7 +134,7 @@ impl SceneSpawner {
|
|||
if let Some(spawned_instances) = self.spawned_scenes.get(scene_handle) {
|
||||
for instance_id in spawned_instances.iter() {
|
||||
if let Some(instance_info) = self.spawned_instances.get_mut(instance_id) {
|
||||
Self::load_internal(world, resources, *scene_handle, Some(instance_info))?;
|
||||
Self::spawn_internal(world, resources, *scene_handle, instance_info)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -172,31 +142,11 @@ impl SceneSpawner {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn load_queued_scenes(
|
||||
&mut self,
|
||||
world: &mut World,
|
||||
resources: &Resources,
|
||||
) -> Result<(), SceneSpawnError> {
|
||||
let scenes_to_load = std::mem::take(&mut self.scenes_to_load);
|
||||
pub fn despawn_queued_scenes(&mut self, world: &mut World) -> Result<(), SceneSpawnError> {
|
||||
let scenes_to_despawn = std::mem::take(&mut self.scenes_to_despawn);
|
||||
|
||||
for scene_handle in scenes_to_load {
|
||||
match self.load_sync(world, resources, scene_handle) {
|
||||
Ok(_) => {}
|
||||
Err(SceneSpawnError::NonExistentScene { .. }) => {
|
||||
self.scenes_to_load.push(scene_handle)
|
||||
}
|
||||
Err(err) => return Err(err),
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn unload_queued_scenes(&mut self, world: &mut World) -> Result<(), SceneSpawnError> {
|
||||
let scenes_to_unload = std::mem::take(&mut self.scenes_to_unload);
|
||||
|
||||
for scene_handle in scenes_to_unload {
|
||||
self.unload_sync(world, scene_handle)?;
|
||||
for scene_handle in scenes_to_despawn {
|
||||
self.despawn_sync(world, scene_handle)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
@ -206,13 +156,13 @@ impl SceneSpawner {
|
|||
world: &mut World,
|
||||
resources: &Resources,
|
||||
) -> Result<(), SceneSpawnError> {
|
||||
let scenes_to_spawn = std::mem::take(&mut self.scenes_to_spawn);
|
||||
let scenes_to_spawn = std::mem::take(&mut self.scenes_to_instance);
|
||||
|
||||
for scene_handle in scenes_to_spawn {
|
||||
match self.spawn_sync(world, resources, scene_handle) {
|
||||
Ok(_) => {}
|
||||
Err(SceneSpawnError::NonExistentScene { .. }) => {
|
||||
self.scenes_to_spawn.push(scene_handle)
|
||||
self.scenes_to_instance.push(scene_handle)
|
||||
}
|
||||
Err(err) => return Err(err),
|
||||
}
|
||||
|
@ -232,17 +182,13 @@ pub fn scene_spawner_system(world: &mut World, resources: &mut Resources) {
|
|||
.iter(&scene_asset_events)
|
||||
{
|
||||
if let AssetEvent::Modified { handle } = event {
|
||||
if scene_spawner.loaded_scenes.contains(handle) {
|
||||
scene_spawner.load(*handle);
|
||||
}
|
||||
if scene_spawner.spawned_scenes.contains_key(handle) {
|
||||
updated_spawned_scenes.push(*handle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
scene_spawner.unload_queued_scenes(world).unwrap();
|
||||
scene_spawner.load_queued_scenes(world, resources).unwrap();
|
||||
scene_spawner.despawn_queued_scenes(world).unwrap();
|
||||
scene_spawner.spawn_queued_scenes(world, resources).unwrap();
|
||||
scene_spawner
|
||||
.update_spawned_scenes(world, resources, &updated_spawned_scenes)
|
||||
|
|
|
@ -182,7 +182,7 @@ impl<'a, 'de> Visitor<'de> for SceneEntityVisiter<'a> {
|
|||
if id.is_some() {
|
||||
return Err(Error::duplicate_field(ENTITY_FIELD_ENTITY));
|
||||
}
|
||||
id = Some(map.next_value::<u128>()?);
|
||||
id = Some(map.next_value::<u32>()?);
|
||||
}
|
||||
EntityField::Components => {
|
||||
if components.is_some() {
|
||||
|
|
|
@ -11,7 +11,7 @@ pub struct Parent(pub Entity);
|
|||
// ways to handle cases like this.
|
||||
impl FromResources for Parent {
|
||||
fn from_resources(_resources: &bevy_ecs::Resources) -> Self {
|
||||
Parent(Entity::from_id(u128::MAX))
|
||||
Parent(Entity::new(u32::MAX))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -74,16 +74,10 @@ impl WorldWriter for PushChildren {
|
|||
|
||||
impl<'a> ChildBuilder<'a> {
|
||||
pub fn spawn(&mut self, components: impl DynamicBundle + Send + Sync + 'static) -> &mut Self {
|
||||
self.spawn_as_entity(Entity::new(), components)
|
||||
}
|
||||
|
||||
pub fn spawn_as_entity(
|
||||
&mut self,
|
||||
entity: Entity,
|
||||
components: impl DynamicBundle + Send + Sync + 'static,
|
||||
) -> &mut Self {
|
||||
self.commands.spawn_as_entity(entity, components);
|
||||
self.push_children.children.push(entity);
|
||||
self.commands.spawn(components);
|
||||
self.push_children
|
||||
.children
|
||||
.push(self.commands.current_entity.unwrap());
|
||||
self
|
||||
}
|
||||
|
||||
|
@ -216,6 +210,7 @@ mod tests {
|
|||
let mut world = World::default();
|
||||
let mut resources = Resources::default();
|
||||
let mut commands = Commands::default();
|
||||
commands.set_entity_reserver(world.get_entity_reserver());
|
||||
|
||||
let mut parent = None;
|
||||
let mut child1 = None;
|
||||
|
|
|
@ -45,8 +45,8 @@ pub struct DespawnRecursive {
|
|||
|
||||
fn despawn_with_children_recursive(world: &mut World, entity: Entity) {
|
||||
// first, make the entity's own parent forget about it
|
||||
if let Ok(parent) = world.get::<Parent>(entity) {
|
||||
if let Ok(mut children) = world.get_mut::<Children>(parent.0) {
|
||||
if let Ok(parent) = world.get::<Parent>(entity).map(|parent| parent.0) {
|
||||
if let Ok(mut children) = world.get_mut::<Children>(parent) {
|
||||
children.retain(|c| *c != entity);
|
||||
}
|
||||
}
|
||||
|
@ -98,6 +98,7 @@ mod tests {
|
|||
let mut world = World::default();
|
||||
let mut resources = Resources::default();
|
||||
let mut command_buffer = Commands::default();
|
||||
command_buffer.set_entity_reserver(world.get_entity_reserver());
|
||||
|
||||
command_buffer.spawn((0u32, 0u64)).with_children(|parent| {
|
||||
parent.spawn((0u32, 0u64));
|
||||
|
|
|
@ -125,6 +125,7 @@ mod test {
|
|||
|
||||
// Add parent entities
|
||||
let mut commands = Commands::default();
|
||||
commands.set_entity_reserver(world.get_entity_reserver());
|
||||
let mut parent = None;
|
||||
let mut children = Vec::new();
|
||||
commands
|
||||
|
|
|
@ -8,22 +8,15 @@ pub struct WorldChildBuilder<'a, 'b> {
|
|||
|
||||
impl<'a, 'b> WorldChildBuilder<'a, 'b> {
|
||||
pub fn spawn(&mut self, components: impl DynamicBundle + Send + Sync + 'static) -> &mut Self {
|
||||
self.spawn_as_entity(Entity::new(), components)
|
||||
}
|
||||
|
||||
pub fn spawn_as_entity(
|
||||
&mut self,
|
||||
entity: Entity,
|
||||
components: impl DynamicBundle + Send + Sync + 'static,
|
||||
) -> &mut Self {
|
||||
let parent_entity = self
|
||||
.parent_entities
|
||||
.last()
|
||||
.cloned()
|
||||
.expect("There should always be a parent at this point.");
|
||||
self.world_builder
|
||||
.spawn_as_entity(entity, components)
|
||||
.spawn(components)
|
||||
.with_bundle((Parent(parent_entity), PreviousParent(Some(parent_entity))));
|
||||
let entity = self.world_builder.current_entity.unwrap();
|
||||
{
|
||||
let world = &mut self.world_builder.world;
|
||||
let mut added = false;
|
||||
|
|
|
@ -117,6 +117,7 @@ mod test {
|
|||
|
||||
// Root entity
|
||||
let mut commands = Commands::default();
|
||||
commands.set_entity_reserver(world.get_entity_reserver());
|
||||
let mut children = Vec::new();
|
||||
commands
|
||||
.spawn((
|
||||
|
|
|
@ -56,26 +56,18 @@ fn load_scene_system(asset_server: Res<AssetServer>, mut scene_spawner: ResMut<S
|
|||
.load("assets/scenes/load_scene_example.scn")
|
||||
.unwrap();
|
||||
|
||||
// SceneSpawner can "instance" scenes. "instancing" a scene creates a new instance of the scene in the World with new entity ids.
|
||||
// SceneSpawner can "spawn" scenes. "Spawning" a scene creates a new instance of the scene in the World with new entity ids.
|
||||
// This guarantees that it will not overwrite existing entities.
|
||||
scene_spawner.instance(scene_handle);
|
||||
|
||||
// SceneSpawner can also "load" scenes. "loading" a scene preserves the entity ids in the scene.
|
||||
// In general, you should "instance" scenes when you are dynamically composing your World and "load" scenes for things like game saves.
|
||||
scene_spawner.load(scene_handle);
|
||||
|
||||
// we have now loaded `scene_handle` AND instanced it, which means our World now has one set of entities with the Scene's ids and
|
||||
// one set of entities with new ids
|
||||
scene_spawner.spawn(scene_handle);
|
||||
|
||||
// This tells the AssetServer to watch for changes to assets.
|
||||
// It enables our scenes to automatically reload in game when we modify their files
|
||||
asset_server.watch_for_changes().unwrap();
|
||||
}
|
||||
|
||||
// Using SceneSpawner instance() and load() queues them up to be added to the World at the beginning of the next update. However if
|
||||
// Using SceneSpawner.spawn() queues up the scene to be spawned. It will be added to the World at the beginning of the next update. However if
|
||||
// you need scenes to load immediately, you can use the following approach. But be aware that this takes full control of the ECS world
|
||||
// and therefore blocks other parallel systems from executing until it finishes. In most cases you should use the SceneSpawner
|
||||
// instance() and load() methods.
|
||||
// and therefore blocks other parallel systems from executing until it finishes. In most cases you should use the SceneSpawner.spawn() method.
|
||||
#[allow(dead_code)]
|
||||
fn load_scene_right_now_system(world: &mut World, resources: &mut Resources) {
|
||||
let scene_handle: Handle<Scene> = {
|
||||
|
@ -87,7 +79,7 @@ fn load_scene_right_now_system(world: &mut World, resources: &mut Resources) {
|
|||
};
|
||||
let mut scene_spawner = resources.get_mut::<SceneSpawner>().unwrap();
|
||||
scene_spawner
|
||||
.load_sync(world, resources, scene_handle)
|
||||
.spawn_sync(world, resources, scene_handle)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue