Skip to content
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
116 changes: 104 additions & 12 deletions core/ecs.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 <type_traits>
#include <typeinfo>
#include <concepts>
#include <algorithm>
#include <span>
#include <array>

namespace ecs
{
Expand Down Expand Up @@ -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<u64, T *>();
}

Expand Down Expand Up @@ -121,56 +125,96 @@ namespace ecs
}
};

struct ISystem
{
virtual const std::span<char const* const> get_types() const noexcept = 0;
virtual const std::span<const size_t> 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 <Component_Type... TArgs>
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<size_t, types_count> 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<char const* const> get_types() const noexcept { return types; }
const std::span<const size_t> get_hashes() const noexcept { return hashes; }
void run(ECS &self) const noexcept { f(self); }
};

struct Level
{
Array<ISystem *> systems;
Array<size_t> hashes;
};

struct ECS
{
Hash_Table<Component_Hash, IComponent_Table *> component_tables;
Array<Level> systems;

template <Component_Type T>
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);
}

template <Component_Type T>
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);
}

template <Component_Type T>
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 <Component_Type ... TArgs>
template <Component_Type... TArgs>
Array<Entity>
list()
{
Array<Entity> entities[sizeof...(TArgs)] = { hash_table_find(component_tables, (u64)typeid(TArgs).hash_code())->value->entities()... };
Array<Entity> 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<Entity> 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;
}

void
reload()
{
for (auto& [_, v] : component_tables)
for (auto &[_, v] : component_tables)
{
auto temp = v->reload();
std::swap(temp, v);
Expand Down Expand Up @@ -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<Component_Table<T>>());
hash_table_insert(self.component_tables, (u64) typeid(T).hash_code(), (IComponent_Table *)memory::allocate_and_call_constructor<Component_Table<T>>());
}

template <Component_Type T>
Expand All @@ -229,7 +273,7 @@ namespace ecs
self.remove<T>(e);
}

template <Component_Type ...TArgs>
template <Component_Type... TArgs>
inline static Array<Entity>
ecs_entity_list(ECS &self)
{
Expand All @@ -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 <Component_Type... TArgs>
inline static void
ecs_system_add(ECS &self, const func_type f)
{
const ISystem *s = memory::allocate_and_call_constructor<System<TArgs...>>(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<ISystem *>(),
.hashes = array_init<size_t>(),
});
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);
}
}
Loading