diff --git a/SDK/include/NDK/Entity.hpp b/SDK/include/NDK/Entity.hpp index 73214fb40..51995c1a4 100644 --- a/SDK/include/NDK/Entity.hpp +++ b/SDK/include/NDK/Entity.hpp @@ -8,57 +8,50 @@ #define NDK_ENTITY_HPP #include +#include namespace Ndk { + class EntityHandle; class World; class NDK_API Entity { + friend EntityHandle; friend World; public: - class Id; + using Id = nzUInt32; - Entity(); - Entity(const Entity&) = default; - ~Entity() = default; + Entity(const Entity&) = delete; + Entity(Entity&& entity); + ~Entity(); - void Kill(); + EntityHandle CreateHandle(); Id GetId() const; World* GetWorld() const; + void Kill(); + bool IsValid() const; - Entity& operator=(const Entity&) = default; - - bool operator==(const Entity& other) const; - bool operator!=(const Entity& other) const; - - // Identifiant - struct Id - { - struct Part - { - nzUInt32 counter, index; - }; - - union - { - Part part; - nzUInt64 value; - }; - - bool operator==(const Id& other) const; - bool operator!=(const Id& other) const; - }; + Entity& operator=(const Entity&) = delete; + Entity& operator=(Entity&&) = delete; private: - Entity(Id id, World* world); + Entity(World& world, Id id); + void Create(); + void Destroy(); + + void RegisterHandle(EntityHandle* handle); + void UnregisterHandle(EntityHandle* handle); + + std::set m_handles; Id m_id; World* m_world; + bool m_valid; }; } diff --git a/SDK/include/NDK/Entity.inl b/SDK/include/NDK/Entity.inl index 0a27680e0..bd111175e 100644 --- a/SDK/include/NDK/Entity.inl +++ b/SDK/include/NDK/Entity.inl @@ -6,15 +6,9 @@ namespace Ndk { - inline Entity::Entity() : - m_world(nullptr) - { - m_id.value = 0; - } - - inline Entity::Entity(Id id, World* world) : + inline Entity::Entity(World& world, Id id) : m_id(id), - m_world(world) + m_world(&world) { } @@ -28,23 +22,13 @@ namespace Ndk return m_world; } - inline bool Entity::operator==(const Entity& other) const + inline void Entity::RegisterHandle(EntityHandle* handle) { - return m_world == other.m_world && m_id == other.m_id; + m_handles.insert(handle); } - inline bool Entity::operator!=(const Entity& other) const + inline void Entity::UnregisterHandle(EntityHandle* handle) { - return !operator==(other); - } - - inline bool Entity::Id::operator==(const Id& other) const - { - return value == other.value; - } - - inline bool Entity::Id::operator!=(const Id& other) const - { - return !operator==(other); + m_handles.erase(handle); } } diff --git a/SDK/include/NDK/EntityHandle.hpp b/SDK/include/NDK/EntityHandle.hpp new file mode 100644 index 000000000..0cef8441a --- /dev/null +++ b/SDK/include/NDK/EntityHandle.hpp @@ -0,0 +1,71 @@ +// Copyright (C) 2015 Jérôme Leclercq +// This file is part of the "Nazara Development Kit" +// For conditions of distribution and use, see copyright notice in Prerequesites.hpp + +#pragma once + +#ifndef NDK_ENTITYHANDLE_HPP +#define NDK_ENTITYHANDLE_HPP + +#include +#include +#include + +namespace Ndk +{ + class Entity; + + class EntityHandle + { + friend Entity; + + public: + EntityHandle(); + explicit EntityHandle(Entity* entity); + EntityHandle(const EntityHandle& handle); + EntityHandle(EntityHandle&& handle); + ~EntityHandle(); + + Entity* GetEntity() const; + + bool IsValid() const; + + void Reset(Entity* entity = nullptr); + void Reset(const EntityHandle& handle); + void Reset(EntityHandle&& handle); + + EntityHandle& Swap(EntityHandle& handle); + + operator bool() const; + operator Entity*() const; + Entity* operator->() const; + + EntityHandle& operator=(Entity* entity); + EntityHandle& operator=(const EntityHandle& handle); + EntityHandle& operator=(EntityHandle&& handle); + + friend std::ostream& operator<<(std::ostream& out, const EntityHandle& handle); + + friend bool operator==(const EntityHandle& lhs, const EntityHandle& rhs); + friend bool operator!=(const EntityHandle& lhs, const EntityHandle& rhs); + friend bool operator<(const EntityHandle& lhs, const EntityHandle& rhs); + friend bool operator<=(const EntityHandle& lhs, const EntityHandle& rhs); + friend bool operator>(const EntityHandle& lhs, const EntityHandle& rhs); + friend bool operator>=(const EntityHandle& lhs, const EntityHandle& rhs); + + private: + void OnEntityDestroyed(); + void OnEntityMoved(Entity* newEntity); + + Entity* m_entity; + }; +} + +namespace std +{ + void swap(Ndk::EntityHandle& lhs, Ndk::EntityHandle& rhs); +} + +#include + +#endif // NDK_ENTITYHANDLE_HPP diff --git a/SDK/include/NDK/EntityHandle.inl b/SDK/include/NDK/EntityHandle.inl new file mode 100644 index 000000000..32bb5f254 --- /dev/null +++ b/SDK/include/NDK/EntityHandle.inl @@ -0,0 +1,177 @@ +// Copyright (C) 2015 Jérôme Leclercq +// This file is part of the "Nazara Development Kit" +// For conditions of distribution and use, see copyright notice in Prerequesites.hpp + +#include + +namespace Ndk +{ + inline EntityHandle::EntityHandle() : + m_entity(nullptr) + { + } + + inline EntityHandle::EntityHandle(Entity* entity) : + EntityHandle() + { + Reset(entity); + } + + inline EntityHandle::EntityHandle(const EntityHandle& handle) : + EntityHandle() + { + Reset(handle); + } + + inline EntityHandle::EntityHandle(EntityHandle&& handle) : + EntityHandle() + { + Reset(handle); + } + + inline EntityHandle::~EntityHandle() + { + Reset(nullptr); + } + + inline Entity* EntityHandle::GetEntity() const + { + return m_entity; + } + + inline bool EntityHandle::IsValid() const + { + return m_entity != nullptr; + } + + inline void EntityHandle::Reset(Entity* entity) + { + // Si nous avions déjà une entité, nous devons l'informer que nous ne pointons plus vers elle + if (m_entity) + m_entity->UnregisterHandle(this); + + m_entity = entity; + if (m_entity) + // On informe la nouvelle entité que nous pointons vers elle + m_entity->RegisterHandle(this); + } + + inline void EntityHandle::Reset(const EntityHandle& handle) + { + Reset(handle.GetEntity()); + } + + inline void EntityHandle::Reset(EntityHandle&& handle) + { + Reset(handle.GetEntity()); + } + + inline EntityHandle& EntityHandle::Swap(EntityHandle& handle) + { + std::swap(m_entity, handle.m_entity); + } + + inline EntityHandle::operator bool() const + { + return IsValid(); + } + + inline EntityHandle::operator Entity*() const + { + return m_entity; + } + + inline Entity* EntityHandle::operator->() const + { + return m_entity; + } + + inline EntityHandle& EntityHandle::operator=(Entity* entity) + { + Reset(entity); + } + + inline EntityHandle& EntityHandle::operator=(const EntityHandle& handle) + { + Reset(handle); + } + + inline EntityHandle& EntityHandle::operator=(EntityHandle&& handle) + { + Reset(handle); + } + + inline void EntityHandle::OnEntityDestroyed() + { + // Un raccourci, un appel à Reset nous enlèverait de la liste des handles que nous ne pouvons pas modifier + // maintenant car elle est actuellement parcourue + m_entity = nullptr; + } + + inline void EntityHandle::OnEntityMoved(Entity* newEntity) + { + // L'entité a été déplacée (peut arriver lors d'un changement de taille du conteneur du monde) + // nous mettons à jour notre pointeur + m_entity = newEntity; + } + + inline std::ostream& operator<<(std::ostream& out, const EntityHandle& handle) + { + out << "EntityHandle("; + if (handle.IsValid()) + out << "Entity(" << handle->GetId() << ")"; + else + out << "Null entity"; + + out << ')'; + + return out; + } + + inline bool operator==(const EntityHandle& lhs, const EntityHandle& rhs) + { + return lhs.m_entity == rhs.m_entity; + } + + inline bool operator!=(const EntityHandle& lhs, const EntityHandle& rhs) + { + return !(lhs == rhs); + } + + inline bool operator<(const EntityHandle& lhs, const EntityHandle& rhs) + { + return lhs.m_entity < rhs.m_entity; + } + + inline bool operator<=(const EntityHandle& lhs, const EntityHandle& rhs) + { + return !(lhs > rhs); + } + + inline bool operator>(const EntityHandle& lhs, const EntityHandle& rhs) + { + return rhs < lhs; + } + + inline bool operator>=(const EntityHandle& lhs, const EntityHandle& rhs) + { + return !(lhs < rhs); + } +} + +namespace std +{ + template<> + struct hash + { + size_t operator()(const Ndk::EntityHandle& handle) const + { + return hash()(handle.GetEntity()); + } + }; + + inline void swap(Ndk::EntityHandle& lhs, Ndk::EntityHandle& rhs) + { + lhs.Swap(rhs); + } +} diff --git a/SDK/include/NDK/World.hpp b/SDK/include/NDK/World.hpp index 0759c12b0..41d98c827 100644 --- a/SDK/include/NDK/World.hpp +++ b/SDK/include/NDK/World.hpp @@ -10,39 +10,41 @@ #include #include #include +#include #include namespace Ndk { + class EntityHandle; + class NDK_API World : NzNonCopyable { public: - using EntityList = std::vector; + using EntityList = std::vector; - World(); - ~World() = default; + World() = default; + ~World(); - Entity CreateEntity(); + EntityHandle CreateEntity(); EntityList CreateEntities(unsigned int count); void Clear(); - void KillEntity(Entity& entity); - void KillEntities(EntityList& list); + void KillEntity(Entity* entity); + void KillEntities(const EntityList& list); - Entity GetEntity(Entity::Id id); + Entity* GetEntity(Entity::Id id); - bool IsEntityValid(const Entity& entity) const; + bool IsEntityValid(Entity* entity) const; bool IsEntityIdValid(Entity::Id id) const; void Update(); private: - std::vector m_entitiesCounter; std::vector m_freeIdList; + std::vector m_entities; EntityList m_aliveEntities; EntityList m_killedEntities; - nzUInt32 m_nextIndex; }; } diff --git a/SDK/include/NDK/World.inl b/SDK/include/NDK/World.inl index bac8a1501..b20960c44 100644 --- a/SDK/include/NDK/World.inl +++ b/SDK/include/NDK/World.inl @@ -4,40 +4,31 @@ namespace Ndk { - inline World::World() : - m_nextIndex(0) - { - } - inline World::EntityList World::CreateEntities(unsigned int count) { EntityList list; list.reserve(count); for (unsigned int i = 0; i < count; ++i) - list.push_back(CreateEntity()); + list.emplace_back(CreateEntity()); return list; } - inline void World::KillEntities(EntityList& list) + inline void World::KillEntities(const EntityList& list) { m_killedEntities.reserve(m_killedEntities.size() + list.size()); - for (Entity& entity : list) + for (const EntityHandle& entity : list) KillEntity(entity); } - inline bool World::IsEntityValid(const Entity& entity) const + inline bool World::IsEntityValid(Entity* entity) const { - ///DOC: Cette méthode vérifie également l'appartenance de l'entité au monde (et est donc plus sûre) - return entity.GetWorld() == this && IsEntityIdValid(entity.GetId()); + return entity != nullptr && entity->GetWorld() == this && IsEntityIdValid(entity->GetId()); } inline bool World::IsEntityIdValid(Entity::Id id) const { - ///DOC: Il est possible que si l'identifiant vienne d'un autre monde, il soit considéré valide - /// alors qu'aucune entité de ce monde-ci ne l'utilise (encore) - - return m_entitiesCounter[id.part.index] == id.part.counter; + return id < m_entities.size() && m_entities[id].IsValid(); } } diff --git a/SDK/src/NDK/Entity.cpp b/SDK/src/NDK/Entity.cpp index 21f9c6bd8..634d6d9ad 100644 --- a/SDK/src/NDK/Entity.cpp +++ b/SDK/src/NDK/Entity.cpp @@ -1,17 +1,52 @@ // This file was automatically generated on 26 May 2014 at 01:05:31 #include +#include #include namespace Ndk { + Entity::Entity(Entity&& entity) : + m_handles(std::move(entity.m_handles)), + m_id(entity.m_id), + m_world(entity.m_world), + m_valid(entity.m_valid) + { + for (EntityHandle* handle : m_handles) + handle->OnEntityMoved(this); + } + + Entity::~Entity() + { + Destroy(); + } + + EntityHandle Entity::CreateHandle() + { + return EntityHandle(this); + } + void Entity::Kill() { - m_world->KillEntity(*this); + m_world->KillEntity(this); } bool Entity::IsValid() const { - return m_world != nullptr && m_world->IsEntityIdValid(m_id); + return m_valid; + } + + void Entity::Create() + { + m_valid = true; + } + + void Entity::Destroy() + { + m_valid = false; + + // On informe chaque handle de notre destruction pour éviter qu'il ne continue de pointer sur nous + for (EntityHandle* handle : m_handles) + handle->OnEntityDestroyed(); } } diff --git a/SDK/src/NDK/World.cpp b/SDK/src/NDK/World.cpp index 936afb8e9..6e7244ef4 100644 --- a/SDK/src/NDK/World.cpp +++ b/SDK/src/NDK/World.cpp @@ -5,7 +5,13 @@ namespace Ndk { - Entity World::CreateEntity() + World::~World() + { + // La destruction doit se faire dans un ordre précis + Clear(); + } + + EntityHandle World::CreateEntity() { Entity::Id id; if (!m_freeIdList.empty()) @@ -16,19 +22,17 @@ namespace Ndk } else { - // On alloue un nouvel identifiant - m_entitiesCounter.resize(m_entitiesCounter.size() + 1); + // On alloue une nouvelle entité + id = m_entities.size(); - auto& counter = m_entitiesCounter.back(); - counter = 1; - - id.part.counter = counter; - id.part.index = m_nextIndex; - - m_nextIndex++; + // Impossible d'utiliser emplace_back à cause de la portée + m_entities.push_back(Entity(*this, id)); } - Entity entity(id, this); + EntityHandle entity = m_entities[id].CreateHandle(); + + // On initialise l'entité et on l'ajoute à la liste des entités vivantes + entity->Create(); m_aliveEntities.push_back(entity); return entity; @@ -36,33 +40,32 @@ namespace Ndk void World::Clear() { - ///DOC: Les handles existants avant Clear ne sont plus garantis de ne pas être réutilisés - /// et devraient être détruits avant la création d'une nouvelle entité. + ///DOC: Tous les handles sont correctement invalidés + + // Destruction des entités d'abord, et des handles ensuite + // ceci pour éviter que les handles n'informent les entités inutilement lors de leur destruction + m_entities.clear(); m_aliveEntities.clear(); - m_entitiesCounter.clear(); - m_freeIdList.clear(); m_killedEntities.clear(); - - m_nextIndex = 0; } - void World::KillEntity(Entity& entity) + void World::KillEntity(Entity* entity) { ///DOC: Ignoré si l'entité est invalide if (IsEntityValid(entity)) - m_killedEntities.push_back(entity); + m_killedEntities.emplace_back(entity); } - Entity World::GetEntity(Entity::Id id) + Entity* World::GetEntity(Entity::Id id) { if (IsEntityIdValid(id)) - return Entity(id, this); + return &m_entities[id]; else { NazaraError("Invalid ID"); - return Entity(); + return nullptr; } } @@ -72,19 +75,17 @@ namespace Ndk { for (unsigned int i = 0; i < m_killedEntities.size(); ++i) { - Entity::Id e1 = m_aliveEntities[i].GetId(); + EntityHandle entity = m_killedEntities[i]; for (unsigned int j = 0; j < m_aliveEntities.size(); ++j) { - Entity::Id e2 = m_killedEntities[j].GetId(); - if (e1 == e2) + if (entity == m_aliveEntities[j]) { - // Remise en file de l'identifiant d'entité - nzUInt32& counter = m_entitiesCounter[e1.part.index]; - counter++; + // Remise en file d'attente de l'identifiant d'entité + m_freeIdList.push_back(entity->GetId()); - e1.part.counter = counter; - m_freeIdList.push_back(e1); + // Destruction de l'entité (invalidation du handle par la même occasion) + entity->Destroy(); // Suppression de l'entité des deux tableaux m_aliveEntities.erase(m_aliveEntities.begin() + j);