diff --git a/src/Common/EcsEdgeWorld.cs b/src/Common/EcsEdgeWorld.cs new file mode 100644 index 0000000..8eaa9ff --- /dev/null +++ b/src/Common/EcsEdgeWorld.cs @@ -0,0 +1,4 @@ +namespace DCFApixels.DragonECS +{ + public sealed class EcsEdgeWorld : EcsWorld { } +} diff --git a/src/Common/EcsRelationWorld.cs.meta b/src/Common/EcsEdgeWorld.cs.meta similarity index 100% rename from src/Common/EcsRelationWorld.cs.meta rename to src/Common/EcsEdgeWorld.cs.meta diff --git a/src/Common/EcsRelationWorld.cs b/src/Common/EcsRelationWorld.cs deleted file mode 100644 index 5848d20..0000000 --- a/src/Common/EcsRelationWorld.cs +++ /dev/null @@ -1,4 +0,0 @@ -namespace DCFApixels.DragonECS -{ - public sealed class EcsRelationWorld : EcsWorld { } -} diff --git a/src/Common/RelationTargets.cs b/src/Common/RelationTargets.cs index 6271d3f..bcd3871 100644 --- a/src/Common/RelationTargets.cs +++ b/src/Common/RelationTargets.cs @@ -5,11 +5,26 @@ public static readonly RelationTargets Empty = new RelationTargets(); public readonly int entity; public readonly int otherEntity; + + public bool IsEmpty => entity == 0 && otherEntity == 0; + public RelationTargets(int entity, int otherEntity) { this.entity = entity; this.otherEntity = otherEntity; } + + public override bool Equals(object obj) + { + return obj is RelationTargets targets && + entity == targets.entity && + otherEntity == targets.otherEntity; + } + public override int GetHashCode() => ~entity ^ otherEntity; public override string ToString() => $"rel({entity}, {otherEntity})"; + + + public static bool operator ==(RelationTargets a, RelationTargets b) => (a.entity == b.entity && a.otherEntity == b.otherEntity); + public static bool operator !=(RelationTargets a, RelationTargets b) => a.entity != b.entity || a.otherEntity != b.otherEntity; } } \ No newline at end of file diff --git a/src/RelationManager.cs b/src/EcsEdge.cs similarity index 67% rename from src/RelationManager.cs rename to src/EcsEdge.cs index 3bf320c..60a0483 100644 --- a/src/RelationManager.cs +++ b/src/EcsEdge.cs @@ -4,29 +4,19 @@ using System.Runtime.CompilerServices; namespace DCFApixels.DragonECS { + //Edge world //Relation entity //Relation //Relation component - public readonly struct RelationData + public class EcsEdge : IEcsWorldEventListener, IEcsEntityEventListener { - public readonly RelationManager manager; - public RelationData(RelationManager manager) - { - this.manager = manager; - } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ref readonly RelationTargets GetRelationTargets(int relationEntityID) => ref manager.GetRelationTargets(relationEntityID); - } - public class RelationManager : IEcsWorldEventListener, IEcsEntityEventListener - { - private EcsRelationWorld _relationWorld; - - private EcsWorld _world; - private EcsWorld _otherWorld; + private readonly EcsWorld _world; + private readonly EcsWorld _otherWorld; + private readonly EcsEdgeWorld _edgeWorld; private readonly IdsBasket _basket = new IdsBasket(256); private readonly IdsBasket _otherBasket = new IdsBasket(256); - private SparseArray64 _relationsMatrix = new SparseArray64(); + private readonly SparseArray64 _relationsMatrix = new SparseArray64(); public readonly ForwardOrientation Forward; public readonly ReverseOrientation Reverse; @@ -34,20 +24,21 @@ namespace DCFApixels.DragonECS private RelationTargets[] _relationTargets; public EcsWorld World => _world; - public EcsWorld RelationWorld => _relationWorld; public EcsWorld OtherWorld => _otherWorld; + public EcsEdgeWorld EdgeWorld => _edgeWorld; + public bool IsSolo => _world == _otherWorld; - internal RelationManager(EcsWorld world, EcsRelationWorld relationWorld, EcsWorld otherWorld) + internal EcsEdge(EcsWorld world, EcsWorld otherWorld, EcsEdgeWorld relationWorld) { - _relationWorld = relationWorld; + _edgeWorld = relationWorld; _world = world; _otherWorld = otherWorld; _relationTargets = new RelationTargets[relationWorld.Capacity]; - _relationWorld.AddListener(worldEventListener: this); - _relationWorld.AddListener(entityEventListener: this); + _edgeWorld.AddListener(worldEventListener: this); + _edgeWorld.AddListener(entityEventListener: this); Forward = new ForwardOrientation(this); Reverse = new ReverseOrientation(this); @@ -67,13 +58,23 @@ namespace DCFApixels.DragonECS if (HasRelation(entityID, otherEntityID)) throw new EcsRelationException(); - int e = _relationWorld.NewEmptyEntity(); + int e = _edgeWorld.NewEmptyEntity(); _basket.AddToHead(entityID, otherEntityID); _otherBasket.AddToHead(otherEntityID, entityID); _relationsMatrix.Add(entityID, otherEntityID, e); _relationTargets[e] = new RelationTargets(entityID, otherEntityID); return e; } + private void BindRelation(int relationEntityID, int entityID, int otherEntityID) + { + ref var rel = ref _relationTargets[relationEntityID]; + if (HasRelation(entityID, otherEntityID) || rel.IsEmpty) + throw new EcsRelationException(); + _basket.AddToHead(entityID, otherEntityID); + _otherBasket.AddToHead(otherEntityID, entityID); + _relationsMatrix.Add(entityID, otherEntityID, relationEntityID); + rel = new RelationTargets(entityID, otherEntityID); + } private void DelRelation(int entityID, int otherEntityID) { if (!_relationsMatrix.TryGetValue(entityID, otherEntityID, out int e)) @@ -81,7 +82,7 @@ namespace DCFApixels.DragonECS _relationsMatrix.Remove(entityID, otherEntityID); _basket.DelHead(entityID); _otherBasket.Del(entityID); - _relationWorld.DelEntity(e); + _edgeWorld.DelEntity(e); _relationTargets[e] = RelationTargets.Empty; } #endregion @@ -113,6 +114,7 @@ namespace DCFApixels.DragonECS //} #endregion + #region GetRelations //#region GetRelations //private IdsLinkedList.Span GetRelations(int entityID) //{ @@ -126,6 +128,7 @@ namespace DCFApixels.DragonECS //// throw new NotImplementedException(); ////} //#endregion + #endregion #endregion @@ -146,27 +149,28 @@ namespace DCFApixels.DragonECS } #endregion - #region Orientation public readonly struct ForwardOrientation { - private readonly RelationManager _source; - internal ForwardOrientation(RelationManager source) => _source = source; + private readonly EcsEdge _source; + internal ForwardOrientation(EcsEdge source) => _source = source; public int NewRelation(int entityID, int otherEntityID) => _source.NewRelation(entityID, otherEntityID); - public void DelRelation(int entityID, int otherEntityID) => _source.DelRelation(entityID, otherEntityID); + public void BindRelation(int relationEntityID, int entityID, int otherEntityID) => _source.BindRelation(relationEntityID, entityID, otherEntityID); public bool HasRelation(int entityID, int otherEntityID) => _source.HasRelation(entityID, otherEntityID); public int GetRelation(int entityID, int otherEntityID) => _source.GetRelation(entityID, otherEntityID); + public void DelRelation(int entityID, int otherEntityID) => _source.DelRelation(entityID, otherEntityID); public bool TryGetRelation(int entityID, int otherEntityID, out int relationEntityID) => _source.TryGetRelation(entityID, otherEntityID, out relationEntityID); public IdsLinkedList.Span GetRelations(int entityID) => _source._basket.GetSpanFor(entityID); } public readonly struct ReverseOrientation { - private readonly RelationManager _source; - internal ReverseOrientation(RelationManager source) => _source = source; + private readonly EcsEdge _source; + internal ReverseOrientation(EcsEdge source) => _source = source; public int NewRelation(int otherEntityID, int entityID) => _source.NewRelation(entityID, otherEntityID); - public void DelRelation(int otherEntityID, int entityID) => _source.DelRelation(entityID, otherEntityID); + public void BindRelation(int relationEntityID, int entityID, int otherEntityID) => _source.BindRelation(relationEntityID, otherEntityID, entityID); public bool HasRelation(int otherEntityID, int entityID) => _source.HasRelation(entityID, otherEntityID); public int GetRelation(int otherEntityID, int entityID) => _source.GetRelation(entityID, otherEntityID); + public void DelRelation(int otherEntityID, int entityID) => _source.DelRelation(entityID, otherEntityID); public bool TryGetRelation(int otherEntityID, int entityID, out int relationEntityID) => _source.TryGetRelation(entityID, otherEntityID, out relationEntityID); public IdsLinkedList.Span GetRelations(int otherEntityID) => _source._otherBasket.GetSpanFor(otherEntityID); } @@ -178,38 +182,4 @@ namespace DCFApixels.DragonECS } #endregion } - - public static class WorldRelationExtensions - { - public static void SetRelationWithSelf(this EcsWorld self) => SetRelationWith(self, self); - public static void SetRelationWith(this EcsWorld self, EcsWorld otherWorld) - { - if (self == null || otherWorld == null) - throw new ArgumentNullException(); - WorldRelationsMatrix.Register(self, otherWorld, new EcsRelationWorld()); - } - public static void SetRelationWithSelf(this EcsWorld self, EcsRelationWorld relationWorld) => SetRelationWith(self, relationWorld); - public static void SetRelationWith(this EcsWorld self, EcsWorld otherWorld, EcsRelationWorld relationWorld) - { - if (self == null || otherWorld == null || relationWorld == null) - throw new ArgumentNullException(); - WorldRelationsMatrix.Register(self, otherWorld, relationWorld); - } - - public static void DelRelationWithSelf(this EcsWorld self, EcsWorld otherWorld) => DelRelationWith(self, self); - public static void DelRelationWith(this EcsWorld self, EcsWorld otherWorld) - { - if (self == null || otherWorld == null) - throw new ArgumentNullException(); - WorldRelationsMatrix.Unregister(self, otherWorld); - } - - public static RelationManager GetRelationWithSelf(this EcsWorld self) => GetRelationWith(self, self); - public static RelationManager GetRelationWith(this EcsWorld self, EcsWorld otherWorld) - { - if (self == null || otherWorld == null) - throw new ArgumentNullException(); - return WorldRelationsMatrix.Get(self, otherWorld); - } - } } diff --git a/src/RelationManager.cs.meta b/src/EcsEdge.cs.meta similarity index 100% rename from src/RelationManager.cs.meta rename to src/EcsEdge.cs.meta diff --git a/src/WorldGraph.cs b/src/WorldGraph.cs new file mode 100644 index 0000000..ab0566e --- /dev/null +++ b/src/WorldGraph.cs @@ -0,0 +1,83 @@ +using DCFApixels.DragonECS.Relations.Utils; +using System; + +namespace DCFApixels.DragonECS +{ + internal static class WorldGraph + { + private static SparseArray64 _matrix = new SparseArray64(4); + + internal static EcsEdge Register(EcsWorld world, EcsWorld otherWorld, EcsEdgeWorld edgeWorld) + { + int worldID = world.id; + int otherWorldID = otherWorld.id; +#if DEBUG + if (_matrix.Contains(worldID, otherWorldID)) + throw new EcsFrameworkException(); +#endif + EcsEdge edge = new EcsEdge(world, otherWorld, edgeWorld); + _matrix[worldID, otherWorldID] = edge; + return edge; + } + internal static void Unregister(EcsWorld world, EcsWorld otherWorld) + { + int worldID = world.id; + int otherWorldID = otherWorld.id; + //var manager = _matrix[worldID, otherWorldID]; + _matrix.Remove(worldID, otherWorldID); + } + + internal static EcsEdge Get(EcsWorld world, EcsWorld otherWorld) + { +#if DEBUG + if (!_matrix.Contains(world.id, otherWorld.id)) + throw new EcsFrameworkException(); +#endif + return _matrix[world.id, otherWorld.id]; + } + internal static bool HasEdge(EcsWorld world, EcsWorld otherWorld) => HasEdge(world.id, otherWorld.id); + internal static bool HasEdge(int worldID, int otherWorldID) => _matrix.Contains(worldID, otherWorldID); + } + + public static class WorldGraphExtensions + { + public static void SetEdgeWithSelf(this EcsWorld self) => SetEdgeWith(self, self); + public static void SetEdgeWith(this EcsWorld self, EcsWorld otherWorld) + { + if (self == null || otherWorld == null) + throw new ArgumentNullException(); + WorldGraph.Register(self, otherWorld, new EcsEdgeWorld()); + } + public static void SetEdgeWithSelf(this EcsWorld self, EcsEdgeWorld relationWorld) => SetEdgeWith(self, self, relationWorld); + public static void SetEdgeWith(this EcsWorld self, EcsWorld otherWorld, EcsEdgeWorld edgeWorld) + { + if (self == null || otherWorld == null || edgeWorld == null) + throw new ArgumentNullException(); + WorldGraph.Register(self, otherWorld, edgeWorld); + } + + public static void HasEdgeWithSelf(this EcsWorld self) => HasEdgeWith(self, self); + public static void HasEdgeWith(this EcsWorld self, EcsWorld otherWorld) + { + if (self == null || otherWorld == null) + throw new ArgumentNullException(); + WorldGraph.HasEdge(self, otherWorld); + } + + public static EcsEdge GetEdgeWithSelf(this EcsWorld self) => GetRelationWith(self, self); + public static EcsEdge GetRelationWith(this EcsWorld self, EcsWorld otherWorld) + { + if (self == null || otherWorld == null) + throw new ArgumentNullException(); + return WorldGraph.Get(self, otherWorld); + } + + public static void DelEdgeWithSelf(this EcsWorld self) => DelEdgeWith(self, self); + public static void DelEdgeWith(this EcsWorld self, EcsWorld otherWorld) + { + if (self == null || otherWorld == null) + throw new ArgumentNullException(); + WorldGraph.Unregister(self, otherWorld); + } + } +} diff --git a/src/WorldRelationsMatrix.cs.meta b/src/WorldGraph.cs.meta similarity index 100% rename from src/WorldRelationsMatrix.cs.meta rename to src/WorldGraph.cs.meta diff --git a/src/WorldRelationsMatrix.cs b/src/WorldRelationsMatrix.cs deleted file mode 100644 index 3f747b5..0000000 --- a/src/WorldRelationsMatrix.cs +++ /dev/null @@ -1,40 +0,0 @@ -using DCFApixels.DragonECS.Relations.Utils; - -namespace DCFApixels.DragonECS -{ - internal static class WorldRelationsMatrix - { - private static SparseArray64 _matrix = new SparseArray64(4); - - internal static RelationManager Register(EcsWorld world, EcsWorld otherWorld, EcsRelationWorld relationWorld) - { - int worldID = world.id; - int otherWorldID = otherWorld.id; -#if DEBUG - if (_matrix.Contains(worldID, otherWorldID)) - throw new EcsFrameworkException(); -#endif - RelationManager manager = new RelationManager(world, relationWorld, otherWorld); - _matrix[worldID, otherWorldID] = manager; - return manager; - } - internal static void Unregister(EcsWorld world, EcsWorld otherWorld) - { - int worldID = world.id; - int otherWorldID = otherWorld.id; - //var manager = _matrix[worldID, otherWorldID]; - _matrix.Remove(worldID, otherWorldID); - } - - internal static RelationManager Get(EcsWorld world, EcsWorld otherWorld) - { -#if DEBUG - if (!_matrix.Contains(world.id, otherWorld.id)) - throw new EcsFrameworkException(); -#endif - return _matrix[world.id, otherWorld.id]; - } - internal static bool HasRelation(EcsWorld world, EcsWorld otherWorld) => HasRelation(world.id, otherWorld.id); - internal static bool HasRelation(int worldID, int otherWorldID) => _matrix.Contains(worldID, otherWorldID); - } -}