using System; using System.Runtime.CompilerServices; using DBC.ECS; using Svelto.Common; using Svelto.DataStructures; namespace Svelto.ECS.Internal { public static class TypeSafeDictionaryMethods { [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void AddEntitiesToDictionary( in SveltoDictionary fromDictionary , ITypeSafeDictionary toDic #if SLOW_SVELTO_SUBMISSION , in EnginesRoot.EntityReferenceMap entityLocator #endif , ExclusiveGroupStruct toGroupID) where Strategy1 : struct, IBufferStrategy> where Strategy2 : struct, IBufferStrategy where Strategy3 : struct, IBufferStrategy where TValue : struct, _IInternalEntityComponent { foreach (var tuple in fromDictionary) { #if SLOW_SVELTO_SUBMISSION var egid = new EGID(tuple.key, toGroupID); if (SlowSubmissionInfo.hasEgid) SetEGIDWithoutBoxing.SetIDWithoutBoxing(ref tuple.value, egid); if (SlowSubmissionInfo.hasReference) SetEGIDWithoutBoxing.SetRefWithoutBoxing( ref tuple.value, entityLocator.GetEntityReference(egid)); #endif #if DEBUG && !PROFILE_SVELTO try { #endif toDic.Add(tuple.key, tuple.value); #if DEBUG && !PROFILE_SVELTO } catch (Exception e) { Console.LogException( e, "trying to add an EntityComponent with the same ID more than once Entity: ".FastConcat(typeof(TValue).ToString()) .FastConcat(", group ").FastConcat(toGroupID.ToName()).FastConcat(", id ").FastConcat(tuple.key)); throw; } #endif #if PARANOID_CHECK && SLOW_SVELTO_SUBMISSION DBC.ECS.Check.Ensure(_hasEgid == false || ((INeedEGID)fromDictionary[egid.entityID]).ID == egid, "impossible situation happened during swap"); #endif } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void ExecuteEnginesAddCallbacks( ref SveltoDictionary fromDictionary , ITypeSafeDictionary todic, ExclusiveGroupStruct togroup , FasterDictionary>> entitycomponentenginesdb , in PlatformProfiler sampler) where Strategy1 : struct, IBufferStrategy> where Strategy2 : struct, IBufferStrategy where Strategy3 : struct, IBufferStrategy where TValue : struct, _IInternalEntityComponent { if (entitycomponentenginesdb.TryGetValue( ComponentTypeID.id , out var entityComponentsEngines)) { if (entityComponentsEngines.count == 0) return; var dictionaryKeyEnumerator = fromDictionary.unsafeKeys; var count = fromDictionary.count; for (var i = 0; i < count; ++i) try { var key = dictionaryKeyEnumerator[i].key; ref var entity = ref todic.GetValueByRef(key); var egid = new EGID(key, togroup); //get all the engines linked to TValue for (var j = 0; j < entityComponentsEngines.count; j++) using (sampler.Sample(entityComponentsEngines[j].name)) { #pragma warning disable CS0612 ((IReactOnAdd)entityComponentsEngines[j].engine).Add(ref entity, egid); #pragma warning restore CS0612 } } catch (Exception e) { Console.LogException(e, "Code crashed inside Add callback with Type ".FastConcat(TypeCache.name)); throw; } } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void ExecuteEnginesDisposeCallbacks_Group( ref SveltoDictionary fromDictionary , FasterDictionary>> reactiveEnginesDispose , FasterDictionary>> reactiveEnginesDisposeEx , IEntityIDs entityids, ExclusiveGroupStruct group, in PlatformProfiler sampler) where Strategy1 : struct, IBufferStrategy> where Strategy2 : struct, IBufferStrategy where Strategy3 : struct, IBufferStrategy where TValue : struct, _IInternalEntityComponent { if (reactiveEnginesDispose.TryGetValue(ComponentTypeID.id, out var entityComponentsEngines) == true) { var resultCount = entityComponentsEngines.count; for (var i = 0; i < resultCount; i++) try { using (sampler.Sample(entityComponentsEngines[i].name)) { foreach (var value in fromDictionary) { ref var entity = ref value.value; var egid = new EGID(value.key, group); #pragma warning disable CS0618 var reactOnRemove = (IReactOnDispose)entityComponentsEngines[i].engine; #pragma warning restore CS0618 reactOnRemove.Remove(ref entity, egid); } } } catch { Console.LogError("Code crashed inside Remove callback ".FastConcat(entityComponentsEngines[i].name)); throw; } } if (reactiveEnginesDisposeEx.TryGetValue(ComponentTypeID.id, out var reactiveEnginesDisposeExPerType)) { var count = fromDictionary.count; var enginesCount = reactiveEnginesDisposeExPerType.count; if (count > 0) { for (var i = 0; i < enginesCount; i++) { try { using (sampler.Sample(reactiveEnginesDisposeExPerType[i].name)) { ((IReactOnDisposeEx)reactiveEnginesDisposeExPerType[i].engine).Remove( (0, (uint)count) , new EntityCollection(fromDictionary.UnsafeGetValues(out _), entityids, (uint)count), group); } } catch { Console.LogError("Code crashed inside Remove callback ".FastConcat(reactiveEnginesDisposeExPerType[i].name)); throw; } } } } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void ExecuteEnginesRemoveCallbacks(FasterList<(uint, string)> infostoprocess , ref SveltoDictionary fromDictionary , FasterDictionary>> reactiveenginesremove , ExclusiveGroupStruct fromgroup, in PlatformProfiler profiler) where Strategy1 : struct, IBufferStrategy> where Strategy2 : struct, IBufferStrategy where Strategy3 : struct, IBufferStrategy where TValue : struct, _IInternalEntityComponent { if (reactiveenginesremove.TryGetValue( ComponentTypeID.id , out var entityComponentsEngines)) { if (entityComponentsEngines.count == 0) return; var iterations = infostoprocess.count; for (var i = 0; i < iterations; i++) { var (entityID, trace) = infostoprocess[i]; try { ref var entity = ref fromDictionary.GetValueByRef(entityID); var egid = new EGID(entityID, fromgroup); for (var j = 0; j < entityComponentsEngines.count; j++) using (profiler.Sample(entityComponentsEngines[j].name)) { #pragma warning disable CS0612 ((IReactOnRemove)entityComponentsEngines[j].engine).Remove(ref entity, egid); #pragma warning restore CS0612 } } catch { var str = "Crash while executing Remove Entity callback on ".FastConcat(TypeCache.name) .FastConcat(" from : ", trace); Console.LogError(str); throw; } } } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void ExecuteEnginesRemoveCallbacks_Group( ref SveltoDictionary fromDictionary , FasterDictionary>> reactiveenginesremove , FasterDictionary>> reactiveenginesremoveex , IEntityIDs entityids, ExclusiveGroupStruct group, in PlatformProfiler sampler) where Strategy1 : struct, IBufferStrategy> where Strategy2 : struct, IBufferStrategy where Strategy3 : struct, IBufferStrategy where TValue : struct, _IInternalEntityComponent { if (reactiveenginesremove.TryGetValue( ComponentTypeID.id , out var reactiveEnginesRemovePerType)) { var enginesCount = reactiveEnginesRemovePerType.count; for (var i = 0; i < enginesCount; i++) try { foreach (var value in fromDictionary) { ref var entity = ref value.value; var egid = new EGID(value.key, group); using (sampler.Sample(reactiveEnginesRemovePerType[i].name)) { #pragma warning disable CS0612 ((IReactOnRemove)reactiveEnginesRemovePerType[i].engine).Remove( #pragma warning restore CS0612 ref entity, egid); } } } catch { Console.LogError("Code crashed inside Remove callback ".FastConcat(reactiveEnginesRemovePerType[i].name)); throw; } } if (reactiveenginesremoveex.TryGetValue( ComponentTypeID.id , out var reactiveEnginesRemoveExPerType)) { var count = fromDictionary.count; var enginesCount = reactiveEnginesRemoveExPerType.count; for (var i = 0; i < enginesCount; i++) try { using (sampler.Sample(reactiveEnginesRemoveExPerType[i].name)) { ((IReactOnRemoveEx)reactiveEnginesRemoveExPerType[i].engine).Remove( (0, (uint)count) , new EntityCollection( fromDictionary.UnsafeGetValues(out _), entityids , (uint)count), group); } } catch { Console.LogError("Code crashed inside Remove callback ".FastConcat(reactiveEnginesRemoveExPerType[i].name)); throw; } } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void ExecuteEnginesSwapCallbacks(FasterDictionary infostoprocess , ref SveltoDictionary fromDictionary , FasterList> reactiveenginesswap, ExclusiveGroupStruct togroup , ExclusiveGroupStruct fromgroup, in PlatformProfiler sampler) where Strategy1 : struct, IBufferStrategy> where Strategy2 : struct, IBufferStrategy where Strategy3 : struct, IBufferStrategy where TValue : struct, _IInternalEntityComponent { if (reactiveenginesswap.count == 0) return; var iterations = infostoprocess.count; var infostoprocessUnsafeValues = infostoprocess.unsafeValues; for (var i = 0; i < iterations; i++) { var (fromEntityID, toEntityID, trace) = infostoprocessUnsafeValues[i]; try { ref var entityComponent = ref fromDictionary.GetValueByRef(toEntityID); var newEgid = new EGID(toEntityID, togroup); for (var j = 0; j < reactiveenginesswap.count; j++) using (sampler.Sample(reactiveenginesswap[j].name)) { #pragma warning disable CS0612 #pragma warning disable CS0618 ((IReactOnSwap)reactiveenginesswap[j].engine).MovedTo( #pragma warning restore CS0618 #pragma warning restore CS0612 ref entityComponent, fromgroup, newEgid); } } catch { var str = "Crash while executing Swap Entity callback on ".FastConcat(TypeCache.name) .FastConcat(" from : ", trace); Console.LogError(str); throw; } } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void ExecuteEnginesSwapCallbacks_Group( ref SveltoDictionary fromDictionary , ITypeSafeDictionary toDic, ExclusiveGroupStruct togroup, ExclusiveGroupStruct fromgroup , FasterDictionary>> reactiveenginesswap , FasterDictionary>> reactiveenginesswapex , IEntityIDs entityids, in PlatformProfiler sampler) where Strategy1 : struct, IBufferStrategy> where Strategy2 : struct, IBufferStrategy where Strategy3 : struct, IBufferStrategy where TValue : struct, _IInternalEntityComponent { //get all the engines linked to TValue if (!reactiveenginesswap.TryGetValue( ComponentTypeID.id , out var reactiveEnginesSwapPerType)) return; var componentsEnginesCount = reactiveEnginesSwapPerType.count; for (var i = 0; i < componentsEnginesCount; i++) try { foreach (var value in fromDictionary) { ref var entityComponent = ref toDic.GetValueByRef(value.key); var newEgid = new EGID(value.key, togroup); using (sampler.Sample(reactiveEnginesSwapPerType[i].name)) { #pragma warning disable CS0612 #pragma warning disable CS0618 ((IReactOnSwap)reactiveEnginesSwapPerType[i].engine).MovedTo( #pragma warning restore CS0618 #pragma warning restore CS0612 ref entityComponent, fromgroup, newEgid); } } } catch (Exception) { Console.LogError("Code crashed inside MoveTo callback ".FastConcat(reactiveEnginesSwapPerType[i].name)); throw; } if (reactiveenginesswapex.TryGetValue( ComponentTypeID.id , out var reactiveEnginesRemoveExPerType)) { var enginesCount = reactiveEnginesRemoveExPerType.count; var count = fromDictionary.count; for (var i = 0; i < enginesCount; i++) try { using (sampler.Sample(reactiveEnginesRemoveExPerType[i].name)) { ((IReactOnSwapEx)reactiveEnginesRemoveExPerType[i].engine).MovedTo( (0, (uint)count) , new EntityCollection( fromDictionary.UnsafeGetValues(out _), entityids , (uint)count), fromgroup, togroup); } } catch { Console.LogError("Code crashed inside Remove callback ".FastConcat(reactiveEnginesRemoveExPerType[i].name)); throw; } } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void RemoveEntitiesFromDictionary(FasterList<(uint, string)> infostoprocess , ref SveltoDictionary fromDictionary , FasterDictionary entityIDsAffectedByRemoveAtSwapBack) where Strategy1 : struct, IBufferStrategy> where Strategy2 : struct, IBufferStrategy where Strategy3 : struct, IBufferStrategy where TValue : struct, _IInternalEntityComponent { var iterations = infostoprocess.count; for (var i = 0; i < iterations; i++) { var (id, trace) = infostoprocess[i]; #if DEBUG && !PROFILE_SVELTO try { #endif if (fromDictionary.Remove(id, out var index, out var value)) { //Note I am doing this to be able to use a range of values even with the //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) { fromDictionary.unsafeValues[(uint)fromDictionary.count] = value; fromDictionary.unsafeKeys[(uint)fromDictionary.count] = new SveltoDictionaryNode(id, 0); } //when a component is removed from a component array, a remove swap back happens. This means //that not only we have to remove the index of the component of the entity deleted from the array //but we need also to update the index of the component that has been swapped in the cell //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; } #if DEBUG && !PROFILE_SVELTO } catch { var str = "Crash while executing Remove Entity operation on ".FastConcat(TypeCache.name) .FastConcat(" from : ", trace); Console.LogError(str); throw; } #endif } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void SwapEntitiesBetweenDictionaries(in FasterDictionary entitiesIDsToSwap , ref SveltoDictionary fromDictionary , ITypeSafeDictionary toDictionary, ExclusiveGroupStruct fromgroup, ExclusiveGroupStruct togroup , FasterDictionary entityIDsAffectedByRemoveAtSwapBack) where Strategy1 : struct, IBufferStrategy> where Strategy2 : struct, IBufferStrategy where Strategy3 : struct, IBufferStrategy where TValue : struct, _IInternalEntityComponent { var iterations = entitiesIDsToSwap.count; var entitiesToSwapInfo = entitiesIDsToSwap.unsafeValues; var fromDictionaryUnsafeKeys = fromDictionary.unsafeKeys; for (var i = 0; i < iterations; i++) { ref SwapInfo swapInfo = ref entitiesToSwapInfo[i]; #if DEBUG && !PROFILE_SVELTO try { #endif var fromEntityGid = new EGID(swapInfo.fromID, fromgroup); var toEntityEgid = new EGID(swapInfo.toID, togroup); Check.Require(togroup.isInvalid == false, "Invalid To Group"); if (fromDictionary.Remove(fromEntityGid.entityID, out var index, out var value)) entityIDsAffectedByRemoveAtSwapBack[fromDictionaryUnsafeKeys[index].key] = index; //after the removal, the entity ad index is the entity that was at the end of the buffer (swapped back). else Check.Assert(false, "Swapping an entity that doesn't exist"); #if SLOW_SVELTO_SUBMISSION if (SlowSubmissionInfo.hasEgid) SetEGIDWithoutBoxing.SetIDWithoutBoxing(ref value, toEntityEgid); #endif swapInfo.toIndex = toDictionary.Add(toEntityEgid.entityID, value); #if PARANOID_CHECK DBC.ECS.Check.Ensure(_hasEgid == false || ((INeedEGID)toGroupCasted[toEntityEGID.entityID]).ID == toEntityEGID, "impossible situation happened during swap"); #endif #if DEBUG && !PROFILE_SVELTO } catch { var str = "Crash while executing Swap Entity operation on ".FastConcat(TypeCache.name) .FastConcat(" from : ", swapInfo.trace); Console.LogError(str); throw; } #endif } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void ExecuteEnginesAddEntityCallbacksFast( FasterDictionary>> fasterDictionary , ExclusiveGroupStruct groupId, (uint, uint) rangeTuple, IEntityIDs entityids , ITypeSafeDictionary typeSafeDictionary, PlatformProfiler profiler) where TValue : struct, _IInternalEntityComponent { //get all the engines linked to TValue if (!fasterDictionary.TryGetValue(ComponentTypeID.id, out var entityComponentsEngines)) return; for (var i = 0; i < entityComponentsEngines.count; i++) try { using (profiler.Sample(entityComponentsEngines[i].name)) { ((IReactOnAddEx)entityComponentsEngines[i].engine).Add( rangeTuple , new EntityCollection(typeSafeDictionary.GetValues(out var count), entityids, count) , groupId); } } catch (Exception e) { Console.LogException(e, "Code crashed inside Add callback ".FastConcat(entityComponentsEngines[i].name)); throw; } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void ExecuteEnginesSwapCallbacksFast( FasterList> callbackEngines, ExclusiveGroupStruct fromGroup, ExclusiveGroupStruct toGroup, IEntityIDs entityids, ITypeSafeDictionary typeSafeDictionary , (uint, uint) rangeOfSubmittedEntitiesIndicies, PlatformProfiler sampler) where TValue : struct, _IInternalEntityComponent { for (var i = 0; i < callbackEngines.count; i++) try { using (sampler.Sample(callbackEngines[i].name)) { var values = typeSafeDictionary.GetValues(out var count); ((IReactOnSwapEx)callbackEngines[i].engine).MovedTo( rangeOfSubmittedEntitiesIndicies, new EntityCollection(values, entityids, count), fromGroup, toGroup); } } catch (Exception e) { Console.LogException(e, "Code crashed inside Add callback ".FastConcat(callbackEngines[i].name)); throw; } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void ExecuteEnginesRemoveCallbacksFast(FasterList> fasterList, ExclusiveGroupStruct exclusiveGroupStruct , (uint, uint) valueTuple, IEntityIDs entityids, ITypeSafeDictionary typeSafeDictionary , PlatformProfiler sampler) where TValue : struct, _IInternalEntityComponent { for (var i = 0; i < fasterList.count; i++) try { using (sampler.Sample(fasterList[i].name)) { ((IReactOnRemoveEx)fasterList[i].engine).Remove( valueTuple , new EntityCollection(typeSafeDictionary.GetValues(out var count), entityids, count) , exclusiveGroupStruct); } } catch (Exception e) { Console.LogException(e, "Code crashed inside Add callback ".FastConcat(fasterList[i].name)); throw; } } } }