From 8c5d283a858012542de0cdc5cb5b2ff615a7357c Mon Sep 17 00:00:00 2001 From: mathiasmagnusson Date: Mon, 9 May 2022 17:10:25 +0200 Subject: [PATCH] Get handle to spawned entity through commands --- ecs/src/commands/command.rs | 22 ++-------- ecs/src/commands/command_buffer.rs | 27 +++++++++++++ ecs/src/commands/mod.rs | 64 ++++++++---------------------- ecs/src/entity.rs | 39 +++++++++--------- ecs/src/lib.rs | 22 ++++++---- ecs/src/world.rs | 2 +- 6 files changed, 83 insertions(+), 93 deletions(-) create mode 100644 ecs/src/commands/command_buffer.rs diff --git a/ecs/src/commands/command.rs b/ecs/src/commands/command.rs index 38cd66f..185473d 100644 --- a/ecs/src/commands/command.rs +++ b/ecs/src/commands/command.rs @@ -1,15 +1,12 @@ use std::{alloc::Layout, any::TypeId, borrow::Cow}; -use crate::World; - -use super::{EntityRef, ExecutionContext}; +use crate::{Entity, World}; #[derive(Debug)] pub enum Command { - Spawn, - Despawn(EntityRef), + Despawn(Entity), AddComponent { - entity: EntityRef, + entity: Entity, type_id: TypeId, component: *mut u8, // TODO: watch out for memory leak? name: Cow<'static, str>, @@ -19,16 +16,9 @@ pub enum Command { } impl Command { - pub(super) fn execute(self, world: &mut World, ctx: &mut ExecutionContext) { + pub(super) fn execute(self, world: &mut World) { match self { - Command::Spawn => { - ctx.entities_created.push(world.spawn()); - } Command::Despawn(entity) => { - let entity = match entity { - EntityRef::Entity(entity) => entity, - EntityRef::New(index) => ctx.entities_created[index], - }; world.despawn(entity); } Command::AddComponent { @@ -39,10 +29,6 @@ impl Command { layout, drop, } => { - let entity = match entity { - EntityRef::Entity(entity) => entity, - EntityRef::New(index) => ctx.entities_created[index], - }; let comp_id = match world .component_registry_mut() .component_id_from_type_id(type_id) diff --git a/ecs/src/commands/command_buffer.rs b/ecs/src/commands/command_buffer.rs new file mode 100644 index 0000000..9e9909a --- /dev/null +++ b/ecs/src/commands/command_buffer.rs @@ -0,0 +1,27 @@ +use crate::World; + +use super::Command; + +#[derive(Debug, Default)] +pub struct CommandBuffer { + commands: Vec, +} + +impl CommandBuffer { + pub fn new() -> Self { + CommandBuffer { + commands: Vec::new(), + } + } + + pub fn add(&mut self, command: Command) { + self.commands.push(command); + } + + /// Executes all commands on the `World` and clears the command buffer. + pub fn apply(&mut self, world: &mut World) { + for command in self.commands.drain(..) { + command.execute(world); + } + } +} diff --git a/ecs/src/commands/mod.rs b/ecs/src/commands/mod.rs index 1fbccd0..d082d16 100644 --- a/ecs/src/commands/mod.rs +++ b/ecs/src/commands/mod.rs @@ -5,76 +5,44 @@ use std::{ }; mod command; +mod command_buffer; pub(crate) use command::Command; +pub(crate) use command_buffer::CommandBuffer; -use crate::{Entity, World}; - -#[derive(Debug, Clone, Copy)] -pub enum EntityRef { - Entity(Entity), - New(usize), -} - -impl From for EntityRef { - fn from(entity: Entity) -> Self { - EntityRef::Entity(entity) - } -} - -#[derive(Default)] -struct ExecutionContext { - entities_created: Vec, -} +use crate::{Entities, Entity}; /// Allows commands to be issued without exclusive access the world by deferring them to be run at /// a later state, when `World::maintain` is called. /// Issuing commands on a command buffer after calling `World::maintain` since creation will result /// in a panic. -pub struct Commands { - commands: Vec, - entity_counter: usize, +pub struct Commands<'b, 'e> { + buffer: &'b mut CommandBuffer, + entities: &'e Entities, } -impl Commands { - /// Retrieves a `Commands` which can be used to issue commands to be run on this `World` when - /// possible, which is when `maintain` is called. - /// After `maintain` is called, issuing more commands to the `Commands` will result in a panic. - pub fn new() -> Self { - Self { - commands: vec![], - entity_counter: 0, - } - } - - /// Executes all commands on the `World` and clears the command buffer. - pub fn apply(&mut self, world: &mut World) { - let mut ctx = ExecutionContext::default(); - for command in self.commands.drain(..) { - command.execute(world, &mut ctx); - } - self.entity_counter = 0; +impl<'b, 'e> Commands<'b, 'e> { + pub fn new(buffer: &'b mut CommandBuffer, entities: &'e Entities) -> Self { + Self { buffer, entities } } - pub fn spawn(&mut self) -> EntityRef { - let id = self.entity_counter; - self.entity_counter += 1; - self.commands.push(Command::Spawn); - EntityRef::New(id) + pub fn spawn(&mut self) -> Entity { + let entity = self.entities.spawn(); + entity } - pub fn despawn>(&mut self, entity: E) { - self.commands.push(Command::Despawn(entity.into())); + pub fn despawn(&mut self, entity: Entity) { + self.buffer.add(Command::Despawn(entity.into())); } - pub fn add>(&mut self, entity: E, component: T) { + pub fn add(&mut self, entity: Entity, component: T) { unsafe fn drop(ptr: *mut u8) { ptr.cast::().drop_in_place(); } let component = Box::new(component); let component = Box::into_raw(component); let component = component as *mut u8; - self.commands.push(Command::AddComponent { + self.buffer.add(Command::AddComponent { entity: entity.into(), type_id: TypeId::of::(), name: Cow::Borrowed(any::type_name::()), diff --git a/ecs/src/entity.rs b/ecs/src/entity.rs index 36f711e..db5c9bc 100644 --- a/ecs/src/entity.rs +++ b/ecs/src/entity.rs @@ -1,4 +1,7 @@ -use std::collections::HashSet; +use std::{ + cell::{Cell, RefCell}, + collections::HashSet, +}; type EntityId = u32; type Generation = u32; @@ -22,8 +25,8 @@ impl Entity { /// these are exceeded there will be a panic. #[derive(Debug, Default)] pub struct Entities { - generations: Vec, - unused_ids: Vec, + generations: RefCell>>, + unused_ids: RefCell>, } impl Entities { @@ -32,9 +35,9 @@ impl Entities { /// *O*(1) (ammortized). /// The current implementation keeps a `Vec` of all entities' *generations* which might have to /// grow. - pub fn spawn(&mut self) -> Entity { - if let Some(id) = self.unused_ids.pop() { - let gen = self.generations[id as usize]; + pub fn spawn(&self) -> Entity { + if let Some(id) = self.unused_ids.borrow_mut().pop() { + let gen = self.generations.borrow()[id as usize].get(); Entity { id, gen } } else { Entity { @@ -58,14 +61,14 @@ impl Entities { /// Despawns the entity with id `id`. Does not check generation or if `id` is already currently /// despawned. pub fn despawn_unchecked(&mut self, id: EntityId) { - let gen = &mut self.generations[id as usize]; - if *gen == Generation::MAX { + let gen = &self.generations.borrow()[id as usize]; + if gen.get() == Generation::MAX { // TODO: we're not doomed in this scenario. We can still mark this id as no longer // usable somehow. panic!("Generation counter for entity id {} has overflown.", id); } - *gen += 1; - self.unused_ids.push(id); + gen.set(gen.get() + 1); + self.unused_ids.borrow_mut().push(id); } /// Indicates whether `entity` still is alive. @@ -73,7 +76,7 @@ impl Entities { /// *O*(1) pub fn exists(&self, entity: Entity) -> bool { let Entity { id, gen } = entity; - self.generations[id as usize] == gen + self.generations.borrow()[id as usize].get() == gen } /// Returns the id of `entity` if `entity` is still alive. @@ -92,15 +95,15 @@ impl Entities { Iter::new(self) } - fn create_new_id(&mut self) -> EntityId { - let id = self - .generations + fn create_new_id(&self) -> EntityId { + let mut g = self.generations.borrow_mut(); + let id = g .len() .try_into() // The bookkeeping alone for all those entities would require more than 17 GB so this // shouldn'n be an issue. .expect("Max entity count (4 294 967 296) exceeded"); - self.generations.push(0); + g.push(Cell::new(0)); id } } @@ -116,7 +119,7 @@ impl<'e> Iter<'e> { Self { curr: 0, entities, - unused_ids: entities.unused_ids.iter().copied().collect(), + unused_ids: entities.unused_ids.borrow().iter().copied().collect(), } } @@ -130,13 +133,13 @@ impl<'e> Iterator for Iter<'e> { type Item = Entity; fn next(&mut self) -> Option { - while (self.curr as usize) < self.entities.generations.len() { + while (self.curr as usize) < self.entities.generations.borrow().len() { let curr = self.curr; self.curr += 1; if self.unused_ids.contains(&curr) { continue; } else { - let gen = self.entities.generations[curr as usize]; + let gen = self.entities.generations.borrow()[curr as usize].get(); return Some(Entity { id: curr, gen }); } } diff --git a/ecs/src/lib.rs b/ecs/src/lib.rs index 86007be..aee52e6 100644 --- a/ecs/src/lib.rs +++ b/ecs/src/lib.rs @@ -26,7 +26,10 @@ mod tests { time::{Duration, Instant}, }; - use crate::component::{ComponentRegistry, Storage, StorageType}; + use crate::{ + commands::CommandBuffer, + component::{ComponentRegistry, Storage, StorageType}, + }; use super::*; @@ -116,7 +119,7 @@ mod tests { drop_counter, ) }; - let mut entities = Entities::default(); + let entities = Entities::default(); let es: Vec<_> = (0..100).map(|_| entities.spawn()).collect(); for i in (0..100).step_by(2) { assert_eq!(i / 2, counter.get()); @@ -687,7 +690,8 @@ mod tests { let d = world.spawn(); world.add(d, Health(10)); - let mut commands = Commands::new(); + let mut command_buffer = CommandBuffer::new(); + let mut commands = Commands::new(&mut command_buffer, world.entities()); query_iter!(world, (entity: Entity, health: mut Health) => { health.0 -= 10; if health.0 == 0 { @@ -698,7 +702,7 @@ mod tests { assert!(world.entities().exists(b)); assert!(world.entities().exists(c)); assert!(world.entities().exists(d)); - commands.apply(&mut world); + command_buffer.apply(&mut world); assert!(world.entities().exists(a)); assert!(!world.entities().exists(b)); assert!(world.entities().exists(c)); @@ -711,7 +715,8 @@ mod tests { let e1 = world.spawn(); let e2 = world.spawn(); let counter = Rc::new(Cell::new(0)); - let mut commands = Commands::new(); + let mut command_buffer = CommandBuffer::new(); + let mut commands = Commands::new(&mut command_buffer, world.entities()); commands.add(e1, Counter::named(counter.clone(), "a")); assert_eq!(counter.get(), 1); @@ -721,7 +726,7 @@ mod tests { assert_eq!(counter.get(), 3); commands.despawn(e2); assert_eq!(counter.get(), 3); - commands.apply(&mut world); + command_buffer.apply(&mut world); assert_eq!(counter.get(), 1); } @@ -729,7 +734,8 @@ mod tests { fn add_component_to_newly_created_entity_through_commands() { let mut world = World::default(); let counter = Rc::new(Cell::new(0)); - let mut commands = Commands::new(); + let mut command_buffer = CommandBuffer::new(); + let mut commands = Commands::new(&mut command_buffer, world.entities()); let e1 = commands.spawn(); commands.add(e1, Counter::named(counter.clone(), "a")); @@ -742,7 +748,7 @@ mod tests { commands.despawn(e2); assert_eq!(counter.get(), 3); - commands.apply(&mut world); + command_buffer.apply(&mut world); assert_eq!(counter.get(), 1); query_iter!(world, (c: Counter) => { assert_eq!(c.1, "b"); diff --git a/ecs/src/world.rs b/ecs/src/world.rs index 848a884..a88c274 100644 --- a/ecs/src/world.rs +++ b/ecs/src/world.rs @@ -18,7 +18,7 @@ pub struct World { impl Default for World { fn default() -> Self { - let mut entities = Entities::default(); + let entities = Entities::default(); let resource_holder = entities.spawn(); Self { entities,