diff --git a/libretrogd/src/entities/mod.rs b/libretrogd/src/entities/mod.rs index 74e340c..c060923 100644 --- a/libretrogd/src/entities/mod.rs +++ b/libretrogd/src/entities/mod.rs @@ -17,8 +17,14 @@ pub type RefComponents<'a, T> = Ref<'a, HashMap>; pub type RefMutComponents<'a, T> = RefMut<'a, HashMap>; pub trait GenericComponentStore: AsAny { + /// Returns true if this component store currently has a component for the specified entity. fn has(&self, entity: EntityId) -> bool; + + /// If this component store has a component for the specified entity, removes it and returns + /// true. Otherwise, returns false. fn remove(&mut self, entity: EntityId) -> bool; + + /// Removes all components from this store. fn clear(&mut self); } @@ -55,6 +61,7 @@ pub fn as_component_store_mut( /////////////////////////////////////////////////////////////////////////////////////////////////// +/// Entity manager. Stores entity components and manages entity IDs. pub struct Entities { entities: HashSet, component_stores: HashMap>, @@ -62,6 +69,7 @@ pub struct Entities { } impl Entities { + /// Creates and returns a new instance of an entity manager. pub fn new() -> Self { Entities { entities: HashSet::new(), @@ -99,11 +107,13 @@ impl Entities { } } + /// Returns true if the entity manager currently is aware of an entity with the given ID. #[inline] pub fn has_entity(&self, entity: EntityId) -> bool { self.entities.contains(&entity) } + /// Returns a previously unused entity ID. Use this to "create" a new entity. pub fn new_entity(&mut self) -> EntityId { let new_entity_id = self.next_id; self.next_id = self.next_id.wrapping_add(1); @@ -111,6 +121,9 @@ impl Entities { new_entity_id } + /// Removes an entity, making the entity ID unusable with this entity manager as well as + /// removing all of the entity's components. Returns true if the entity was removed, false if + /// the entity ID given did not exist. pub fn remove_entity(&mut self, entity: EntityId) -> bool { if !self.has_entity(entity) { return false; @@ -123,6 +136,7 @@ impl Entities { true } + /// Removes all entities from the entity manager, as well as all of their components. pub fn remove_all_entities(&mut self) { self.entities.clear(); for (_, component_store) in self.component_stores.iter_mut() { @@ -130,6 +144,7 @@ impl Entities { } } + /// Returns true if the given entity currently has a component of the specified type. pub fn has_component(&self, entity: EntityId) -> bool { if !self.has_entity(entity) { false @@ -143,6 +158,9 @@ impl Entities { } } + /// Adds the given component to the entity manager, associating it with the given entity ID. + /// If this entity already had a component of the same type, that existing component is replaced + /// with this one. Returns true if the component was set to the entity. pub fn add_component(&mut self, entity: EntityId, component: T) -> bool { if !self.has_entity(entity) { false @@ -158,6 +176,8 @@ impl Entities { } } + /// Removes any component of the given type from the specified entity. If the entity had a + /// component of that type and it was removed, returns true. pub fn remove_component(&mut self, entity: EntityId) -> bool { if !self.has_entity(entity) { false @@ -171,6 +191,9 @@ impl Entities { } } + /// Returns a reference to the component store for the given component type. This allows you + /// to get components of the specified type for any number of entities. If there is currently + /// no component store for this type of component, `None` is returned. #[inline] pub fn components(&self) -> Option> { if let Some(component_store) = self.get_component_store() { @@ -180,6 +203,13 @@ impl Entities { } } + /// Returns a reference to the mutable component store for the given component type. This allows + /// you to get and modify components of the specified type for any number of entities. IF there + /// is currently no component store for this type of component, `None` is returned. + /// + /// Note that while technically you can add/remove components using the returned store, you + /// should instead prefer to use [`Entities::add_component`] and [`Entities::remove_component`] + /// instead. #[inline] pub fn components_mut(&self) -> Option> { if let Some(component_store) = self.get_component_store() { @@ -189,6 +219,14 @@ impl Entities { } } + /// Initializes a component store for the given component type if one does not exist already. + /// + /// This is technically never needed to be called explicitly (because + /// [`Entities::add_component`] will initialize a missing component store automatically if + /// needed), but is provided as a convenience so that you could, for example, always + /// pre-initialize all of your component stores so that subsequent calls to + /// [`Entities::components`] and [`Entities::components_mut`] are guaranteed to never return + /// `None`. pub fn init_components(&mut self) { if self.get_component_store::().is_none() { self.add_component_store::(); @@ -200,15 +238,36 @@ impl Entities { // TODO: is there some fancy way to get rid of the impl duplication here ... ? +/// Convenience methods that slightly help the ergonomics of using component stores returned from +/// [`Entities::components`]. pub trait ComponentStoreConvenience { + /// Returns the "first" component from the component store along with the entity ID the + /// component is for. This method should only ever be used with components that you know will + /// only ever be attached to one entity (and therefore, the component store for this type of + /// component only has a single entry in it). Otherwise, which component it returns is + /// undefined. fn single(&self) -> Option<(&EntityId, &T)>; + + /// Returns the component for the given entity, if one exists, otherwise returns `None`. fn get(&self, k: &EntityId) -> Option<&T>; + + /// Returns true if there is a component for the given entity in this store. fn contains_key(&self, k: &EntityId) -> bool; } pub trait ComponentStoreConvenienceMut { + /// Returns the "first" component from the component store along with the entity ID the + /// component is for as a mutable reference. This method should only ever be used with + /// components that you know will only ever be attached to one entity (and therefore, the + /// component store for this type of component only has a single entry in it). Otherwise, which + /// component it returns is undefined. fn single_mut(&mut self) -> Option<(&EntityId, &mut T)>; + + /// Returns the component for the given entity as a mutable reference if one exists, otherwise + /// returns `None`. fn get_mut(&mut self, k: &EntityId) -> Option<&mut T>; + + /// Returns true if there is a component for the given entity in this store. fn contains_key(&mut self, k: &EntityId) -> bool; } @@ -293,8 +352,14 @@ impl<'a, T: Component> ComponentStoreConvenienceMut for Option { + /// Returns the total number of components in this component store. This is the same as the + /// number of entities that have a component of this type. fn len(&self) -> usize; + + /// Returns true if this store is empty. fn is_empty(&self) -> bool; } @@ -336,9 +401,22 @@ impl<'a, T: Component> OptionComponentStore for Option = fn(&mut T); + +/// A "render component system" function that is used to execute render logic for entities based +/// on a certain type/set of components. The generic type used is some application-specific context +/// type that should be passed to all of your "render component system" functions. pub type RenderFn = fn(&mut T); +/// This is a totally optional minor convenience to help you to manage your "component systems" +/// and ensure they are always called in the same order. +/// +/// The generic types `U` and `R` refer to application-specific context types that are needed by +/// all of your "update" and "render" component system functions. Both of these types may be the +/// same type or different depending on your needs. pub struct ComponentSystems { update_systems: Vec>, render_systems: Vec>, @@ -352,25 +430,34 @@ impl ComponentSystems { } } + /// Adds an update component system function to the list of functions that will be called in + /// order whenever [`ComponentSystems::update`] is called. pub fn add_update_system(&mut self, f: UpdateFn) { self.update_systems.push(f); } + /// Adds a render component system function to the list of functions that will be called in + /// order whenever [`ComponentSystems::render`] is called. pub fn add_render_system(&mut self, f: RenderFn) { self.render_systems.push(f); } + /// Removes all existing update and render component system functions. pub fn reset(&mut self) { self.update_systems.clear(); self.render_systems.clear(); } + /// Calls each of the update component system functions in the same order that they were added, + /// passing each of them the context argument provided. pub fn update(&mut self, context: &mut U) { for f in self.update_systems.iter_mut() { f(context); } } + /// Calls each of the render component system functions in the same order that they were added, + /// passing each of them the context argument provided. pub fn render(&mut self, context: &mut R) { for f in self.render_systems.iter_mut() { f(context); diff --git a/libretrogd/src/events/mod.rs b/libretrogd/src/events/mod.rs index 060cad9..0513397 100644 --- a/libretrogd/src/events/mod.rs +++ b/libretrogd/src/events/mod.rs @@ -1,7 +1,12 @@ use std::collections::VecDeque; +/// An event listener/handler function that returns true if it handled the event and no other +/// listeners/handlers should be called next with the same event, or false if the event was not +/// handled and any subsequent listeners/handlers should be called. pub type ListenerFn = fn(event: &EventType, &mut ContextType) -> bool; +/// An event publisher that code can use to queue up events to be handled by an [`EventListeners`] +/// instance. The `EventType` here should usually be an application-specific "events" enum. pub struct EventPublisher { queue: VecDeque, } @@ -13,16 +18,19 @@ impl EventPublisher { } } + /// Returns the number of events that have been queued. #[inline] pub fn len(&self) -> usize { self.queue.len() } + /// Clears the current event queue. The events will not be processed/handled. #[inline] pub fn clear(&mut self) { self.queue.clear(); } + /// Pushes the given event to the back of the queue. #[inline] pub fn queue(&mut self, event: EventType) { self.queue.push_back(event); @@ -35,6 +43,14 @@ impl EventPublisher { } } +/// A manager for application event listeners/handlers that can dispatch events queued up by a +/// [`EventPublisher`] to each of the event listeners/handlers registered with this manager. +/// +/// The `EventType` specified here should usually be an application-specific "events" enum and +/// should be the same as the type used in your application's [`EventPublisher`]. +/// +/// The `ContextType` specified here should be some application-specific context type that you +/// want available in all of your event listener/handler functions. pub struct EventListeners { listeners: Vec>, dispatch_queue: VecDeque, @@ -48,16 +64,20 @@ impl EventListeners { } } + /// Returns the number of event listeners/handlers registered with this manager. #[inline] pub fn len(&self) -> usize { self.listeners.len() } + /// Unregisters all event listeners/managers previously registered with this manager. #[inline] pub fn clear(&mut self) { self.listeners.clear(); } + /// Adds/Registers the given event listener/handler function with this manager so that + /// it will be called during dispatching of events. Returns true if the function was added. pub fn add(&mut self, listener: ListenerFn) -> bool { // HACK?: most advice i've seen right now for comparing function pointers suggests doing // this, but i've also come across comments suggesting there are times where this @@ -70,6 +90,7 @@ impl EventListeners { } } + /// Removes/Unregisters the specified event listener/handler function from this manager. pub fn remove(&mut self, listener: ListenerFn) -> bool { let before_size = self.listeners.len(); // HACK?: comparing function pointers -- see above "HACK?" comment. same concern here. @@ -78,11 +99,19 @@ impl EventListeners { return before_size != self.listeners.len() } + /// Moves the queue from the given [`EventPublisher`] to this manager in preparation for + /// dispatching the queued events via [`EventListeners::dispatch_queue`]. After calling this, + /// the [`EventPublisher`]'s queue will be empty. pub fn take_queue_from(&mut self, publisher: &mut EventPublisher) -> usize { publisher.take_queue(&mut self.dispatch_queue); self.dispatch_queue.len() } + /// Dispatches the previous obtained event queue (via a call to + /// [`EventListeners::take_queue_from`]) to all of the registered event listeners/handlers, + /// passing each of them the given context argument. Not all of the event listeners/handlers + /// will necessarily be called for each event being dispatched depending on which ones handled + /// which events. pub fn dispatch_queue(&mut self, context: &mut ContextType) { while let Some(event) = self.dispatch_queue.pop_front() { for listener in &self.listeners {