Sdk/EntityList: Fix crash when world was moved
This commit is contained in:
parent
02805aade3
commit
560061b4a5
|
|
@ -16,6 +16,7 @@ namespace Ndk
|
||||||
class NDK_API EntityList
|
class NDK_API EntityList
|
||||||
{
|
{
|
||||||
friend Entity;
|
friend Entity;
|
||||||
|
friend World;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
class iterator;
|
class iterator;
|
||||||
|
|
@ -23,16 +24,16 @@ namespace Ndk
|
||||||
using size_type = std::size_t;
|
using size_type = std::size_t;
|
||||||
|
|
||||||
inline EntityList();
|
inline EntityList();
|
||||||
inline EntityList(const EntityList& entityList);
|
EntityList(const EntityList& entityList);
|
||||||
inline EntityList(EntityList&& entityList) noexcept;
|
EntityList(EntityList&& entityList) noexcept;
|
||||||
inline ~EntityList();
|
~EntityList();
|
||||||
|
|
||||||
inline void Clear();
|
void Clear();
|
||||||
|
|
||||||
inline bool Has(const Entity* entity) const;
|
inline bool Has(const Entity* entity) const;
|
||||||
inline bool Has(EntityId entity) const;
|
inline bool Has(EntityId entity) const;
|
||||||
|
|
||||||
inline void Insert(Entity* entity);
|
void Insert(Entity* entity);
|
||||||
|
|
||||||
inline void Remove(Entity* entity);
|
inline void Remove(Entity* entity);
|
||||||
inline void Reserve(std::size_t entityCount);
|
inline void Reserve(std::size_t entityCount);
|
||||||
|
|
@ -50,6 +51,7 @@ namespace Ndk
|
||||||
inline std::size_t FindNext(std::size_t currentId) const;
|
inline std::size_t FindNext(std::size_t currentId) const;
|
||||||
inline World* GetWorld() const;
|
inline World* GetWorld() const;
|
||||||
inline void NotifyEntityDestruction(const Entity* entity);
|
inline void NotifyEntityDestruction(const Entity* entity);
|
||||||
|
inline void SetWorld(World* world);
|
||||||
|
|
||||||
Nz::Bitset<Nz::UInt64> m_entityBits;
|
Nz::Bitset<Nz::UInt64> m_entityBits;
|
||||||
World* m_world;
|
World* m_world;
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
// This file is part of the "Nazara Development Kit"
|
// This file is part of the "Nazara Development Kit"
|
||||||
// For conditions of distribution and use, see copyright notice in Prerequisites.hpp
|
// For conditions of distribution and use, see copyright notice in Prerequisites.hpp
|
||||||
|
|
||||||
|
#include <NDK/EntityList.hpp>
|
||||||
#include <Nazara/Core/Error.hpp>
|
#include <Nazara/Core/Error.hpp>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
|
|
@ -21,52 +22,6 @@ namespace Ndk
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
* \brief Construct a new entity list by copying another one
|
|
||||||
*/
|
|
||||||
inline EntityList::EntityList(const EntityList& entityList) :
|
|
||||||
m_entityBits(entityList.m_entityBits),
|
|
||||||
m_world(entityList.m_world)
|
|
||||||
{
|
|
||||||
for (const Ndk::EntityHandle& entity : *this)
|
|
||||||
entity->RegisterEntityList(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* \brief Construct a new entity list by moving a list into this one
|
|
||||||
*/
|
|
||||||
inline EntityList::EntityList(EntityList&& entityList) noexcept :
|
|
||||||
m_entityBits(std::move(entityList.m_entityBits)),
|
|
||||||
m_world(entityList.m_world)
|
|
||||||
{
|
|
||||||
for (const Ndk::EntityHandle& entity : *this)
|
|
||||||
{
|
|
||||||
entity->UnregisterEntityList(&entityList);
|
|
||||||
entity->RegisterEntityList(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inline EntityList::~EntityList()
|
|
||||||
{
|
|
||||||
for (const Ndk::EntityHandle& entity : *this)
|
|
||||||
entity->UnregisterEntityList(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* \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()
|
|
||||||
{
|
|
||||||
for (const Ndk::EntityHandle& entity : *this)
|
|
||||||
entity->UnregisterEntityList(this);
|
|
||||||
|
|
||||||
m_entityBits.Clear();
|
|
||||||
m_world = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \brief Checks whether or not the EntityList contains the entity
|
* \brief Checks whether or not the EntityList contains the entity
|
||||||
* \return true If it is the case
|
* \return true If it is the case
|
||||||
|
|
@ -93,29 +48,6 @@ namespace Ndk
|
||||||
return m_entityBits.UnboundedTest(entity);
|
return m_entityBits.UnboundedTest(entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
* \brief Inserts the entity into the set
|
|
||||||
*
|
|
||||||
* Marks an entity as present in this entity list, it must belongs to the same world as others entities contained in this list.
|
|
||||||
*
|
|
||||||
* \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)
|
|
||||||
{
|
|
||||||
NazaraAssert(entity, "Invalid entity");
|
|
||||||
|
|
||||||
if (!Has(entity))
|
|
||||||
{
|
|
||||||
entity->RegisterEntityList(this);
|
|
||||||
|
|
||||||
m_entityBits.UnboundedSet(entity->GetId(), true);
|
|
||||||
m_world = entity->GetWorld();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \brief Removes the entity from the set
|
* \brief Removes the entity from the set
|
||||||
*
|
*
|
||||||
|
|
@ -167,40 +99,6 @@ namespace Ndk
|
||||||
return m_entityBits.Count();
|
return m_entityBits.Count();
|
||||||
}
|
}
|
||||||
|
|
||||||
inline EntityList& EntityList::operator=(const EntityList& entityList)
|
|
||||||
{
|
|
||||||
for (const Ndk::EntityHandle& entity : *this)
|
|
||||||
entity->UnregisterEntityList(this);
|
|
||||||
|
|
||||||
m_entityBits = entityList.m_entityBits;
|
|
||||||
m_world = entityList.m_world;
|
|
||||||
|
|
||||||
for (const Ndk::EntityHandle& entity : *this)
|
|
||||||
entity->RegisterEntityList(this);
|
|
||||||
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline EntityList& EntityList::operator=(EntityList&& entityList) noexcept
|
|
||||||
{
|
|
||||||
if (this == &entityList)
|
|
||||||
return *this;
|
|
||||||
|
|
||||||
for (const Ndk::EntityHandle& entity : *this)
|
|
||||||
entity->UnregisterEntityList(this);
|
|
||||||
|
|
||||||
m_entityBits = std::move(entityList.m_entityBits);
|
|
||||||
m_world = entityList.m_world;
|
|
||||||
|
|
||||||
for (const Ndk::EntityHandle& entity : *this)
|
|
||||||
{
|
|
||||||
entity->UnregisterEntityList(&entityList);
|
|
||||||
entity->RegisterEntityList(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline std::size_t EntityList::FindNext(std::size_t currentId) const
|
inline std::size_t EntityList::FindNext(std::size_t currentId) const
|
||||||
{
|
{
|
||||||
return m_entityBits.FindNext(currentId);
|
return m_entityBits.FindNext(currentId);
|
||||||
|
|
@ -218,6 +116,11 @@ namespace Ndk
|
||||||
m_entityBits.Reset(entity->GetId());
|
m_entityBits.Reset(entity->GetId());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline void EntityList::SetWorld(World* world)
|
||||||
|
{
|
||||||
|
m_world = world;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
inline EntityList::iterator::iterator(const EntityList* list, std::size_t nextId) :
|
inline EntityList::iterator::iterator(const EntityList* list, std::size_t nextId) :
|
||||||
m_nextEntityId(nextId),
|
m_nextEntityId(nextId),
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,7 @@ namespace Ndk
|
||||||
{
|
{
|
||||||
friend BaseSystem;
|
friend BaseSystem;
|
||||||
friend Entity;
|
friend Entity;
|
||||||
|
friend EntityList;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
using EntityVector = std::vector<EntityHandle>;
|
using EntityVector = std::vector<EntityHandle>;
|
||||||
|
|
@ -97,7 +98,9 @@ namespace Ndk
|
||||||
inline void Invalidate();
|
inline void Invalidate();
|
||||||
inline void Invalidate(EntityId id);
|
inline void Invalidate(EntityId id);
|
||||||
inline void InvalidateSystemOrder();
|
inline void InvalidateSystemOrder();
|
||||||
|
inline void RegisterEntityList(EntityList* list);
|
||||||
void ReorderSystems();
|
void ReorderSystems();
|
||||||
|
inline void UnregisterEntityList(EntityList* list);
|
||||||
|
|
||||||
struct DoubleBitset
|
struct DoubleBitset
|
||||||
{
|
{
|
||||||
|
|
@ -123,6 +126,7 @@ namespace Ndk
|
||||||
std::vector<BaseSystem*> m_orderedSystems;
|
std::vector<BaseSystem*> m_orderedSystems;
|
||||||
std::vector<EntityBlock> m_entities;
|
std::vector<EntityBlock> m_entities;
|
||||||
std::vector<EntityBlock*> m_entityBlocks;
|
std::vector<EntityBlock*> m_entityBlocks;
|
||||||
|
std::vector<EntityList*> m_referencedByLists;
|
||||||
std::vector<std::unique_ptr<EntityBlock>> m_waitingEntities;
|
std::vector<std::unique_ptr<EntityBlock>> m_waitingEntities;
|
||||||
EntityList m_aliveEntities;
|
EntityList m_aliveEntities;
|
||||||
ProfilerData m_profilerData;
|
ProfilerData m_profilerData;
|
||||||
|
|
|
||||||
|
|
@ -443,24 +443,30 @@ namespace Ndk
|
||||||
m_dirtyEntities = std::move(world.m_dirtyEntities);
|
m_dirtyEntities = std::move(world.m_dirtyEntities);
|
||||||
m_entityBlocks = std::move(world.m_entityBlocks);
|
m_entityBlocks = std::move(world.m_entityBlocks);
|
||||||
m_freeEntityIds = std::move(world.m_freeEntityIds);
|
m_freeEntityIds = std::move(world.m_freeEntityIds);
|
||||||
|
m_isProfilerEnabled = world.m_isProfilerEnabled;
|
||||||
m_killedEntities = std::move(world.m_killedEntities);
|
m_killedEntities = std::move(world.m_killedEntities);
|
||||||
m_orderedSystems = std::move(world.m_orderedSystems);
|
m_orderedSystems = std::move(world.m_orderedSystems);
|
||||||
m_orderedSystemsUpdated = world.m_orderedSystemsUpdated;
|
m_orderedSystemsUpdated = world.m_orderedSystemsUpdated;
|
||||||
m_profilerData = std::move(world.m_profilerData);
|
m_profilerData = std::move(world.m_profilerData);
|
||||||
m_isProfilerEnabled = world.m_isProfilerEnabled;
|
m_referencedByLists = std::move(world.m_referencedByLists);
|
||||||
|
|
||||||
m_entities = std::move(world.m_entities);
|
m_entities = std::move(world.m_entities);
|
||||||
for (EntityBlock& block : m_entities)
|
for (EntityBlock& block : m_entities)
|
||||||
block.entity.SetWorld(this);
|
block.entity.SetWorld(this);
|
||||||
|
|
||||||
|
for (EntityList* list : m_referencedByLists)
|
||||||
|
list->SetWorld(this);
|
||||||
|
|
||||||
m_waitingEntities = std::move(world.m_waitingEntities);
|
m_waitingEntities = std::move(world.m_waitingEntities);
|
||||||
for (auto& blockPtr : m_waitingEntities)
|
for (auto& blockPtr : m_waitingEntities)
|
||||||
blockPtr->entity.SetWorld(this);
|
blockPtr->entity.SetWorld(this);
|
||||||
|
|
||||||
m_systems = std::move(world.m_systems);
|
m_systems = std::move(world.m_systems);
|
||||||
for (const auto& systemPtr : m_systems)
|
for (const auto& systemPtr : m_systems)
|
||||||
|
{
|
||||||
if (systemPtr)
|
if (systemPtr)
|
||||||
systemPtr->SetWorld(this);
|
systemPtr->SetWorld(this);
|
||||||
|
}
|
||||||
|
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
@ -480,4 +486,19 @@ namespace Ndk
|
||||||
{
|
{
|
||||||
m_orderedSystemsUpdated = false;
|
m_orderedSystemsUpdated = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline void World::RegisterEntityList(EntityList* list)
|
||||||
|
{
|
||||||
|
m_referencedByLists.push_back(list);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void World::UnregisterEntityList(EntityList* list)
|
||||||
|
{
|
||||||
|
auto it = std::find(m_referencedByLists.begin(), m_referencedByLists.end(), list);
|
||||||
|
assert(it != m_referencedByLists.end());
|
||||||
|
|
||||||
|
// Swap and pop idiom
|
||||||
|
*it = m_referencedByLists.back();
|
||||||
|
m_referencedByLists.pop_back();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,149 @@
|
||||||
|
|
||||||
namespace Ndk
|
namespace Ndk
|
||||||
{
|
{
|
||||||
|
/*!
|
||||||
|
* \brief Construct a new entity list by copying another one
|
||||||
|
*/
|
||||||
|
EntityList::EntityList(const EntityList& entityList) :
|
||||||
|
m_entityBits(entityList.m_entityBits),
|
||||||
|
m_world(entityList.m_world)
|
||||||
|
{
|
||||||
|
for (const Ndk::EntityHandle& entity : *this)
|
||||||
|
entity->RegisterEntityList(this);
|
||||||
|
|
||||||
|
if (m_world)
|
||||||
|
m_world->RegisterEntityList(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Construct a new entity list by moving a list into this one
|
||||||
|
*/
|
||||||
|
EntityList::EntityList(EntityList&& entityList) noexcept :
|
||||||
|
m_entityBits(std::move(entityList.m_entityBits)),
|
||||||
|
m_world(entityList.m_world)
|
||||||
|
{
|
||||||
|
for (const Ndk::EntityHandle& entity : *this)
|
||||||
|
{
|
||||||
|
entity->UnregisterEntityList(&entityList);
|
||||||
|
entity->RegisterEntityList(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_world)
|
||||||
|
{
|
||||||
|
m_world->UnregisterEntityList(&entityList);
|
||||||
|
m_world->RegisterEntityList(this);
|
||||||
|
|
||||||
|
entityList.m_world = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
EntityList::~EntityList()
|
||||||
|
{
|
||||||
|
for (const Ndk::EntityHandle& entity : *this)
|
||||||
|
entity->UnregisterEntityList(this);
|
||||||
|
|
||||||
|
if (m_world)
|
||||||
|
m_world->UnregisterEntityList(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \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
|
||||||
|
*/
|
||||||
|
void EntityList::Clear()
|
||||||
|
{
|
||||||
|
for (const Ndk::EntityHandle& entity : *this)
|
||||||
|
entity->UnregisterEntityList(this);
|
||||||
|
|
||||||
|
m_entityBits.Clear();
|
||||||
|
|
||||||
|
if (m_world)
|
||||||
|
{
|
||||||
|
m_world->UnregisterEntityList(this);
|
||||||
|
m_world = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Inserts the entity into the set
|
||||||
|
*
|
||||||
|
* Marks an entity as present in this entity list, it must belongs to the same world as others entities contained in this list.
|
||||||
|
*
|
||||||
|
* \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
|
||||||
|
*/
|
||||||
|
void EntityList::Insert(Entity* entity)
|
||||||
|
{
|
||||||
|
NazaraAssert(entity, "Invalid entity");
|
||||||
|
|
||||||
|
if (!Has(entity))
|
||||||
|
{
|
||||||
|
entity->RegisterEntityList(this);
|
||||||
|
|
||||||
|
m_entityBits.UnboundedSet(entity->GetId(), true);
|
||||||
|
if (!m_world)
|
||||||
|
{
|
||||||
|
m_world = entity->GetWorld();
|
||||||
|
m_world->RegisterEntityList(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
EntityList& EntityList::operator=(const EntityList& entityList)
|
||||||
|
{
|
||||||
|
if (m_world)
|
||||||
|
m_world->UnregisterEntityList(this);
|
||||||
|
|
||||||
|
for (const Ndk::EntityHandle& entity : *this)
|
||||||
|
entity->UnregisterEntityList(this);
|
||||||
|
|
||||||
|
m_entityBits = entityList.m_entityBits;
|
||||||
|
m_world = entityList.m_world;
|
||||||
|
|
||||||
|
for (const Ndk::EntityHandle& entity : *this)
|
||||||
|
entity->RegisterEntityList(this);
|
||||||
|
|
||||||
|
if (m_world)
|
||||||
|
m_world->RegisterEntityList(this);
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
EntityList& EntityList::operator=(EntityList&& entityList) noexcept
|
||||||
|
{
|
||||||
|
if (this == &entityList)
|
||||||
|
return *this;
|
||||||
|
|
||||||
|
if (m_world)
|
||||||
|
m_world->UnregisterEntityList(this);
|
||||||
|
|
||||||
|
for (const Ndk::EntityHandle& entity : *this)
|
||||||
|
entity->UnregisterEntityList(this);
|
||||||
|
|
||||||
|
m_entityBits = std::move(entityList.m_entityBits);
|
||||||
|
m_world = entityList.m_world;
|
||||||
|
|
||||||
|
if (m_world)
|
||||||
|
{
|
||||||
|
m_world->UnregisterEntityList(&entityList);
|
||||||
|
m_world->RegisterEntityList(this);
|
||||||
|
|
||||||
|
entityList.m_world = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const Ndk::EntityHandle& entity : *this)
|
||||||
|
{
|
||||||
|
entity->UnregisterEntityList(&entityList);
|
||||||
|
entity->RegisterEntityList(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
const EntityHandle& EntityList::iterator::operator*() const
|
const EntityHandle& EntityList::iterator::operator*() const
|
||||||
{
|
{
|
||||||
return m_list->GetWorld()->GetEntity(static_cast<EntityId>(m_nextEntityId));
|
return m_list->GetWorld()->GetEntity(static_cast<EntityId>(m_nextEntityId));
|
||||||
|
|
|
||||||
|
|
@ -131,6 +131,11 @@ namespace Ndk
|
||||||
}
|
}
|
||||||
m_entityBlocks.clear();
|
m_entityBlocks.clear();
|
||||||
|
|
||||||
|
// Reset world for entity lists
|
||||||
|
for (EntityList* list : m_referencedByLists)
|
||||||
|
list->SetWorld(nullptr);
|
||||||
|
m_referencedByLists.clear();
|
||||||
|
|
||||||
m_entities.clear();
|
m_entities.clear();
|
||||||
m_waitingEntities.clear();
|
m_waitingEntities.clear();
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue