From 62e62849767c9de744c74766e23fc1f5ae1d8ec5 Mon Sep 17 00:00:00 2001 From: Patedam Date: Sun, 6 Sep 2026 12:08:31 -0400 Subject: [PATCH] preparing serialization of entities --- Game/Data/World.cpp | 64 ++++++++++++++++++---- Game/Data/World.h | 7 --- Game/Entity/Entity.cpp | 22 ++++++++ Game/Entity/Entity.h | 57 +++++++++++-------- Game/Entity/EntityManager.cpp | 15 ++++- Game/Entity/EntityManager.h | 2 +- Game/game.h | 7 --- Juliet/include/Core/Common/serialization.h | 10 +++- Juliet/include/Engine/Class.h | 5 +- Juliet/src/Core/Common/serialization.cpp | 10 ++-- Juliet/src/Engine/class.cpp | 1 + 11 files changed, 138 insertions(+), 62 deletions(-) create mode 100644 Juliet/src/Engine/class.cpp diff --git a/Game/Data/World.cpp b/Game/Data/World.cpp index 337d075..e6e6443 100644 --- a/Game/Data/World.cpp +++ b/Game/Data/World.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #ifdef JULIET_ENABLE_IMGUI @@ -30,41 +31,80 @@ void AddToWorld(NonNullPtr world, NonNullPtr entity) void RemoveWorldEntity(World& world, size_t index) {} -void Serialize(archive& data, World& world, String filename) +void Serialize(archive& ar, World& world, String filename) { Assert(IsValid(filename)); - TempArena temp = scratch_begin(nullptr, 0); + auto& entityManager = GetEntityManager(); - if (data.loading) + if (ar.loading) { // Load - ByteBuffer fileBuffer = LoadFile(temp.Arena, filename); + ByteBuffer fileBuffer = LoadFile(ar.arena, filename); + if (fileBuffer.Size >= sizeof(WorldFileHeader)) + { + ar.base_ptr = fileBuffer.Data; + + WorldFileHeader header; + serialize_elem(ar, header); + Assert(header.Magic == kWorldMagic); + Assert(header.Version == kWorldVersion); + + for (typed_entity_array& type : entityManager.by_type) + { + serialize_elem(ar, type.count); + + if (type.count > 0) + { + + // Unserialize the base entity to get informations + Entity entity; + serialize(ar, &entity); + } + } + } } else { // Save - IOStream* stream = IOFromFile(temp.Arena, filename, WrapString("wb")); + IOStream* stream = IOFromFile(ar.arena, filename, WrapString("wb")); - index_t beginPos = ArenaPos(temp.Arena); + index_t beginPos = ArenaPos(ar.arena); // Headers - auto* header = ArenaPushStruct(temp.Arena); + auto* header = ArenaPushStruct(ar.arena); header->Magic = kWorldMagic; header->Version = kWorldVersion; - index_t endPos = ArenaPos(temp.Arena); + ar.base_ptr = header; + + // TODO : Move to a one file per entity model + for (typed_entity_array& type : entityManager.by_type) + { + serialize_elem(ar, type.count); + auto* element = type.array; + uint8* rawElement = reinterpret_cast(element); + size_t stride = element->Base->Kind->size_of; + for (index_t idx = 0; idx < type.count; ++idx) + { + // Todo : utils + // Get base entity from type + Entity* entity = reinterpret_cast(rawElement + (idx * stride))->Base; + serialize(ar, entity); + } + } // Write + index_t endPos = ArenaPos(ar.arena); ByteBuffer writeBuffer = { .Data = reinterpret_cast(header), .Size = endPos - beginPos }; - size_t written = IOWrite(stream, writeBuffer); + + size_t written = IOWrite(stream, writeBuffer); Assert(writeBuffer.Size == written); IOClose(stream); } - scratch_end(temp); // // uint32 entityCount = static_cast(world.Entities.Size()); // size_t totalBytes = sizeof(WorldFileHeader) + static_cast(entityCount) * sizeof(WorldEntityDiskRecord); @@ -196,13 +236,13 @@ void RenderWorldEditorUI(World& world) if (ImGui::Button("Save World")) { - archive data{ .loading = false }; + archive data{ .arena = temp.Arena, .loading = false }; Serialize(data, world, path); } ImGui::SameLine(); if (ImGui::Button("Load World")) { - archive data{ .loading = true }; + archive data{ .arena = temp.Arena, .loading = true }; Serialize(data, world, path); } diff --git a/Game/Data/World.h b/Game/Data/World.h index 45f1457..9ba1ff4 100644 --- a/Game/Data/World.h +++ b/Game/Data/World.h @@ -16,13 +16,6 @@ struct WorldFileHeader uint32 Magic = 0x444C574A; // 'JWLD' in little-endian uint32 Version = 1; }; - -struct WorldEntityDiskRecord -{ - float X = 0.0f; - float Y = 0.0f; - float Z = 0.0f; -}; #pragma pack(pop) constexpr uint32 kWorldMagic = 0x444C574A; // 'JWLD' diff --git a/Game/Entity/Entity.cpp b/Game/Entity/Entity.cpp index b9772f4..2b8a011 100644 --- a/Game/Entity/Entity.cpp +++ b/Game/Entity/Entity.cpp @@ -1,3 +1,25 @@ #include +#include + DEFINE_ENTITY(Inert); + +void serialize(archive& ar, NonNullPtr entity) +{ + serialize_elem(ar, entity->ID); + + if (ar.loading) + { + serialize_elem(ar, entity->Kind->kind); + } + else + { + uint8 kind; + serialize_elem(ar, kind); + entity->Kind = kEntity_type_class_ptr[kind]; + } + + serialize_elem(ar, entity->X); + serialize_elem(ar, entity->Y); + serialize_elem(ar, entity->Z); +} diff --git a/Game/Entity/Entity.h b/Game/Entity/Entity.h index 03f789b..a2f990c 100644 --- a/Game/Entity/Entity.h +++ b/Game/Entity/Entity.h @@ -7,24 +7,41 @@ #include #define DECLARE_ENTITY() \ - Entity* Base; \ - static const Class* Kind; + Entity* Base; \ + static Class* Kind; // Will register the class globally at launch #define DEFINE_ENTITY(entity) \ - constexpr Class entityKind##entity = \ + Class entityKind##entity = \ MakeClass(ConstString(#entity), (uint8)Entity_Type::entity, sizeof(entity), alignof(entity), nullptr); \ - const Class* entity::Kind = &entityKind##entity; + Class* entity::Kind = &entityKind##entity; #define DEFINE_ENTITY_SERIALIZED(entity, serialize_fct) \ - constexpr Class entityKind##entity = \ + Class entityKind##entity = \ MakeClass(ConstString(#entity), (uint8)Entity_Type::entity, sizeof(entity), alignof(entity), serialize_fct); \ - const Class* entity::Kind = &entityKind##entity; + Class* entity::Kind = &entityKind##entity; struct EntityManager; using DerivedType = void*; using EntityID = uint64_t; +struct Entity final +{ + EntityID ID = 0; + Class* Kind = nullptr; + DerivedType Derived = nullptr; + float X = 0.0f; + float Y = 0.0f; + float Z = 0.0f; +}; + +struct Inert +{ + DECLARE_ENTITY() + + index_t MeshInstance = indexMax; +}; + // clang-format off #define ENTITY_TYPE_LIST(X) X(Inert), @@ -41,33 +58,25 @@ inline const char* kEntity_type_names[] = { ENTITY_TYPE_LIST(AS_STR) "Count" }; +#undef AS_STR + +#define AS_CLASS(name) name::Kind +inline Class* kEntity_type_class_ptr[] +{ + ENTITY_TYPE_LIST(AS_CLASS) + nullptr +}; +#undef AS_CLASS #define ENTITY(kind) ToUnderlying(Entity_Type::kind) // clang-format on -struct Entity final -{ - EntityID ID = 0; - const Class* Kind = nullptr; - DerivedType Derived = nullptr; - float X = 0.0f; - float Y = 0.0f; - float Z = 0.0f; -}; - // Can reinterpret cast to this to have the offset of Base and Kind for any entity struct entity_template { DECLARE_ENTITY(); }; -struct Inert -{ - DECLARE_ENTITY() - - index_t MeshInstance = indexMax; -}; - // template concept EntityConcept = requires(EntityType entity) { @@ -105,3 +114,5 @@ template Assert(IsA(entity)); return static_cast(entity->Derived); } + +void serialize(archive& ar, NonNullPtr entity); diff --git a/Game/Entity/EntityManager.cpp b/Game/Entity/EntityManager.cpp index ea19a0a..3c0680e 100644 --- a/Game/Entity/EntityManager.cpp +++ b/Game/Entity/EntityManager.cpp @@ -28,7 +28,13 @@ void InitEntityManager(NonNullPtr world) void ShutdownEntityManager() { - GetEntityManager().Entities.Destroy(); + auto& manager = GetEntityManager(); + for (typed_entity_array& type : manager.by_type) + { + ArenaRelease(type.arena); + } + + manager.Entities.Destroy(); } EntityManager& GetEntityManager() @@ -37,8 +43,6 @@ EntityManager& GetEntityManager() return *entityManager; } -void Serialize(NonNullPtr entityManager) {} - entity_template* RegisterEntity(EntityManager& manager, Entity* base, DerivedType entity) { base->ID = EntityManager::ID++; @@ -61,6 +65,11 @@ entity_template* RegisterEntity(EntityManager& manager, Entity* base, DerivedTyp return ptr; } +void RegisterBaseEntity(EntityManager& manager, Entity&& base) +{ + GetEntityManager().Entities.PushBack(std::move(base)); +} + void UpdateEntityManager(EntityManager& manager) { // Todo : inert by definition dont move, but this is for test diff --git a/Game/Entity/EntityManager.h b/Game/Entity/EntityManager.h index 410fed5..33d56b1 100644 --- a/Game/Entity/EntityManager.h +++ b/Game/Entity/EntityManager.h @@ -27,6 +27,6 @@ struct EntityManager void InitEntityManager(NonNullPtr world); void ShutdownEntityManager(); EntityManager& GetEntityManager(); -void Serialize(NonNullPtr entityManager); entity_template* RegisterEntity(EntityManager& manager, Entity* base, DerivedType entity); +void RegisterBaseEntity(EntityManager& manager, Entity&& base); void UpdateEntityManager(EntityManager& manager); diff --git a/Game/game.h b/Game/game.h index ac36348..5c3584d 100644 --- a/Game/game.h +++ b/Game/game.h @@ -5,13 +5,6 @@ struct EntityManager; -struct SerializedEntityTest -{ - DECLARE_ENTITY() - - int A = 0; -}; - enum class GameMode { Editor, diff --git a/Juliet/include/Core/Common/serialization.h b/Juliet/include/Core/Common/serialization.h index 8d60f40..81d785e 100644 --- a/Juliet/include/Core/Common/serialization.h +++ b/Juliet/include/Core/Common/serialization.h @@ -1,13 +1,17 @@ #pragma once +#include + struct Arena; struct archive { - Arena* arena; - bool loading; + Arena* arena; + void* base_ptr; + index_t offset; + bool loading; }; -void serialize(archive* ar, void* data, size_t size); +JULIET_API void serialize(archive& ar, void* data, size_t size); #define serialize_elem(ar, val) serialize((ar), &(val), sizeof(val)) diff --git a/Juliet/include/Engine/Class.h b/Juliet/include/Engine/Class.h index 2d9ab35..241e6f8 100644 --- a/Juliet/include/Engine/Class.h +++ b/Juliet/include/Engine/Class.h @@ -1,9 +1,10 @@ #pragma once -#include -#include #include +#include +#include + struct archive; using serialize_fct_type = void (*)(archive*, void* payload); diff --git a/Juliet/src/Core/Common/serialization.cpp b/Juliet/src/Core/Common/serialization.cpp index 5b3fd35..2126afb 100644 --- a/Juliet/src/Core/Common/serialization.cpp +++ b/Juliet/src/Core/Common/serialization.cpp @@ -4,16 +4,18 @@ #include #include -void serialize(archive* ar, void* data, size_t size) +void serialize(archive& ar, void* data, size_t size) { - if (ar->loading) + if (ar.loading) { - auto* ptr = ArenaPushSize(ar->arena, size, 8, false JULIET_DEBUG_PARAM("serialized load field")); + uint8* ptr = static_cast(ar.base_ptr) + ar.offset; MemCopy(data, ptr, size); + ar.offset += size; } else { - auto* ptr = ArenaPushSize(ar->arena, size, 8, false JULIET_DEBUG_PARAM("serialized save field")); + auto* ptr = ArenaPushSize(ar.arena, size, 8, false JULIET_DEBUG_PARAM("serialized save field")); MemCopy(ptr, data, size); + ar.offset += size; } } diff --git a/Juliet/src/Engine/class.cpp b/Juliet/src/Engine/class.cpp new file mode 100644 index 0000000..a7f6d89 --- /dev/null +++ b/Juliet/src/Engine/class.cpp @@ -0,0 +1 @@ +#include