#pragma once #include "dbc.hpp" #include <any> #include <functional> #include <queue> #include <tuple> #include <typeindex> #include <typeinfo> #include <unordered_map> #include <optional> namespace DinkyECS { typedef unsigned long Entity; using EntityMap = std::unordered_map<Entity, size_t>; template <typename T> struct ComponentStorage { std::vector<T> data; std::queue<size_t> free_indices; }; struct Event { int event = 0; Entity entity = 0; std::any data; }; typedef std::queue<Event> EventQueue; struct World { unsigned long entity_count = 0; std::unordered_map<std::type_index, EntityMap> $components; std::unordered_map<std::type_index, std::any> $facts; std::unordered_map<std::type_index, EventQueue> $events; std::unordered_map<std::type_index, std::any> $component_storages; std::vector<Entity> $constants; Entity entity() { return ++entity_count; } void clone_into(DinkyECS::World &to_world) { to_world.$constants = $constants; to_world.$facts = $facts; to_world.entity_count = entity_count; to_world.$component_storages = $component_storages; for(auto eid : $constants) { for(const auto &[tid, eid_map] : $components) { auto &their_map = to_world.$components[tid]; if(eid_map.contains(eid)) { their_map.insert_or_assign(eid, eid_map.at(eid)); } } } } void make_constant(DinkyECS::Entity entity) { $constants.push_back(entity); } template <typename Comp> size_t make_component() { auto &storage = component_storage_for<Comp>(); size_t index; if(!storage.free_indices.empty()) { index = storage.free_indices.front(); storage.free_indices.pop(); } else { storage.data.emplace_back(); index = storage.data.size() - 1; } return index; } template <typename Comp> ComponentStorage<Comp> &component_storage_for() { auto type_index = std::type_index(typeid(Comp)); $component_storages.try_emplace(type_index, ComponentStorage<Comp>{}); return std::any_cast<ComponentStorage<Comp> &>( $component_storages.at(type_index)); } template <typename Comp> EntityMap &entity_map_for() { return $components[std::type_index(typeid(Comp))]; } template <typename Comp> EventQueue &queue_map_for() { return $events[std::type_index(typeid(Comp))]; } template <typename Comp> void remove(Entity ent) { EntityMap &map = entity_map_for<Comp>(); if(map.contains(ent)) { size_t index = map.at(ent); component_storage_for<Comp>().free_indices.push(index); } map.erase(ent); } template <typename Comp> void set_the(Comp val) { $facts.insert_or_assign(std::type_index(typeid(Comp)), val); } template <typename Comp> Comp &get_the() { auto comp_id = std::type_index(typeid(Comp)); dbc::check($facts.contains(comp_id), fmt::format("!!!! ATTEMPT to access world fact that hasn't " "been set yet: {}", typeid(Comp).name())); // use .at to get std::out_of_range if fact not set std::any &res = $facts.at(comp_id); return std::any_cast<Comp &>(res); } template <typename Comp> bool has_the() { auto comp_id = std::type_index(typeid(Comp)); return $facts.contains(comp_id); } template <typename Comp> void set(Entity ent, Comp val) { EntityMap &map = entity_map_for<Comp>(); if(has<Comp>(ent)) { get<Comp>(ent) = val; return; } map.insert_or_assign(ent, make_component<Comp>()); get<Comp>(ent) = val; } template <typename Comp> Comp &get(Entity ent) { EntityMap &map = entity_map_for<Comp>(); auto &storage = component_storage_for<Comp>(); auto index = map.at(ent); return storage.data[index]; } template <typename Comp> bool has(Entity ent) { EntityMap &map = entity_map_for<Comp>(); return map.contains(ent); } template <typename Comp> void query(std::function<void(Entity, Comp &)> cb) { EntityMap &map = entity_map_for<Comp>(); for(auto &[entity, index] : map) { cb(entity, get<Comp>(entity)); } } template <typename CompA, typename CompB> void query(std::function<void(Entity, CompA &, CompB &)> cb) { EntityMap &map_a = entity_map_for<CompA>(); EntityMap &map_b = entity_map_for<CompB>(); for(auto &[entity, index_a] : map_a) { if(map_b.contains(entity)) { cb(entity, get<CompA>(entity), get<CompB>(entity)); } } } template <typename Comp> void send(Comp event, Entity entity, std::any data) { EventQueue &queue = queue_map_for<Comp>(); queue.push({event, entity, data}); } template <typename Comp> Event recv() { EventQueue &queue = queue_map_for<Comp>(); Event evt = queue.front(); queue.pop(); return evt; } template <typename Comp> bool has_event() { EventQueue &queue = queue_map_for<Comp>(); return !queue.empty(); } /* std::optional can't do references. Don't try it! * Actually, this sucks, either delete it or have it * return pointers (assuming optional can handle pointers) */ template <typename Comp> std::optional<Comp> get_if(DinkyECS::Entity entity) { if(has<Comp>(entity)) { return std::make_optional<Comp>(get<Comp>(entity)); } else { return std::nullopt; } } }; } // namespace DinkyECS