//#define VERBOSE
#if !DEBUG || PROFILE_SVELTO
#define DONT_USE
using System.Diagnostics;
#endif
using System;
using System.Collections.Generic;
using Svelto.DataStructures;
namespace Svelto.ECS
{
///
/// Note: this check doesn't catch the case when an add and remove is done on the same entity before the next
/// submission. Two operations on the same entity are not allowed between submissions.
///
public partial class EnginesRoot
{
enum OperationType
{
Add,
Remove,
SwapFrom,
SwapTo
}
#if DONT_USE
[Conditional("MEANINGLESS")]
#endif
void InitDebugChecks()
{
_multipleOperationOnSameEGIDChecker = new FasterDictionary();
_idChecker = new FasterDictionary>();
}
#if DONT_USE
[Conditional("MEANINGLESS")]
#endif
void CheckSwapEntityID(EGID fromEgid, EGID toEgid, Type entityDescriptorType, string caller)
{
if (_multipleOperationOnSameEGIDChecker.TryGetValue(fromEgid, out var fromOperationType) == true)
{
var operationName = OperationName(fromOperationType);
throw new ECSException(
"Executing multiple structural changes (swapFrom) on the same entity is not supported "
.FastConcat(" caller: ", caller, " ").FastConcat(fromEgid.entityID).FastConcat(" groupid: ")
.FastConcat(fromEgid.groupID.ToName()).FastConcat(" type: ")
.FastConcat(entityDescriptorType != null ? entityDescriptorType.Name : "not available")
.FastConcat(" previous operation was: ")
.FastConcat(operationName));
}
if (_multipleOperationOnSameEGIDChecker.TryGetValue(toEgid, out var toOperationType) == true)
{
var operationName = OperationName(toOperationType);
throw new ECSException(
"Executing multiple structural changes (swapTo) on the same entity is not supported "
.FastConcat(" caller: ", caller, " ").FastConcat(toEgid.entityID).FastConcat(" groupid: ")
.FastConcat(toEgid.groupID.ToName()).FastConcat(" type: ")
.FastConcat(entityDescriptorType != null ? entityDescriptorType.Name : "not available")
.FastConcat(" previous operation was: ")
.FastConcat(operationName));
}
HashRemove(fromEgid, entityDescriptorType, false, caller);
HashAdd(toEgid, entityDescriptorType, caller);
_multipleOperationOnSameEGIDChecker.Add(fromEgid, OperationType.SwapFrom);
_multipleOperationOnSameEGIDChecker.Add(toEgid, OperationType.SwapTo);
}
#if DONT_USE
[Conditional("MEANINGLESS")]
#endif
void CheckRemoveEntityID(EGID egid, Type entityDescriptorType, string caller)
{
bool isAllowed = false;
if (_multipleOperationOnSameEGIDChecker.TryGetValue(egid, out var operationType) == true)
{
isAllowed = operationType == OperationType.Remove || operationType == OperationType.SwapFrom;
if (isAllowed)
{
#if VERBOSE
var operationName = OperationName(operationType);
Console.LogDebugWarning(
"Executing multiple structural changes (remove) in one submission on the same entity. Remove supersedes swap and remove operations "
.FastConcat(" caller: ", caller, " ").FastConcat(egid.entityID).FastConcat(" groupid: ")
.FastConcat(egid.groupID.ToName()).FastConcat(" type: ")
.FastConcat(entityDescriptorType != null ? entityDescriptorType.Name : "not available")
.FastConcat(" previous operation was: ")
.FastConcat(operationName));
#endif
}
else
throw new ECSException(
"Executing multiple structural changes (remove) in one submission on the same entity is not supported "
.FastConcat(" caller: ", caller, " ").FastConcat(egid.entityID).FastConcat(" groupid: ")
.FastConcat(egid.groupID.ToName()).FastConcat(" type: ")
.FastConcat(entityDescriptorType != null ? entityDescriptorType.Name : "not available")
.FastConcat(" previous operation was: ")
.FastConcat("add"));
}
HashRemove(egid, entityDescriptorType, isAllowed, caller);
if (isAllowed == false)
_multipleOperationOnSameEGIDChecker.Add(egid, OperationType.Remove);
else
_multipleOperationOnSameEGIDChecker[egid] = OperationType.Remove;
}
#if DONT_USE
[Conditional("MEANINGLESS")]
#endif
void CheckAddEntityID(EGID egid, Type entityDescriptorType, string caller)
{
if (_multipleOperationOnSameEGIDChecker.TryGetValue(egid, out var operationType) == true)
{
var operationName = OperationName(operationType);
throw new ECSException(
"Executing multiple structural changes (build) on the same entity is not supported "
.FastConcat(" caller: ", caller, " ").FastConcat(egid.entityID).FastConcat(" groupid: ")
.FastConcat(egid.groupID.ToName()).FastConcat(" type: ")
.FastConcat(entityDescriptorType != null ? entityDescriptorType.Name : "not available")
.FastConcat(" previous operation was: ")
.FastConcat(operationName));
}
HashAdd(egid, entityDescriptorType, caller);
_multipleOperationOnSameEGIDChecker.Add(egid, OperationType.Add);
}
#if DONT_USE
[Conditional("MEANINGLESS")]
#endif
void RemoveGroupID(ExclusiveBuildGroup groupID)
{
_idChecker.Remove(groupID);
}
#if DONT_USE
[Conditional("MEANINGLESS")]
#endif
void ClearChecksForMultipleOperationsOnTheSameEgid()
{
_multipleOperationOnSameEGIDChecker.Clear();
}
void HashRemove(EGID egid, Type entityDescriptorType, bool isAllowed, string caller)
{
if (_idChecker.TryGetValue(egid.groupID, out HashSet hash))
{
if (hash.Contains(egid.entityID) == false && isAllowed == false)
throw new ECSException(
"Trying to remove an Entity not present in the database "
.FastConcat(" caller: ", caller, " entityID ").FastConcat(egid.entityID).FastConcat(" groupid: ")
.FastConcat(egid.groupID.ToName()).FastConcat(" type: ")
.FastConcat(entityDescriptorType != null ? entityDescriptorType.Name : "not available"));
}
else
{
throw new ECSException(
"Trying to remove an Entity with a group never used so far "
.FastConcat(" caller: ", caller, " entityID ").FastConcat(egid.entityID).FastConcat(" groupid: ")
.FastConcat(egid.groupID.ToName()).FastConcat(" type: ")
.FastConcat(entityDescriptorType != null ? entityDescriptorType.Name : "not available"));
}
hash.Remove(egid.entityID);
}
void HashAdd(EGID egid, Type entityDescriptorType, string caller)
{
var hash = _idChecker.GetOrAdd(egid.groupID, () => new HashSet());
if (hash.Contains(egid.entityID) == true)
throw new ECSException(
"Trying to add an Entity already present in the database "
.FastConcat(" caller: ", caller, " entityID ").FastConcat(egid.entityID)
.FastConcat(" groupid: ").FastConcat(egid.groupID.ToName()).FastConcat(" type: ")
.FastConcat(
entityDescriptorType != null
? entityDescriptorType.Name
: "not available"));
hash.Add(egid.entityID);
}
string OperationName(OperationType operationType)
{
string operationName;
switch (operationType)
{
case OperationType.Remove:
operationName = "remove";
break;
case OperationType.Add:
operationName = "add";
break;
case OperationType.SwapFrom:
operationName = "swapFrom";
break;
default:
operationName = "swapTo";
break;
}
return operationName;
}
DataStructures.FasterDictionary _multipleOperationOnSameEGIDChecker;
DataStructures.FasterDictionary> _idChecker;
}
}