mirror of
https://github.com/DCFApixels/DragonECS-Graphs.git
synced 2025-09-17 19:24:36 +08:00
Update
This commit is contained in:
parent
b7b2e1dd79
commit
04d4fd2afd
172
src/Common/IdsBasket.cs
Normal file
172
src/Common/IdsBasket.cs
Normal file
@ -0,0 +1,172 @@
|
|||||||
|
using DCFApixels.DragonECS;
|
||||||
|
using DCFApixels.DragonECS.Relations.Utils;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Drawing;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace DragonECS.DragonECS
|
||||||
|
{
|
||||||
|
[DebuggerTypeProxy(typeof(DebuggerProxy))]
|
||||||
|
public class IdsBasket
|
||||||
|
{
|
||||||
|
private IdsLinkedList _headList = new IdsLinkedList(4);
|
||||||
|
private IdsLinkedList _valueList = new IdsLinkedList(4);
|
||||||
|
private SpanInfo[] _headMapping;
|
||||||
|
private SpanInfo[] _valueMapping;
|
||||||
|
private int _size;
|
||||||
|
|
||||||
|
public IdsBasket(int size)
|
||||||
|
{
|
||||||
|
_headMapping = new SpanInfo[size];
|
||||||
|
_valueMapping = new SpanInfo[size];
|
||||||
|
_size = size;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Resize(int newSize)
|
||||||
|
{
|
||||||
|
Array.Resize(ref _headMapping, newSize);
|
||||||
|
Array.Resize(ref _valueMapping, newSize);
|
||||||
|
_size = newSize;
|
||||||
|
}
|
||||||
|
public void Clear()
|
||||||
|
{
|
||||||
|
_headList.Clear();
|
||||||
|
_valueList.Clear();
|
||||||
|
ArrayUtility.Fill(_headMapping, SpanInfo.Empty);
|
||||||
|
ArrayUtility.Fill(_valueMapping, SpanInfo.Empty);
|
||||||
|
_size = 0;
|
||||||
|
}
|
||||||
|
public void AddToHead(int headID, int id)
|
||||||
|
{
|
||||||
|
int _savedNode;
|
||||||
|
ref var headSpan = ref _headMapping[headID];
|
||||||
|
if (headSpan.startNodeIndex <= 0)
|
||||||
|
{
|
||||||
|
_savedNode = _headList.Add(id);
|
||||||
|
headSpan.startNodeIndex = _savedNode;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_savedNode = _headList.InsertAfter(headSpan.startNodeIndex, id);
|
||||||
|
}
|
||||||
|
headSpan.count++;
|
||||||
|
|
||||||
|
ref var valueSpan = ref _valueMapping[id];
|
||||||
|
if (valueSpan.startNodeIndex <= 0)
|
||||||
|
valueSpan.startNodeIndex = _valueList.Add(_savedNode);
|
||||||
|
else
|
||||||
|
_valueList.InsertAfter(valueSpan.startNodeIndex, _savedNode);
|
||||||
|
valueSpan.count++;
|
||||||
|
}
|
||||||
|
public void Del(int id)
|
||||||
|
{
|
||||||
|
ref var valueSpan = ref _valueMapping[id];
|
||||||
|
ref var headSpan = ref _headMapping[id];
|
||||||
|
if (valueSpan.startNodeIndex <= 0)
|
||||||
|
return;
|
||||||
|
foreach (var nodeIndex in _valueList.GetSpan(valueSpan.startNodeIndex, valueSpan.count))
|
||||||
|
_headList.Remove(nodeIndex);
|
||||||
|
_valueList.RemoveSpan(valueSpan.startNodeIndex, valueSpan.count);
|
||||||
|
valueSpan = SpanInfo.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void DelHead(int headID)
|
||||||
|
{
|
||||||
|
ref var headSpan = ref _headMapping[headID];
|
||||||
|
_valueList.RemoveSpan(headSpan.startNodeIndex, headSpan.count);
|
||||||
|
headSpan = SpanInfo.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IdsLinkedList.Span GetSpanFor(int value)
|
||||||
|
{
|
||||||
|
ref var head = ref _headMapping[value];
|
||||||
|
if (head.startNodeIndex <= 0)
|
||||||
|
return _headList.EmptySpan();
|
||||||
|
else
|
||||||
|
return _headList.GetSpan(head.startNodeIndex, head.count);
|
||||||
|
}
|
||||||
|
|
||||||
|
private struct SpanInfo
|
||||||
|
{
|
||||||
|
public readonly static SpanInfo Empty = default;
|
||||||
|
public int startNodeIndex;
|
||||||
|
public int count;
|
||||||
|
}
|
||||||
|
|
||||||
|
#region DebuggerProxy
|
||||||
|
internal class DebuggerProxy
|
||||||
|
{
|
||||||
|
private IdsBasket _basket;
|
||||||
|
|
||||||
|
public SpanDebugInfo[] HeadSpans => GetSpans(_basket._headList, _basket._headMapping);
|
||||||
|
public SpanDebugInfo[] ValueSpans => GetSpans(_basket._valueList, _basket._valueMapping);
|
||||||
|
|
||||||
|
private SpanDebugInfo[] GetSpans(IdsLinkedList list, SpanInfo[] mapping)
|
||||||
|
{
|
||||||
|
SpanDebugInfo[] result = new SpanDebugInfo[mapping.Length];
|
||||||
|
for (int i = 0; i < mapping.Length; i++)
|
||||||
|
result[i] = new SpanDebugInfo(list, mapping[i].startNodeIndex, mapping[i].count);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
public DebuggerProxy(IdsBasket basket)
|
||||||
|
{
|
||||||
|
_basket = basket;
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct SpanDebugInfo
|
||||||
|
{
|
||||||
|
private IdsLinkedList _list;
|
||||||
|
public int index;
|
||||||
|
public int count;
|
||||||
|
public NodeDebugInfo[] Nodes
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
var result = new NodeDebugInfo[this.count];
|
||||||
|
var nodes = _list.Nodes;
|
||||||
|
int index;
|
||||||
|
int count = this.count;
|
||||||
|
int next = this.index;
|
||||||
|
int i = 0;
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
index = next;
|
||||||
|
next = nodes[next].next;
|
||||||
|
if (!(index > 0 && count-- > 0))
|
||||||
|
break;
|
||||||
|
var node = nodes[index];
|
||||||
|
result[i] = new NodeDebugInfo(index, node.prev, node.next, node.value);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public SpanDebugInfo(IdsLinkedList list, int index, int count)
|
||||||
|
{
|
||||||
|
_list = list;
|
||||||
|
this.index = index;
|
||||||
|
this.count = count;
|
||||||
|
}
|
||||||
|
public override string ToString() => $"[{index}] {count}";
|
||||||
|
}
|
||||||
|
public struct NodeDebugInfo
|
||||||
|
{
|
||||||
|
public int index;
|
||||||
|
public int prev;
|
||||||
|
public int next;
|
||||||
|
public int value;
|
||||||
|
public NodeDebugInfo(int index, int prev, int next, int value)
|
||||||
|
{
|
||||||
|
this.index = index;
|
||||||
|
this.prev = prev;
|
||||||
|
this.next = next;
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
public override string ToString() => $"[{index}] {prev}_{next} - {value}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
14
src/Common/RelationTargets.cs
Normal file
14
src/Common/RelationTargets.cs
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
namespace DragonECS.DragonECS
|
||||||
|
{
|
||||||
|
public readonly struct RelationTargets
|
||||||
|
{
|
||||||
|
public static readonly RelationTargets Empty = new RelationTargets();
|
||||||
|
public readonly int entity;
|
||||||
|
public readonly int otherEntity;
|
||||||
|
public RelationTargets(int entity, int otherEntity)
|
||||||
|
{
|
||||||
|
this.entity = entity;
|
||||||
|
this.otherEntity = otherEntity;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,10 +0,0 @@
|
|||||||
using DCFApixels.DragonECS;
|
|
||||||
|
|
||||||
namespace DragonECS.DragonECS
|
|
||||||
{
|
|
||||||
internal readonly struct Relation : IEcsComponent
|
|
||||||
{
|
|
||||||
public readonly int entity;
|
|
||||||
public readonly int otherEntity;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,89 +1,173 @@
|
|||||||
using DCFApixels.DragonECS;
|
using DCFApixels.DragonECS;
|
||||||
using DCFApixels.DragonECS.Relations.Utils;
|
using DCFApixels.DragonECS.Relations.Utils;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
namespace DragonECS.DragonECS
|
namespace DragonECS.DragonECS
|
||||||
{
|
{
|
||||||
internal static class WorldRelationsMatrix
|
//Relation entity
|
||||||
|
//Relation
|
||||||
|
//Relation component
|
||||||
|
public readonly struct Relations
|
||||||
{
|
{
|
||||||
private static SparseArray64<RelationManager> _matrix;
|
public readonly RelationManager manager;
|
||||||
|
public Relations(RelationManager manager)
|
||||||
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);
|
|
||||||
|
|
||||||
internal static RelationManager Register(EcsWorld world, EcsWorld otherWorld, EcsRelationWorld relationWorld)
|
|
||||||
{
|
{
|
||||||
int worldID = world.id;
|
this.manager = manager;
|
||||||
int otherWorldID = otherWorld.id;
|
|
||||||
#if DEBUG
|
|
||||||
if (_matrix.Contains(worldID, otherWorldID))
|
|
||||||
throw new EcsFrameworkException();
|
|
||||||
#endif
|
|
||||||
RelationManager manager = new RelationManager(relationWorld, world, 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
public 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];
|
|
||||||
}
|
}
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public ref readonly RelationTargets GetRelationTargets(int relationEntityID) => ref manager.GetRelationTargets(relationEntityID);
|
||||||
}
|
}
|
||||||
|
public class RelationManager : IEcsWorldEventListener, IEcsEntityEventListener
|
||||||
public class RelationManager : IEcsPoolEventListener, IEcsWorldEventListener, IEcsEntityEventListener
|
|
||||||
{
|
{
|
||||||
private EcsRelationWorld _relationWorld;
|
private EcsRelationWorld _relationWorld;
|
||||||
|
|
||||||
private EcsWorld _world;
|
private EcsWorld _world;
|
||||||
private EcsWorld _otherWorld;
|
private EcsWorld _otherWorld;
|
||||||
|
|
||||||
private int[] _mapping = new int[256];
|
private SparseArray64<int> _relationsMatrix = new SparseArray64<int>();
|
||||||
private IdsLinkedList _idsBasket = new IdsLinkedList(256);
|
|
||||||
private EcsPool<Relation> _relationsPool;
|
|
||||||
|
|
||||||
|
public readonly Orientation Forward;
|
||||||
|
public readonly Orientation Reverse;
|
||||||
|
|
||||||
|
private RelationTargets[] _relationTargets;
|
||||||
|
|
||||||
|
public EcsWorld World => _world;
|
||||||
public EcsWorld RelationWorld => _relationWorld;
|
public EcsWorld RelationWorld => _relationWorld;
|
||||||
|
public EcsWorld OtherWorld => _otherWorld;
|
||||||
public bool IsSolo => _world == _otherWorld;
|
public bool IsSolo => _world == _otherWorld;
|
||||||
|
|
||||||
internal RelationManager(EcsRelationWorld relationWorld, EcsWorld world, EcsWorld otherWorld)
|
internal RelationManager(EcsWorld world, EcsRelationWorld relationWorld, EcsWorld otherWorld)
|
||||||
{
|
{
|
||||||
_relationWorld = relationWorld;
|
_relationWorld = relationWorld;
|
||||||
_world = world;
|
_world = world;
|
||||||
_otherWorld = otherWorld;
|
_otherWorld = otherWorld;
|
||||||
|
|
||||||
_relationsPool = relationWorld.GetPool<Relation>();
|
_relationTargets = new RelationTargets[relationWorld.Capacity];
|
||||||
_relationsPool.AddListener(this);
|
|
||||||
_relationWorld.AddListener(worldEventListener: this);
|
_relationWorld.AddListener(worldEventListener: this);
|
||||||
_relationWorld.AddListener(entityEventListener: this);
|
_relationWorld.AddListener(entityEventListener: this);
|
||||||
|
|
||||||
|
IdsBasket basket = new IdsBasket(256);
|
||||||
|
IdsBasket otherBasket = new IdsBasket(256);
|
||||||
|
|
||||||
|
Forward = new Orientation(this, false, relationWorld, IsSolo, basket, otherBasket);
|
||||||
|
Reverse = new Orientation(this, true, relationWorld, IsSolo, otherBasket, basket);
|
||||||
}
|
}
|
||||||
|
|
||||||
void IEcsPoolEventListener.OnAdd(int entityID)
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public ref readonly RelationTargets GetRelationTargets(int relationEntityID)
|
||||||
{
|
{
|
||||||
//_idsBasket.
|
return ref _relationTargets[relationEntityID];
|
||||||
}
|
}
|
||||||
void IEcsPoolEventListener.OnGet(int entityID) { }
|
|
||||||
void IEcsPoolEventListener.OnDel(int entityID) { }
|
|
||||||
|
|
||||||
void IEcsWorldEventListener.OnWorldResize(int newSize) { }
|
#region Callbacks
|
||||||
|
void IEcsWorldEventListener.OnWorldResize(int newSize)
|
||||||
|
{
|
||||||
|
Array.Resize(ref _relationTargets, newSize);
|
||||||
|
}
|
||||||
void IEcsWorldEventListener.OnReleaseDelEntityBuffer(ReadOnlySpan<int> buffer) { }
|
void IEcsWorldEventListener.OnReleaseDelEntityBuffer(ReadOnlySpan<int> buffer) { }
|
||||||
void IEcsWorldEventListener.OnWorldDestroy() { }
|
void IEcsWorldEventListener.OnWorldDestroy() { }
|
||||||
|
|
||||||
void IEcsEntityEventListener.OnNewEntity(int entityID) { }
|
void IEcsEntityEventListener.OnNewEntity(int entityID) { }
|
||||||
void IEcsEntityEventListener.OnDelEntity(int entityID)
|
void IEcsEntityEventListener.OnDelEntity(int entityID)
|
||||||
{
|
{
|
||||||
|
ref RelationTargets rel = ref _relationTargets[entityID];
|
||||||
|
if (_relationsMatrix.Contains(rel.entity, rel.otherEntity))
|
||||||
|
Forward.DelRelation(rel.entity, rel.otherEntity);
|
||||||
}
|
}
|
||||||
}
|
#endregion
|
||||||
|
|
||||||
|
#region Orientation
|
||||||
|
public readonly struct Orientation
|
||||||
|
{
|
||||||
|
private readonly RelationManager _source;
|
||||||
|
|
||||||
|
private readonly EcsWorld _relationWorld;
|
||||||
|
|
||||||
|
private readonly IdsBasket _basket;
|
||||||
|
private readonly IdsBasket _otherBasket;
|
||||||
|
|
||||||
|
private readonly bool _isSolo;
|
||||||
|
|
||||||
|
private readonly bool _isReverce;
|
||||||
|
|
||||||
|
public Orientation(RelationManager source, bool isReverce, EcsWorld relationWorld, bool isSolo, IdsBasket basket, IdsBasket otherBasket)
|
||||||
|
{
|
||||||
|
_source = source;
|
||||||
|
_isReverce = isReverce;
|
||||||
|
_relationWorld = relationWorld;
|
||||||
|
_isSolo = isSolo;
|
||||||
|
_basket = basket;
|
||||||
|
_otherBasket = otherBasket;
|
||||||
|
}
|
||||||
|
|
||||||
|
#region New/Del
|
||||||
|
public int NewRelation(int entityID, int otherEntityID)
|
||||||
|
{
|
||||||
|
if (HasRelation(entityID, otherEntityID))
|
||||||
|
throw new EcsRelationException();
|
||||||
|
int e = _relationWorld.NewEmptyEntity();
|
||||||
|
_basket.AddToHead(entityID, otherEntityID);
|
||||||
|
_otherBasket.AddToHead(otherEntityID, entityID);
|
||||||
|
_source._relationTargets[e] = new RelationTargets(entityID, otherEntityID);
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
public void DelRelation(int entityID, int otherEntityID)
|
||||||
|
{
|
||||||
|
if (!_source._relationsMatrix.TryGetValue(entityID, otherEntityID, out int e))
|
||||||
|
throw new EcsRelationException();
|
||||||
|
_basket.DelHead(entityID);
|
||||||
|
_otherBasket.Del(entityID);
|
||||||
|
_relationWorld.DelEntity(e);
|
||||||
|
_source._relationTargets[e] = RelationTargets.Empty;
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Has
|
||||||
|
public bool HasRelation(int entityID, int otherEntityID) => _source._relationsMatrix.Contains(entityID, otherEntityID);
|
||||||
|
public bool HasRelationWith(EcsSubject subject, int entityID, int otherEntityID)
|
||||||
|
{
|
||||||
|
if (subject.World != _relationWorld)
|
||||||
|
throw new ArgumentException();
|
||||||
|
return _source._relationsMatrix.TryGetValue(entityID, otherEntityID, out int entity) && subject.IsMatches(entity);
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region GetRelation
|
||||||
|
public int GetRelation(int entityID, int otherEntityID)
|
||||||
|
{
|
||||||
|
if (!_source._relationsMatrix.TryGetValue(entityID, otherEntityID, out int e))
|
||||||
|
throw new EcsRelationException();
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
public bool TryGetRelation(int entityID, int otherEntityID, out int entity)
|
||||||
|
{
|
||||||
|
return _source._relationsMatrix.TryGetValue(entityID, otherEntityID, out entity);
|
||||||
|
}
|
||||||
|
public bool TryGetRelation(EcsSubject subject, int entityID, int otherEntityID, out int entity)
|
||||||
|
{
|
||||||
|
return _source._relationsMatrix.TryGetValue(entityID, otherEntityID, out entity) && subject.IsMatches(entity);
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region GetRelations
|
||||||
|
//ReadOnlySpan<int> временная заглушка, потому тут будет спан из линкедлиста
|
||||||
|
public ReadOnlySpan<int> GetRelations(int entityID)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
//ReadOnlySpan<int> временная заглушка, потому тут будет спан из линкедлиста
|
||||||
|
public ReadOnlySpan<int> GetRelationsWith(EcsSubject subject, int entityID)
|
||||||
|
{
|
||||||
|
if (subject.World != _relationWorld)
|
||||||
|
throw new ArgumentException();
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
|
||||||
public static class WorldRelationExtensions
|
public static class WorldRelationExtensions
|
||||||
{
|
{
|
||||||
|
14
src/Utils/Exceptions.cs
Normal file
14
src/Utils/Exceptions.cs
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
using System;
|
||||||
|
using System.Runtime.Serialization;
|
||||||
|
|
||||||
|
namespace DCFApixels.DragonECS
|
||||||
|
{
|
||||||
|
[Serializable]
|
||||||
|
public class EcsRelationException : Exception
|
||||||
|
{
|
||||||
|
public EcsRelationException() { }
|
||||||
|
public EcsRelationException(string message) : base(EcsConsts.EXCEPTION_MESSAGE_PREFIX + message) { }
|
||||||
|
public EcsRelationException(string message, Exception inner) : base(EcsConsts.EXCEPTION_MESSAGE_PREFIX + message, inner) { }
|
||||||
|
protected EcsRelationException(SerializationInfo info, StreamingContext context) : base(info, context) { }
|
||||||
|
}
|
||||||
|
}
|
@ -1,50 +0,0 @@
|
|||||||
using DCFApixels.DragonECS.Relations.Utils;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace DragonECS.DragonECS
|
|
||||||
{
|
|
||||||
internal class IdsBasket
|
|
||||||
{
|
|
||||||
private IdsLinkedList _linketdList = new IdsLinkedList(4);
|
|
||||||
private int[] _mapping;
|
|
||||||
private int[] _counts;
|
|
||||||
|
|
||||||
public void Clear()
|
|
||||||
{
|
|
||||||
_linketdList.Clear();
|
|
||||||
ArrayUtility.Fill(_mapping, 0, 0, _mapping.Length);
|
|
||||||
ArrayUtility.Fill(_counts, 0, 0, _counts.Length);
|
|
||||||
}
|
|
||||||
public void AddToHead(int headEntityID, int addedEntityID)
|
|
||||||
{
|
|
||||||
ref var nodeIndex = ref _mapping[headEntityID];
|
|
||||||
if (nodeIndex <= 0)
|
|
||||||
{
|
|
||||||
nodeIndex = _linketdList.Add(addedEntityID);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_linketdList.InsertAfter(nodeIndex, addedEntityID);
|
|
||||||
}
|
|
||||||
_counts[headEntityID]++;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void DelHead(int headEntityID)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public IdsLinkedList.Span GetEntitiesFor(int entity)
|
|
||||||
{
|
|
||||||
ref var nodeIndex = ref _mapping[entity];
|
|
||||||
if (nodeIndex <= 0)
|
|
||||||
return _linketdList.EmptySpan();
|
|
||||||
else
|
|
||||||
return _linketdList.GetSpan(nodeIndex, _counts[entity]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -2,6 +2,7 @@
|
|||||||
using System.Collections;
|
using System.Collections;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
using System.Reflection.Metadata.Ecma335;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace DCFApixels.DragonECS.Relations.Utils
|
namespace DCFApixels.DragonECS.Relations.Utils
|
||||||
@ -22,6 +23,7 @@ namespace DCFApixels.DragonECS.Relations.Utils
|
|||||||
public int Count => _count;
|
public int Count => _count;
|
||||||
public int Capacity => _nodes.Length;
|
public int Capacity => _nodes.Length;
|
||||||
public int Last => _lastNodeIndex;
|
public int Last => _lastNodeIndex;
|
||||||
|
public ReadOnlySpan<Node> Nodes => new ReadOnlySpan<Node>(_nodes);
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Constructors
|
#region Constructors
|
||||||
@ -52,19 +54,34 @@ namespace DCFApixels.DragonECS.Relations.Utils
|
|||||||
/// <returns> new node index</returns>
|
/// <returns> new node index</returns>
|
||||||
public int InsertAfter(int nodeIndex, int value)
|
public int InsertAfter(int nodeIndex, int value)
|
||||||
{
|
{
|
||||||
_count++;
|
if(++_count >= _nodes.Length)
|
||||||
|
Array.Resize(ref _nodes, _nodes.Length << 1);
|
||||||
int newNodeIndex = _recycledNodesCount > 0 ? _recycledNodes[--_recycledNodesCount] : _count;
|
int newNodeIndex = _recycledNodesCount > 0 ? _recycledNodes[--_recycledNodesCount] : _count;
|
||||||
|
|
||||||
ref Node prevNode = ref _nodes[nodeIndex];
|
ref Node prevNode = ref _nodes[nodeIndex];
|
||||||
ref Node nextNode = ref _nodes[prevNode.next];
|
ref Node nextNode = ref _nodes[prevNode.next];
|
||||||
|
if (prevNode.next == 0)
|
||||||
|
_lastNodeIndex = newNodeIndex;
|
||||||
_nodes[newNodeIndex].Set(value, nextNode.prev, prevNode.next);
|
_nodes[newNodeIndex].Set(value, nextNode.prev, prevNode.next);
|
||||||
prevNode.next = newNodeIndex;
|
prevNode.next = newNodeIndex;
|
||||||
nextNode.prev = newNodeIndex;
|
nextNode.prev = newNodeIndex;
|
||||||
if(prevNode.next == 0)
|
|
||||||
_lastNodeIndex = newNodeIndex;
|
return newNodeIndex;
|
||||||
|
}
|
||||||
|
public int InsertBefore(int nodeIndex, int value)
|
||||||
|
{
|
||||||
|
if (++_count >= _nodes.Length)
|
||||||
|
Array.Resize(ref _nodes, _nodes.Length << 1);
|
||||||
|
int newNodeIndex = _recycledNodesCount > 0 ? _recycledNodes[--_recycledNodesCount] : _count;
|
||||||
|
|
||||||
|
ref Node nextNode = ref _nodes[nodeIndex];
|
||||||
|
ref Node prevNode = ref _nodes[nextNode.prev];
|
||||||
|
_nodes[newNodeIndex].Set(value, nextNode.prev, prevNode.next);
|
||||||
|
prevNode.next = newNodeIndex;
|
||||||
|
nextNode.prev = newNodeIndex;
|
||||||
|
|
||||||
return newNodeIndex;
|
return newNodeIndex;
|
||||||
}
|
}
|
||||||
//public int InsertBefore(int nodeIndex, int value) { }
|
|
||||||
public void Remove(int nodeIndex)
|
public void Remove(int nodeIndex)
|
||||||
{
|
{
|
||||||
if(nodeIndex <= 0)
|
if(nodeIndex <= 0)
|
||||||
@ -80,6 +97,37 @@ namespace DCFApixels.DragonECS.Relations.Utils
|
|||||||
_count--;
|
_count--;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void RemoveSpan(int startNodeIndex, int count)
|
||||||
|
{
|
||||||
|
if (count <= 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
int endNodeIndex = startNodeIndex;
|
||||||
|
|
||||||
|
if (_recycledNodesCount >= _recycledNodes.Length)
|
||||||
|
Array.Resize(ref _recycledNodes, _recycledNodes.Length << 1);
|
||||||
|
_recycledNodes[_recycledNodesCount++] = startNodeIndex;
|
||||||
|
|
||||||
|
for (int i = 1; i < count; i++)
|
||||||
|
{
|
||||||
|
endNodeIndex = _nodes[endNodeIndex].next;
|
||||||
|
if (endNodeIndex == 0)
|
||||||
|
throw new ArgumentOutOfRangeException();
|
||||||
|
|
||||||
|
if (_recycledNodesCount >= _recycledNodes.Length)
|
||||||
|
Array.Resize(ref _recycledNodes, _recycledNodes.Length << 1);
|
||||||
|
_recycledNodes[_recycledNodesCount++] = endNodeIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
ref var startNode = ref _nodes[startNodeIndex];
|
||||||
|
ref var endNode = ref _nodes[endNodeIndex];
|
||||||
|
|
||||||
|
_nodes[endNode.next].prev = startNode.prev;
|
||||||
|
_nodes[startNode.prev].next = endNode.next;
|
||||||
|
|
||||||
|
_count -= count;
|
||||||
|
}
|
||||||
|
|
||||||
public int Add(int id) => InsertAfter(_lastNodeIndex, id);
|
public int Add(int id) => InsertAfter(_lastNodeIndex, id);
|
||||||
public ref readonly Node GetNode(int nodeIndex) => ref _nodes[nodeIndex];
|
public ref readonly Node GetNode(int nodeIndex) => ref _nodes[nodeIndex];
|
||||||
|
|
||||||
@ -108,39 +156,10 @@ namespace DCFApixels.DragonECS.Relations.Utils
|
|||||||
this.next = next;
|
this.next = next;
|
||||||
this.prev = prev;
|
this.prev = prev;
|
||||||
}
|
}
|
||||||
public override string ToString() => $"node(<:{prev} >:{next} v:{value})";
|
public override string ToString() => $"node({prev}<>{next} v:{value})";
|
||||||
}
|
}
|
||||||
|
|
||||||
#region Enumerator/Span
|
#region Span/Enumerator
|
||||||
public struct Enumerator : IEnumerator<int>
|
|
||||||
{
|
|
||||||
private readonly Node[] _nodes;
|
|
||||||
private int _index;
|
|
||||||
private int _next;
|
|
||||||
public Enumerator(Node[] nodes)
|
|
||||||
{
|
|
||||||
_nodes = nodes;
|
|
||||||
_index = -1;
|
|
||||||
_next = _nodes[Head].next;
|
|
||||||
}
|
|
||||||
public int Current => _nodes[_index].value;
|
|
||||||
|
|
||||||
|
|
||||||
public bool MoveNext()
|
|
||||||
{
|
|
||||||
_index = _next;
|
|
||||||
_next = _nodes[_next].next;
|
|
||||||
return _index > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
object IEnumerator.Current => Current;
|
|
||||||
void IDisposable.Dispose() { }
|
|
||||||
void IEnumerator.Reset()
|
|
||||||
{
|
|
||||||
_index = -1;
|
|
||||||
_next = Head;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public readonly ref struct Span
|
public readonly ref struct Span
|
||||||
{
|
{
|
||||||
private readonly IdsLinkedList _source;
|
private readonly IdsLinkedList _source;
|
||||||
@ -187,7 +206,8 @@ namespace DCFApixels.DragonECS.Relations.Utils
|
|||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
public class DebuggerProxy
|
#region Debug
|
||||||
|
internal class DebuggerProxy
|
||||||
{
|
{
|
||||||
private IdsLinkedList list;
|
private IdsLinkedList list;
|
||||||
public NodeInfo[] Nodes
|
public NodeInfo[] Nodes
|
||||||
@ -206,15 +226,12 @@ namespace DCFApixels.DragonECS.Relations.Utils
|
|||||||
{
|
{
|
||||||
index = next;
|
index = next;
|
||||||
next = nodes[next].next;
|
next = nodes[next].next;
|
||||||
if (index > 0 && count-- > 0)
|
if (!(index > 0 && count-- > 0))
|
||||||
{ }
|
break;
|
||||||
else break;
|
|
||||||
|
|
||||||
ref var node = ref nodes[index];
|
ref var node = ref nodes[index];
|
||||||
result[i] = new NodeInfo(index, node.prev, node.next, node.value);
|
result[i] = new NodeInfo(index, node.prev, node.next, node.value);
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -239,5 +256,6 @@ namespace DCFApixels.DragonECS.Relations.Utils
|
|||||||
public override string ToString() => $"[{index}] {prev}_{next} - {value}";
|
public override string ToString() => $"[{index}] {prev}_{next} - {value}";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endregion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@ using System.Runtime.InteropServices;
|
|||||||
|
|
||||||
namespace DCFApixels.DragonECS.Relations.Utils
|
namespace DCFApixels.DragonECS.Relations.Utils
|
||||||
{
|
{
|
||||||
public class SparseArray64<TValue>
|
internal class SparseArray64<TValue>
|
||||||
{
|
{
|
||||||
public const int MIN_CAPACITY_BITS_OFFSET = 4;
|
public const int MIN_CAPACITY_BITS_OFFSET = 4;
|
||||||
public const int MIN_CAPACITY = 1 << MIN_CAPACITY_BITS_OFFSET;
|
public const int MIN_CAPACITY = 1 << MIN_CAPACITY_BITS_OFFSET;
|
||||||
@ -148,6 +148,17 @@ namespace DCFApixels.DragonECS.Relations.Utils
|
|||||||
value = _entries[index].value;
|
value = _entries[index].value;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
public bool TryGetValue(long keyX, long keyY, out TValue value)
|
||||||
|
{
|
||||||
|
int index = FindEntry(keyX + (keyY << 32));
|
||||||
|
if (index < 0)
|
||||||
|
{
|
||||||
|
value = default;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
value = _entries[index].value;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Contains
|
#region Contains
|
||||||
|
41
src/WorldRelationsMatrix.cs
Normal file
41
src/WorldRelationsMatrix.cs
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
using DCFApixels.DragonECS;
|
||||||
|
using DCFApixels.DragonECS.Relations.Utils;
|
||||||
|
|
||||||
|
namespace DragonECS.DragonECS
|
||||||
|
{
|
||||||
|
internal static class WorldRelationsMatrix
|
||||||
|
{
|
||||||
|
private static SparseArray64<RelationManager> _matrix;
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user