From 277f4484e72282e517625207f934a85327db9db5 Mon Sep 17 00:00:00 2001 From: AbdelHameed-Hamed Date: Sat, 6 Sep 2025 15:49:02 +0100 Subject: [PATCH] Add initial systems implementation. --- core/ecs.h | 116 +++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 104 insertions(+), 12 deletions(-) diff --git a/core/ecs.h b/core/ecs.h index 6aa0c5dc..3ba26e8e 100644 --- a/core/ecs.h +++ b/core/ecs.h @@ -2,12 +2,16 @@ #include "core/export.h" #include "core/defines.h" +#include "core/memory/memory.h" #include "core/memory/pool_allocator.h" #include "core/containers/hash_table.h" #include #include #include +#include +#include +#include namespace ecs { @@ -56,7 +60,7 @@ namespace ecs Component_Table() { - pool = memory::pool_allocator_init(sizeof(T), 64); + pool = memory::pool_allocator_init(sizeof(T), 64); components = hash_table_init(); } @@ -121,15 +125,54 @@ namespace ecs } }; + struct ISystem + { + virtual const std::span get_types() const noexcept = 0; + virtual const std::span get_hashes() const noexcept = 0; + virtual void run(struct ECS &) const noexcept = 0; + }; + + using func_type = void (*)(ECS &); + + // TODO: + // 1: Seperate out Readable and Writable components as it might allow for an extra degree of + // parallizm + // 2: Maybe we ought to figure out a way to enforce the components used via type systems and + // function arguments + template + struct System : ISystem + { + inline static constexpr auto types_count = sizeof...(TArgs); + inline static const std::array types = {typeid(TArgs).name()...}; + inline static const std::array hashes = [] { + std::array hashes = {typeid(TArgs).hash_code()...}; + std::ranges::sort(hashes); + return hashes; + }(); + const func_type f; + + System(const func_type func) : f(func) {} + const std::span get_types() const noexcept { return types; } + const std::span get_hashes() const noexcept { return hashes; } + void run(ECS &self) const noexcept { f(self); } + }; + + struct Level + { + Array systems; + Array hashes; + }; + struct ECS { Hash_Table component_tables; + Array systems; template const T * read(Entity e) { - const auto [_, v] = *hash_table_find(component_tables, (u64)typeid(T).hash_code()); + const auto [_, v] = *hash_table_find(component_tables, (u64) typeid(T).hash_code()); return (const T *)v->read(e); } @@ -137,7 +180,7 @@ namespace ecs T * write(Entity e) { - auto [_, v] = *hash_table_find(component_tables, (u64)typeid(T).hash_code()); + auto [_, v] = *hash_table_find(component_tables, (u64) typeid(T).hash_code()); return (T *)v->write(e); } @@ -145,24 +188,25 @@ namespace ecs void remove(Entity e) { - auto [_, v] = *hash_table_find(component_tables, (u64)typeid(T).hash_code()); + auto [_, v] = *hash_table_find(component_tables, (u64) typeid(T).hash_code()); v->remove(e); } - template + template Array list() { - Array entities[sizeof...(TArgs)] = { hash_table_find(component_tables, (u64)typeid(TArgs).hash_code())->value->entities()... }; + Array entities[sizeof...(TArgs)] = {hash_table_find(component_tables, (u64) typeid(TArgs).hash_code())->value->entities()...}; u64 min_idx = u64(-1); for (u64 i = 0; i < sizeof...(TArgs); ++i) if (entities[i].count < min_idx) min_idx = i; - IComponent_Table *tables[sizeof...(TArgs)] = { hash_table_find(component_tables, (u64)typeid(TArgs).hash_code())->value... }; + IComponent_Table *tables[sizeof...(TArgs)] = {hash_table_find(component_tables, (u64) typeid(TArgs).hash_code())->value...}; Array res = array_copy(entities[min_idx], memory::temp_allocator()); for (auto table : tables) - array_remove_if(res, [table](Entity e) { return table->read(e) == nullptr; }); + array_remove_if(res, [table](Entity e) + { return table->read(e) == nullptr; }); return res; } @@ -170,7 +214,7 @@ namespace ecs void reload() { - for (auto& [_, v] : component_tables) + for (auto &[_, v] : component_tables) { auto temp = v->reload(); std::swap(temp, v); @@ -205,7 +249,7 @@ namespace ecs inline static void ecs_add_table(ECS &self) { - hash_table_insert(self.component_tables, (u64)typeid(T).hash_code(), (IComponent_Table *)memory::allocate_and_call_constructor>()); + hash_table_insert(self.component_tables, (u64) typeid(T).hash_code(), (IComponent_Table *)memory::allocate_and_call_constructor>()); } template @@ -229,7 +273,7 @@ namespace ecs self.remove(e); } - template + template inline static Array ecs_entity_list(ECS &self) { @@ -243,10 +287,58 @@ namespace ecs self.reload(); } - // TODO: Rename this to ecs_entity_remove()? inline static void ecs_entity_free(ECS &self, Entity e) { self.entity_free(e); } + + template + inline static void + ecs_system_add(ECS &self, const func_type f) + { + const ISystem *s = memory::allocate_and_call_constructor>(f); + const auto &s_hashes = s->get_hashes(); + for (size_t i = 0; i < self.systems.count; ++i) { + auto &level_hashes = self.systems[i].hashes; + bool found = false; + for (int j = 0, k = 0; j < level_hashes.count && k < s_hashes.size();) { + if (level_hashes[j] == s_hashes[k]) { + found = true; + break; + } + else if (level_hashes[j] < s_hashes[k]) + ++j; + else + ++k; + } + if (found) + continue; + else { + array_push(self.systems[i].systems, s); + for (const size_t h : s_hashes) + array_push(level_hashes, h); + std::ranges::sort(level_hashes); + return; + } + } + + array_push(self.systems, Level{ + .systems = array_init(), + .hashes = array_init(), + }); + auto &last_level = array_last(self.systems); + array_push(last_level.systems, s); + for (const size_t h : s_hashes) + array_push(last_level.hashes, h); + } + + inline static void + ecs_systems_run(ECS &self) + { + for (const auto level : self.systems) + /// TODO: Parallalize this later + for (const auto sys : level.systems) + sys->run(self); + } } \ No newline at end of file