updating serialization tdd with latest changes
This commit is contained in:
@@ -206,13 +206,13 @@ When `GetOrLoadTemplate` is invoked with a relative path:
|
||||
6. **Default Base Setup**: Initialize a local `Entity defaultBase`:
|
||||
```cpp
|
||||
Entity defaultBase = {};
|
||||
defaultBase.Kind = entityClass;
|
||||
defaultBase.Derived = archetypeMem;
|
||||
defaultBase.derived_kind = entityClass;
|
||||
defaultBase.derived = archetypeMem;
|
||||
```
|
||||
7. **Back-Pointer Linking**: Set `entity_template::Base` in the archetype memory:
|
||||
7. **Back-Pointer Linking**: Set `entity_template::base` in the archetype memory:
|
||||
```cpp
|
||||
auto* archetypeTemplate = reinterpret_cast<entity_template*>(archetypeMem);
|
||||
archetypeTemplate->Base = &cachedEntry->DefaultBase;
|
||||
archetypeTemplate->base = &cachedEntry->DefaultBase;
|
||||
```
|
||||
8. **KV Property Parsing**: Parse all key-value pairs in the template `.jasset` file and write their deserialized values directly into `archetypeMem` and `defaultBase`.
|
||||
9. **Cache Insertion**: Store the fully baked `CachedTemplate` record in `cache->Templates`.
|
||||
@@ -228,21 +228,22 @@ In Juliet, an entity is split into two tightly coupled structures:
|
||||
```cpp
|
||||
struct Entity final
|
||||
{
|
||||
EntityID ID = 0;
|
||||
Class* Kind = nullptr;
|
||||
DerivedType Derived = nullptr; // Points to the specialized struct
|
||||
float X = 0.0f;
|
||||
float Y = 0.0f;
|
||||
float Z = 0.0f;
|
||||
DECLARE_CLASS()
|
||||
|
||||
EntityID ID = 0;
|
||||
Class* derived_kind = nullptr;
|
||||
DerivedType derived = nullptr; // Points to the specialized struct
|
||||
Vector4 position = {};
|
||||
bool is_dirty = false;
|
||||
};
|
||||
```
|
||||
2. **`Derived` (Specialized Type)**: e.g., `Inert`, `Collectible`, `Player`. The first member is always `DECLARE_ENTITY()`, which expands to:
|
||||
```cpp
|
||||
Entity* Base; // Back-pointer to the base Entity
|
||||
static Class* Kind;
|
||||
Entity* base; // Back-pointer to the base Entity
|
||||
DECLARE_CLASS() // static Class* kind;
|
||||
```
|
||||
|
||||
Because `DerivedType` stores a back-pointer (`Base`) to `Entity`, **a shallow memory copy of an archetype invalidates this pointer**! The loading pipeline must explicitly restore this invariant.
|
||||
Because `DerivedType` stores a back-pointer (`base`) to `Entity`, **a shallow memory copy of an archetype invalidates this pointer**! The loading pipeline must explicitly restore this invariant.
|
||||
|
||||
### 4.2 The 5-Step Instantiation Pipeline
|
||||
When an entity instance is spawned or deserialized from a `.jasset` file, the engine executes this strict 5-step sequence:
|
||||
@@ -400,12 +401,12 @@ void ApplyKvDeltaOverrides(Entity* base, void* derived, Class* cls, String kvCon
|
||||
}
|
||||
|
||||
// Check base properties first
|
||||
if (StringCompare(pair.Key, ConstString("Position")) == 0)
|
||||
if (StringCompare(pair.Key, ConstString("Position")) == 0 || StringCompare(pair.Key, ConstString("position")) == 0)
|
||||
{
|
||||
Vector3 pos = ParseVector3(pair.Value);
|
||||
base->X = pos.X;
|
||||
base->Y = pos.Y;
|
||||
base->Z = pos.Z;
|
||||
base->position.x = pos.x;
|
||||
base->position.y = pos.y;
|
||||
base->position.z = pos.z;
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -483,8 +484,8 @@ bool CreateTemplateFromEntity(World& world,
|
||||
Assert(entityIndex < manager.Entities.Size());
|
||||
|
||||
Entity* sourceEntity = &manager.Entities[entityIndex];
|
||||
Class* entityKind = sourceEntity->Kind;
|
||||
void* derivedMem = sourceEntity->Derived;
|
||||
Class* entityKind = sourceEntity->derived_kind;
|
||||
void* derivedMem = sourceEntity->derived;
|
||||
|
||||
// Format destination template path
|
||||
String templatePath = Format(scratchArena, "Assets/Templates/{}.jasset", CStr(templateName));
|
||||
@@ -561,11 +562,11 @@ void RenderEntityPropertyInspector(Entity* entity, CachedTemplate* archetype)
|
||||
}
|
||||
|
||||
// Iterate through properties
|
||||
Class* cls = entity->Kind;
|
||||
Class* cls = entity->derived_kind;
|
||||
for (size_t i = 0; i < cls->PropertyCount; ++i)
|
||||
{
|
||||
const PropertyDescriptor& prop = cls->Properties[i];
|
||||
void* instanceField = static_cast<uint8*>(entity->Derived) + prop.Offset;
|
||||
void* instanceField = static_cast<uint8*>(entity->derived) + prop.Offset;
|
||||
void* templateField = isTemplated ? (static_cast<uint8*>(archetype->DefaultDerivedMemory) + prop.Offset) : nullptr;
|
||||
|
||||
const bool isOverridden = isTemplated && (MemCompare(instanceField, templateField, GetPropertySize(prop.Type)) != 0);
|
||||
@@ -605,10 +606,10 @@ Reverting an entire entity to its template archetype restores all properties whi
|
||||
```cpp
|
||||
void RevertEntityToTemplate(NonNullPtr<Entity> instance, NonNullPtr<const CachedTemplate> archetype)
|
||||
{
|
||||
Assert(instance->Kind == archetype->EntityKind);
|
||||
Assert(instance->derived_kind == archetype->EntityKind);
|
||||
Assert(archetype->DefaultDerivedMemory != nullptr);
|
||||
|
||||
void* derivedMem = instance->Derived;
|
||||
void* derivedMem = instance->derived;
|
||||
const size_t derivedSize = archetype->EntityKind->size_of;
|
||||
|
||||
// Preserve the current Base pointer
|
||||
@@ -619,16 +620,16 @@ void RevertEntityToTemplate(NonNullPtr<Entity> instance, NonNullPtr<const Cached
|
||||
|
||||
// 2. Re-establish the Base back-pointer
|
||||
auto* templateDerived = reinterpret_cast<entity_template*>(derivedMem);
|
||||
templateDerived->Base = basePtr;
|
||||
templateDerived->base = basePtr;
|
||||
|
||||
// 3. Mark visual / physics state as updated
|
||||
if (instance->Kind->kind == ENTITY(Inert))
|
||||
if (instance->derived_kind->kind == ENTITY(Inert))
|
||||
{
|
||||
auto* inert = reinterpret_cast<Inert*>(derivedMem);
|
||||
if (inert->MeshInstance != indexMax)
|
||||
{
|
||||
SetMeshInstanceTransform(inert->MeshInstance,
|
||||
MatrixTranslation(basePtr->X, basePtr->Y, basePtr->Z));
|
||||
MatrixTranslation(basePtr->position.x, basePtr->position.y, basePtr->position.z));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -844,17 +845,17 @@ namespace UnitTest
|
||||
Assert(instance != nullptr);
|
||||
|
||||
// Verify Step 1 & 2: Base spatial delta applied, non-overridden derived property retained
|
||||
Assert(instance->X == 100.0f);
|
||||
Assert(instance->Y == 200.0f);
|
||||
Assert(instance->Z == 300.0f);
|
||||
Assert(instance->position.x == 100.0f);
|
||||
Assert(instance->position.y == 200.0f);
|
||||
Assert(instance->position.z == 300.0f);
|
||||
|
||||
auto* inertDerived = DownCast<Inert>(instance);
|
||||
Assert(inertDerived != nullptr);
|
||||
Assert(inertDerived->MeshInstance == 42); // Retained from template!
|
||||
|
||||
// Verify Step 3: CRITICAL back-pointer fixup check
|
||||
Assert(inertDerived->Base == instance);
|
||||
Assert(instance->Derived == inertDerived);
|
||||
Assert(inertDerived->base == instance);
|
||||
Assert(instance->derived == inertDerived);
|
||||
|
||||
ShutdownEntityManager();
|
||||
ShutdownWorld(&world);
|
||||
@@ -900,12 +901,12 @@ namespace UnitTest
|
||||
Assert(inertDerived->MeshInstance == 100);
|
||||
|
||||
// Verify world position is preserved across revert
|
||||
Assert(instance->X == 5.0f);
|
||||
Assert(instance->Y == 5.0f);
|
||||
Assert(instance->Z == 5.0f);
|
||||
Assert(instance->position.x == 5.0f);
|
||||
Assert(instance->position.y == 5.0f);
|
||||
Assert(instance->position.z == 5.0f);
|
||||
|
||||
// Verify back-pointer invariant preserved after revert
|
||||
Assert(inertDerived->Base == instance);
|
||||
Assert(inertDerived->base == instance);
|
||||
|
||||
ShutdownEntityManager();
|
||||
ShutdownWorld(&world);
|
||||
|
||||
Reference in New Issue
Block a user