114 lines
3.2 KiB
C++
114 lines
3.2 KiB
C++
#ifndef LIBCATBOY_ECS_ECS_HPP
|
|
#define LIBCATBOY_ECS_ECS_HPP
|
|
|
|
#include "fwd.hpp"
|
|
#include "sparseSet.hpp"
|
|
|
|
#include <concepts>
|
|
#include <queue>
|
|
#include <stdexcept>
|
|
#include <type_traits>
|
|
#include <typeindex>
|
|
#include <unordered_map>
|
|
#include <unordered_set>
|
|
#include <vector>
|
|
|
|
namespace libcatboy {
|
|
namespace ecs {
|
|
|
|
template <std::integral Entity>
|
|
class basic_registry {
|
|
public:
|
|
using entity_type = Entity;
|
|
public:
|
|
entity_type create_entity() {
|
|
if (m_graveyard.empty()) {
|
|
entity_type entity = m_entityCounter++;
|
|
m_alive.insert(entity);
|
|
return entity;
|
|
}
|
|
entity_type entity = m_graveyard.front();
|
|
m_alive.insert(entity);
|
|
m_graveyard.pop();
|
|
return entity;
|
|
}
|
|
|
|
void erase_entity(entity_type entity) {
|
|
if (!m_alive.contains(entity)) return;
|
|
m_alive.erase(entity);
|
|
for (const auto& [type, set] : m_components) {
|
|
set->erase(entity);
|
|
}
|
|
m_graveyard.push(entity);
|
|
}
|
|
public:
|
|
template <typename T>
|
|
void register_component() {
|
|
get_set<T>();
|
|
}
|
|
|
|
template <typename T>
|
|
void unregister_component() {
|
|
auto it = m_components.find(typeid(T));
|
|
if (it == m_components.end()) return;
|
|
delete reinterpret_cast<sparse_set<T>*>(it->second);
|
|
m_components.erase(it);
|
|
}
|
|
public:
|
|
template <typename T>
|
|
void insert(entity_type entity, T&& component) {
|
|
emplace<std::remove_cvref_t<T>>(entity, std::forward<T>(component));
|
|
}
|
|
|
|
template <typename T, typename... Args>
|
|
T& emplace(entity_type entity, Args&&... args) {
|
|
if (m_alive.find(entity) == m_alive.end()) throw std::runtime_error("entity is dead");
|
|
return get_set<T>().emplace(entity, std::forward<Args>(args)...);
|
|
}
|
|
public:
|
|
template <typename T>
|
|
T& at(entity_type entity) {
|
|
if (m_alive.find(entity) == m_alive.end()) throw std::runtime_error("entity is dead");
|
|
return get_set<T>().at(entity);
|
|
}
|
|
|
|
template <typename T>
|
|
const T& at(entity_type entity) const {
|
|
if (m_alive.find(entity) == m_alive.end()) throw std::runtime_error("entity is dead");
|
|
return get_set<T>().at(entity);
|
|
}
|
|
public:
|
|
template <typename T>
|
|
void erase(entity_type entity) {
|
|
if (m_alive.find(entity) == m_alive.end()) throw std::runtime_error("entity is dead");
|
|
get_set<T>().erase(entity);
|
|
}
|
|
private:
|
|
template <typename T>
|
|
sparse_set<T>& get_set() {
|
|
auto it = m_components.find(typeid(T));
|
|
if (it != m_components.end()) return dynamic_cast<sparse_set<T>&>(*it->second);
|
|
sparse_set<T>* set = new sparse_set<T>();
|
|
m_components.emplace(typeid(T), set);
|
|
return *set;
|
|
}
|
|
|
|
template <typename T>
|
|
const sparse_set<T>& get_set() const {
|
|
auto it = m_components.find(typeid(T));
|
|
if (it != m_components.end()) return dynamic_cast<sparse_set<T>&>(*it->second);
|
|
throw std::runtime_error("error");
|
|
}
|
|
private:
|
|
std::unordered_map<std::type_index, isparse_set*> m_components;
|
|
|
|
std::unordered_set<entity_type> m_alive;
|
|
std::queue<entity_type> m_graveyard;
|
|
entity_type m_entityCounter = 0;
|
|
};
|
|
|
|
} // namespace ecs
|
|
} // namespace libcatboy
|
|
|
|
#endif // LIBCATBOY_ECS_ECS_HPP
|