@@ -1 +1 @@ | |||
Subproject commit e95bcb19dc7aa7eed74f0c099a471b3dcd0ef5a3 | |||
Subproject commit 94d4210c6f59ebed95b45ed6bc599d3b255f76ca |
@@ -1,18 +1,25 @@ | |||
# Svelto.ECS Changelog | |||
All notable changes to this project will be documented in this file. Changes are listed in random order of importance. | |||
## [3.5.2] - 07-2024 | |||
* Minor serialization code improvements | |||
* Remove legacy filters once for all | |||
* breaking change: GetOrCreate*Filter, Create*Filter, Get*Filter don't return by ref anymore to fix reported bug | |||
* references are now updated at the end of the submission frame so they are accessible inside callbacks | |||
## [3.5.1] - 01-2024 | |||
* Remove UnityEntitySubmissionScheduler, it was never needed, the user can use the standard EntitySubmissionScheduler and tick it manually | |||
* Dropped the idea to specialise EntitiesSubmissionScheduler. In hindsight it was never necessary. | |||
* Added better support for range exclusive groups, now they are correctly registered in the group hash map | |||
* Removed announg Group compound/tag {type} is not sealed warning | |||
* Removed annoying Group compound/tag {type} is not sealed warning | |||
* Merged Cuyi's workaround to be able to query compound groups in abstract engine. Never had the time to implement a better solution | |||
* It is now possible again to add an entity multiple times inside a filter (it will be overriden) | |||
* Fixed issue https://github.com/sebas77/Svelto.ECS/issues/123 | |||
* Fixed issue https://github.com/sebas77/Svelto.ECS/issues/122 | |||
* Fixed issue https://github.com/sebas77/Svelto.ECS/issues/121 | |||
* AddEngine now adds engines contained in a GroupEngine to the EnginesGroup optionally~~~~ | |||
* AddEngine now adds engines contained in a GroupEngine to the EnginesGroup optionally | |||
## [3.5.0] - 09-2023 | |||
@@ -76,10 +76,7 @@ namespace Svelto.ECS | |||
new FasterDictionary<ComponentID, FasterDictionary<ExclusiveGroupStruct, ITypeSafeDictionary>>(); | |||
_groupedEntityToAdd = new DoubleBufferedEntitiesToAdd(); | |||
_entityStreams = EntitiesStreams.Create(); | |||
#if SVELTO_LEGACY_FILTERS | |||
_groupFilters = | |||
new FasterDictionary<ComponentID, FasterDictionary<ExclusiveGroupStruct, LegacyGroupFilters>>(); | |||
#endif | |||
_entityLocator.InitEntityReferenceMap(); | |||
_entitiesDB = new EntitiesDB(this, _entityLocator); | |||
@@ -296,14 +293,6 @@ namespace Svelto.ECS | |||
foreach (var entityList in groups.value) | |||
entityList.value.Dispose(); | |||
#if SVELTO_LEGACY_FILTERS | |||
foreach (var type in _groupFilters) | |||
foreach (var group in type.value) | |||
group.value.Dispose(); | |||
_groupFilters.Clear(); | |||
#endif | |||
DisposeFilters(); | |||
#if UNITY_NATIVE | |||
@@ -205,11 +205,6 @@ namespace Svelto.ECS | |||
// <EntityComponentType <groupID <entityID, EntityComponent>>> | |||
internal readonly FasterDictionary<ComponentID, FasterDictionary<ExclusiveGroupStruct, ITypeSafeDictionary>> | |||
_groupsPerEntity; | |||
#if SVELTO_LEGACY_FILTERS | |||
//The filters stored for each component and group | |||
internal readonly FasterDictionary<ComponentID, FasterDictionary<ExclusiveGroupStruct, LegacyGroupFilters>> | |||
_groupFilters; | |||
#endif | |||
readonly EntitiesDB _entitiesDB; | |||
@@ -51,16 +51,7 @@ namespace Svelto.ECS | |||
{ | |||
using (var sampler = new PlatformProfiler("remove Entities")) | |||
{ | |||
using (sampler.Sample("Remove Entity References")) | |||
{ | |||
var count = entitiesRemoved.count; | |||
for (int i = 0; i < count; i++) | |||
{ | |||
enginesRoot._entityLocator.RemoveEntityReference(entitiesRemoved[i]); | |||
} | |||
} | |||
using (sampler.Sample("Execute remove callbacks and remove entities")) | |||
using (sampler.Sample("Execute obsolete remove callbacks")) | |||
{ | |||
foreach (var entitiesToRemove in removeOperations) | |||
{ | |||
@@ -157,6 +148,16 @@ namespace Svelto.ECS | |||
} | |||
} | |||
} | |||
//doing this at the end to be able to use EGID.ToEntityReference inside callbacks | |||
using (sampler.Sample("Remove Entity References")) | |||
{ | |||
var count = entitiesRemoved.count; | |||
for (int i = 0; i < count; i++) | |||
{ | |||
enginesRoot._entityLocator.RemoveEntityReference(entitiesRemoved[i]); | |||
} | |||
} | |||
} | |||
} | |||
@@ -1,4 +1,5 @@ | |||
using System.Runtime.CompilerServices; | |||
using System.Collections.Concurrent; | |||
using System.Runtime.CompilerServices; | |||
using Svelto.Common; | |||
using Svelto.DataStructures; | |||
using Svelto.DataStructures.Native; | |||
@@ -6,7 +7,7 @@ using Svelto.ECS.Reference; | |||
namespace Svelto.ECS | |||
{ | |||
// The EntityLocatorMap provides a bidirectional map to help locate entities without using an EGID which might | |||
// The EntityReferenceMap provides a bidirectional map to help locate entities without using an EGID which might | |||
// change at runtime. The Entity Locator map uses a reusable unique identifier struct called EntityLocator to | |||
// find the last known EGID from last entity submission. | |||
public partial class EnginesRoot |
@@ -19,6 +19,7 @@ namespace Svelto.ECS.Schedulers | |||
public void Dispose() { } | |||
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] | |||
public void SubmitEntities() | |||
{ | |||
try | |||
@@ -77,8 +77,7 @@ namespace Svelto.ECS | |||
} | |||
//efficient way to collect the fields of every EntityComponentType | |||
var setters = FasterList<KeyValuePair<Type, FastInvokeActionCast<T>>>.NoVirt.ToArrayFast( | |||
entityComponentBlazingFastReflection, out var count); | |||
var setters = entityComponentBlazingFastReflection.ToArrayFast(out var count); | |||
for (var i = 0; i < count; i++) | |||
{ | |||
@@ -61,19 +61,19 @@ namespace Svelto.ECS | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public ref EntityFilterCollection GetOrCreatePersistentFilter<T>(int filterID, FilterContextID filterContextId) | |||
public EntityFilterCollection GetOrCreatePersistentFilter<T>(int filterID, FilterContextID filterContextId) | |||
where T : struct, _IInternalEntityComponent | |||
{ | |||
return ref GetOrCreatePersistentFilter<T>(new CombinedFilterID(filterID, filterContextId)); | |||
return GetOrCreatePersistentFilter<T>(new CombinedFilterID(filterID, filterContextId)); | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public ref EntityFilterCollection GetOrCreatePersistentFilter<T>(CombinedFilterID filterID) where T : struct, _IInternalEntityComponent | |||
public EntityFilterCollection GetOrCreatePersistentFilter<T>(CombinedFilterID filterID) where T : struct, _IInternalEntityComponent | |||
{ | |||
var componentAndFilterID = Internal_FilterHelper.CombineFilterIDWithComponentID<T>(filterID); | |||
if (_persistentEntityFilters.TryFindIndex(componentAndFilterID, out var index) == true) | |||
return ref _persistentEntityFilters.GetDirectValueByRef(index); | |||
return _persistentEntityFilters.GetDirectValueByRef(index); | |||
_persistentEntityFilters.Add(componentAndFilterID, new EntityFilterCollection(filterID)); | |||
@@ -92,7 +92,7 @@ namespace Svelto.ECS | |||
array.Add(lastIndex); | |||
} | |||
return ref _persistentEntityFilters.GetDirectValueByRef((uint)lastIndex); | |||
return _persistentEntityFilters.GetDirectValueByRef((uint)lastIndex); | |||
} | |||
/// <summary> | |||
@@ -107,7 +107,7 @@ namespace Svelto.ECS | |||
[Unity.Burst.BurstDiscard] //not burst compatible because of ComponentTypeID<T>.id and GetOrAdd callback; | |||
#endif | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public ref EntityFilterCollection CreatePersistentFilter<T>(CombinedFilterID filterID) | |||
public EntityFilterCollection CreatePersistentFilter<T>(CombinedFilterID filterID) | |||
where T : struct, _IInternalEntityComponent | |||
{ | |||
var componentAndFilterID = Internal_FilterHelper.CombineFilterIDWithComponentID<T>(filterID); | |||
@@ -123,7 +123,7 @@ namespace Svelto.ECS | |||
_indicesOfPersistentFiltersUsedByThisComponent.GetOrAdd(ComponentTypeID<T>.id, _builder).Add(lastIndex); | |||
return ref _persistentEntityFilters.GetDirectValueByRef((uint)lastIndex); | |||
return _persistentEntityFilters.GetDirectValueByRef((uint)lastIndex); | |||
} | |||
static NativeDynamicArrayCast<int> Builder() | |||
@@ -132,20 +132,20 @@ namespace Svelto.ECS | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public ref EntityFilterCollection GetPersistentFilter<T>(int filterID, FilterContextID filterContextId) | |||
public EntityFilterCollection GetPersistentFilter<T>(int filterID, FilterContextID filterContextId) | |||
where T : struct, _IInternalEntityComponent | |||
{ | |||
return ref GetPersistentFilter<T>(new CombinedFilterID(filterID, filterContextId)); | |||
return GetPersistentFilter<T>(new CombinedFilterID(filterID, filterContextId)); | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public ref EntityFilterCollection GetPersistentFilter<T>(CombinedFilterID filterID) | |||
public EntityFilterCollection GetPersistentFilter<T>(CombinedFilterID filterID) | |||
where T : struct, _IInternalEntityComponent | |||
{ | |||
var componentAndFilterID = Internal_FilterHelper.CombineFilterIDWithComponentID<T>(filterID); | |||
if (_persistentEntityFilters.TryFindIndex(componentAndFilterID, out var index) == true) | |||
return ref _persistentEntityFilters.GetDirectValueByRef(index); | |||
return _persistentEntityFilters.GetDirectValueByRef(index); | |||
throw new ECSException("filter not found"); | |||
} | |||
@@ -220,26 +220,26 @@ namespace Svelto.ECS | |||
/// <typeparam name="T"></typeparam> | |||
/// <returns></returns> | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public ref EntityFilterCollection GetOrCreateTransientFilter<T>(CombinedFilterID combinedFilterID, bool trackFilter = false) | |||
public EntityFilterCollection GetOrCreateTransientFilter<T>(CombinedFilterID combinedFilterID, bool trackFilter = false) | |||
where T : struct, _IInternalEntityComponent | |||
{ | |||
var componentAndFilterID = Internal_FilterHelper.CombineFilterIDWithComponentID<T>(combinedFilterID); | |||
if (_transientEntityFilters.TryFindIndex(componentAndFilterID, out var index)) | |||
return ref _transientEntityFilters.GetDirectValueByRef(index); | |||
return _transientEntityFilters.GetDirectValueByRef(index); | |||
return ref InternalCreateTransientFilter<T>(combinedFilterID, componentAndFilterID, trackFilter); | |||
return InternalCreateTransientFilter<T>(combinedFilterID, componentAndFilterID, trackFilter); | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public ref EntityFilterCollection GetOrCreateTransientFilter<T>(int filterID, FilterContextID filterContextId) | |||
public EntityFilterCollection GetOrCreateTransientFilter<T>(int filterID, FilterContextID filterContextId) | |||
where T : struct, _IInternalEntityComponent | |||
{ | |||
return ref GetOrCreateTransientFilter<T>(new CombinedFilterID(filterID, filterContextId)); | |||
return GetOrCreateTransientFilter<T>(new CombinedFilterID(filterID, filterContextId)); | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public ref EntityFilterCollection CreateTransientFilter<T>(CombinedFilterID combinedFilterID, bool trackFilter = false) | |||
public EntityFilterCollection CreateTransientFilter<T>(CombinedFilterID combinedFilterID, bool trackFilter = false) | |||
where T : struct, _IInternalEntityComponent | |||
{ | |||
CombinedFilterComponentID componentAndFilterID = Internal_FilterHelper.CombineFilterIDWithComponentID<T>(combinedFilterID); | |||
@@ -247,7 +247,7 @@ namespace Svelto.ECS | |||
if (_transientEntityFilters.TryFindIndex(componentAndFilterID, out _)) | |||
throw new ECSException($"filter already exists {TypeCache<T>.name}"); | |||
#endif | |||
return ref InternalCreateTransientFilter<T>(combinedFilterID, componentAndFilterID, trackFilter); | |||
return InternalCreateTransientFilter<T>(combinedFilterID, componentAndFilterID, trackFilter); | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
@@ -267,14 +267,14 @@ namespace Svelto.ECS | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public ref EntityFilterCollection GetTransientFilter<T>(CombinedFilterID filterID) | |||
public EntityFilterCollection GetTransientFilter<T>(CombinedFilterID filterID) | |||
where T : struct, _IInternalEntityComponent | |||
{ | |||
var componentAndFilterID = Internal_FilterHelper.CombineFilterIDWithComponentID<T>(filterID); | |||
if (_transientEntityFilters.TryFindIndex(componentAndFilterID, out var index)) | |||
{ | |||
return ref _transientEntityFilters.GetDirectValueByRef(index); | |||
return _transientEntityFilters.GetDirectValueByRef(index); | |||
} | |||
throw new ECSException($"no filters associated with the type {TypeCache<T>.name}"); | |||
@@ -326,7 +326,7 @@ namespace Svelto.ECS | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
ref EntityFilterCollection InternalCreateTransientFilter<T>(CombinedFilterID filterID, CombinedFilterComponentID componentAndFilterID, | |||
EntityFilterCollection InternalCreateTransientFilter<T>(CombinedFilterID filterID, CombinedFilterComponentID componentAndFilterID, | |||
bool trackFilter) | |||
where T : struct, _IInternalEntityComponent | |||
{ | |||
@@ -336,12 +336,11 @@ namespace Svelto.ECS | |||
if (trackFilter) | |||
{ | |||
var typeRef = ComponentTypeID<T>.id; | |||
var lastIndex = _transientEntityFilters.count - 1; | |||
_indicesOfTransientFiltersUsedByThisComponent.GetOrAdd(ComponentTypeID<T>.id, _builder).Add(lastIndex); | |||
} | |||
return ref _transientEntityFilters.GetDirectValueByRef((uint)(_transientEntityFilters.count - 1)); | |||
return _transientEntityFilters.GetDirectValueByRef((uint)(_transientEntityFilters.count - 1)); | |||
} | |||
public struct EntityFilterCollectionsEnumerator | |||
@@ -371,10 +370,10 @@ namespace Svelto.ECS | |||
return false; | |||
} | |||
public ref EntityFilterCollection Current | |||
public EntityFilterCollection Current | |||
{ | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
get => ref _sharedSveltoDictionaryNative.GetDirectValueByRef((uint)_currentIndex - 1); | |||
get => _sharedSveltoDictionaryNative.GetDirectValueByRef((uint)_currentIndex - 1); | |||
} | |||
readonly NativeDynamicArrayCast<int> _getDirectValueByRef; | |||
@@ -1,213 +0,0 @@ | |||
#if SVELTO_LEGACY_FILTERS | |||
using DBC.ECS; | |||
using Svelto.DataStructures; | |||
using Svelto.DataStructures.Native; | |||
using Svelto.ECS.Internal; | |||
namespace Svelto.ECS | |||
{ | |||
/// <summary> | |||
/// This feature must be eventually tied to the new ExclusiveGroup that won't allow the use of | |||
/// custom EntitiesID | |||
/// The filters could be updated when entities buffer changes during the submission, while now this process | |||
/// is completely manual. | |||
/// Making this working properly is not in my priorities right now, as I will need to add the new group type | |||
/// AND optimize the submission process to be able to add more overhead | |||
/// </summary> | |||
public partial class EntitiesDB | |||
{ | |||
FasterDictionary<ComponentID, FasterDictionary<ExclusiveGroupStruct, LegacyGroupFilters>> _filters => | |||
_enginesRoot._groupFilters; | |||
public LegacyFilters GetLegacyFilters() | |||
{ | |||
return new LegacyFilters(_filters); | |||
} | |||
public readonly struct LegacyFilters | |||
{ | |||
public LegacyFilters( | |||
FasterDictionary<ComponentID, FasterDictionary<ExclusiveGroupStruct, LegacyGroupFilters>> | |||
filtersLegacy) | |||
{ | |||
_filtersLegacy = filtersLegacy; | |||
} | |||
public ref LegacyFilterGroup CreateOrGetFilterForGroup<T>(int filterID, ExclusiveGroupStruct groupID) | |||
where T : struct, _IInternalEntityComponent | |||
{ | |||
var refWrapper = ComponentTypeID<T>.id; | |||
return ref CreateOrGetFilterForGroup(filterID, groupID, refWrapper); | |||
} | |||
public bool HasFiltersForGroup<T>(ExclusiveGroupStruct groupID) where T : struct, _IInternalEntityComponent | |||
{ | |||
if (_filtersLegacy.TryGetValue(ComponentTypeID<T>.id, out var fasterDictionary) == false) | |||
return false; | |||
return fasterDictionary.ContainsKey(groupID); | |||
} | |||
public bool HasFilterForGroup<T>(int filterID, ExclusiveGroupStruct groupID) | |||
where T : struct, _IInternalEntityComponent | |||
{ | |||
if (_filtersLegacy.TryGetValue(ComponentTypeID<T>.id, out var fasterDictionary) == false) | |||
return false; | |||
if (fasterDictionary.TryGetValue(groupID, out var result)) | |||
return result.HasFilter(filterID); | |||
return false; | |||
} | |||
public ref LegacyGroupFilters CreateOrGetFiltersForGroup<T>(ExclusiveGroupStruct groupID) | |||
where T : struct, _IInternalEntityComponent | |||
{ | |||
var fasterDictionary = _filtersLegacy.GetOrAdd(ComponentTypeID<T>.id, | |||
() => new FasterDictionary<ExclusiveGroupStruct, LegacyGroupFilters>()); | |||
return ref fasterDictionary.GetOrAdd(groupID, | |||
() => new LegacyGroupFilters(new SharedSveltoDictionaryNative<int, LegacyFilterGroup>(0), groupID)); | |||
} | |||
public ref LegacyGroupFilters GetFiltersForGroup<T>(ExclusiveGroupStruct groupID) | |||
where T : struct, _IInternalEntityComponent | |||
{ | |||
#if DEBUG && !PROFILE_SVELTO | |||
if (_filtersLegacy.ContainsKey(ComponentTypeID<T>.id) == false) | |||
throw new ECSException($"trying to fetch not existing filters, type {typeof(T)}"); | |||
if (_filtersLegacy[ComponentTypeID<T>.id].ContainsKey(groupID) == false) | |||
throw new ECSException( | |||
$"trying to fetch not existing filters, type {typeof(T)} group {groupID.ToName()}"); | |||
#endif | |||
return ref _filtersLegacy[ComponentTypeID<T>.id].GetValueByRef(groupID); | |||
} | |||
public ref LegacyFilterGroup GetFilterForGroup<T>(int filterId, ExclusiveGroupStruct groupID) | |||
where T : struct, _IInternalEntityComponent | |||
{ | |||
#if DEBUG && !PROFILE_SVELTO | |||
if (_filtersLegacy.ContainsKey(ComponentTypeID<T>.id) == false) | |||
throw new ECSException($"trying to fetch not existing filters, type {typeof(T)}"); | |||
if (_filtersLegacy[ComponentTypeID<T>.id].ContainsKey(groupID) == false) | |||
throw new ECSException( | |||
$"trying to fetch not existing filters, type {typeof(T)} group {groupID.ToName()}"); | |||
#endif | |||
return ref _filtersLegacy[ComponentTypeID<T>.id][groupID].GetFilter(filterId); | |||
} | |||
public bool TryGetFilterForGroup<T>(int filterId, ExclusiveGroupStruct groupID, | |||
out LegacyFilterGroup groupLegacyFilter) where T : struct, _IInternalEntityComponent | |||
{ | |||
groupLegacyFilter = default; | |||
if (_filtersLegacy.TryGetValue(ComponentTypeID<T>.id, out var fasterDictionary) == false) | |||
return false; | |||
if (fasterDictionary.TryGetValue(groupID, out var groupFilters) == false) | |||
return false; | |||
if (groupFilters.TryGetFilter(filterId, out groupLegacyFilter) == false) | |||
return false; | |||
return true; | |||
} | |||
public bool TryGetFiltersForGroup<T>(ExclusiveGroupStruct groupID, | |||
out LegacyGroupFilters legacyGroupFilters) where T : struct, _IInternalEntityComponent | |||
{ | |||
legacyGroupFilters = default; | |||
if (_filtersLegacy.TryGetValue(ComponentTypeID<T>.id, out var fasterDictionary) == false) | |||
return false; | |||
return fasterDictionary.TryGetValue(groupID, out legacyGroupFilters); | |||
} | |||
public void ClearFilter<T>(int filterID, ExclusiveGroupStruct exclusiveGroupStruct) | |||
where T : struct, _IInternalEntityComponent | |||
{ | |||
if (_filtersLegacy.TryGetValue(ComponentTypeID<T>.id, out var fasterDictionary)) | |||
{ | |||
Check.Require(fasterDictionary.ContainsKey(exclusiveGroupStruct), | |||
$"trying to clear filter not present in group {exclusiveGroupStruct}"); | |||
fasterDictionary[exclusiveGroupStruct].ClearFilter(filterID); | |||
} | |||
} | |||
public void ClearFilters<T>(int filterID) where T : struct, _IInternalEntityComponent | |||
{ | |||
if (_filtersLegacy.TryGetValue(ComponentTypeID<T>.id, out var fasterDictionary)) | |||
foreach (var filtersPerGroup in fasterDictionary) | |||
filtersPerGroup.value.ClearFilter(filterID); | |||
} | |||
public void DisposeFilters<T>(ExclusiveGroupStruct exclusiveGroupStruct) where T : struct, _IInternalEntityComponent | |||
{ | |||
if (_filtersLegacy.TryGetValue(ComponentTypeID<T>.id, out var fasterDictionary)) | |||
{ | |||
fasterDictionary[exclusiveGroupStruct].DisposeFilters(); | |||
fasterDictionary.Remove(exclusiveGroupStruct); | |||
} | |||
} | |||
public void DisposeFilters<T>() where T : struct, _IInternalEntityComponent | |||
{ | |||
if (_filtersLegacy.TryGetValue(ComponentTypeID<T>.id, out var fasterDictionary)) | |||
foreach (var filtersPerGroup in fasterDictionary) | |||
filtersPerGroup.value.DisposeFilters(); | |||
_filtersLegacy.Remove(ComponentTypeID<T>.id); | |||
} | |||
public void DisposeFilterForGroup<T>(int resetFilterID, ExclusiveGroupStruct group) where T : struct, _IInternalEntityComponent | |||
{ | |||
if (_filtersLegacy.TryGetValue(ComponentTypeID<T>.id, out var fasterDictionary)) | |||
fasterDictionary[@group].DisposeFilter(resetFilterID); | |||
} | |||
public bool TryRemoveEntityFromFilter<T>(int filtersID, EGID egid) where T : struct, _IInternalEntityComponent | |||
{ | |||
if (TryGetFilterForGroup<T>(filtersID, egid.groupID, out var filter)) | |||
return filter.TryRemove(egid.entityID); | |||
return false; | |||
} | |||
public void RemoveEntityFromFilter<T>(int filtersID, EGID egid) where T : struct, _IInternalEntityComponent | |||
{ | |||
ref var filter = ref GetFilterForGroup<T>(filtersID, egid.groupID); | |||
filter.Remove(egid.entityID); | |||
} | |||
public bool AddEntityToFilter<N>(int filtersID, EGID egid, N mapper) where N : IEGIDMapper | |||
{ | |||
ref var filter = ref CreateOrGetFilterForGroup(filtersID, egid.groupID, ComponentTypeMap.FetchID(mapper.entityType)); | |||
return filter.Add(egid.entityID, mapper); | |||
} | |||
internal ref LegacyFilterGroup CreateOrGetFilterForGroup(int filterID, ExclusiveGroupStruct groupID, | |||
ComponentID refWrapper) | |||
{ | |||
var fasterDictionary = _filtersLegacy.GetOrAdd(refWrapper, | |||
() => new FasterDictionary<ExclusiveGroupStruct, LegacyGroupFilters>()); | |||
var filters = fasterDictionary.GetOrAdd(groupID, | |||
(ref ExclusiveGroupStruct gid) => | |||
new LegacyGroupFilters(new SharedSveltoDictionaryNative<int, LegacyFilterGroup>(0), gid), | |||
ref groupID); | |||
return ref filters.CreateOrGetFilter(filterID); | |||
} | |||
readonly FasterDictionary<ComponentID, FasterDictionary<ExclusiveGroupStruct, LegacyGroupFilters>> | |||
_filtersLegacy; | |||
} | |||
} | |||
} | |||
#endif |
@@ -1,214 +0,0 @@ | |||
#if SVELTO_LEGACY_FILTERS | |||
using System.Runtime.CompilerServices; | |||
using Svelto.Common; | |||
using Svelto.DataStructures.Native; | |||
using Svelto.DataStructures; | |||
namespace Svelto.ECS | |||
{ | |||
/// <summary> | |||
/// In order to complete this feature, I need to be able to detect if the entity pointed by the filter | |||
/// is still present. | |||
/// This feature should work only with groups where entityID cannot be chosen by the user, so that | |||
/// a real sparse set can be used like explained at: https://skypjack.github.io/2020-08-02-ecs-baf-part-9/ | |||
/// For a sparse set to work, the index in the sparse list must coincide with the ID of the entity | |||
/// so that from the dense list (that holds unordered entity index), I can get to the sparse list index | |||
/// sparse[0] = position in the dense list of the entity 0 | |||
/// dense[index] = entity ID but also index in the sparse list of the same entity ID | |||
/// </summary> | |||
public struct LegacyFilterGroup | |||
{ | |||
internal LegacyFilterGroup(ExclusiveGroupStruct exclusiveGroupStruct, int ID) | |||
{ | |||
_denseListOfIndicesToEntityComponentArray = | |||
new NativeDynamicArrayCast<uint>(NativeDynamicArray.Alloc<uint>(Allocator.Persistent)); | |||
//from the index, find the entityID | |||
_reverseEIDs = new NativeDynamicArrayCast<uint>(NativeDynamicArray.Alloc<uint>(Allocator.Persistent)); | |||
//from the entityID, find the index | |||
_indexOfEntityInDenseList = new SharedSveltoDictionaryNative<uint, uint>(0); | |||
_exclusiveGroupStruct = exclusiveGroupStruct; | |||
_ID = ID; | |||
} | |||
/// <summary> | |||
/// Todo: how to detect if the indices are still pointing to valid entities? | |||
/// </summary> | |||
public LegacyFilteredIndices filteredIndices => new LegacyFilteredIndices(_denseListOfIndicesToEntityComponentArray); | |||
public bool Add<N>(uint entityID, N mapper) where N:IEGIDMapper | |||
{ | |||
#if DEBUG && !PROFILE_SVELTO | |||
if (mapper.Exists(entityID) == false) | |||
throw new ECSException( | |||
$"trying adding an entity {entityID} to filter {mapper.entityType} - {_ID} with group {mapper.groupID}, but entity is not found! "); | |||
#endif | |||
return InternalAdd(entityID, mapper.GetIndex(entityID)); | |||
} | |||
public void Remove(uint entityID) | |||
{ | |||
#if DEBUG && !PROFILE_SVELTO | |||
if (_denseListOfIndicesToEntityComponentArray.isValid == false) | |||
throw new ECSException($"invalid Filter"); | |||
if (_indexOfEntityInDenseList.ContainsKey(entityID) == false) | |||
throw new ECSException( | |||
$"trying to remove a not existing entity {new EGID(entityID, _exclusiveGroupStruct)} from filter"); | |||
#endif | |||
InternalRemove(entityID); | |||
} | |||
public bool Exists(uint entityID) | |||
{ | |||
#if DEBUG && !PROFILE_SVELTO | |||
if (_denseListOfIndicesToEntityComponentArray.isValid == false) | |||
throw new ECSException($"invalid Filter"); | |||
#endif | |||
return _indexOfEntityInDenseList.ContainsKey(entityID); | |||
} | |||
public bool TryRemove(uint entityID) | |||
{ | |||
#if DEBUG && !PROFILE_SVELTO | |||
if (_denseListOfIndicesToEntityComponentArray.isValid == false) | |||
throw new ECSException($"invalid Filter"); | |||
#endif | |||
if (_indexOfEntityInDenseList.ContainsKey(entityID) == false) | |||
return false; | |||
InternalRemove(entityID); | |||
return true; | |||
} | |||
/// <summary> | |||
/// Filters were initially designed to be used for tagging operations within submissions of entities. | |||
/// They were designed as a fast tagging mechanism to be used within the submission frame. However I then | |||
/// extended it, but the extension includes a drawback: | |||
/// If filters are not in sync with the operations of remove and swap, filters may end up pointing to | |||
/// invalid indices. I need to put in place a way to be able to recognised an invalid filter. | |||
/// This is currently a disadvantage of the filters. The filters are not updated by the framework | |||
/// but they must be updated by the user. | |||
/// When to use this method: Add and Removed should be used to add and remove entities in the filters. This is | |||
/// valid as long as no structural changes happen in the group of entities involved. | |||
/// IF structural changes happen, the indices stored in the filters won't be valid anymore as they will possibly | |||
/// point to entities that were not the original ones. On structural changes | |||
/// (specifically entities swapped or removed) | |||
/// the filters must then be rebuilt. It would be too slow to add this in the standard flow of Svelto in | |||
/// the current state, so calling this method is a user responsibility. | |||
/// </summary> | |||
public void RebuildIndicesOnStructuralChange<N>(N mapper) where N:IEGIDMapper | |||
{ | |||
#if DEBUG && !PROFILE_SVELTO | |||
if (_denseListOfIndicesToEntityComponentArray.isValid == false) | |||
throw new ECSException($"invalid Filter"); | |||
#endif | |||
_denseListOfIndicesToEntityComponentArray.Clear(); | |||
_reverseEIDs.Clear(); | |||
foreach (var value in _indexOfEntityInDenseList) | |||
if (mapper.FindIndex(value.key, out var indexOfEntityInBufferComponent) == true) | |||
{ | |||
_denseListOfIndicesToEntityComponentArray.Add(indexOfEntityInBufferComponent); | |||
var lastIndex = (uint) (_denseListOfIndicesToEntityComponentArray.Count() - 1); | |||
_reverseEIDs.AddAt(lastIndex) = value.key; | |||
} | |||
_indexOfEntityInDenseList.Clear(); | |||
for (uint i = 0; i < _reverseEIDs.Count(); i++) | |||
_indexOfEntityInDenseList[_reverseEIDs[i]] = i; | |||
} | |||
public void Clear() | |||
{ | |||
#if DEBUG && !PROFILE_SVELTO | |||
if (_denseListOfIndicesToEntityComponentArray.isValid == false) | |||
throw new ECSException($"invalid Filter"); | |||
#endif | |||
_indexOfEntityInDenseList.Clear(); | |||
_reverseEIDs.Clear(); | |||
_denseListOfIndicesToEntityComponentArray.Clear(); | |||
} | |||
internal void Dispose() | |||
{ | |||
#if DEBUG && !PROFILE_SVELTO | |||
if (_denseListOfIndicesToEntityComponentArray.isValid == false) | |||
throw new ECSException($"invalid Filter"); | |||
#endif | |||
_denseListOfIndicesToEntityComponentArray.Dispose(); | |||
_indexOfEntityInDenseList.Dispose(); | |||
_reverseEIDs.Dispose(); | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
internal bool InternalAdd(uint entityID, uint indexOfEntityInBufferComponent) | |||
{ | |||
#if DEBUG && !PROFILE_SVELTO | |||
if (_denseListOfIndicesToEntityComponentArray.isValid == false) | |||
throw new ECSException($"using an invalid filter"); | |||
#endif | |||
if (_indexOfEntityInDenseList.ContainsKey(entityID) == true) | |||
return false; | |||
//add the index in the list of filtered indices | |||
_denseListOfIndicesToEntityComponentArray.Add(indexOfEntityInBufferComponent); | |||
//inverse map: need to get the entityID from the index. This wouldn't be needed with a real sparseset | |||
var lastIndex = (uint) (_denseListOfIndicesToEntityComponentArray.Count() - 1); | |||
_reverseEIDs.AddAt(lastIndex) = entityID; | |||
//remember the entities indices. This is needed to remove entities from the filter | |||
_indexOfEntityInDenseList.Add(entityID, lastIndex); | |||
return true; | |||
} | |||
void InternalRemove(uint entityID) | |||
{ | |||
var count = (uint) _denseListOfIndicesToEntityComponentArray.Count(); | |||
if (count > 0) | |||
{ | |||
if (count > 1) | |||
{ | |||
//get the index in the filter array of the entity to delete | |||
var indexInDenseListFromEGID = _indexOfEntityInDenseList[entityID]; | |||
//get the entityID of the last entity in the filter array | |||
uint entityIDToMove = _reverseEIDs[count - 1]; | |||
//the last index of the last entity is updated to the slot of the deleted entity | |||
if (entityIDToMove != entityID) | |||
{ | |||
_indexOfEntityInDenseList[entityIDToMove] = indexInDenseListFromEGID; | |||
//the reverseEGID is updated accordingly | |||
_reverseEIDs[indexInDenseListFromEGID] = entityIDToMove; | |||
} | |||
// | |||
_reverseEIDs.UnorderedRemoveAt(count - 1); | |||
//finally remove the deleted entity from the filters array | |||
_denseListOfIndicesToEntityComponentArray.UnorderedRemoveAt(indexInDenseListFromEGID); | |||
//remove the entity to delete from the tracked Entity | |||
_indexOfEntityInDenseList.Remove(entityID); | |||
} | |||
else | |||
{ | |||
_indexOfEntityInDenseList.Clear(); | |||
_reverseEIDs.Clear(); | |||
_denseListOfIndicesToEntityComponentArray.Clear(); | |||
} | |||
} | |||
} | |||
NativeDynamicArrayCast<uint> _denseListOfIndicesToEntityComponentArray; | |||
NativeDynamicArrayCast<uint> _reverseEIDs; //forced to use this because it's not a real sparse set | |||
SharedSveltoDictionaryNative<uint, uint> _indexOfEntityInDenseList; | |||
internal readonly ExclusiveGroupStruct _exclusiveGroupStruct; | |||
internal readonly int _ID; | |||
} | |||
} | |||
#endif |
@@ -1,40 +0,0 @@ | |||
#if SVELTO_LEGACY_FILTERS | |||
using System.Runtime.CompilerServices; | |||
using Svelto.DataStructures; | |||
namespace Svelto.ECS | |||
{ | |||
public readonly struct LegacyFilteredIndices | |||
{ | |||
public LegacyFilteredIndices(NativeDynamicArrayCast<uint> denseListOfIndicesToEntityComponentArray) | |||
{ | |||
_denseListOfIndicesToEntityComponentArray = denseListOfIndicesToEntityComponentArray; | |||
_count = _denseListOfIndicesToEntityComponentArray.count; | |||
} | |||
public int count | |||
{ | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
get { return _count; } | |||
} | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
public uint Get(uint index) => _denseListOfIndicesToEntityComponentArray[index]; | |||
public uint this[uint index] | |||
{ | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
get => _denseListOfIndicesToEntityComponentArray[index]; | |||
} | |||
public uint this[int index] | |||
{ | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
get => _denseListOfIndicesToEntityComponentArray[index]; | |||
} | |||
readonly NativeDynamicArrayCast<uint> _denseListOfIndicesToEntityComponentArray; | |||
readonly int _count; | |||
} | |||
} | |||
#endif |
@@ -1,106 +0,0 @@ | |||
#if SVELTO_LEGACY_FILTERS | |||
using Svelto.DataStructures; | |||
using Svelto.DataStructures.Native; | |||
namespace Svelto.ECS | |||
{ | |||
public struct LegacyGroupFilters | |||
{ | |||
internal LegacyGroupFilters(SharedSveltoDictionaryNative<int, LegacyFilterGroup> legacyFilters, ExclusiveGroupStruct group) | |||
{ | |||
this._legacyFilters = legacyFilters; | |||
_group = @group; | |||
} | |||
public ref LegacyFilterGroup GetFilter(int filterIndex) | |||
{ | |||
#if DEBUG && !PROFILE_SVELTO | |||
if (_legacyFilters.isValid == false) | |||
throw new ECSException($"trying to fetch not existing filters {filterIndex} group {_group.ToName()}"); | |||
if (_legacyFilters.ContainsKey(filterIndex) == false) | |||
throw new ECSException($"trying to fetch not existing filters {filterIndex} group {_group.ToName()}"); | |||
#endif | |||
return ref _legacyFilters.GetValueByRef(filterIndex); | |||
} | |||
public bool HasFilter(int filterIndex) { return _legacyFilters.ContainsKey(filterIndex); } | |||
public void ClearFilter(int filterIndex) | |||
{ | |||
if (_legacyFilters.TryFindIndex(filterIndex, out var index)) | |||
_legacyFilters.GetValues(out _)[index].Clear(); | |||
} | |||
public void ClearFilters() | |||
{ | |||
foreach (var filter in _legacyFilters) | |||
filter.value.Clear(); | |||
} | |||
public bool TryGetFilter(int filterIndex, out LegacyFilterGroup legacyFilter) | |||
{ | |||
return _legacyFilters.TryGetValue(filterIndex, out legacyFilter); | |||
} | |||
public SveltoDictionaryKeyValueEnumerator<int, LegacyFilterGroup, NativeStrategy<SveltoDictionaryNode<int>>, NativeStrategy<LegacyFilterGroup> | |||
, NativeStrategy<int>> GetEnumerator() | |||
{ | |||
return _legacyFilters.GetEnumerator(); | |||
} | |||
//Note the following methods are internal because I was pondering the idea to be able to return | |||
//the list of LegacyGroupFilters linked to a specific filter ID. However this would mean to be able to | |||
//maintain a revers map which at this moment seems too much and also would need the following | |||
//method to be for ever internal (at this point in time I am not sure it's a good idea) | |||
internal void DisposeFilter(int filterIndex) | |||
{ | |||
if (_legacyFilters.TryFindIndex(filterIndex, out var index)) | |||
{ | |||
ref var filterGroup = ref _legacyFilters.GetValues(out _)[index]; | |||
filterGroup.Dispose(); | |||
_legacyFilters.Remove(filterIndex); | |||
} | |||
} | |||
internal void DisposeFilters() | |||
{ | |||
//must release the native buffers! | |||
foreach (var filter in _legacyFilters) | |||
filter.value.Dispose(); | |||
_legacyFilters.Clear(); | |||
} | |||
internal ref LegacyFilterGroup CreateOrGetFilter(int filterID) | |||
{ | |||
if (_legacyFilters.TryFindIndex(filterID, out var index) == false) | |||
{ | |||
var orGetFilterForGroup = new LegacyFilterGroup(_group, filterID); | |||
_legacyFilters[filterID] = orGetFilterForGroup; | |||
return ref _legacyFilters.GetValueByRef(filterID); | |||
} | |||
return ref _legacyFilters.GetValues(out _)[index]; | |||
} | |||
internal void Dispose() | |||
{ | |||
foreach (var filter in _legacyFilters) | |||
{ | |||
filter.value.Dispose(); | |||
} | |||
_legacyFilters.Dispose(); | |||
} | |||
readonly ExclusiveGroupStruct _group; | |||
//filterID, filter | |||
SharedSveltoDictionaryNative<int, LegacyFilterGroup> _legacyFilters; | |||
} | |||
} | |||
#endif |
@@ -141,8 +141,8 @@ namespace Svelto.ECS | |||
var nameHash = DesignatedHash.Hash(Encoding.ASCII.GetBytes(name)); | |||
if (_groupsByHash.ContainsKey(nameHash)) | |||
throw new ECSException($"Group hash collision with {name} and {_groupsByHash[nameHash]}"); | |||
if (_groupsByHash.TryGetValue(nameHash, out var value)) | |||
throw new ECSException($"Group hash collision with {name} and {value}"); | |||
Console.LogDebug($"Registering group {name} with ID {exclusiveGroupStruct.id} to {nameHash}"); | |||
@@ -433,10 +433,12 @@ namespace Svelto.ECS.Internal | |||
//remove Ex callbacks. Basically I am copying back the deleted value | |||
//at the end of the array, so I can use as range count, count + number of deleted entities | |||
//I need to swap the keys too to have matching EntityIDs | |||
if (index != fromDictionary.count) | |||
var fromDictionaryCount = fromDictionary.count; | |||
var fromDictionaryUnsafeKeys = fromDictionary.unsafeKeys; | |||
if (index != fromDictionaryCount) | |||
{ | |||
fromDictionary.unsafeValues[(uint)fromDictionary.count] = value; | |||
fromDictionary.unsafeKeys[(uint)fromDictionary.count] = new SveltoDictionaryNode<uint>(id, 0); | |||
fromDictionary.unsafeValues[(uint)fromDictionaryCount] = value; | |||
fromDictionaryUnsafeKeys[(uint)fromDictionaryCount] = new SveltoDictionaryNode<uint>(id, 0); | |||
} | |||
//when a component is removed from a component array, a remove swap back happens. This means | |||
@@ -445,7 +447,7 @@ namespace Svelto.ECS.Internal | |||
//of the deleted component | |||
//entityIDsAffectedByRemoval tracks all the entitiesID of the components that need to be updated | |||
//in the filters because their indices in the array changed. | |||
entityIDsAffectedByRemoveAtSwapBack[fromDictionary.unsafeKeys[index].key] = index; | |||
entityIDsAffectedByRemoveAtSwapBack[fromDictionaryUnsafeKeys[index].key] = index; | |||
} | |||
#if DEBUG && !PROFILE_SVELTO | |||
} | |||
@@ -1,18 +0,0 @@ | |||
#if SVELTO_LEGACY_FILTERS | |||
using Svelto.DataStructures; | |||
using Svelto.ECS.Native; | |||
namespace Svelto.ECS | |||
{ | |||
public static class EntitiesDBFiltersExtension | |||
{ | |||
public static bool AddEntityToFilter<N>(this EntitiesDB.LegacyFilters legacyFilters, int filtersID, EGID egid, N mapper) where N : IEGIDMultiMapper | |||
{ | |||
ref var filter = | |||
ref legacyFilters.CreateOrGetFilterForGroup(filtersID, egid.groupID, ComponentTypeMap.FetchID(mapper.entityType)); | |||
return filter.Add(egid.entityID, mapper); | |||
} | |||
} | |||
} | |||
#endif |
@@ -1,21 +0,0 @@ | |||
#if SVELTO_LEGACY_FILTERS | |||
using Svelto.ECS.Native; | |||
namespace Svelto.ECS | |||
{ | |||
public static class FilterGroupExtensions | |||
{ | |||
public static bool Add<N>(this LegacyFilterGroup legacyFilter, uint entityID, N mapper) where N : IEGIDMultiMapper | |||
{ | |||
#if DEBUG && !PROFILE_SVELTO | |||
if (mapper.Exists(legacyFilter._exclusiveGroupStruct, entityID) == false) | |||
throw new ECSException( | |||
$"trying adding an entity {entityID} to filter {mapper.entityType} - {legacyFilter._ID} with group {legacyFilter._exclusiveGroupStruct}, but entity is not found! "); | |||
#endif | |||
return legacyFilter.InternalAdd(entityID, mapper.GetIndex(new EGID(entityID, legacyFilter._exclusiveGroupStruct))); | |||
} | |||
} | |||
} | |||
#endif |
@@ -6,15 +6,17 @@ namespace Svelto.ECS.Serialization | |||
#if ENABLE_IL2CPP | |||
[UnityEngine.Scripting.Preserve] | |||
#endif | |||
public class DefaultVersioningFactory<T> : IDeserializationFactory where T : IEntityDescriptor, new() | |||
public class DefaultVersioningFactory<T> : IDeserializationFactory where T : ISerializableEntityDescriptor, new() | |||
{ | |||
public EntityInitializer BuildDeserializedEntity(EGID egid, ISerializationData serializationData, | |||
ISerializableEntityDescriptor entityDescriptor, int serializationType, | |||
IEntitySerialization entitySerialization, IEntityFactory factory, bool enginesRootIsDeserializationOnly) | |||
{ | |||
var newEntityDescriptor = EntityDescriptorTemplate<T>.realDescriptor as ISerializableEntityDescriptor; | |||
var entityDescriptorEntitiesToSerialize = enginesRootIsDeserializationOnly | |||
? entityDescriptor.componentsToSerialize | |||
: entityDescriptor.componentsToBuild; | |||
? newEntityDescriptor.componentsToSerialize | |||
: newEntityDescriptor.componentsToBuild; | |||
var initializer = (factory as IEntitySerializationFactory).BuildEntity(egid, entityDescriptorEntitiesToSerialize, TypeCache<T>.type); | |||
@@ -25,7 +25,8 @@ namespace Svelto.ECS | |||
new SerializableEntityHeader(descriptorHash, egid, (byte)entityComponentsToSerialise.Length); | |||
header.Copy(serializationData); | |||
for (int index = 0; index < entityComponentsToSerialise.Length; index++) | |||
var length = entityComponentsToSerialise.Length; | |||
for (int index = 0; index < length; index++) | |||
{ | |||
var entityBuilder = entityComponentsToSerialise[index]; | |||
@@ -16,6 +16,8 @@ namespace Svelto.ECS.Serialization | |||
where T : unmanaged, IEntityComponent | |||
{ | |||
public static uint SIZE => (uint)MemoryUtilities.SizeOf<T>(); | |||
protected SerializableComponentBuilder() { } | |||
public void Serialize(uint entityID, ITypeSafeDictionary dictionary, ISerializationData serializationData | |||
, int serializationType) | |||
@@ -11,7 +11,7 @@ | |||
"url": "https://github.com/sebas77/Svelto.ECS.git" | |||
}, | |||
"dependencies": { | |||
"com.sebaslab.svelto.common": "3.5.1" | |||
"com.sebaslab.svelto.common": "3.5.0" | |||
}, | |||
"keywords": [ | |||
"svelto", | |||