Permalink
Cannot retrieve contributors at this time
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
320 lines (268 sloc)
10.7 KB
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* @file EntityManager.h | |
* @author Jeff Kiah | |
*/ | |
#pragma once | |
#ifndef GRIFFIN_ENTITYMANAGER_H_ | |
#define GRIFFIN_ENTITYMANAGER_H_ | |
#include <boost/container/flat_map.hpp> | |
#include <vector> | |
#include <memory> | |
#include <algorithm> | |
#include <utility/memory_reserve.h> | |
#include "ComponentStore.h" | |
#include "Entity.h" | |
namespace griffin { | |
namespace entity { | |
class EntityManager { | |
public: | |
// Typedefs | |
typedef griffin::handle_map<Entity> EntityMap; | |
typedef boost::container::flat_multimap<uint64_t, EntityId> ComponentMaskMap; | |
typedef std::shared_ptr<ComponentStoreBase> ComponentStoreBasePtr; | |
// Functions | |
explicit EntityManager() : | |
m_entityStore(EntityId_typeId, RESERVE_ENTITYMANAGER_ENTITIES), | |
m_componentStores{}, // zero-init fills with nullptr, component store created on first access | |
m_dataComponentStoreSizes{} | |
{} | |
// Entity Functions | |
/** | |
* @return true if entity id is valid | |
*/ | |
bool entityIsValid(EntityId entityId) const | |
{ | |
return m_entityStore.isValid(entityId); | |
} | |
/** | |
* @return ComponentMask of the entity | |
*/ | |
ComponentMask getEntityComponentMask(EntityId entityId) const | |
{ | |
return m_entityStore[entityId].componentMask; | |
} | |
/** | |
* Uses the component mask to quickly see if a component type exists in the entity | |
* @return true if the component type exists at least once, false if not | |
*/ | |
bool entityHasComponent(EntityId entityId, ComponentType ct) const | |
{ | |
return m_entityStore[entityId].hasComponent(ct); | |
} | |
/** | |
* Creates a new empty entity | |
* @return entity id | |
*/ | |
EntityId createEntity(); | |
/** | |
* @return ComponentId of a component with a certain type. Returns the first component | |
* in the list encountered. Use the getEntityComponents function if more than one | |
* component of a type is expected. | |
*/ | |
ComponentId getEntityComponentId(EntityId entityId, ComponentType ct) const | |
{ | |
if (m_entityStore.isValid(entityId)) { | |
auto& entity = m_entityStore[entityId]; | |
for (auto cmp : entity.components) { | |
if (cmp.typeId == ct) { | |
return cmp; | |
} | |
} | |
} | |
return NullId_T; | |
} | |
/** | |
* Get the ComponentIds that belong to an entity | |
* @return const reference to the components vector, don't store it just use it | |
* immediately and discard | |
*/ | |
const std::vector<ComponentId>& getAllEntityComponents(EntityId entityId) const | |
{ | |
return m_entityStore[entityId].components; | |
} | |
/** | |
* @return pointer to component data by entity and component type. | |
* nullptr if component doesn't exist. | |
* Returns the first component in the list encountered. Use the getEntityComponents | |
* function if more than one component of a type is expected. | |
*/ | |
template <typename T> | |
T* getEntityComponent(EntityId entityId) | |
{ | |
auto cmpId = getEntityComponentId(entityId, T::componentType); | |
if (cmpId == NullId_T) { | |
return nullptr; | |
} | |
return &getComponent<T>(cmpId); | |
} | |
/** | |
* This call is "unsafe" if the componentId is invalid. It asserts in debug builds only. | |
* Use this function directly only when you know the component is present. | |
* @tparam T the component type, requires member T::componentType | |
* @return reference to component by id, throws if component doesn't exist | |
*/ | |
template <typename T> | |
T& getComponent(ComponentId componentId) | |
{ | |
return getComponentStore<T>().getComponent(componentId); | |
} | |
/** | |
* Adds a new component to an existing entity | |
* @param entityId entity to receive the new component | |
* @tparam T the component type, requires member T::componentType | |
* @return new ComponentId, or NullId_T if entityId is invalid | |
*/ | |
template <typename T> | |
ComponentId addComponentToEntity(T&& component, EntityId entityId) | |
{ | |
if (!m_entityStore.isValid(entityId)) { | |
return NullId_T; | |
} | |
auto& entity = m_entityStore[entityId]; | |
auto& store = getComponentStore<T>(); | |
auto componentId = store.addComponent(std::forward<T>(component), entityId); | |
//auto previousMask = entity.componentMask; | |
entity.addComponent(componentId); | |
//auto newMask = entity.componentMask; | |
/* // commented out because not sure the component mask index is a keeper | |
if (newMask != previousMask) { | |
// fix up the mask index with the new mask, remove the old entry with old mask | |
auto rng = m_componentIndex.equal_range(previousMask.to_ullong()); | |
// TODO: not sure about this erase code, test if it works and is safe to erase in a partial range like this | |
m_componentIndex.erase(std::remove_if(rng.first, rng.second, | |
[entityId](ComponentMaskMap::value_type& val){ | |
return (val.second == entityId); | |
}), | |
rng.second); | |
// insert the new entry with new mask | |
m_componentIndex.insert(ComponentMaskMap::value_type{ newMask.to_ullong(), entityId }); | |
// we can convert the bitset<64> to a uint64_t and sort that, but if more | |
// components are needed later will need a bigger mask key as well | |
static_assert(sizeof(ComponentMask) <= sizeof(uint64_t), | |
"ComponentMask contains more than 64 bits, need a new sort key for the ComponentMask"); | |
} | |
*/ | |
return componentId; | |
} | |
/** | |
* Removes a component from the entity's components vector, and unsets the bit in the | |
* ComponentMask if the removed component is the last of its type. Also removes the component | |
* from the component store. The entity id is extracted from the component record. | |
* @param componentId the component to be removed from its entity and store | |
* @return true if the component was removed, false if it does not exist | |
*/ | |
bool removeComponent(ComponentId componentId); | |
/** | |
* Removes all components of a type from from the entity's components vector, and unsets | |
* the corresponding bit in the ComponentMask. | |
* @param ct the component type's enum value | |
* @param entityId the entity to remove from | |
* @return true if the component was removed, false if the entity doesn't exist or nothing was removed | |
*/ | |
bool removeComponentsOfTypeFromEntity(ComponentType ct, EntityId entityId); | |
/** | |
* Removes all components where the predicate returns true. | |
* @tparam T the component type, requires member T::componentType | |
* @tparam UnaryPredicate predicate must return bool and accept a single param of type T | |
* @param p_ function pointer, lambda or functor returning a bool | |
*/ | |
template <typename T, class UnaryPredicate> | |
int removeComponent_if(UnaryPredicate p_) | |
{ | |
int removed = 0; | |
auto& store = getComponentStore<T>(); | |
auto& components = store.getComponents(); | |
for (auto it = components.cbegin(); it != components.cend();) { | |
if (p_(it->component)) { | |
auto componentId = store.getComponentIdForItem(it); | |
if (removeComponent(componentId)) { | |
++removed; | |
} | |
} | |
else { | |
++it; | |
} | |
} | |
return removed; | |
} | |
// Component Store Functions | |
/** | |
* Get a reference to component store for a specific type, assumed to be a component | |
* with ::componentType member. Useful for systems. | |
* @tparam T the component type, requires member T::componentType | |
* @return the ComponentStore for a given type | |
*/ | |
template <typename T> | |
ComponentStore<T>& getComponentStore() | |
{ | |
static_assert(T::componentType >= 0 && T::componentType < MAX_COMPONENTS, "componentType out of range"); | |
if (m_componentStores[T::componentType] == nullptr) { | |
createComponentStore<T>(RESERVE_ENTITYMANAGER_COMPONENTS); | |
} | |
return *reinterpret_cast<ComponentStore<T>*>(m_componentStores[T::componentType].get()); | |
} | |
/** | |
* Create a store for a specific type, assumed to be a component with ::componentType member | |
* which identifies the type id and becomes the index into the stores vector. | |
* @tparam T the component type, requires member T::componentType | |
*/ | |
template <typename T> | |
void createComponentStore(size_t reserve) | |
{ | |
static_assert(T::componentType < ComponentType::last_ComponentType_enum, "static component with dynamic id"); | |
createComponentStore<T>(T::componentType, reserve); | |
} | |
// Size-based data component functions | |
/* | |
* Note: the interface to data components is limited to single-component interactions; | |
* it is impractical to get a data component store and iteratate since type casts might | |
* yield different object sizes, so iteration might be at the wrong stride. It's safe to | |
* reinterpret_cast a single data component with a known source type. | |
*/ | |
/** | |
* Create a store for size-based data components | |
* @typeId zero-based unique id for the data component type | |
* @componentSize size in bytes of the component struct | |
* @reserve how many components to reserve memory for upfront | |
* @return Index id of the component store. Stores cannot be removed once created so | |
* this index is stable for the lifetime of the EntityManager. The number returned is | |
* equal to one above the highest fixed entity plus typeId. | |
*/ | |
uint16_t createDataComponentStore(uint16_t typeId, uint32_t componentSize, size_t reserve); | |
ComponentId addDataComponentToEntity(uint16_t typeId, EntityId entityId); | |
inline uint16_t getDataComponentSize(uint16_t typeId) | |
{ | |
assert(typeId + ComponentType::last_ComponentType_enum < MAX_COMPONENTS && "typeId out of range"); | |
return m_dataComponentStoreSizes[typeId]; | |
} | |
/** | |
* Get a data component from its store as raw bytes, useful for pass-throughs to Lua. | |
*/ | |
void* getDataComponent(ComponentId componentId); | |
private: | |
// Private Functions | |
/** | |
* Create a store for a specific type, without assuming that the type has a ::componentType | |
* member identiying the type id. | |
* @tparam T the component type, requires member T::componentType | |
*/ | |
template <typename T> | |
void createComponentStore(uint16_t typeId, size_t reserve) | |
{ | |
assert(m_componentStores[typeId] == nullptr && "componentStore already exists"); | |
m_componentStores[typeId] = std::make_unique<ComponentStore<T>>( | |
typeId, | |
(reserve > RESERVE_ENTITYMANAGER_COMPONENTS ? reserve : RESERVE_ENTITYMANAGER_COMPONENTS)); | |
} | |
// Private Variables | |
EntityMap m_entityStore; //<! Entity indexed by handle, stores relationship to components internally | |
//ComponentMaskMap m_componentIndex; //<! index of sorted ComponentMask for O(log n) entity search by multiple component types | |
//ComponentStoreMap m_componentStores; //<! ComponentStore indexed by componentType, stores component and parent entityId | |
ComponentStoreBasePtr m_componentStores[MAX_COMPONENTS]; | |
uint32_t m_dataComponentStoreSizes[MAX_COMPONENTS - ComponentType::last_ComponentType_enum]; //<! one size per data component type | |
}; | |
typedef std::shared_ptr<EntityManager> EntityManagerPtr; | |
typedef std::weak_ptr<EntityManager> EntityManagerWeakPtr; | |
} | |
} | |
#endif |