ecs/ecs/include/ecs.h
2019-09-13 01:03:47 +02:00

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;
};
}