2023-01-10 00:41:00 +00:00
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
2023-10-13 20:42:22 +00:00
|
|
|
|
using MoonTools.ECS.Collections;
|
2023-01-10 00:41:00 +00:00
|
|
|
|
|
|
|
|
|
namespace MoonTools.ECS
|
|
|
|
|
{
|
|
|
|
|
internal class FilterStorage
|
|
|
|
|
{
|
|
|
|
|
private EntityStorage EntityStorage;
|
|
|
|
|
private TypeIndices ComponentTypeIndices;
|
|
|
|
|
private Dictionary<FilterSignature, IndexableSet<Entity>> filterSignatureToEntityIDs = new Dictionary<FilterSignature, IndexableSet<Entity>>();
|
2023-10-13 20:42:22 +00:00
|
|
|
|
private Dictionary<int, List<FilterSignature>> typeToFilterSignatures = new Dictionary<int, List<FilterSignature>>();
|
2023-01-10 00:41:00 +00:00
|
|
|
|
|
2023-01-27 00:34:15 +00:00
|
|
|
|
private Dictionary<FilterSignature, Action<Entity>> addCallbacks = new Dictionary<FilterSignature, Action<Entity>>();
|
|
|
|
|
private Dictionary<FilterSignature, Action<Entity>> removeCallbacks = new Dictionary<FilterSignature, Action<Entity>>();
|
|
|
|
|
|
2023-01-10 00:41:00 +00:00
|
|
|
|
public FilterStorage(EntityStorage entityStorage, TypeIndices componentTypeIndices)
|
|
|
|
|
{
|
|
|
|
|
EntityStorage = entityStorage;
|
|
|
|
|
ComponentTypeIndices = componentTypeIndices;
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-13 20:42:22 +00:00
|
|
|
|
private void CopyTypeCache(Dictionary<int, List<FilterSignature>> typeCache)
|
|
|
|
|
{
|
|
|
|
|
foreach (var type in typeCache.Keys)
|
|
|
|
|
{
|
|
|
|
|
if (!typeToFilterSignatures.ContainsKey(type))
|
|
|
|
|
{
|
|
|
|
|
typeToFilterSignatures.Add(type, new List<FilterSignature>());
|
|
|
|
|
|
|
|
|
|
foreach (var signature in typeCache[type])
|
|
|
|
|
{
|
|
|
|
|
typeToFilterSignatures[type].Add(signature);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void CreateMissingStorages(FilterStorage other)
|
|
|
|
|
{
|
|
|
|
|
foreach (var filterSignature in other.filterSignatureToEntityIDs.Keys)
|
|
|
|
|
{
|
|
|
|
|
if (!filterSignatureToEntityIDs.ContainsKey(filterSignature))
|
|
|
|
|
{
|
|
|
|
|
filterSignatureToEntityIDs.Add(filterSignature, new IndexableSet<Entity>());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CopyTypeCache(other.typeToFilterSignatures);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public Filter CreateFilter(IndexableSet<int> included, IndexableSet<int> excluded)
|
2023-01-10 00:41:00 +00:00
|
|
|
|
{
|
|
|
|
|
var filterSignature = new FilterSignature(included, excluded);
|
|
|
|
|
if (!filterSignatureToEntityIDs.ContainsKey(filterSignature))
|
|
|
|
|
{
|
|
|
|
|
filterSignatureToEntityIDs.Add(filterSignature, new IndexableSet<Entity>());
|
|
|
|
|
|
|
|
|
|
foreach (var type in included)
|
|
|
|
|
{
|
|
|
|
|
if (!typeToFilterSignatures.ContainsKey(type))
|
|
|
|
|
{
|
2023-10-13 20:42:22 +00:00
|
|
|
|
typeToFilterSignatures.Add(type, new List<FilterSignature>());
|
2023-01-10 00:41:00 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
typeToFilterSignatures[type].Add(filterSignature);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
foreach (var type in excluded)
|
|
|
|
|
{
|
|
|
|
|
if (!typeToFilterSignatures.ContainsKey(type))
|
|
|
|
|
{
|
2023-10-13 20:42:22 +00:00
|
|
|
|
typeToFilterSignatures.Add(type, new List<FilterSignature>());
|
2023-01-10 00:41:00 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
typeToFilterSignatures[type].Add(filterSignature);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return new Filter(this, included, excluded);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public ReverseSpanEnumerator<Entity> FilterEntities(FilterSignature filterSignature)
|
|
|
|
|
{
|
|
|
|
|
return filterSignatureToEntityIDs[filterSignature].GetEnumerator();
|
|
|
|
|
}
|
|
|
|
|
|
2023-04-06 21:54:20 +00:00
|
|
|
|
public RandomEntityEnumerator FilterEntitiesRandom(FilterSignature filterSignature)
|
2023-01-10 00:41:00 +00:00
|
|
|
|
{
|
2023-04-06 21:54:20 +00:00
|
|
|
|
return new RandomEntityEnumerator(
|
|
|
|
|
this,
|
|
|
|
|
filterSignature,
|
2023-10-13 20:42:22 +00:00
|
|
|
|
RandomManager.LinearCongruentialSequence(FilterCount(filterSignature)));
|
2023-01-10 00:41:00 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public Entity FilterNthEntity(FilterSignature filterSignature, int index)
|
|
|
|
|
{
|
|
|
|
|
return new Entity(filterSignatureToEntityIDs[filterSignature][index]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public Entity FilterRandomEntity(FilterSignature filterSignature)
|
|
|
|
|
{
|
2023-10-13 20:42:22 +00:00
|
|
|
|
var randomIndex = RandomManager.Next(FilterCount(filterSignature));
|
2023-01-10 00:41:00 +00:00
|
|
|
|
return new Entity(filterSignatureToEntityIDs[filterSignature][randomIndex]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public int FilterCount(FilterSignature filterSignature)
|
|
|
|
|
{
|
|
|
|
|
return filterSignatureToEntityIDs[filterSignature].Count;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void Check(int entityID, int componentTypeIndex)
|
|
|
|
|
{
|
|
|
|
|
if (typeToFilterSignatures.TryGetValue(componentTypeIndex, out var filterSignatures))
|
|
|
|
|
{
|
|
|
|
|
foreach (var filterSignature in filterSignatures)
|
|
|
|
|
{
|
|
|
|
|
CheckFilter(entityID, filterSignature);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void Check<TComponent>(int entityID) where TComponent : unmanaged
|
|
|
|
|
{
|
|
|
|
|
Check(entityID, ComponentTypeIndices.GetIndex<TComponent>());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public bool CheckSatisfied(int entityID, FilterSignature filterSignature)
|
|
|
|
|
{
|
|
|
|
|
foreach (var type in filterSignature.Included)
|
|
|
|
|
{
|
|
|
|
|
if (!EntityStorage.HasComponent(entityID, type))
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
foreach (var type in filterSignature.Excluded)
|
|
|
|
|
{
|
|
|
|
|
if (EntityStorage.HasComponent(entityID, type))
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void CheckFilter(int entityID, FilterSignature filterSignature)
|
|
|
|
|
{
|
|
|
|
|
foreach (var type in filterSignature.Included)
|
|
|
|
|
{
|
|
|
|
|
if (!EntityStorage.HasComponent(entityID, type))
|
|
|
|
|
{
|
2023-01-27 17:48:27 +00:00
|
|
|
|
if (filterSignatureToEntityIDs[filterSignature].Remove(entityID))
|
2023-01-27 00:34:15 +00:00
|
|
|
|
{
|
2023-01-27 17:48:27 +00:00
|
|
|
|
if (removeCallbacks.TryGetValue(filterSignature, out var removeCallback))
|
|
|
|
|
{
|
|
|
|
|
removeCallback(entityID);
|
|
|
|
|
}
|
2023-01-27 00:34:15 +00:00
|
|
|
|
}
|
2023-01-10 00:41:00 +00:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
foreach (var type in filterSignature.Excluded)
|
|
|
|
|
{
|
|
|
|
|
if (EntityStorage.HasComponent(entityID, type))
|
|
|
|
|
{
|
2023-01-27 17:48:27 +00:00
|
|
|
|
if (filterSignatureToEntityIDs[filterSignature].Remove(entityID))
|
2023-01-27 00:34:15 +00:00
|
|
|
|
{
|
2023-01-27 17:48:27 +00:00
|
|
|
|
if (removeCallbacks.TryGetValue(filterSignature, out var removeCallback))
|
|
|
|
|
{
|
|
|
|
|
removeCallback(entityID);
|
|
|
|
|
}
|
2023-01-27 00:34:15 +00:00
|
|
|
|
}
|
2023-01-10 00:41:00 +00:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-13 20:42:22 +00:00
|
|
|
|
if (filterSignatureToEntityIDs[filterSignature].Add(entityID))
|
2023-01-27 00:34:15 +00:00
|
|
|
|
{
|
2023-10-13 20:42:22 +00:00
|
|
|
|
if (addCallbacks.TryGetValue(filterSignature, out var addCallback))
|
|
|
|
|
{
|
|
|
|
|
addCallback(entityID);
|
|
|
|
|
}
|
2023-01-27 00:34:15 +00:00
|
|
|
|
}
|
2023-01-10 00:41:00 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void RemoveEntity(int entityID, int componentTypeIndex)
|
|
|
|
|
{
|
|
|
|
|
if (typeToFilterSignatures.TryGetValue(componentTypeIndex, out var filterSignatures))
|
|
|
|
|
{
|
|
|
|
|
foreach (var filterSignature in filterSignatures)
|
|
|
|
|
{
|
2023-01-27 17:48:27 +00:00
|
|
|
|
if (filterSignatureToEntityIDs[filterSignature].Remove(entityID))
|
2023-01-27 00:34:15 +00:00
|
|
|
|
{
|
2023-01-27 17:48:27 +00:00
|
|
|
|
if (removeCallbacks.TryGetValue(filterSignature, out var removeCallback))
|
|
|
|
|
{
|
|
|
|
|
removeCallback(entityID);
|
|
|
|
|
}
|
2023-01-27 00:34:15 +00:00
|
|
|
|
}
|
2023-01-10 00:41:00 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-01-27 00:34:15 +00:00
|
|
|
|
|
2023-10-13 20:42:22 +00:00
|
|
|
|
// Used by TransferEntity
|
|
|
|
|
public void AddEntity(FilterSignature signature, int entityID)
|
|
|
|
|
{
|
|
|
|
|
filterSignatureToEntityIDs[signature].Add(entityID);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void TransferStorage(Dictionary<int, int> worldToTransferID, FilterStorage other)
|
|
|
|
|
{
|
|
|
|
|
foreach (var (filterSignature, entityIDs) in filterSignatureToEntityIDs)
|
|
|
|
|
{
|
|
|
|
|
foreach (var entity in entityIDs)
|
|
|
|
|
{
|
|
|
|
|
if (worldToTransferID.ContainsKey(entity))
|
|
|
|
|
{
|
|
|
|
|
var otherEntityID = worldToTransferID[entity];
|
|
|
|
|
other.AddEntity(filterSignature, otherEntityID);
|
|
|
|
|
|
|
|
|
|
if (other.addCallbacks.TryGetValue(filterSignature, out var addCallback))
|
|
|
|
|
{
|
|
|
|
|
addCallback(otherEntityID);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-07-21 21:41:53 +00:00
|
|
|
|
// used by World.Clear, ignores callbacks
|
|
|
|
|
public void Clear()
|
|
|
|
|
{
|
|
|
|
|
foreach (var (filterSignature, entityIDs) in filterSignatureToEntityIDs)
|
|
|
|
|
{
|
|
|
|
|
entityIDs.Clear();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-27 00:34:15 +00:00
|
|
|
|
public void RegisterAddCallback(FilterSignature filterSignature, Action<Entity> callback)
|
|
|
|
|
{
|
|
|
|
|
addCallbacks.Add(filterSignature, callback);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void RegisterRemoveCallback(FilterSignature filterSignature, Action<Entity> callback)
|
|
|
|
|
{
|
|
|
|
|
removeCallbacks.Add(filterSignature, callback);
|
|
|
|
|
}
|
2023-01-10 00:41:00 +00:00
|
|
|
|
}
|
2023-04-06 21:54:20 +00:00
|
|
|
|
|
|
|
|
|
public ref struct RandomEntityEnumerator
|
|
|
|
|
{
|
|
|
|
|
public RandomEntityEnumerator GetEnumerator() => this;
|
|
|
|
|
|
|
|
|
|
private FilterStorage FilterStorage;
|
|
|
|
|
private FilterSignature FilterSignature;
|
|
|
|
|
private LinearCongruentialEnumerator LinearCongruentialEnumerator;
|
|
|
|
|
|
|
|
|
|
internal RandomEntityEnumerator(
|
|
|
|
|
FilterStorage filterStorage,
|
|
|
|
|
FilterSignature filterSignature,
|
|
|
|
|
LinearCongruentialEnumerator linearCongruentialEnumerator)
|
|
|
|
|
{
|
|
|
|
|
FilterStorage = filterStorage;
|
|
|
|
|
FilterSignature = filterSignature;
|
|
|
|
|
LinearCongruentialEnumerator = linearCongruentialEnumerator;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public bool MoveNext() => LinearCongruentialEnumerator.MoveNext();
|
|
|
|
|
public Entity Current => FilterStorage.FilterNthEntity(FilterSignature, LinearCongruentialEnumerator.Current);
|
|
|
|
|
}
|
2023-01-10 00:41:00 +00:00
|
|
|
|
}
|