220 lines
4.7 KiB
C++
220 lines
4.7 KiB
C++
#pragma once
|
|
#include <unordered_map>
|
|
#include <string>
|
|
#include <functional>
|
|
#include <algorithm>
|
|
#include <list>
|
|
|
|
#include <typeinfo>
|
|
#if __has_include(<cxxabi.h>)
|
|
#include <cxxabi.h>
|
|
#endif
|
|
|
|
#include "uuid.h"
|
|
|
|
namespace ecs {
|
|
template <typename T>
|
|
std::string get_typename() {
|
|
#if __has_include(<cxxabi.h>)
|
|
std::string name = abi::__cxa_demangle(typeid(T).name(), 0, 0, 0);
|
|
#else
|
|
std::string name = typeid(T).name();
|
|
// This is really janky
|
|
name = name.substr(name.find(' ')+1);
|
|
#endif
|
|
|
|
auto index = name.find_last_of(':');
|
|
if (index != std::string::npos) {
|
|
name = name.substr(index+1);
|
|
}
|
|
|
|
return name;
|
|
}
|
|
|
|
class ComponentID {
|
|
private:
|
|
static size_t _id;
|
|
|
|
public:
|
|
// This needs to be a function because otherwise it might not be initialized on time
|
|
static std::unordered_map<std::string, size_t>& _ids();
|
|
static std::vector<size_t> get_id(std::vector<std::string> names);
|
|
|
|
// @todo This is slow...
|
|
static std::string get_name(size_t id) {
|
|
for (auto it = _ids().begin(); it != _ids().end(); ++it) {
|
|
if (it->second == id) {
|
|
return it->first;
|
|
}
|
|
}
|
|
|
|
throw std::runtime_error("ID does not exist");
|
|
}
|
|
|
|
// This looks kind of ugly
|
|
template <typename T>
|
|
inline static const size_t id = get_id({get_typename<T>()})[0];
|
|
};
|
|
|
|
struct Component {
|
|
Component() : _runtime(false), _id(0) {}
|
|
|
|
// @todo Would be nice if this worked with templates
|
|
Component(size_t id) : _runtime(true), _id(id) {}
|
|
|
|
virtual ~Component() {}
|
|
|
|
const bool _runtime;
|
|
const size_t _id;
|
|
};
|
|
|
|
class Entity {
|
|
public:
|
|
Entity(uuids::uuid _uuid) : uuid(_uuid) {}
|
|
~Entity();
|
|
|
|
template <typename T, typename... Args>
|
|
void add_component(Args... args) {
|
|
size_t id = ComponentID::id<T>;
|
|
|
|
if (_components.find(id) != _components.end()) {
|
|
throw std::runtime_error("Component already exists");
|
|
}
|
|
|
|
_components[id] = new T(args...);
|
|
}
|
|
|
|
template <typename... Ts>
|
|
bool has_components() const {
|
|
std::vector<size_t> ids = {ComponentID::id<Ts>...};
|
|
for (const auto& id : ids) {
|
|
if (_components.find(id) == _components.end()) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
template <typename T>
|
|
T* get_component() const {
|
|
size_t id = ComponentID::id<T>;
|
|
auto it = _components.find(id);
|
|
if (it == _components.end()) {
|
|
throw std::runtime_error("Component does not exist");
|
|
}
|
|
|
|
return (T*)it->second;
|
|
}
|
|
|
|
const std::unordered_map<size_t, Component*> get_components() const {
|
|
return _components;
|
|
}
|
|
|
|
void add_component(size_t id, Component* component);
|
|
bool has_components(std::vector<size_t> ids);
|
|
Component* get_component(size_t id);
|
|
|
|
const uuids::uuid uuid;
|
|
|
|
private:
|
|
std::unordered_map<size_t, Component*> _components;
|
|
};
|
|
|
|
template <typename... Ts>
|
|
class View {
|
|
public:
|
|
View(std::unordered_map<uuids::uuid, Entity*> entities) : _entities(entities) {}
|
|
|
|
// @todo What should we do with the uuid
|
|
void for_each(std::function<void(Entity*, std::add_pointer_t<Ts>...)> function) {
|
|
for (auto [uuid, entity] : _entities) {
|
|
function(entity, entity->template get_component<Ts>()...);
|
|
}
|
|
}
|
|
|
|
size_t size() const {
|
|
return _entities.size();
|
|
}
|
|
|
|
auto begin() {
|
|
return _entities.begin();
|
|
}
|
|
|
|
auto begin() const {
|
|
return _entities.begin();
|
|
}
|
|
|
|
auto cbegin() const {
|
|
return _entities.cbegin();
|
|
}
|
|
|
|
auto end() {
|
|
return _entities.end();
|
|
}
|
|
|
|
auto end() const {
|
|
return _entities.end();
|
|
}
|
|
|
|
auto cend() const {
|
|
return _entities.cend();
|
|
}
|
|
|
|
private:
|
|
std::unordered_map<uuids::uuid, Entity*> _entities;
|
|
};
|
|
|
|
class Manager {
|
|
public:
|
|
~Manager();
|
|
|
|
template <typename... Ts>
|
|
View<Ts...> view() {
|
|
std::unordered_map<uuids::uuid, Entity*> entities;
|
|
|
|
// @todo Fix this so it uses structed bindings, for some reason it does not work
|
|
for (auto entity : _entities) {
|
|
if (entity.second->has_components<Ts...>()) {
|
|
entities.insert(entity);
|
|
}
|
|
}
|
|
|
|
return View<Ts...>(entities);
|
|
}
|
|
|
|
View<> view(std::vector<size_t> ids) {
|
|
std::unordered_map<uuids::uuid, Entity*> entities;
|
|
|
|
for (auto [uuid, entity] : _entities) {
|
|
if (entity->has_components(ids)) {
|
|
entities.insert({uuid, entity});
|
|
}
|
|
}
|
|
|
|
return View<>(entities);
|
|
}
|
|
|
|
Entity* create_entity(uuids::uuid uuid);
|
|
Entity* create_entity();
|
|
|
|
void remove_entity(ecs::Entity* entity);
|
|
|
|
bool has_entity(uuids::uuid uuid) {
|
|
// @todo c++20 has .contains()
|
|
return _entities.count(uuid);
|
|
}
|
|
|
|
Entity* get_entity(uuids::uuid uuid) {
|
|
auto it = _entities.find(uuid);
|
|
if (it == _entities.end()) {
|
|
throw std::runtime_error("Unable to find entity with uuid!");
|
|
}
|
|
return it->second;
|
|
}
|
|
|
|
private:
|
|
std::unordered_map<uuids::uuid, Entity*> _entities;
|
|
};
|
|
}
|