// Copyright (C) 2017 Jérôme Leclercq // This file is part of the "Nazara Development Kit" // For conditions of distribution and use, see copyright notice in Prerequisites.hpp #include #include #include namespace Ndk { /*! * \brief Constructs a World object * * \param addDefaultSystems Should default provided systems be used */ inline World::World(bool addDefaultSystems) : m_orderedSystemsUpdated(false), m_isProfilerEnabled(false) { if (addDefaultSystems) AddDefaultSystems(); } /*! * \brief Constructs a World object by move semantic * * \param world World to move into this */ inline World::World(World&& world) noexcept : HandledObject(std::move(world)) { operator=(std::move(world)); } /*! * \brief Adds a system to the world * \return A reference to the newly created system * * \param system System to add to the world */ inline BaseSystem& World::AddSystem(std::unique_ptr&& system) { NazaraAssert(system, "System must be valid"); SystemIndex index = system->GetIndex(); // We must ensure that the vector is big enough to hold the new system if (index >= m_systems.size()) { m_systems.resize(index + 1); m_profilerData.updateTime.resize(index + 1, 0); } // Affectation and return of system m_systems[index] = std::move(system); m_systems[index]->SetWorld(this); Invalidate(); // We force an update for every entities InvalidateSystemOrder(); // And regenerate the system update list return *m_systems[index].get(); } /*! * \brief Adds a system to the world * \return A reference to the newly created system * * \param args Arguments used to create the system */ template SystemType& World::AddSystem(Args&&... args) { static_assert(std::is_base_of::value, "SystemType is not a component"); // Allocation and affectation of the system std::unique_ptr ptr(new SystemType(std::forward(args)...)); return static_cast(AddSystem(std::move(ptr))); } /*! * \brief Creates multiple entities in the world * \return The set of entities created * * \param count Number of entities to create */ inline World::EntityVector World::CreateEntities(unsigned int count) { EntityVector list; list.reserve(count); for (unsigned int i = 0; i < count; ++i) list.emplace_back(CreateEntity()); return list; } /*! * \brief Disables the profiler, clearing up results * * This is just a shortcut to EnableProfiler(false) * * \param enable Should the entity be enabled * * \see EnableProfiler */ inline void World::DisableProfiler() { EnableProfiler(false); } /*! * \brief Enables/Disables the internal profiler * * Worlds come with a built-in profiler, allowing to measure update count along with time passed in refresh and system updates. * This is disabled by default as it adds an small overhead to the update process. * * \param enable Should the profiler be enabled * * \remark Disabling the profiler clears up results, as if ResetProfiler has been called */ inline void World::EnableProfiler(bool enable) { if (m_isProfilerEnabled != enable) { m_isProfilerEnabled = enable; if (enable) ResetProfiler(); } } /*! * \brief Executes a function on every present system * * Calls iterationFunc on every previously added system, in the same order as their indexes * * \param iterationFunc Function to be called */ template void World::ForEachSystem(const F& iterationFunc) { for (const auto& systemPtr : m_systems) { if (systemPtr) iterationFunc(*systemPtr); } } /*! * \brief Executes a function on every present system * * Calls iterationFunc on every previously added system, in the same order as their indexes * * \param iterationFunc Function to be called */ template void World::ForEachSystem(const F& iterationFunc) const { for (const auto& systemPtr : m_systems) { if (systemPtr) iterationFunc(static_cast(*systemPtr)); //< Force const reference } } /*! * \brief Gets an entity * \return A constant reference to a handle of the entity * * \param id Identifier of the entity * * \remark Handle referenced by this function can move in memory when updating the world, do not keep a handle reference from a world update to another * \remark If an invalid identifier is provided, an error got triggered and an invalid handle is returned */ inline const EntityHandle& World::GetEntity(EntityId id) { if (IsEntityIdValid(id)) return m_entityBlocks[id]->handle; else { NazaraError("Invalid ID"); return EntityHandle::InvalidHandle; } } /*! * \brief Gets every entities in the world * \return A constant reference to the entities */ inline const EntityList& World::GetEntities() const { return m_aliveEntities; } /*! * \brief Gets the latest profiler data * \return A constant reference to the profiler data */ inline const World::ProfilerData& World::GetProfilerData() const { return m_profilerData; } /*! * \brief Gets a system in the world by index * \return A reference to the system * * \param index Index of the system * * \remark The world must have the system before calling this function */ inline BaseSystem& World::GetSystem(SystemIndex index) { NazaraAssert(HasSystem(index), "This system is not part of the world"); BaseSystem* system = m_systems[index].get(); NazaraAssert(system, "Invalid system pointer"); return *system; } /*! * \brief Gets a system in the world by index * \return A const reference to the system * * \param index Index of the system * * \remark The world must have the system before calling this function */ inline const BaseSystem& World::GetSystem(SystemIndex index) const { NazaraAssert(HasSystem(index), "This system is not part of the world"); const BaseSystem* system = m_systems[index].get(); NazaraAssert(system, "Invalid system pointer"); return *system; } /*! * \brief Gets a system in the world by type * \return A reference to the system * * \remark Produces a NazaraAssert if system is not available in this world */ template SystemType& World::GetSystem() { static_assert(std::is_base_of::value, "SystemType is not a system"); SystemIndex index = GetSystemIndex(); return static_cast(GetSystem(index)); } /*! * \brief Gets a system in the world by type * \return A const reference to the system * * \remark Produces a NazaraAssert if system is not available in this world */ template const SystemType& World::GetSystem() const { static_assert(std::is_base_of::value, "SystemType is not a system"); SystemIndex index = GetSystemIndex(); return static_cast(GetSystem(index)); } /*! * \brief Checks whether or not a system is present in the world by index * \return true If it is the case * * \param index Index of the system */ inline bool World::HasSystem(SystemIndex index) const { return index < m_systems.size() && m_systems[index]; } /*! * \brief Checks whether or not a system is present in the world by type * \return true If it is the case */ template bool World::HasSystem() const { static_assert(std::is_base_of::value, "SystemType is not a component"); SystemIndex index = GetSystemIndex(); return HasSystem(index); } /*! * \brief Marks an entity for deletion * * \param Pointer to the entity * * \remark If the entity pointer is invalid, nothing is done * \remark For safety, entities are not killed until the next world update */ inline void World::KillEntity(Entity* entity) { if (IsEntityValid(entity)) m_killedEntities.front.UnboundedSet(entity->GetId(), true); } /*! * \brief Kills a set of entities * * This function has the same effect as calling KillEntity for every entity contained in the vector * * \param list Set of entities to kill */ inline void World::KillEntities(const EntityVector& list) { for (const EntityHandle& entity : list) KillEntity(entity); } /*! * \brief Checks whether or not an entity is dying (has been killed this update) * \return true If the entity exists and is dying * * \param entity Pointer to the entity */ inline bool World::IsEntityDying(const Entity* entity) const { return entity && IsEntityDying(entity->GetId()); } /*! * \brief Checks whether or not an entity is dying (has been killed this update) * \return true If it is the case, false if the entity is alive (and hasn't been killed yet) or if the entity id is invalid * * \param id Identifier of the entity */ inline bool World::IsEntityDying(EntityId id) const { return m_killedEntities.front.UnboundedTest(id); } /*! * \brief Checks whether or not an entity is valid * \return true If it is the case * * \param entity Pointer to the entity */ inline bool World::IsEntityValid(const Entity* entity) const { return entity && entity->GetWorld() == this && IsEntityIdValid(entity->GetId()); } /*! * \brief Checks whether or not an entity is valid * \return true If it is the case * * \param id Identifier of the entity */ inline bool World::IsEntityIdValid(EntityId id) const { return id < m_entityBlocks.size() && m_entityBlocks[id]->entity.IsValid(); } /*! * \brief Checks whether or not the profiler is enabled * \return true If it is the case * * \see EnableProfiler */ inline bool World::IsProfilerEnabled() const { return m_isProfilerEnabled; } /*! * \brief Removes each system from the world */ inline void World::RemoveAllSystems() { m_systems.clear(); InvalidateSystemOrder(); } /*! * \brief Removes a system from the world by index * * \param index Index of the system * * \remark No change is done if system is not present */ inline void World::RemoveSystem(SystemIndex index) { if (HasSystem(index)) { m_systems[index].reset(); InvalidateSystemOrder(); } } /*! * \brief Clear profiler results * * This reset the profiler results, filling all counters with zero */ inline void World::ResetProfiler() { m_profilerData.refreshTime = 0; m_profilerData.updateCount = 0; std::fill(m_profilerData.updateTime.begin(), m_profilerData.updateTime.end(), 0); } /*! * \brief Removes a system from the world by type */ template void World::RemoveSystem() { static_assert(std::is_base_of(), "SystemType is not a system"); SystemIndex index = GetSystemIndex(); RemoveSystem(index); } /*! * \brief Moves a world into another world object * \return A reference to the object */ inline World& World::operator=(World&& world) noexcept { m_aliveEntities = std::move(world.m_aliveEntities); m_dirtyEntities = std::move(world.m_dirtyEntities); m_entityBlocks = std::move(world.m_entityBlocks); m_freeEntityIds = std::move(world.m_freeEntityIds); m_killedEntities = std::move(world.m_killedEntities); m_orderedSystems = std::move(world.m_orderedSystems); m_orderedSystemsUpdated = world.m_orderedSystemsUpdated; m_profilerData = std::move(world.m_profilerData); m_isProfilerEnabled = world.m_isProfilerEnabled; m_entities = std::move(world.m_entities); for (EntityBlock& block : m_entities) block.entity.SetWorld(this); m_waitingEntities = std::move(world.m_waitingEntities); for (auto& blockPtr : m_waitingEntities) blockPtr->entity.SetWorld(this); m_systems = std::move(world.m_systems); for (const auto& systemPtr : m_systems) if (systemPtr) systemPtr->SetWorld(this); return *this; } inline void World::Invalidate() { m_dirtyEntities.front.Resize(m_entityBlocks.size(), false); m_dirtyEntities.front.Set(true); // Activation of all bits } inline void World::Invalidate(EntityId id) { m_dirtyEntities.front.UnboundedSet(id, true); } inline void World::InvalidateSystemOrder() { m_orderedSystemsUpdated = false; } }