Skip to content

Commit

Permalink
ECS - add Entity.Revision
Browse files Browse the repository at this point in the history
  • Loading branch information
friflo committed Jul 30, 2024
1 parent 218531f commit b5f9014
Show file tree
Hide file tree
Showing 18 changed files with 471 additions and 198 deletions.
6 changes: 3 additions & 3 deletions src/ECS/Archetype/EntityStore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -152,9 +152,9 @@ protected EntityStoreBase()
internal static NullReferenceException EntityNullException(Entity entity) {
return new NullReferenceException($"entity is null. id: {entity.Id}");
}

internal static Exception EntityDetachedException(string parameterName) {
return ExceptionUtils.ArgumentException("entity is detached", parameterName);
internal static ArgumentNullException EntityArgumentNullException(Entity entity, string param) {
return new ArgumentNullException(param, $"entity is null. id: {entity.Id}");
}

internal static Exception InvalidStoreException(string parameterName) {
Expand Down
18 changes: 15 additions & 3 deletions src/ECS/Base/Types/ComponentType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -95,18 +95,30 @@ internal ComponentType(string componentKey, int structIndex, Type indexType, Typ

internal override bool RemoveEntityComponent(Entity entity) {
int archIndex = 0;
return EntityStoreBase.RemoveComponent<T>(entity.Id, ref entity.refArchetype, ref entity.refCompIndex, ref archIndex, StructIndex);
ref var node = ref entity.store.nodes[entity.Id];
if (node.archetype != null && node.revision == entity.Revision) {
return EntityStoreBase.RemoveComponent<T>(entity.Id, ref node.archetype, ref node.compIndex, ref archIndex, StructIndex);
}
throw EntityStoreBase.EntityArgumentNullException(entity, nameof(entity));
}

internal override bool AddEntityComponent(Entity entity) {
int archIndex = 0;
return EntityStoreBase.AddComponent<T>(entity.Id, StructIndex, ref entity.refArchetype, ref entity.refCompIndex, ref archIndex, default);
ref var node = ref entity.store.nodes[entity.Id];
if (node.archetype != null && node.revision == entity.Revision) {
return EntityStoreBase.AddComponent<T>(entity.Id, StructIndex, ref node.archetype, ref node.compIndex, ref archIndex, default);
}
throw EntityStoreBase.EntityArgumentNullException(entity, nameof(entity));
}

internal override bool AddEntityComponentValue(Entity entity, object value) {
int archIndex = 0;
var componentValue = (T)value;
return EntityStoreBase.AddComponent(entity.Id, StructIndex, ref entity.refArchetype, ref entity.refCompIndex, ref archIndex, componentValue);
ref var node = ref entity.store.nodes[entity.Id];
if (node.archetype != null && node.revision == entity.Revision) {
return EntityStoreBase.AddComponent(entity.Id, StructIndex, ref node.archetype, ref node.compIndex, ref archIndex, componentValue);
}
throw EntityStoreBase.EntityArgumentNullException(entity, nameof(entity));
}

internal override StructHeap CreateHeap() {
Expand Down
291 changes: 200 additions & 91 deletions src/ECS/Entity.cs

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion src/ECS/Entity/EntityInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ internal readonly struct EntityInfo
#region properties
internal long Pid => entity.Pid;
internal bool Enabled => entity.Enabled;
internal Archetype Archetype => entity.archetype;
internal Archetype Archetype => entity.GetArchetype();
internal Scripts Scripts => entity.Scripts;
internal Entity Parent => entity.Parent;
internal JSON JSON => new JSON(EntityUtils.EntityToJSON(entity));
Expand Down
4 changes: 3 additions & 1 deletion src/ECS/Entity/EntityNode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,10 @@ public struct EntityNode

[Browse(Never)] internal int compIndex; // 4 index within Archetype.entityIds & StructHeap<>.components

[Browse(Never)] internal short revision; // 2

// /// <summary> Use <see cref="Is"/> or <see cref="IsNot"/> for read access. </summary>
// [Browse(Never)] internal NodeFlags flags; // 1
// [Browse(Never)] internal NodeFlags flags; // 1

/// <summary>
/// Bit mask for all <see cref="EntityRelations"/> and all <see cref="ComponentIndex"/> instances.<br/>
Expand Down
22 changes: 14 additions & 8 deletions src/ECS/Entity/EntityUtils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@ public static class EntityUtils
/// So avoid using this method whenever possible. Use <see cref="Entity.GetComponent{T}"/> instead.
/// </summary>
public static IComponent GetEntityComponent (Entity entity, ComponentType componentType) {
return entity.archetype.heapMap[componentType.StructIndex].GetComponentDebug(entity.compIndex);
var type = entity.GetArchetype() ?? throw EntityStoreBase.EntityArgumentNullException(entity, nameof(entity));
return type.heapMap[componentType.StructIndex].GetComponentDebug(entity.compIndex);
}

public static bool RemoveEntityComponent (Entity entity, ComponentType componentType)
Expand Down Expand Up @@ -65,7 +66,8 @@ public static bool AddEntityComponentValue(Entity entity, ComponentType compone
// ------------------------------------------- internal methods -------------------------------------------
#region internal - methods
internal static int ComponentCount (this Entity entity) {
return entity.archetype.componentCount + entity.Scripts.Length;
var type = entity.GetArchetype() ?? throw EntityStoreBase.EntityArgumentNullException(entity, nameof(entity));
return type.componentCount + entity.Scripts.Length;
}

internal static Exception NotImplemented(int id, string use) {
Expand All @@ -77,7 +79,7 @@ internal static string EntityToString(Entity entity) {
if (entity.store == null) {
return "null";
}
return EntityToString(entity.Id, entity.archetype, new StringBuilder());
return EntityToString(entity.Id, entity.GetArchetype(), new StringBuilder());
}

internal static string EntityToString(int id, Archetype archetype, StringBuilder sb)
Expand Down Expand Up @@ -196,31 +198,35 @@ private static Script AddNewScript(Entity entity, ScriptType scriptType)
}

internal static Script AddScript (Entity entity, Script script) {
var scriptType = ScriptTypeByType[script.GetType()];
return entity.archetype.entityStore.extension.AddScript(entity, script, scriptType);
var store = entity.GetStore() ?? throw EntityStoreBase.EntityArgumentNullException(entity, nameof(entity));
var scriptType = ScriptTypeByType[script.GetType()];
return store.extension.AddScript(entity, script, scriptType);
}

private static Script AddScriptInternal(Entity entity, Script script, ScriptType scriptType)
{
if (!script.entity.IsNull) {
throw new InvalidOperationException($"script already added to an entity. current entity id: {script.entity.Id}");
}
return entity.archetype.entityStore.extension.AddScript(entity, script, scriptType);
var store = entity.GetStore() ?? throw EntityStoreBase.EntityArgumentNullException(entity, nameof(entity));
return store.extension.AddScript(entity, script, scriptType);
}

internal static Script RemoveScript(Entity entity, int scriptTypeIndex) {
var store = entity.GetStore() ?? throw EntityStoreBase.EntityArgumentNullException(entity, nameof(entity));
if (!entity.store.extension.scriptMap.TryGetValue(entity.Id, out int scriptIndex)) {
return null;
}
var scriptType = ScriptTypes[scriptTypeIndex];
return entity.archetype.entityStore.extension.RemoveScript(entity, scriptType, scriptIndex);
return store.extension.RemoveScript(entity, scriptType, scriptIndex);
}

private static Script RemoveScriptType(Entity entity, ScriptType scriptType) {
var store = entity.GetStore() ?? throw EntityStoreBase.EntityArgumentNullException(entity, nameof(entity));
if (!entity.store.extension.scriptMap.TryGetValue(entity.Id, out int scriptIndex)) {
return null;
}
return entity.archetype.entityStore.extension.RemoveScript(entity, scriptType, scriptIndex);
return store.extension.RemoveScript(entity, scriptType, scriptIndex);
}

internal static void AddTreeTags(Entity entity, in Tags tags)
Expand Down
6 changes: 3 additions & 3 deletions src/ECS/Entity/Extensions/EntityLinks.cs
Original file line number Diff line number Diff line change
Expand Up @@ -74,11 +74,11 @@ public static int CountAllIncomingLinks(this Entity target)
#region outgoing links
public static int CountAllOutgoingLinks(this Entity entity)
{
var schema = EntityStoreBase.Static.EntitySchema;

var schema = EntityStoreBase.Static.EntitySchema;
var type = entity.GetArchetype() ?? throw EntityStoreBase.EntityNullException(entity);
// --- count link components
var linkTypes = new ComponentTypes {
bitSet = BitSet.Intersect(entity.archetype.componentTypes.bitSet, schema.linkComponentTypes.bitSet)
bitSet = BitSet.Intersect(type.componentTypes.bitSet, schema.linkComponentTypes.bitSet)
};
// --- count relations components
int count = linkTypes.Count;
Expand Down
10 changes: 5 additions & 5 deletions src/ECS/Entity/Store/Entities.cs
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ internal int CreateEntityInternal(Archetype archetype, int id)
/// </remarks>
public Entity CloneEntity(Entity entity)
{
var archetype = entity.archetype ?? throw EntityNullException(entity);
var archetype = entity.GetArchetype() ?? throw EntityArgumentNullException(entity, nameof(entity));
var id = NewId();
CreateEntityInternal(archetype, id);
var clone = new Entity(this, id);
Expand Down Expand Up @@ -174,6 +174,7 @@ private int CreateEntityNode(Archetype archetype, int id)
entityCount++;
node.compIndex = Archetype.AddEntity(archetype, id);
node.archetype = archetype;
node.revision++;
// node.flags = Created;
return node.compIndex;
}
Expand All @@ -193,6 +194,7 @@ internal void CreateEntityNodes(Archetype archetype, int count)
ref var node = ref localNodes[id];
node.compIndex = index;
node.archetype = archetype;
node.revision++;
// node.flags = Created;
}
if (intern.pidType == PidType.RandomPids) {
Expand All @@ -208,10 +210,8 @@ internal void CreateEntityNodes(Archetype archetype, int count)
/// Set the passed <paramref name="entity"/> as the <see cref="StoreRoot"/> entity.
/// </summary>
public void SetStoreRoot(Entity entity) {
if (entity.IsNull) {
throw new ArgumentNullException(nameof(entity));
}
if (this != entity.archetype.store) {
var store = entity.GetStore() ?? throw EntityArgumentNullException(entity, nameof(entity));
if (this != store) {
throw InvalidStoreException(nameof(entity));
}
SetStoreRootEntity(entity);
Expand Down
2 changes: 2 additions & 0 deletions src/ECS/Entity/Store/NodeTree.cs
Original file line number Diff line number Diff line change
Expand Up @@ -525,7 +525,9 @@ internal void DeleteNode(Entity entity)
RemoveAllEntityEventHandlers(this, node, id);
int parentId = GetTreeParentId(id);
// --- clear node entry.
var revision = node.revision;
node = default;
node.revision = revision;
extension.RemoveEntity(id);

// --- remove child from parent
Expand Down
4 changes: 2 additions & 2 deletions src/ECS/Entity/Store/Scripts.cs
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,8 @@ internal void AppendScript(Entity entity, Script script)
/// <br/>
/// The entity state refers to:
/// <list type="bullet">
/// <item><see cref="Entity.refArchetype"/></item>
/// <item><see cref="Entity.refCompIndex"/></item>
/// <item><see cref="EntityNode.archetype"/></item>
/// <item><see cref="EntityNode.compIndex"/></item>
/// <item><see cref="RawEntity.archIndex"/></item>
/// </list>
/// </remarks>
Expand Down
2 changes: 1 addition & 1 deletion src/ECS/Index/IndexExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ public static class IndexExtensions
public static EntityLinks<TComponent> GetIncomingLinks<TComponent>(this Entity target)
where TComponent: struct, ILinkComponent
{
if (target.archetype == null) throw EntityStoreBase.EntityNullException(target);
if (target.IsNull) throw EntityStoreBase.EntityNullException(target);
var index = (ComponentIndex<Entity>)StoreIndex.GetIndex(target.store, StructInfo<TComponent>.Index);
return new EntityLinks<TComponent>(target, index.GetHasValueEntities(target), null);
}
Expand Down
14 changes: 7 additions & 7 deletions src/ECS/Relations/RelationExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public static class RelationExtensions
public static ref TComponent GetRelation<TComponent, TKey>(this Entity entity, TKey key)
where TComponent : struct, IRelationComponent<TKey>
{
if (entity.archetype == null) throw EntityStoreBase.EntityNullException(entity);
if (entity.IsNull) throw EntityStoreBase.EntityNullException(entity);
return ref EntityRelations.GetRelation<TComponent, TKey>(entity.store, entity.Id, key);
}

Expand All @@ -33,7 +33,7 @@ public static ref TComponent GetRelation<TComponent, TKey>(this Entity entity, T
public static bool TryGetRelation<TComponent, TKey>(this Entity entity, TKey key, out TComponent value)
where TComponent : struct, IRelationComponent<TKey>
{
if (entity.archetype == null) throw EntityStoreBase.EntityNullException(entity);
if (entity.IsNull) throw EntityStoreBase.EntityNullException(entity);
return EntityRelations.TryGetRelation(entity.store, entity.Id, key, out value);
}

Expand All @@ -45,7 +45,7 @@ public static bool TryGetRelation<TComponent, TKey>(this Entity entity, TKey key
public static RelationComponents<TComponent> GetRelations<TComponent>(this Entity entity)
where TComponent : struct, IRelationComponent
{
if (entity.archetype == null) throw EntityStoreBase.EntityNullException(entity);
if (entity.IsNull) throw EntityStoreBase.EntityNullException(entity);
return EntityRelations.GetRelations<TComponent>(entity.store, entity.Id);
}

Expand All @@ -58,7 +58,7 @@ public static RelationComponents<TComponent> GetRelations<TComponent>(this Entit
public static bool AddRelation<TComponent>(this Entity entity, in TComponent component)
where TComponent : struct, IRelationComponent
{
if (entity.archetype == null) throw EntityStoreBase.EntityNullException(entity);
if (entity.IsNull) throw EntityStoreBase.EntityNullException(entity);
return EntityRelations.AddRelation(entity.store, entity.Id, component);
}

Expand All @@ -71,7 +71,7 @@ public static bool AddRelation<TComponent>(this Entity entity, in TComponent com
public static bool RemoveRelation<TComponent, TKey>(this Entity entity, TKey key)
where TComponent : struct, IRelationComponent<TKey>
{
if (entity.archetype == null) throw EntityStoreBase.EntityNullException(entity);
if (entity.IsNull) throw EntityStoreBase.EntityNullException(entity);
return EntityRelations.RemoveRelation<TComponent, TKey>(entity.store, entity.Id, key);
}

Expand All @@ -84,7 +84,7 @@ public static bool RemoveRelation<TComponent, TKey>(this Entity entity, TKey key
public static bool RemoveRelation<TComponent>(this Entity entity, Entity target)
where TComponent : struct, ILinkRelation
{
if (entity.archetype == null) throw EntityStoreBase.EntityNullException(entity);
if (entity.IsNull) throw EntityStoreBase.EntityNullException(entity);
return EntityRelations.RemoveRelation<TComponent, Entity>(entity.store, entity.Id, target);
}

Expand All @@ -96,7 +96,7 @@ public static bool RemoveRelation<TComponent>(this Entity entity, Entity target)
public static EntityLinks<TComponent> GetIncomingLinks<TComponent>(this Entity target)
where TComponent: struct, IRelationComponent
{
if (target.archetype == null) throw EntityStoreBase.EntityNullException(target);
if (target.IsNull) throw EntityStoreBase.EntityNullException(target);
var entities = EntityRelations.GetIncomingLinkRelations(target.store, target.Id, StructInfo<TComponent>.Index, out var relations);
return new EntityLinks<TComponent>(target, entities, relations);
}
Expand Down
5 changes: 1 addition & 4 deletions src/ECS/Serialize/EntityConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,7 @@ public EntityConverter() {
/// </summary>
public DataEntity EntityToDataEntity(Entity entity, DataEntity dataEntity, bool pretty)
{
if (entity.IsNull) {
throw new ArgumentNullException(nameof(entity));
}
var store = entity.archetype.entityStore;
var store = entity.GetStore() ?? throw EntityStoreBase.EntityArgumentNullException(entity, nameof(entity));
var pid = store.IdToPid(entity.Id);
dataEntity ??= new DataEntity();
dataEntity.pid = pid;
Expand Down
51 changes: 0 additions & 51 deletions src/Tests/ECS/Entity/Test_Entity.cs
Original file line number Diff line number Diff line change
Expand Up @@ -307,57 +307,6 @@ public static void Test_Entity_EnableTree()
IsFalse (root.Enabled);
}

[Test]
public static void Test_Entity_NullReferenceException()
{
var store = new EntityStore();
var entity = store.CreateEntity();
entity.DeleteEntity();
var expect = "entity is null. id: 1";

// --- components
var nre = Throws<NullReferenceException>(() => {
entity.AddComponent(new Position());
});
AreEqual(expect, nre!.Message);

nre = Throws<NullReferenceException>(() => {
entity.AddComponent<Position>();
});
AreEqual(expect, nre!.Message);

nre = Throws<NullReferenceException>(() => {
entity.RemoveComponent<Position>();
});
AreEqual(expect, nre!.Message);

// --- tags
nre = Throws<NullReferenceException>(() => {
entity.AddTag<TestTag>();
});
AreEqual(expect, nre!.Message);

nre = Throws<NullReferenceException>(() => {
entity.AddTags(default);
});
AreEqual(expect, nre!.Message);

nre = Throws<NullReferenceException>(() => {
entity.RemoveTag<TestTag>();
});
AreEqual(expect, nre!.Message);

nre = Throws<NullReferenceException>(() => {
entity.RemoveTags(default);
});
AreEqual(expect, nre!.Message);

nre = Throws<NullReferenceException>(() => {
store.CloneEntity(entity);
});
AreEqual(expect, nre!.Message);
}

[Test]
public static void Test_Entity_create_delete_Entity_events()
{
Expand Down
Loading

0 comments on commit b5f9014

Please sign in to comment.