Refactor EntityList and prevent World to invalidate its own handles between updates
This commit is contained in:
0
SDK/include/NDK/EntityList.cpp
Normal file
0
SDK/include/NDK/EntityList.cpp
Normal file
@@ -16,45 +16,59 @@ namespace Ndk
|
||||
class NDK_API EntityList
|
||||
{
|
||||
public:
|
||||
using Container = std::vector<EntityHandle>;
|
||||
class iterator;
|
||||
friend iterator;
|
||||
using size_type = std::size_t;
|
||||
|
||||
EntityList() = default;
|
||||
inline EntityList();
|
||||
~EntityList() = default;
|
||||
|
||||
inline void Clear();
|
||||
|
||||
inline bool Has(const Entity* entity);
|
||||
inline bool Has(EntityId entity);
|
||||
inline bool Has(const Entity* entity) const;
|
||||
inline bool Has(EntityId entity) const;
|
||||
|
||||
inline void Insert(Entity* entity);
|
||||
|
||||
inline void Remove(Entity* entity);
|
||||
|
||||
// STL API
|
||||
inline Container::iterator begin();
|
||||
inline Container::const_iterator begin() const;
|
||||
|
||||
inline Container::const_iterator cbegin() const;
|
||||
inline Container::const_iterator cend() const;
|
||||
inline Container::const_reverse_iterator crbegin() const;
|
||||
inline Container::const_reverse_iterator crend() const;
|
||||
|
||||
inline iterator begin();
|
||||
inline bool empty() const;
|
||||
|
||||
inline Container::iterator end();
|
||||
inline Container::const_iterator end() const;
|
||||
|
||||
inline Container::reverse_iterator rbegin();
|
||||
inline Container::const_reverse_iterator rbegin() const;
|
||||
|
||||
inline Container::reverse_iterator rend();
|
||||
inline Container::const_reverse_iterator rend() const;
|
||||
|
||||
inline Container::size_type size() const;
|
||||
inline iterator end();
|
||||
inline size_type size() const;
|
||||
|
||||
private:
|
||||
std::vector<EntityHandle> m_entities;
|
||||
inline std::size_t FindNext(std::size_t currentId) const;
|
||||
inline World* GetWorld() const;
|
||||
|
||||
Nz::Bitset<Nz::UInt64> m_entityBits;
|
||||
World* m_world;
|
||||
};
|
||||
|
||||
class EntityList::iterator : public std::iterator<std::forward_iterator_tag, const EntityHandle>
|
||||
{
|
||||
friend EntityList;
|
||||
|
||||
public:
|
||||
inline iterator(const iterator& iterator);
|
||||
|
||||
const EntityHandle& operator*() const;
|
||||
|
||||
inline iterator& operator=(const iterator& iterator);
|
||||
inline iterator& operator++();
|
||||
inline iterator operator++(int);
|
||||
|
||||
friend inline bool operator==(const iterator& lhs, const iterator& rhs);
|
||||
friend inline bool operator!=(const iterator& lhs, const iterator& rhs);
|
||||
|
||||
friend inline void swap(iterator& lhs, iterator& rhs);
|
||||
|
||||
private:
|
||||
inline iterator(const EntityList* world, std::size_t nextId);
|
||||
|
||||
std::size_t m_nextEntityId;
|
||||
const EntityList* m_list;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -2,8 +2,10 @@
|
||||
// This file is part of the "Nazara Development Kit"
|
||||
// For conditions of distribution and use, see copyright notice in Prerequesites.hpp
|
||||
|
||||
#include <NDK/EntityList.hpp>
|
||||
#include <Nazara/Core/Error.hpp>
|
||||
#include <algorithm>
|
||||
#include "EntityList.hpp"
|
||||
|
||||
namespace Ndk
|
||||
{
|
||||
@@ -16,22 +18,34 @@ namespace Ndk
|
||||
/*!
|
||||
* \brief Clears the set from every entities
|
||||
*/
|
||||
|
||||
inline void EntityList::Clear()
|
||||
inline EntityList::EntityList() :
|
||||
m_world(nullptr)
|
||||
{
|
||||
m_entities.clear();
|
||||
m_entityBits.Clear();
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Checks whether or not the set contains the entity
|
||||
* \brief Clears the set from every entities
|
||||
*
|
||||
* \remark This resets the implicit world member, allowing you to insert entities from a different world than previously
|
||||
*/
|
||||
inline void EntityList::Clear()
|
||||
{
|
||||
m_entityBits.Clear();
|
||||
m_world = nullptr;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Checks whether or not the EntityList contains the entity
|
||||
* \return true If it is the case
|
||||
*
|
||||
* \param entity Pointer to the entity
|
||||
*
|
||||
* \remark If the Insert function was called since the EntityList construction (or last call to Clear), the entity passed by parameter must belong to the same world as the previously inserted entities.
|
||||
*/
|
||||
|
||||
inline bool EntityList::Has(const Entity* entity)
|
||||
inline bool EntityList::Has(const Entity* entity) const
|
||||
{
|
||||
NazaraAssert(!m_world || !entity || entity->GetWorld() == m_world, "Incompatible world");
|
||||
|
||||
return entity && entity->IsValid() && Has(entity->GetId());
|
||||
}
|
||||
|
||||
@@ -41,8 +55,7 @@ namespace Ndk
|
||||
*
|
||||
* \param id Identifier of the entity
|
||||
*/
|
||||
|
||||
inline bool EntityList::Has(EntityId entity)
|
||||
inline bool EntityList::Has(EntityId entity) const
|
||||
{
|
||||
return m_entityBits.UnboundedTest(entity);
|
||||
}
|
||||
@@ -50,18 +63,18 @@ namespace Ndk
|
||||
/*!
|
||||
* \brief Inserts the entity into the set
|
||||
*
|
||||
* \param entity Pointer to the entity
|
||||
* \param entity Valid pointer to an entity
|
||||
*
|
||||
* \remark If entity is already contained, no action is performed
|
||||
* \remark If any entity has been inserted since construction (or last Clear call), the entity must belong to the same world as the previously inserted entities
|
||||
*/
|
||||
|
||||
inline void EntityList::Insert(Entity* entity)
|
||||
{
|
||||
if (!Has(entity))
|
||||
{
|
||||
m_entities.emplace_back(entity);
|
||||
m_entityBits.UnboundedSet(entity->GetId(), true);
|
||||
}
|
||||
NazaraAssert(entity, "Invalid entity");
|
||||
NazaraAssert(!m_world || entity->GetWorld() == m_world, "Incompatible world");
|
||||
|
||||
m_entityBits.UnboundedSet(entity->GetId(), true);
|
||||
m_world = entity->GetWorld();
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -70,89 +83,101 @@ namespace Ndk
|
||||
* \param entity Pointer to the entity
|
||||
*
|
||||
* \remark If entity is not contained, no action is performed
|
||||
* \remark This function never resets the implicit world member, even if it empties the list. Use the Clear method if you want to reset it.
|
||||
*
|
||||
* \see Clear
|
||||
*/
|
||||
|
||||
inline void EntityList::Remove(Entity* entity)
|
||||
{
|
||||
if (Has(entity))
|
||||
{
|
||||
auto it = std::find(m_entities.begin(), m_entities.end(), *entity);
|
||||
NazaraAssert(it != m_entities.end(), "Entity should be part of the vector");
|
||||
|
||||
std::swap(*it, m_entities.back());
|
||||
m_entities.pop_back(); // We get it out of the vector
|
||||
m_entityBits.UnboundedSet(entity->GetId(), false);
|
||||
}
|
||||
m_entityBits.UnboundedSet(entity->GetId(), false);
|
||||
}
|
||||
|
||||
// Nz::Interface STD
|
||||
inline EntityList::Container::iterator EntityList::begin()
|
||||
// STL Interface
|
||||
inline EntityList::iterator EntityList::begin()
|
||||
{
|
||||
return m_entities.begin();
|
||||
}
|
||||
|
||||
inline EntityList::Container::const_iterator EntityList::begin() const
|
||||
{
|
||||
return m_entities.begin();
|
||||
}
|
||||
|
||||
inline EntityList::Container::const_iterator EntityList::cbegin() const
|
||||
{
|
||||
return m_entities.cbegin();
|
||||
}
|
||||
|
||||
inline EntityList::Container::const_iterator EntityList::cend() const
|
||||
{
|
||||
return m_entities.cend();
|
||||
}
|
||||
|
||||
inline EntityList::Container::const_reverse_iterator EntityList::crbegin() const
|
||||
{
|
||||
return m_entities.crbegin();
|
||||
}
|
||||
|
||||
inline EntityList::Container::const_reverse_iterator EntityList::crend() const
|
||||
{
|
||||
return m_entities.crend();
|
||||
return EntityList::iterator(this, m_entityBits.FindFirst());
|
||||
}
|
||||
|
||||
inline bool EntityList::empty() const
|
||||
{
|
||||
return m_entities.empty();
|
||||
return m_entityBits.TestAny();
|
||||
}
|
||||
|
||||
inline EntityList::Container::iterator EntityList::end()
|
||||
inline EntityList::iterator EntityList::end()
|
||||
{
|
||||
return m_entities.end();
|
||||
return EntityList::iterator(this, m_entityBits.npos);
|
||||
}
|
||||
|
||||
inline EntityList::Container::const_iterator EntityList::end() const
|
||||
inline EntityList::size_type EntityList::size() const
|
||||
{
|
||||
return m_entities.end();
|
||||
return m_entityBits.Count();
|
||||
}
|
||||
|
||||
inline EntityList::Container::reverse_iterator EntityList::rbegin()
|
||||
inline std::size_t EntityList::FindNext(std::size_t currentId) const
|
||||
{
|
||||
return m_entities.rbegin();
|
||||
return m_entityBits.FindNext(currentId);
|
||||
}
|
||||
|
||||
inline EntityList::Container::const_reverse_iterator EntityList::rbegin() const
|
||||
inline World* EntityList::GetWorld() const
|
||||
{
|
||||
return m_entities.rbegin();
|
||||
return m_world;
|
||||
}
|
||||
|
||||
inline EntityList::Container::reverse_iterator EntityList::rend()
|
||||
|
||||
inline EntityList::iterator::iterator(const EntityList* list, std::size_t nextId) :
|
||||
m_nextEntityId(nextId),
|
||||
m_list(list)
|
||||
{
|
||||
return m_entities.rend();
|
||||
}
|
||||
|
||||
inline EntityList::Container::const_reverse_iterator EntityList::rend() const
|
||||
inline EntityList::iterator::iterator(const iterator& iterator) :
|
||||
m_nextEntityId(iterator.m_nextEntityId),
|
||||
m_list(iterator.m_list)
|
||||
{
|
||||
return m_entities.rend();
|
||||
}
|
||||
|
||||
inline EntityList::Container::size_type EntityList::size() const
|
||||
inline EntityList::iterator& EntityList::iterator::operator=(const iterator& iterator)
|
||||
{
|
||||
return m_entities.size();
|
||||
m_nextEntityId = iterator.m_nextEntityId;
|
||||
m_list = iterator.m_list;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline EntityList::iterator& EntityList::iterator::operator++()
|
||||
{
|
||||
m_nextEntityId = m_list->FindNext(m_nextEntityId);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline EntityList::iterator EntityList::iterator::operator++(int)
|
||||
{
|
||||
std::size_t previousId = m_nextEntityId;
|
||||
|
||||
m_nextEntityId = m_list->FindNext(m_nextEntityId);
|
||||
|
||||
return iterator(m_list, previousId);
|
||||
}
|
||||
|
||||
inline bool operator==(const EntityList::iterator& lhs, const EntityList::iterator& rhs)
|
||||
{
|
||||
NazaraAssert(lhs.m_list == rhs.m_list, "Cannot compare iterator coming from different lists");
|
||||
|
||||
return lhs.m_nextEntityId == rhs.m_nextEntityId;
|
||||
}
|
||||
|
||||
inline bool operator!=(const EntityList::iterator& lhs, const EntityList::iterator& rhs)
|
||||
{
|
||||
return !operator==(lhs, rhs);
|
||||
}
|
||||
|
||||
inline void swap(EntityList::iterator& lhs, EntityList::iterator& rhs)
|
||||
{
|
||||
NazaraAssert(lhs.m_list == rhs.m_list, "Cannot compare iterator coming from different lists");
|
||||
|
||||
using std::swap;
|
||||
|
||||
std::swap(lhs.m_nextEntityId, rhs.m_nextEntityId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,7 +57,7 @@ namespace Ndk
|
||||
|
||||
std::unique_ptr<Nz::AbstractRenderTechnique> m_renderTechnique;
|
||||
std::vector<GraphicsComponentCullingList::VolumeEntry> m_volumeEntries;
|
||||
EntityList m_cameras;
|
||||
std::vector<EntityHandle> m_cameras;
|
||||
EntityList m_drawables;
|
||||
EntityList m_directionalLights;
|
||||
EntityList m_lights;
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include <Nazara/Core/Bitset.hpp>
|
||||
#include <Nazara/Core/HandledObject.hpp>
|
||||
#include <NDK/Entity.hpp>
|
||||
#include <NDK/EntityList.hpp>
|
||||
#include <NDK/System.hpp>
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
@@ -28,7 +29,7 @@ namespace Ndk
|
||||
friend Entity;
|
||||
|
||||
public:
|
||||
using EntityList = std::vector<EntityHandle>;
|
||||
using EntityVector = std::vector<EntityHandle>;
|
||||
|
||||
inline World(bool addDefaultSystems = true);
|
||||
World(const World&) = delete;
|
||||
@@ -41,13 +42,13 @@ namespace Ndk
|
||||
template<typename SystemType, typename... Args> SystemType& AddSystem(Args&&... args);
|
||||
|
||||
const EntityHandle& CreateEntity();
|
||||
inline EntityList CreateEntities(unsigned int count);
|
||||
inline EntityVector CreateEntities(unsigned int count);
|
||||
|
||||
void Clear() noexcept;
|
||||
const EntityHandle& CloneEntity(EntityId id);
|
||||
|
||||
const EntityHandle& GetEntity(EntityId id);
|
||||
inline const EntityList& GetEntities();
|
||||
inline const EntityList& GetEntities() const;
|
||||
inline BaseSystem& GetSystem(SystemIndex index);
|
||||
template<typename SystemType> SystemType& GetSystem();
|
||||
|
||||
@@ -55,7 +56,7 @@ namespace Ndk
|
||||
template<typename SystemType> bool HasSystem() const;
|
||||
|
||||
void KillEntity(Entity* entity);
|
||||
inline void KillEntities(const EntityList& list);
|
||||
inline void KillEntities(const EntityVector& list);
|
||||
|
||||
inline bool IsEntityValid(const Entity* entity) const;
|
||||
inline bool IsEntityIdValid(EntityId id) const;
|
||||
@@ -79,19 +80,22 @@ namespace Ndk
|
||||
struct EntityBlock
|
||||
{
|
||||
EntityBlock(Entity&& e) :
|
||||
entity(std::move(e))
|
||||
entity(std::move(e)),
|
||||
handle(&entity)
|
||||
{
|
||||
}
|
||||
|
||||
EntityBlock(EntityBlock&& block) = default;
|
||||
|
||||
Entity entity;
|
||||
std::size_t aliveIndex;
|
||||
EntityHandle handle;
|
||||
};
|
||||
|
||||
std::vector<std::unique_ptr<BaseSystem>> m_systems;
|
||||
std::vector<BaseSystem*> m_orderedSystems;
|
||||
std::vector<EntityBlock> m_entities;
|
||||
std::vector<EntityBlock*> m_entityBlocks;
|
||||
std::vector<std::unique_ptr<EntityBlock>> m_waitingEntities;
|
||||
std::vector<EntityId> m_freeIdList;
|
||||
EntityList m_aliveEntities;
|
||||
Nz::Bitset<Nz::UInt64> m_dirtyEntities;
|
||||
|
||||
@@ -82,9 +82,9 @@ namespace Ndk
|
||||
* \param count Number of entities to create
|
||||
*/
|
||||
|
||||
inline World::EntityList World::CreateEntities(unsigned int count)
|
||||
inline World::EntityVector World::CreateEntities(unsigned int count)
|
||||
{
|
||||
EntityList list;
|
||||
EntityVector list;
|
||||
list.reserve(count);
|
||||
|
||||
for (unsigned int i = 0; i < count; ++i)
|
||||
@@ -98,7 +98,7 @@ namespace Ndk
|
||||
* \return A constant reference to the entities
|
||||
*/
|
||||
|
||||
inline const World::EntityList& World::GetEntities()
|
||||
inline const EntityList& World::GetEntities() const
|
||||
{
|
||||
return m_aliveEntities;
|
||||
}
|
||||
@@ -170,7 +170,7 @@ namespace Ndk
|
||||
* \param list Set of entities to kill
|
||||
*/
|
||||
|
||||
inline void World::KillEntities(const EntityList& list)
|
||||
inline void World::KillEntities(const EntityVector& list)
|
||||
{
|
||||
for (const EntityHandle& entity : list)
|
||||
KillEntity(entity);
|
||||
@@ -197,7 +197,7 @@ namespace Ndk
|
||||
|
||||
inline bool World::IsEntityIdValid(EntityId id) const
|
||||
{
|
||||
return id < m_entities.size() && m_entities[id].entity.IsValid();
|
||||
return id < m_entityBlocks.size() && m_entityBlocks[id]->entity.IsValid();
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -270,6 +270,7 @@ namespace Ndk
|
||||
m_killedEntities = std::move(world.m_killedEntities);
|
||||
m_orderedSystems = std::move(world.m_orderedSystems);
|
||||
m_orderedSystemsUpdated = world.m_orderedSystemsUpdated;
|
||||
m_waitingEntities = std::move(world.m_waitingEntities);
|
||||
|
||||
m_entities = std::move(world.m_entities);
|
||||
for (EntityBlock& block : m_entities)
|
||||
|
||||
Reference in New Issue
Block a user