Skip to content

Commit

Permalink
Fix. Update indexed components on deserialization:
Browse files Browse the repository at this point in the history
See: #15
  • Loading branch information
friflo committed Nov 4, 2024
1 parent 63de865 commit 784c765
Show file tree
Hide file tree
Showing 5 changed files with 170 additions and 0 deletions.
3 changes: 3 additions & 0 deletions src/ECS/Archetype/StructHeap.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ internal abstract class StructHeap : IComponentStash
internal abstract IComponent GetComponentDebug (int compIndex);
internal abstract Bytes Write (ObjectWriter writer, int compIndex);
internal abstract void Read (ObjectReader reader, int compIndex, JsonValue json);
internal abstract void UpdateIndex (Entity entity);
internal abstract void AddIndex (Entity entity);
internal abstract void RemoveIndex (Entity entity);

internal StructHeap(int structIndex) {
this.structIndex = structIndex;
Expand Down
13 changes: 13 additions & 0 deletions src/ECS/Archetype/StructHeap.generic.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// See LICENSE file in the project root for full license information.

using System;
using Friflo.Engine.ECS.Index;
using Friflo.Json.Burst;
using Friflo.Json.Fliox;
using Friflo.Json.Fliox.Mapper;
Expand Down Expand Up @@ -109,4 +110,16 @@ internal override Bytes Write(ObjectWriter writer, int compIndex) {
internal override void Read(ObjectReader reader, int compIndex, JsonValue json) {
components[compIndex] = reader.ReadMapper(componentType.TypeMapper, json); // todo avoid boxing within typeMapper, T is struct
}

internal override void UpdateIndex (Entity entity) {
StoreIndex.UpdateIndex(entity.store, entity.Id, components[entity.compIndex], this);
}

internal override void AddIndex (Entity entity) {
StoreIndex.AddIndex(entity.store, entity.Id, components[entity.compIndex]);
}

internal override void RemoveIndex (Entity entity) {
StoreIndex.RemoveIndex(entity.store, entity.Id, this);
}
}
37 changes: 37 additions & 0 deletions src/ECS/Entity/Store/DataEntities.cs
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,13 @@ internal Entity DataEntityToEntity(DataEntity dataEntity, out string error, Comp
} else {
entity = CreateFromDataEntityRandomPid (dataEntity);
}
// stash current component values to update indexed components
var oldType = entity.archetype;
foreach (var heap in oldType.structHeaps) {
heap.StashComponent(entity.compIndex);
}
error = reader.Read(dataEntity, entity, this, options);
UpdateComponentIndexes(entity, oldType);
return entity;
}

Expand Down Expand Up @@ -175,5 +181,36 @@ private void EnsureIdBufferCapacity(int count) {
}
ArrayUtils.Resize(ref idBuffer, Math.Max(2 * idBuffer.Length, count));
}

private static void UpdateComponentIndexes(Entity entity, Archetype oldType)
{
var newType = entity.archetype;
var indexTypesMask = Static.EntitySchema.indexTypes.bitSet.l0;
var oldIndexBits = oldType.ComponentTypes.bitSet.l0 & indexTypesMask;
var newIndexBits = newType.ComponentTypes.bitSet.l0 & indexTypesMask;
var indexBits = oldIndexBits | newIndexBits;
if (indexBits == 0) {
return;
}
var indexTypes = new ComponentTypes ();
indexTypes.bitSet.l0 = indexBits;
foreach (var type in indexTypes)
{
var oldHeap = oldType.heapMap[type.StructIndex];
var newHeap = newType.heapMap[type.StructIndex];
if (oldHeap == null) {
// case: indexed component added
newHeap.AddIndex(entity);
continue;
}
if (newHeap != null) {
// case: indexed component updated
oldHeap.UpdateIndex(entity);
continue;
}
// case: indexed component removed
oldHeap.RemoveIndex(entity);
}
}
#endregion
}
40 changes: 40 additions & 0 deletions src/Tests/ECS/Github/Test_GitHub_15.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
using System.IO;
using Friflo.Engine.ECS;
using Friflo.Engine.ECS.Serialize;
using NUnit.Framework;
using Tests.ECS.Index;
using static NUnit.Framework.Assert;

// ReSharper disable InconsistentNaming
namespace Tests.ECS.Github
{
/// <summary>
/// Update indexed components on deserialization
/// https://github.com/friflo/Friflo.Engine.ECS/issues/15
/// </summary>
public static class Test_GitHub_15
{
[Test]
public static void Test_GitHub_15_update_indexed_components_on_deserialization()
{
var store = new EntityStore();
var entityA = store.CreateEntity(1);
entityA.AddComponent(new IndexedInt { value = 11 });
var entityB = store.CreateEntity(2);
entityB.AddComponent(new IndexedInt { value = 12 });

AreEqual(1, store.GetEntitiesWithComponentValue<IndexedInt,int>(11).Count);
AreEqual(2, store.GetAllIndexedComponentValues<IndexedInt,int>().Count);

using var stream = new MemoryStream();
var serializer = new EntitySerializer();
serializer.WriteStore(store, stream);

var readStore = new EntityStore();
serializer.ReadIntoStore(readStore, stream);
AreEqual(2, readStore.Count);
AreEqual(1, readStore.GetEntitiesWithComponentValue<IndexedInt,int>(11).Count);
AreEqual(2, readStore.GetAllIndexedComponentValues<IndexedInt,int>().Count);
}
}
}
77 changes: 77 additions & 0 deletions src/Tests/ECS/Serialize/Test_SerializeIndexedComponent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
using System.IO;
using System.Text;
using Friflo.Engine.ECS;
using Friflo.Engine.ECS.Serialize;
using NUnit.Framework;
using Tests.ECS.Index;
using static NUnit.Framework.Assert;

// ReSharper disable MethodHasAsyncOverload
// ReSharper disable HeuristicUnreachableCode
// ReSharper disable InconsistentNaming
namespace Tests.ECS.Serialize {

public static class Test_SerializeIndexedComponent
{
private const string JSON_withIndexedComponent =
@"[{
""id"": 1,
""components"": {
""IndexedInt"": {""value"":42}
}
}";

private const string JSON_withoutIndexedComponent =
@"[{
""id"": 1,
""components"": {
}
}";

[Test]
public static void Test_SerializeIndexedComponent_add()
{
var store = new EntityStore();
store.CreateEntity(1);

var serializer = new EntitySerializer();
var readStream = new MemoryStream(Encoding.UTF8.GetBytes(JSON_withIndexedComponent));
serializer.ReadIntoStore(store, readStream);
AreEqual(1, store.GetEntitiesWithComponentValue<IndexedInt,int>(42).Count);
AreEqual(1, store.GetAllIndexedComponentValues<IndexedInt,int>().Count);
}

[Test]
public static void Test_SerializeIndexedComponent_update()
{
var store = new EntityStore();
Entity entity = store.CreateEntity(1);
entity.AddComponent(new IndexedInt { value = 10 });
AreEqual(1, store.GetEntitiesWithComponentValue<IndexedInt,int>(10).Count);

var serializer = new EntitySerializer();
var readStream = new MemoryStream(Encoding.UTF8.GetBytes(JSON_withIndexedComponent));
serializer.ReadIntoStore(store, readStream);
AreEqual(1, store.GetEntitiesWithComponentValue<IndexedInt,int>(42).Count);
AreEqual(0, store.GetEntitiesWithComponentValue<IndexedInt,int>(10).Count);
AreEqual(1, store.GetAllIndexedComponentValues<IndexedInt,int>().Count);
}

[Test]
public static void Test_SerializeIndexedComponent_remove()
{
var store = new EntityStore();
var entity = store.CreateEntity(1);
entity.AddComponent(new IndexedInt { value = 10 });
AreEqual(1, store.GetEntitiesWithComponentValue<IndexedInt,int>(10).Count);
// entity.AddComponent(new MyComponent1 { a = 10 });

var serializer = new EntitySerializer();
var readStream = new MemoryStream(Encoding.UTF8.GetBytes(JSON_withoutIndexedComponent));
serializer.ReadIntoStore(store, readStream);
AreEqual(0, store.GetEntitiesWithComponentValue<IndexedInt,int>(10).Count);
AreEqual(0, store.GetAllIndexedComponentValues<IndexedInt,int>().Count);
}
}

}

0 comments on commit 784c765

Please sign in to comment.