Skip to content

Commit

Permalink
Merge pull request #18 from MasterLaplace/51-implement-ecs-in-flakkar…
Browse files Browse the repository at this point in the history
…i-server

51 implement ecs in flakkari server
  • Loading branch information
MasterLaplace authored Jan 8, 2024
2 parents f9866b0 + ac2d42a commit defc1f0
Show file tree
Hide file tree
Showing 5 changed files with 508 additions and 0 deletions.
4 changes: 4 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -27,6 +28,9 @@ set(HEADERS
Flakkari/Network/IOMultiplexer.hpp
Flakkari/Protocol/Header.hpp
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
Expand Down
44 changes: 44 additions & 0 deletions Flakkari/Engine/EntityComponentSystem/Entity.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/**************************************************************************
* 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 <cstddef>

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_ */
46 changes: 46 additions & 0 deletions Flakkari/Engine/EntityComponentSystem/Registry.cpp
Original file line number Diff line number Diff line change
@@ -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
217 changes: 217 additions & 0 deletions Flakkari/Engine/EntityComponentSystem/Registry.hpp
Original file line number Diff line number Diff line change
@@ -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 <unordered_map>
#include <typeindex>
#include <any>
#include <functional>
#include <queue>
#include <stdexcept>
#include <climits>
#include <iostream>

namespace Flakkari::Engine::ECS {

class Registry {
public:
using entity_type = Entity;
using EraseFn = std::function<void(Registry&, const entity_type&)>;
using SystemFn = std::function<void(Registry&)>;

/**
* @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 <typename Component>
[[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<SparseArrays<Component>&>(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 <typename Component>
[[nodiscard]] bool isRegistered()
{
auto componentIt = _components.find(std::type_index(typeid(Component)));

if (componentIt != _components.end()) {
auto &component = std::any_cast<SparseArrays<Component>&>(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<Component>::reference_type The component.
*/
template <typename Component>
typename SparseArrays<Component>::reference_type add_component(const entity_type &to, Component &&c) {
return getComponents<Component>().insert_at(to, std::forward<Component>(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<Component>::reference_type The component.
*/
template <typename Component, typename... Params>
typename SparseArrays<Component>::reference_type emplace_component(const entity_type &to, Params&&... p) {
return getComponents<Component>().emplace_at(to, std::forward<Params>(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 <typename Component>
void remove_component(const entity_type &from) {
getComponents<Component>().erase(from);
}

/**
* @brief Get the component from an entity.
*
* @tparam Component The component to get.
* @return SparseArrays<Component>& The component.
*/
template <typename Component>
SparseArrays<Component> &registerComponent()
{
if (isRegistered<Component>())
return getComponents<Component>();

auto ti = std::type_index(typeid(Component));
_components[ti] = std::make_any<SparseArrays<Component>>();
_eraseFunctions[ti] = [](Registry &r, const entity_type &e) {
r.remove_component<Component>(e);
};
return std::any_cast<SparseArrays<Component>&>(_components[ti]);
}

/**
* @brief Get the Components object from the registry.
*
* @tparam Component The component to get.
* @return SparseArrays<Component>& The component array.
*/
template <typename Component>
SparseArrays<Component> &getComponents()
{
if (!isRegistered<Component>())
return registerComponent<Component>();

auto ti = std::type_index(typeid(Component));
return std::any_cast<SparseArrays<Component>&>(_components[ti]);
}

/**
* @brief Get the Components object from the registry.
*
* @tparam Component The component to get.
* @return const SparseArrays<Component>& The component array.
*/
template <typename Component>
const SparseArrays<Component> &getComponents() const {
auto ti = std::type_index(typeid(Component));
return std::any_cast<const SparseArrays<Component>&>(_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 <typename... Components, typename Function>
void add_system(Function &&f) {
_systems.emplace_back([sys{std::forward<Function>(f)}](Registry &r) {
sys(r, r.getComponents<Components>()...);
});
}

/**
* @brief Run all the systems in the registry.
*
*/
void run_systems();

private:
std::unordered_map<std::type_index, std::any> _components;
std::unordered_map<std::type_index, EraseFn> _eraseFunctions;
std::vector<SystemFn> _systems;
std::queue<entity_type> _deadEntities;
size_t _nextEntity = 0;
};

} // namespace Flakkari::Engine::ECS

#endif /* !REGISTRY_HPP_ */
Loading

0 comments on commit defc1f0

Please sign in to comment.