using System; using Svelto.DataStructures; 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() { internal DynamicEntityDescriptor(bool isExtendible) : this() { var defaultEntities = EntityDescriptorTemplate.descriptor.componentsToBuild; var length = defaultEntities.Length; if (FetchEntityInfoComponent(defaultEntities) == -1) { _componentsToBuild = new IComponentBuilder[length + 1]; Array.Copy(defaultEntities, 0, _componentsToBuild, 0, length); //assign it after otherwise the previous copy will overwrite the value in case the item //is already present _componentsToBuild[length] = new ComponentBuilder(new EntityInfoComponent { componentsToBuild = _componentsToBuild }); } else { _componentsToBuild = new IComponentBuilder[length]; Array.Copy(defaultEntities, 0, _componentsToBuild, 0, length); } } public DynamicEntityDescriptor(IComponentBuilder[] extraEntityBuilders) : this(true) { var extraEntitiesLength = extraEntityBuilders.Length; _componentsToBuild = Construct(extraEntitiesLength, extraEntityBuilders); } public DynamicEntityDescriptor(FasterList extraEntityBuilders) : this(true) { var extraEntities = extraEntityBuilders.ToArrayFast(out _); var extraEntitiesLength = extraEntityBuilders.count; _componentsToBuild = Construct((int)extraEntitiesLength, extraEntities); } public void ExtendWith() where T : IEntityDescriptor, new() { var extraEntities = EntityDescriptorTemplate.descriptor.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, IEntityComponent { IComponentBuilder[] extraEntities = { new ComponentBuilder() }; _componentsToBuild = Construct(extraEntities.Length, extraEntities); } public void Add() where T : struct, IEntityComponent where U : struct, IEntityComponent { IComponentBuilder[] extraEntities = { new ComponentBuilder(), new ComponentBuilder() }; _componentsToBuild = Construct(extraEntities.Length, extraEntities); } public void Add() where T : struct, IEntityComponent where U : struct, IEntityComponent where V : struct, IEntityComponent { 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.value; } 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; } }