From 1d13197526f1bcfda5f715ea9dfe6de337e6bea1 Mon Sep 17 00:00:00 2001 From: Master_Laplace Date: Mon, 8 Jan 2024 09:22:35 +0100 Subject: [PATCH 1/3] feat(ECS): Add Entity class for ECS --- CMakeLists.txt | 1 + .../Engine/EntityComponentSystem/Entity.hpp | 43 +++++++++++++++++++ 2 files changed, 44 insertions(+) create mode 100644 Flakkari/Engine/EntityComponentSystem/Entity.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 42acba64..7312ecc2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -27,6 +27,7 @@ set(HEADERS Flakkari/Network/IOMultiplexer.hpp Flakkari/Protocol/Header.hpp Flakkari/Protocol/Packet.hpp + Flakkari/Engine/EntityComponentSystem/Entity.hpp Flakkari/Server/UDPServer.hpp Flakkari/Server/Client/Client.hpp Flakkari/Server/Client/ClientManager.hpp diff --git a/Flakkari/Engine/EntityComponentSystem/Entity.hpp b/Flakkari/Engine/EntityComponentSystem/Entity.hpp new file mode 100644 index 00000000..1dd5807a --- /dev/null +++ b/Flakkari/Engine/EntityComponentSystem/Entity.hpp @@ -0,0 +1,43 @@ +/************************************************************************** + * Flakkari Library v0.1.0 + * + * Flakkari Library is a C++ Library for Network. + * @file Entity.hpp + * @brief Entity class for ECS (Entity Component System). + * + * Flakkari Library is under MIT License. + * https://opensource.org/licenses/MIT + * © 2023 @MasterLaplace + * @version 0.1.0 + * @date 2023-01-05 + **************************************************************************/ + +#ifndef ENTITY_HPP_ + #define ENTITY_HPP_ + +#include + +namespace Flakkari::Engine::ECS { + +class Registry; + +class Entity { +public: + friend class Registry; + + explicit Entity(std::size_t id) : _id(id) {} + Entity() : _id(0) {} + + operator std::size_t() const { return _id;} + + std::size_t operator++() { return ++_id; } + Entity &operator=(std::size_t id) { _id = id; return *this; } + Entity &operator=(int id) { _id = (id < 0) ? 0 : id; return *this; } + +private: + std::size_t _id; +}; + +} // namespace Flakkari::Engine::ECS + +#endif /* !ENTITY_HPP_ */ From d0ec8ea02061be2505054e82b753bf54ca078e91 Mon Sep 17 00:00:00 2001 From: Master_Laplace Date: Mon, 8 Jan 2024 09:39:15 +0100 Subject: [PATCH 2/3] feat(ECS): Add SparseArrays class for ECS --- CMakeLists.txt | 1 + .../EntityComponentSystem/SparseArrays.hpp | 197 ++++++++++++++++++ 2 files changed, 198 insertions(+) create mode 100644 Flakkari/Engine/EntityComponentSystem/SparseArrays.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 7312ecc2..9000d681 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -28,6 +28,7 @@ set(HEADERS Flakkari/Protocol/Header.hpp Flakkari/Protocol/Packet.hpp Flakkari/Engine/EntityComponentSystem/Entity.hpp + Flakkari/Engine/EntityComponentSystem/SparseArrays.hpp Flakkari/Server/UDPServer.hpp Flakkari/Server/Client/Client.hpp Flakkari/Server/Client/ClientManager.hpp diff --git a/Flakkari/Engine/EntityComponentSystem/SparseArrays.hpp b/Flakkari/Engine/EntityComponentSystem/SparseArrays.hpp new file mode 100644 index 00000000..17d9cead --- /dev/null +++ b/Flakkari/Engine/EntityComponentSystem/SparseArrays.hpp @@ -0,0 +1,197 @@ +/************************************************************************** + * Flakkari Library v0.1.0 + * + * Flakkari Library is a C++ Library for Network. + * @file SparseArrays.hpp + * @brief SparseArrays class for ECS (Entity Component System). + * + * Flakkari Library is under MIT License. + * https://opensource.org/licenses/MIT + * © 2023 @MasterLaplace + * @version 0.1.0 + * @date 2023-01-05 + **************************************************************************/ + + +#ifndef SPARSEARRAYS_HPP_ + #define SPARSEARRAYS_HPP_ + +#include +#include +#include +#include + +namespace Flakkari::Engine::ECS { + +template +class SparseArrays { +public: + using value_type = std::optional; + using reference_type = value_type&; + using const_reference_type = const value_type&; + using container_type = std::vector; + using size_type = typename container_type::size_type; + using iterator = typename container_type::iterator; + using const_iterator = typename container_type::const_iterator; + +public: + SparseArrays() = default; + SparseArrays(const SparseArrays &other) : _data(other._data) {}; + SparseArrays(SparseArrays&&other) noexcept : _data(std::move(other._data)) {}; + ~SparseArrays() = default; + + /** + * @brief Copy assignment operator for SparseArrays. + * + * @param other The SparseArrays to copy. + * @return SparseArrays& The SparseArrays copied. + */ + SparseArrays &operator=(SparseArrays const &other) { + _data = std::move(other._data); + return *this; + } + + /** + * @brief Move assignment operator for SparseArrays. + * + * @param other The SparseArrays to move. + * @return SparseArrays& The SparseArrays moved. + */ + SparseArrays &operator=(SparseArrays &&other) noexcept + { + if (this != &other) + std::swap(_data, other._data); + + return *this; + } + + /** + * @brief Get the component at the index. + * + * @param idx The index of the component. + * @return reference_type The component. + */ + reference_type operator[](size_type idx) + { + if (idx >= _data.size()) + _data.resize(idx + 1, std::nullopt); + + return _data[idx]; + } + + /** + * @brief Get the component at the index. + * Const version. + * + * @param idx The index of the component. + * @return const_reference_type The component. + */ + const_reference_type operator[](size_type idx) const + { + if (idx >= _data.size()) + return std::nullopt; + + return _data[idx]; + } + + iterator begin() { return _data.begin(); } + + const_iterator begin() const { return _data.begin(); } + + const_iterator cbegin() const { return _data.cbegin(); } + + iterator end() { return _data.end(); } + + const_iterator end() const { return _data.end(); } + + const_iterator cend() const { return _data.cend(); } + + size_type size() const { return _data.size(); } + + /** + * @brief Insert a component at the end of the SparseArrays. + * + * @param pos The position of the component. + * @param component The component to insert. + * @return reference_type The component inserted. + */ + reference_type insert_at(size_type pos, const Component &component) + { + if (pos >= _data.size()) + _data.resize(pos + 1, std::nullopt); + + _data[pos].emplace(component); + return _data[pos]; + } + + /** + * @brief Insert a component at the end of the SparseArrays. + * Move version. + * + * @param pos The position of the component. + * @param component The component to insert. + * @return reference_type The component inserted. + */ + reference_type insert_at(size_type pos, Component &&component) + { + if (pos >= _data.size()) + _data.resize(pos + 1, std::nullopt); + + _data[pos] = std::move(component); + return _data[pos]; + } + + /** + * @brief Emplace a component at the end of the SparseArrays. + * + * @tparam Params The parameters to construct the component. + * @param pos The position of the component. + * @param params The parameters to construct the component. + * @return reference_type The component inserted. + */ + template + reference_type emplace_at(size_type pos, Params&&... params) + { + if (pos >= _data.size()) + _data.resize(pos + 1, std::nullopt); + + _data[pos].emplace(std::forward(params)...); + return _data[pos]; + } + + /** + * @brief Erase a component at the position. + * + * @param pos The position of the component to erase. + */ + void erase(size_type pos) + { + if (pos < _data.size()) + _data[pos].reset(); + } + + /** + * @brief Get the index object from a component. + * + * @param component The component to get the index from. + * @return size_type The index of the component. + */ + size_type get_index(value_type const &component) const + { + auto it = std::find_if(_data.begin(), _data.end(), [&component](auto const& opt) { + return opt.has_value() && opt.value() == component; + }); + + if (it == _data.end()) + return _data.size(); + + return std::distance(_data.begin(), it); + } + +private: + container_type _data; +}; + +} // namespace Flakkari::Engine::ECS + +#endif /* !SPARSEARRAYS_HPP_ */ From ac2d42aa49312b112e57878455e62663026c1bba Mon Sep 17 00:00:00 2001 From: Master_Laplace Date: Mon, 8 Jan 2024 09:41:31 +0100 Subject: [PATCH 3/3] feat(ECS): Add Registry class for Entity Component System --- CMakeLists.txt | 2 + .../Engine/EntityComponentSystem/Entity.hpp | 1 + .../Engine/EntityComponentSystem/Registry.cpp | 46 ++++ .../Engine/EntityComponentSystem/Registry.hpp | 217 ++++++++++++++++++ 4 files changed, 266 insertions(+) create mode 100644 Flakkari/Engine/EntityComponentSystem/Registry.cpp create mode 100644 Flakkari/Engine/EntityComponentSystem/Registry.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 9000d681..ff896106 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,6 +13,7 @@ set(SOURCES Flakkari/Network/IOMultiplexer.cpp Flakkari/Protocol/Header.cpp Flakkari/Protocol/Packet.cpp + Flakkari/Engine/EntityComponentSystem/Registry.cpp Flakkari/Server/UDPServer.cpp Flakkari/Server/Client/Client.cpp Flakkari/Server/Client/ClientManager.cpp @@ -29,6 +30,7 @@ set(HEADERS Flakkari/Protocol/Packet.hpp Flakkari/Engine/EntityComponentSystem/Entity.hpp Flakkari/Engine/EntityComponentSystem/SparseArrays.hpp + Flakkari/Engine/EntityComponentSystem/Registry.hpp Flakkari/Server/UDPServer.hpp Flakkari/Server/Client/Client.hpp Flakkari/Server/Client/ClientManager.hpp diff --git a/Flakkari/Engine/EntityComponentSystem/Entity.hpp b/Flakkari/Engine/EntityComponentSystem/Entity.hpp index 1dd5807a..08be1bff 100644 --- a/Flakkari/Engine/EntityComponentSystem/Entity.hpp +++ b/Flakkari/Engine/EntityComponentSystem/Entity.hpp @@ -12,6 +12,7 @@ * @date 2023-01-05 **************************************************************************/ + #ifndef ENTITY_HPP_ #define ENTITY_HPP_ diff --git a/Flakkari/Engine/EntityComponentSystem/Registry.cpp b/Flakkari/Engine/EntityComponentSystem/Registry.cpp new file mode 100644 index 00000000..84c3cb4b --- /dev/null +++ b/Flakkari/Engine/EntityComponentSystem/Registry.cpp @@ -0,0 +1,46 @@ +/* +** EPITECH PROJECT, 2024 +** Title: Flakkari +** Author: MasterLaplace +** Created: 2023-01-05 +** File description: +** Registry +*/ + +#include "Registry.hpp" + +namespace Flakkari::Engine::ECS { + +using entity_type = Registry::entity_type; + +entity_type Registry::spawn_entity() +{ + if (!_deadEntities.empty()) { + entity_type e = _deadEntities.front(); + _deadEntities.pop(); + return e; + } + if (_nextEntity < SIZE_MAX) + return Entity(_nextEntity++); + throw std::runtime_error("No more available entities to spawn."); +} + +entity_type Registry::entity_from_index(std::size_t idx) { + return Entity(idx); +} + +void Registry::kill_entity(const entity_type &e) +{ + for (auto &pair : _eraseFunctions) + pair.second(*this, e); + + _deadEntities.push(e); +} + +void Registry::run_systems() +{ + for (auto &system : _systems) + system(*this); +} + +} // namespace Flakkari::Engine::ECS diff --git a/Flakkari/Engine/EntityComponentSystem/Registry.hpp b/Flakkari/Engine/EntityComponentSystem/Registry.hpp new file mode 100644 index 00000000..cc4f5d5b --- /dev/null +++ b/Flakkari/Engine/EntityComponentSystem/Registry.hpp @@ -0,0 +1,217 @@ +/************************************************************************** + * Flakkari Library v0.1.0 + * + * Flakkari Library is a C++ Library for Network. + * @file Registry.hpp + * @brief Registry class for ECS (Entity Component System). + * + * Flakkari Library is under MIT License. + * https://opensource.org/licenses/MIT + * © 2023 @MasterLaplace + * @version 0.1.0 + * @date 2023-01-05 + **************************************************************************/ + + +#ifndef REGISTRY_HPP_ + #define REGISTRY_HPP_ + +#include "SparseArrays.hpp" +#include "Entity.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Flakkari::Engine::ECS { + +class Registry { +public: + using entity_type = Entity; + using EraseFn = std::function; + using SystemFn = std::function; + + /** + * @brief Spawn a new entity in the registry. + * + * @return entity_type The entity created. + */ + entity_type spawn_entity(); + + /** + * @brief Get the entity from index object from the registry. + * + * @param idx The index of the entity. + * @return entity_type The entity. + */ + entity_type entity_from_index(std::size_t idx); + + /** + * @brief Kill an entity from the registry. + * + * @param e The entity to kill. + */ + void kill_entity(const entity_type &e); + + /** + * @brief Check if an entity is registered in the registry. + * + * @tparam Component The component to check. + * @param entity The entity to check. + * @return true If the entity is registered. + * @return false If the entity is not registered. + */ + template + [[nodiscard]] bool isRegistered(entity_type const &entity) + { + auto componentIt = _components.find(std::type_index(typeid(Component))); + + if (componentIt != _components.end()) { + auto &component = std::any_cast&>(componentIt->second); + if (entity < component.size()) + return component[entity].has_value(); + } + return false; + } + + /** + * @brief Check if a component is registered in the registry. + * + * @tparam Component The component to check. + * @return true If the component is registered. + * @return false If the component is not registered. + */ + template + [[nodiscard]] bool isRegistered() + { + auto componentIt = _components.find(std::type_index(typeid(Component))); + + if (componentIt != _components.end()) { + auto &component = std::any_cast&>(componentIt->second); + return component.size() > 0; + } + return false; + } + + /** + * @brief Get the component from an entity. + * + * @tparam Component The component to get. + * @param to The entity to get the component from. + * @param c The component to get. + * @return SparseArrays::reference_type The component. + */ + template + typename SparseArrays::reference_type add_component(const entity_type &to, Component &&c) { + return getComponents().insert_at(to, std::forward(c)); + } + + /** + * @brief Get the component from an entity. + * + * @tparam Component The component to get. + * @tparam Params The parameters to construct the component. + * @param to The entity to get the component from. + * @param p The parameters to construct the component. + * @return SparseArrays::reference_type The component. + */ + template + typename SparseArrays::reference_type emplace_component(const entity_type &to, Params&&... p) { + return getComponents().emplace_at(to, std::forward(p)...); + } + + /** + * @brief Remove a component from an entity in the registry. + * + * @tparam Component The component to remove. + * @param from The entity to remove the component from. + */ + template + void remove_component(const entity_type &from) { + getComponents().erase(from); + } + + /** + * @brief Get the component from an entity. + * + * @tparam Component The component to get. + * @return SparseArrays& The component. + */ + template + SparseArrays ®isterComponent() + { + if (isRegistered()) + return getComponents(); + + auto ti = std::type_index(typeid(Component)); + _components[ti] = std::make_any>(); + _eraseFunctions[ti] = [](Registry &r, const entity_type &e) { + r.remove_component(e); + }; + return std::any_cast&>(_components[ti]); + } + + /** + * @brief Get the Components object from the registry. + * + * @tparam Component The component to get. + * @return SparseArrays& The component array. + */ + template + SparseArrays &getComponents() + { + if (!isRegistered()) + return registerComponent(); + + auto ti = std::type_index(typeid(Component)); + return std::any_cast&>(_components[ti]); + } + + /** + * @brief Get the Components object from the registry. + * + * @tparam Component The component to get. + * @return const SparseArrays& The component array. + */ + template + const SparseArrays &getComponents() const { + auto ti = std::type_index(typeid(Component)); + return std::any_cast&>(_components.at(ti)); + } + + /** + * @brief Add a system to the registry. + * + * @tparam Components The components to add to the system. + * @tparam Function The function to add to the system. + * @param f The function to add to the system. + */ + template + void add_system(Function &&f) { + _systems.emplace_back([sys{std::forward(f)}](Registry &r) { + sys(r, r.getComponents()...); + }); + } + + /** + * @brief Run all the systems in the registry. + * + */ + void run_systems(); + +private: + std::unordered_map _components; + std::unordered_map _eraseFunctions; + std::vector _systems; + std::queue _deadEntities; + size_t _nextEntity = 0; +}; + +} // namespace Flakkari::Engine::ECS + +#endif /* !REGISTRY_HPP_ */