initial implementation

This commit is contained in:
jadebenn
2024-12-17 00:13:14 -06:00
parent afc2966507
commit 427b7c1047
27 changed files with 300 additions and 109 deletions

View File

@@ -1,4 +1,7 @@
add_library(dECS STATIC "ECS.cpp")
add_library(dECS STATIC
"Core.h"
"Core.cpp"
)
target_include_directories(dECS PUBLIC .)
target_link_libraries(dECS PRIVATE dCommon)
target_link_libraries(dECS PRIVATE dCommon magic_enum::magic_enum)
target_compile_options(dECS PRIVATE "-Wall")

70
dECS/Core.cpp Normal file
View File

@@ -0,0 +1,70 @@
#include <atomic>
#include <magic_enum/magic_enum_containers.hpp>
#include <eReplicaComponentType.h>
#include "Core.h"
namespace dECS {
struct WorldData {
using CompSignature = magic_enum::containers::bitset<eReplicaComponentType>;
using CompMap = std::unordered_map<LWOOBJID, CompSignature>;
using CompStorage = std::unordered_map<eReplicaComponentType, std::unique_ptr<IStorage>>;
std::atomic<LWOOBJID> nextId = 1;
CompMap map;
CompStorage data;
};
World::World() : m_World{ std::make_shared<WorldData>() } {};
Entity World::MakeEntity() {
return Entity{ m_World->nextId.fetch_add(1, std::memory_order::relaxed),
m_World };
}
void* Entity::AddComponent(const eReplicaComponentType kind, const StorageConstructor storageConstructor) {
if (auto w = m_World.lock()) {
// add to kind signature
w->map[m_Id].set(kind, true);
// get or add storage
auto storageIt = w->data.find(kind);
if (storageIt == w->data.cend()) {
bool inserted = false;
std::tie(storageIt, inserted) = w->data.try_emplace(kind, storageConstructor());
if (!inserted) throw "storage emplacement failure";
}
auto& storage = *storageIt->second;
// return reference if already mapped, otherwise add component
auto compIt = storage.rowMap.find(m_Id);
if (compIt == storage.rowMap.cend()) {
const auto curSize = storage.rowMap.size();
storage.rowMap.emplace(m_Id, curSize);
return storage.emplace_back();
}
const auto row = compIt->second;
return storage.at(row);
}
return nullptr;
}
const void* Entity::GetComponent(const eReplicaComponentType kind) const {
if (auto const w = m_World.lock()) {
const auto& compSig = w->map.at(m_Id);
if (!compSig.test(kind)) return nullptr;
const auto& storage = *w->data.at(kind);
const auto it = storage.rowMap.find(m_Id);
if (it == storage.rowMap.cend()) return nullptr;
const auto row = it->second;
return storage.at(row);
}
return nullptr;
}
void* Entity::GetComponent(const eReplicaComponentType kind) {
// Casting away const for this overload is safe, if not at all pretty
return const_cast<void*>(std::as_const(*this).GetComponent(kind));
}
}

124
dECS/Core.h Normal file
View File

@@ -0,0 +1,124 @@
#pragma once
#include <cstdint>
#include <memory>
#include <unordered_map>
#include <vector>
class Component;
enum class eReplicaComponentType : uint32_t;
using LWOOBJID = int64_t;
namespace dECS {
// template <typename C>
// concept IsComponent = std::derived_from<C, Component>;
struct WorldData;
struct World;
struct Entity;
struct IStorage;
template <typename C>
class Storage;
using WorldPtr = std::shared_ptr<WorldData>;
using WeakWorldPtr = std::weak_ptr<WorldData>;
class World {
public:
World();
[[nodiscard]]
Entity MakeEntity();
private:
WorldPtr m_World;
};
class Entity {
public:
friend Entity World::MakeEntity();
using StorageConstructor = std::function<std::unique_ptr<IStorage>()>;
[[nodiscard]]
constexpr LWOOBJID Id() const noexcept {
return m_Id;
}
[[maybe_unused]]
void* AddComponent(eReplicaComponentType, StorageConstructor);
template <typename C>
[[maybe_unused]]
C* AddComponent() {
return static_cast<C*>(AddComponent(C::ComponentType, std::make_unique<Storage<C>>));
}
[[nodiscard]]
const void* GetComponent(eReplicaComponentType) const;
[[nodiscard]]
void* GetComponent(eReplicaComponentType);
template <typename C>
[[nodiscard]]
const C* GetComponent() const {
return static_cast<const C*>(GetComponent(C::ComponentType));
}
template <typename C>
[[nodiscard]]
C* GetComponent() {
return static_cast<C*>(GetComponent(C::ComponentType));
}
private:
Entity(const LWOOBJID id, const WeakWorldPtr world)
: m_Id{ id }
, m_World { world }
{}
LWOOBJID m_Id;
WeakWorldPtr m_World;
};
struct IStorage {
using RowMap = std::unordered_map<LWOOBJID, size_t>;
virtual ~IStorage() = default;
[[nodiscard]]
virtual void* at(size_t) = 0;
[[nodiscard]]
virtual const void* at(size_t) const = 0;
[[nodiscard]]
virtual void* emplace_back() = 0;
RowMap rowMap;
};
template <typename C>
class Storage : public IStorage {
public:
[[nodiscard]]
void* at(const size_t index) override {
return static_cast<void*>(&m_Vec.at(index));
}
[[nodiscard]]
const void* at(const size_t index) const override {
return static_cast<const void*>(&m_Vec.at(index));
}
[[nodiscard]]
void* emplace_back() override {
return static_cast<void*>(&m_Vec.emplace_back());
}
private:
std::vector<C> m_Vec;
};
}

View File

@@ -1,8 +0,0 @@
#include "ECS.h"
#include "dCommonVars.h"
#include <string>
#include <vector>
namespace Component {
}

View File

@@ -1,65 +0,0 @@
#pragma once
#include <bitset>
#include <climits>
#include <cstdint>
#include <vector>
#include "dCommonVars.h"
#include "eReplicaComponentType.h"
namespace Component {
constexpr size_t MAX_KINDS { 32 };
enum class Kind : uint32_t {
NONE = 0,
DESTROYABLE = 7,
PET = 26,
};
using Signature = std::bitset<MAX_KINDS>;
// Components
struct Destroyable;
struct Pet {
static constexpr Kind KIND_ID = Kind::PET;
// The ID under which this pet is stored in the database (if it's tamed)
LWOOBJID m_DatabaseId;
// The ID of the item from which this pet was creat
LWOOBJID m_ItemId;
// The name of this pet
std::string name;
};
class IStorage {
public:
constexpr IStorage(const Kind kind) : m_Kind{ kind } {}
virtual ~IStorage() = default;
constexpr virtual void Remove(const size_t index) = 0;
[[nodiscard]]
constexpr Kind GetKind() const {
return m_Kind;
}
protected:
Kind m_Kind;
};
template <typename C>
class Storage : public IStorage {
public:
constexpr Storage() : IStorage{ C::KIND_ID } {}
constexpr void Remove(const size_t index) override {
auto& elementToDelete = m_Vec.at(index);
auto& lastElement = m_Vec.back();
std::swap(elementToDelete, lastElement);
m_Vec.pop_back();
}
private:
std::vector<C> m_Vec;
};
}