ongoing refactor of serialization and various cleanup

This commit is contained in:
2026-09-07 16:58:57 -04:00
parent 1a045c3578
commit a462575af4
43 changed files with 952 additions and 483 deletions
@@ -205,36 +205,36 @@ The canonical allocation function is defined in `EntityManager.h`:
The implementation replaces the flawed `RegisterEntity` routine in [`Game/Entity/EntityManager.cpp`](file:///w:/Classified/Juliet/Game/Entity/EntityManager.cpp):
```cpp
[[nodiscard]] Entity* AllocateEntity(EntityManager& manager, Class* classPtr)
[[nodiscard]] Entity* AllocateEntity(EntityManager& manager, Class* derivedClassPtr)
{
Assert(classPtr != nullptr);
Assert(classPtr->kind < ENTITY(Count));
Assert(classPtr->size_of >= sizeof(entity_template));
Assert(classPtr->alignment > 0);
Assert(derivedClassPtr != nullptr);
Assert(derivedClassPtr->kind < ENTITY(Count));
Assert(derivedClassPtr->size_of >= sizeof(entity_template));
Assert(derivedClassPtr->alignment > 0);
// 1. Allocate uninitialized Base Entity in the contiguous VectorArena
Entity baseTemplate{};
baseTemplate.ID = EntityManager::ID++;
baseTemplate.Kind = classPtr;
baseTemplate.Derived = nullptr;
baseTemplate.X = 0.0f;
baseTemplate.Y = 0.0f;
baseTemplate.Z = 0.0f;
baseTemplate.IsDirty = true;
baseTemplate.ID = EntityManager::ID++;
baseTemplate.DerivedKind = derivedClassPtr;
baseTemplate.Derived = nullptr;
baseTemplate.X = 0.0f;
baseTemplate.Y = 0.0f;
baseTemplate.Z = 0.0f;
baseTemplate.IsDirty = true;
manager.Entities.PushBack(baseTemplate);
Entity* basePtr = manager.Entities.Back();
Assert(basePtr != nullptr);
// 2. Allocate zeroed derived component memory in the typed arena
typed_entity_array& typedArray = manager.by_type[classPtr->kind];
typed_entity_array& typedArray = manager.by_type[derivedClassPtr->kind];
Assert(typedArray.arena != nullptr);
void* rawMemory = ArenaPushSize(
typedArray.arena,
classPtr->size_of,
classPtr->alignment,
true JULIET_DEBUG_PARAM(kEntity_type_names[classPtr->kind]));
derivedClassPtr->size_of,
derivedClassPtr->alignment,
true JULIET_DEBUG_PARAM(kEntity_type_names[derivedClassPtr->kind]));
Assert(rawMemory != nullptr);
auto* derivedTemplate = reinterpret_cast<entity_template*>(rawMemory);
@@ -330,9 +330,9 @@ entity_instance
0x0100000000000042
; class
Inert
; base_version
; version
1
; derived_version
; class_version
1
; position
0.43 0.32 1.56
@@ -342,7 +342,8 @@ Inert
#### Important: No Header Structs for Derived Types
- **Derived types NEVER require their own file header**: You do **not** write an `InertHeader`, `DoorHeader`, or `PlayerHeader`. Derived types only serialize their own member variables.
- **No binary `EntityFileHeader` struct is needed**: Under the `; variable_name\nvalues` text format, there is no packed binary C-struct header at all. The common properties (`; id`, `; class`, `; base_version`, `; derived_version`, `; position`) are standard text Key-Value nodes read by the exact same `archive` parser.
- **Universal `; version` + optional `; class_version`**: Every `.jasset` file has a universal `; version` tag. For entity assets, `; version` governs base entity properties (`kEntityBaseVersion`), while an optional `; class_version` governs derived class properties (`Class::Version`). Non-entity assets like `WorldSettings.jasset` only have `; version`.
- **No binary `EntityFileHeader` struct is needed**: Under the `; variable_name\nvalues` text format, there is no packed binary C-struct header at all. The common properties (`; id`, `; class`, `; version`, `; class_version`, `; position`) are standard text Key-Value nodes read by the exact same `archive` parser.
### 4.2 Eliminating Intermediate Stack Allocations
Under the new pipeline:
@@ -351,7 +352,7 @@ Under the new pipeline:
3. The engine reads `; class` (e.g. `"Inert"`) and resolves `Class* classPtr = FindClassByName(className)`.
4. `AllocateEntity(manager, classPtr)` is called immediately. Memory for both base and derived components is allocated in-place in their permanent engine arenas.
5. Base properties (`id`, `position`, etc.) are read directly into `*basePtr` via `SerializeEntityBase`.
6. `classPtr->serialize_fct(ar, basePtr->Derived, derivedVersion)` is called. Derived fields stream **directly into the typed arena** without temporary staging buffers or stack copies.
6. `classPtr->serialize_fct(ar, basePtr->Derived, classVersion)` is called. Derived fields stream **directly into the typed arena** without temporary staging buffers or stack copies.
### 4.3 Runtime Class Resolution
To ensure fast and safe type lookup during file deserialization:
@@ -440,18 +441,13 @@ To ensure fast and safe type lookup during file deserialization:
Entity* basePtr = AllocateEntity(manager, classPtr);
Assert(basePtr != nullptr);
// 3. Read base versions and properties in-place
uint16 baseVersion = 1;
uint16 derivedVersion = 1;
SerializeProp(ar, "base_version", baseVersion);
SerializeProp(ar, "derived_version", derivedVersion);
// 3. Serialize Base Entity in-place using Entity::Kind
SerializeClassInstance(ar, Entity::Kind, basePtr);
SerializeEntityBase(ar, *basePtr, baseVersion);
// 4. Stream derived properties in-place directly into the typed arena
if (classPtr->serialize_fct != nullptr)
// 4. Stream derived properties in-place using basePtr->DerivedKind
if (basePtr->DerivedKind != nullptr && basePtr->Derived != nullptr)
{
classPtr->serialize_fct(ar, basePtr->Derived, derivedVersion);
SerializeClassInstance(ar, basePtr->DerivedKind, basePtr->Derived);
}
// Freshly loaded entity matches disk state exactly
@@ -663,13 +659,15 @@ To solve this, `Entity` in [`Game/Entity/Entity.h`](file:///w:/Classified/Juliet
```cpp
struct Entity final
{
EntityID ID = 0;
Class* Kind = nullptr;
DerivedType Derived = nullptr;
float X = 0.0f;
float Y = 0.0f;
float Z = 0.0f;
bool IsDirty = false;
DECLARE_ENTITY() // static Class* Kind; (Entity's own Class descriptor)
EntityID ID = 0;
Class* DerivedKind = nullptr; // Pointer to derived class descriptor (e.g. Inert::Kind)
DerivedType Derived = nullptr; // Pointer to derived component memory
float X = 0.0f;
float Y = 0.0f;
float Z = 0.0f;
bool IsDirty = false;
};
```