using System;
using System.Runtime.CompilerServices;
using Svelto.DataStructures;
using Svelto.Utilities;
namespace Svelto.ECS
{
class EntitiesOperations
{
///
/// Todo: need to go back here and add a ton of comments
///
public EntitiesOperations()
{
_thisSubmissionInfo.Init();
_lastSubmittedInfo.Init();
_newGroupDictionary = NewGroupDictionary;
_newGroupsDictionary = NewGroupsDictionary;
_recycleDictionary = RecycleDictionary;
_newList = NewList;
_clearList = ClearList;
_newGroupsDictionaryWithCaller = NewGroupsDictionaryWithCaller;
_recycleGroupDictionaryWithCaller = RecycleGroupDictionaryWithCaller;
_recycleDicitionaryWithCaller = RecycleDicitionaryWithCaller;
_newListWithCaller = NewListWithCaller;
_clearListWithCaller = ClearListWithCaller;
}
public void QueueRemoveGroupOperation(ExclusiveBuildGroup groupID, string caller)
{
_thisSubmissionInfo._groupsToRemove.Add((groupID, caller));
}
public void QueueRemoveOperation(EGID fromEgid, IComponentBuilder[] componentBuilders, string caller)
{
_thisSubmissionInfo._entitiesRemoved.Add(fromEgid);
RevertSwapOperation(fromEgid);
//todo: limit the number of dictionaries that can be cached
//recycle or create dictionaries of components per group
var removedComponentsPerType = _thisSubmissionInfo._currentRemoveEntitiesOperations.RecycleOrAdd(
fromEgid.groupID, _newGroupsDictionary, _recycleDictionary);
foreach (var operation in componentBuilders)
{
removedComponentsPerType //recycle or create dictionaries per component type
.RecycleOrAdd(operation.getComponentID, _newList, _clearList)
//add entity to remove
.Add((fromEgid.entityID, caller));
}
void RevertSwapOperation(EGID fromEgid)
{
if (_thisSubmissionInfo._entitiesSwapped.Remove(fromEgid, out (EGID fromEgid, EGID toEgid) val)) //Remove supersedes swap, check comment in IEntityFunctions.cs
{
var swappedComponentsPerType = _thisSubmissionInfo._currentSwapEntitiesOperations[fromEgid.groupID];
var componentBuildersLength = componentBuilders.Length - 1;
for (var index = componentBuildersLength; index >= 0; index--)
{
var operation = componentBuilders[index];
//todo: maybe the order of swappedComponentsPerType should be fromID, toGroupID, componentID
swappedComponentsPerType[operation.getComponentID][val.toEgid.groupID].Remove(fromEgid.entityID);
}
}
}
}
public void QueueSwapGroupOperation(ExclusiveBuildGroup fromGroupID, ExclusiveBuildGroup toGroupID, string caller)
{
_thisSubmissionInfo._groupsToSwap.Add((fromGroupID, toGroupID, caller));
}
public void QueueSwapOperation(EGID fromEGID, EGID toEGID, IComponentBuilder[] componentBuilders, string caller)
{
_thisSubmissionInfo._entitiesSwapped.Add(fromEGID, (fromEGID, toEGID));
//todo: limit the number of dictionaries that can be cached
//Get (or create) the dictionary that holds the entities that are swapping from fromEGID group
var swappedComponentsPerType = _thisSubmissionInfo._currentSwapEntitiesOperations.RecycleOrAdd(
fromEGID.groupID, _newGroupsDictionaryWithCaller, _recycleGroupDictionaryWithCaller);
var componentBuildersLength = componentBuilders.Length - 1;
//for each component of the entity that is swapping
for (var index = componentBuildersLength; index >= 0; index--)
{
var operation = componentBuilders[index];
//Get the dictionary for each component that holds the list of entities to swap
swappedComponentsPerType
//recycle or create dictionaries per component type
.RecycleOrAdd(operation.getComponentID, _newGroupDictionary, _recycleDicitionaryWithCaller)
//recycle or create list of entities to swap
.RecycleOrAdd(toEGID.groupID, _newListWithCaller, _clearListWithCaller)
//add entity to swap
.Add(fromEGID.entityID, new SwapInfo(fromEGID.entityID, toEGID.entityID, caller));
}
}
[MethodImpl(MethodImplOptions.Synchronized)]
public bool AnyOperationQueued()
{
return _thisSubmissionInfo.AnyOperationQueued();
}
public void ExecuteRemoveAndSwappingOperations(
Action>>>, FasterDictionary, EnginesRoot> swapEntities, Action>>,
FasterList, EnginesRoot> removeEntities, Action removeGroup,
Action swapGroup, EnginesRoot enginesRoot)
{
(_thisSubmissionInfo, _lastSubmittedInfo) = (_lastSubmittedInfo, _thisSubmissionInfo);
/// todo: entity references should be updated before calling all the methods to avoid callbacks handling
/// references that should be marked as invalid.
foreach (var (group, caller) in _lastSubmittedInfo._groupsToRemove)
try
{
removeGroup(group, enginesRoot);
}
catch
{
var str = "Crash while removing a whole group on ".FastConcat(group.ToString())
.FastConcat(" from : ", caller);
Console.LogError(str);
throw;
}
foreach (var (fromGroup, toGroup, caller) in _lastSubmittedInfo._groupsToSwap)
try
{
swapGroup(fromGroup, toGroup, enginesRoot);
}
catch
{
var str = "Crash while swapping a whole group on "
.FastConcat(fromGroup.ToString(), " ", toGroup.ToString()).FastConcat(" from : ", caller);
Console.LogError(str);
throw;
}
if (_lastSubmittedInfo._entitiesSwapped.count > 0)
swapEntities(_lastSubmittedInfo._currentSwapEntitiesOperations, _lastSubmittedInfo._entitiesSwapped, enginesRoot);
if (_lastSubmittedInfo._entitiesRemoved.count > 0)
removeEntities(
_lastSubmittedInfo._currentRemoveEntitiesOperations, _lastSubmittedInfo._entitiesRemoved
, enginesRoot);
_lastSubmittedInfo.Clear();
}
static FasterDictionary> NewGroupsDictionary()
{
return new FasterDictionary>();
}
static void RecycleDictionary(ref FasterDictionary> recycled)
{
recycled.Recycle();
}
static FasterList<(uint, string)> NewList()
{
return new FasterList<(uint, string)>();
}
static void ClearList(ref FasterList<(uint, string)> target)
{
target.Clear();
}
static void RecycleDicitionaryWithCaller(ref FasterDictionary> target)
{
target.Recycle();
}
static void ClearListWithCaller(ref FasterDictionary target)
{
target.Clear();
}
static FasterDictionary NewListWithCaller()
{
return new FasterDictionary();
}
static FasterDictionary>>
NewGroupsDictionaryWithCaller()
{
return new FasterDictionary>>();
}
static void RecycleGroupDictionaryWithCaller(
ref FasterDictionary>> recycled)
{
recycled.Recycle();
}
static FasterDictionary> NewGroupDictionary()
{
return new FasterDictionary>();
}
struct Info
{
//from group //actual component type
internal FasterDictionary>>> _currentSwapEntitiesOperations;
internal FasterDictionary>> _currentRemoveEntitiesOperations;
internal FasterDictionary _entitiesSwapped;
internal FasterList _entitiesRemoved;
public FasterList<(ExclusiveBuildGroup, ExclusiveBuildGroup, string)> _groupsToSwap;
public FasterList<(ExclusiveBuildGroup, string)> _groupsToRemove;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal bool AnyOperationQueued()
{
return _entitiesSwapped.count > 0 || _entitiesRemoved.count > 0 || _groupsToSwap.count > 0
|| _groupsToRemove.count > 0;
}
internal void Clear()
{
_currentSwapEntitiesOperations.Recycle();
_currentRemoveEntitiesOperations.Recycle();
_entitiesSwapped.Clear();
_entitiesRemoved.Clear();
_groupsToRemove.Clear();
_groupsToSwap.Clear();
}
internal void Init()
{
_entitiesSwapped = new FasterDictionary();
_entitiesRemoved = new FasterList();
_groupsToRemove = new FasterList<(ExclusiveBuildGroup, string)>();
_groupsToSwap = new FasterList<(ExclusiveBuildGroup, ExclusiveBuildGroup, string)>();
_currentSwapEntitiesOperations =
new FasterDictionary>>>();
_currentRemoveEntitiesOperations =
new FasterDictionary>>();
}
}
Info _lastSubmittedInfo;
Info _thisSubmissionInfo;
readonly Func>> _newGroupDictionary;
readonly Func>> _newGroupsDictionary;
readonly ActionRef>> _recycleDictionary;
readonly Func> _newList;
readonly ActionRef> _clearList;
readonly Func>>>
_newGroupsDictionaryWithCaller;
readonly ActionRef>>>
_recycleGroupDictionaryWithCaller;
readonly ActionRef>> _recycleDicitionaryWithCaller;
readonly Func> _newListWithCaller;
readonly ActionRef> _clearListWithCaller;
}
public struct SwapInfo
{
public uint fromID; //to do this information should be redundant, try to remove it
public uint toID;
public uint toIndex;
public string trace;
public SwapInfo(uint fromEgidEntityId, uint toEgidEntityId, string s)
{
fromID = fromEgidEntityId;
toID = toEgidEntityId;
toIndex = 0;
trace = s;
}
public void Deconstruct(out uint fromID, out uint toID, out uint toIndex, out string caller)
{
fromID = this.fromID;
toID = this.toID;
toIndex = this.toIndex;
caller = this.trace;
}
public void Deconstruct(out uint fromID, out uint toID, out string caller)
{
fromID = this.fromID;
toID = this.toID;
caller = this.trace;
}
}
}