using System;
using Svelto.DataStructures;
using Svelto.ECS.Internal;
namespace Svelto.ECS
{
///
/// DynamicEntityDescriptor can be used to add entity components to an existing EntityDescriptor that act as flags,
/// at building time.
/// This method allocates, so it shouldn't be abused
/// TODO:Unit test cases where there could be duplicates of components, especially EntityInfoComponent.
/// Test DynamicED of DynamicED
///
///
public struct DynamicEntityDescriptor : IDynamicEntityDescriptor where TType : IEntityDescriptor, new()
{
public static DynamicEntityDescriptor CreateDynamicEntityDescriptor()
{
var entityDescriptor = new DynamicEntityDescriptor();
var defaultEntities = EntityDescriptorTemplate.realDescriptor.componentsToBuild;
var length = defaultEntities.Length;
if (FetchEntityInfoComponent(defaultEntities) == -1)
{
entityDescriptor._componentsToBuild = new IComponentBuilder[length + 1];
Array.Copy(defaultEntities, 0, entityDescriptor._componentsToBuild, 0, length);
//assign it after otherwise the previous copy will overwrite the value in case the item
//is already present
entityDescriptor._componentsToBuild[length] = new ComponentBuilder(new EntityInfoComponent
{
componentsToBuild = entityDescriptor._componentsToBuild
});
}
else
{
entityDescriptor._componentsToBuild = new IComponentBuilder[length];
Array.Copy(defaultEntities, 0, entityDescriptor._componentsToBuild, 0, length);
}
return entityDescriptor;
}
public DynamicEntityDescriptor(IComponentBuilder[] extraEntityBuilders)
{
this = DynamicEntityDescriptor.CreateDynamicEntityDescriptor();
var extraEntitiesLength = extraEntityBuilders.Length;
_componentsToBuild = Construct(extraEntitiesLength, extraEntityBuilders);
}
public DynamicEntityDescriptor(FasterList extraEntityBuilders)
{
this = DynamicEntityDescriptor.CreateDynamicEntityDescriptor();
var extraEntities = extraEntityBuilders.ToArrayFast(out _);
var extraEntitiesLength = extraEntityBuilders.count;
_componentsToBuild = Construct((int)extraEntitiesLength, extraEntities);
}
public void ExtendWith() where T : IEntityDescriptor, new()
{
var extraEntities = EntityDescriptorTemplate.realDescriptor.componentsToBuild;
_componentsToBuild = Construct(extraEntities.Length, extraEntities);
}
public void ExtendWith(IComponentBuilder[] extraEntities)
{
_componentsToBuild = Construct(extraEntities.Length, extraEntities);
}
public void ExtendWith(FasterList extraEntities)
{
_componentsToBuild = Construct(extraEntities.count, extraEntities.ToArrayFast(out _));
}
public void Add() where T : struct, _IInternalEntityComponent
{
IComponentBuilder[] extraEntities = { new ComponentBuilder() };
_componentsToBuild = Construct(extraEntities.Length, extraEntities);
}
public void Add() where T : struct, _IInternalEntityComponent where U : struct, _IInternalEntityComponent
{
IComponentBuilder[] extraEntities = { new ComponentBuilder(), new ComponentBuilder() };
_componentsToBuild = Construct(extraEntities.Length, extraEntities);
}
public void Add() where T : struct, _IInternalEntityComponent
where U : struct, _IInternalEntityComponent
where V : struct, _IInternalEntityComponent
{
IComponentBuilder[] extraEntities =
{
new ComponentBuilder(), new ComponentBuilder(), new ComponentBuilder()
};
_componentsToBuild = Construct(extraEntities.Length, extraEntities);
}
///
/// Note: unluckily I didn't design the serialization system to be component order independent, so unless
/// I do something about it, this method cannot be optimized, the logic of the component order must stay
/// untouched (no reordering, no use of dictionaries). Components order must stay as it comes, as
/// well as extracomponents order.
/// Speed, however, is not a big issue for this class, as the data is always composed once per entity descriptor
/// at static constructor time
///
///
IComponentBuilder[] Construct(int extraComponentsLength, IComponentBuilder[] extraComponents)
{
IComponentBuilder[] MergeLists
(IComponentBuilder[] startingComponents, IComponentBuilder[] newComponents, int newComponentsLength)
{
var startComponents =
new FasterDictionary, IComponentBuilder>();
var xtraComponents =
new FasterDictionary, IComponentBuilder>();
for (uint i = 0; i < startingComponents.Length; i++)
startComponents
[new RefWrapper(startingComponents[i])] =
startingComponents[i];
for (uint i = 0; i < newComponentsLength; i++)
xtraComponents[new RefWrapper(newComponents[i])] =
newComponents[i];
xtraComponents.Exclude(startComponents);
if (newComponentsLength != xtraComponents.count)
{
newComponentsLength = xtraComponents.count;
uint index = 0;
foreach (var couple in xtraComponents)
newComponents[index++] = couple.key.type;
}
IComponentBuilder[] componentBuilders =
new IComponentBuilder[newComponentsLength + startingComponents.Length];
Array.Copy(startingComponents, 0, componentBuilders, 0, startingComponents.Length);
Array.Copy(newComponents, 0, componentBuilders, startingComponents.Length, newComponentsLength);
var entityInfoComponentIndex = FetchEntityInfoComponent(componentBuilders);
DBC.ECS.Check.Assert(entityInfoComponentIndex != -1);
componentBuilders[entityInfoComponentIndex] = new ComponentBuilder(
new EntityInfoComponent
{
componentsToBuild = componentBuilders
});
return componentBuilders;
}
if (extraComponentsLength == 0)
{
return _componentsToBuild;
}
var safeCopyOfExtraComponents = new IComponentBuilder[extraComponentsLength];
Array.Copy(extraComponents, safeCopyOfExtraComponents, extraComponentsLength);
return MergeLists(_componentsToBuild, safeCopyOfExtraComponents, extraComponentsLength);
}
static int FetchEntityInfoComponent(IComponentBuilder[] defaultEntities)
{
int length = defaultEntities.Length;
int index = -1;
for (var i = 0; i < length; i++)
{
//the special entity already exists
if (defaultEntities[i].GetEntityComponentType() == ComponentBuilderUtilities.ENTITY_INFO_COMPONENT)
{
index = i;
break;
}
}
return index;
}
public IComponentBuilder[] componentsToBuild => _componentsToBuild;
IComponentBuilder[] _componentsToBuild;
}
}