Skip to content

Beyond EntityDescriptor: polymorphic and dynamic EntityDescriptor

Sebastiano Mandalà edited this page Aug 7, 2019 · 5 revisions

The IEntityDescriptor interface is fundamental to develop Svelto code as it represents the Entity to build. More specifically, it represents the set of entity views (EntityViewStructs and EntityStructs) to generate and add in the entity database.

In theory you are supposed to implement the IEntityDescriptor interface using this pattern:

namespace GameServer.ItemStorage.ServerPlayerInventory
{
    class ServerCharacterEntityDescriptor : IEntityDescriptor
    {
        static ServerCharacterEntityDescriptor()
        {
            EntityBuilders = new IEntityBuilder[]
            {
                new EntityBuilder<ServerInventoryEntityViewStruct>(),
                new EntityBuilder<ServerInventoryMoveEntityViewStruct>(),
                new EntityBuilder<ServerInventorySavingEntityViewStruct>(),
                new EntityBuilder<ServerInventorySplitEntityViewStruct>(),
                new EntityBuilder<ServerInventoryMergeEntityViewStruct>(),
                new EntityBuilder<ServerInventoryCollectAllEntityViewStruct>(),
                new EntityBuilder<ServerEquipmentMoveEntityViewStruct>(),
            };
        }

        public IEntityBuilder[] entitiesToBuild { get { return EntityBuilders; } }

        static readonly IEntityBuilder[] EntityBuilders;
    }
}

however most of the times is enough to use the GenericEntityDescriptor:

class ClientAIAgentEntityDescriptor : GenericEntityDescriptor<ConditionKillClientAgentEntityViewStruct, InterestManagementTargetEntityViewClient>
	{ }

EntityDescriptors are explicitly used when BuildEntity<>, RemoveEntity<> and SwapEntity<> functions are used.

This would force to know the entity to remove and swap inside the engines, meaning that theoretically it's not possible to write generic engines to remove or swap entities. This is not strictly true. The tool to use to write generic engines that can use RemoveEntity and SwapEntity is the ExtendibleEntityDescriptor, it's used like this:

class BaseHandEntityDescriptor : GenericEntityDescriptor<HandheldRemovalEntityView>
    {
    }

 class ToolEntityDescriptor : ExtendibleEntityDescriptor<BaseHandEntityDescriptor>
    {
        public ToolEntityDescriptor() : base(new IEntityBuilder[]
        {
            new EntityBuilder<DurabilityThirdPersonItemNode>()
        })
        {
        }
    }
    
    class AnimatedToolEntityDescriptor : ExtendibleEntityDescriptor<BaseHandEntityDescriptor>
    {
        public AnimatedToolEntityDescriptor() : base(new IEntityBuilder[]
        {
            new EntityBuilder<ThirdPersonToolAnimationNode>(),
            new EntityBuilder<DurabilityThirdPersonItemNode>()
        })
        {}
    }

and then write a generic engines like this:

namespace Game.Handhelds
{
    class RemoteHandheldRemovalEngine :
        SingleEntityViewEngine<HandheldRemovalEntityView>,
        IQueryingEntitiesEngine
    {
        public IEntitiesDB entitiesDB { private get; set; }

        public RemoteHandheldRemovalEngine(IEntityFunctions entityFunctions)
        {
            _entityFunctions = entityFunctions;

        }

        public void Ready()
        {}

        protected override void Add(HandheldRemovalEntityView characterEVS)
        {}

        protected override void Remove(HandheldRemovalEntityView entityView)
        {
            int ownerId = entityView.ownerComponent.ownerId;
            int count;
            var EVSs = entitiesDB.QueryEntities<HandheldRemovalEntityView>(GROUP, out count);
            for (int i = 0; i < count; i++)
            {
                var EVS = EVSs[i];
                if (EVS.ownerComponent.ownerId == ownerId)
                {
                    _entityFunctions.RemoveEntity<BaseHandEntityDescriptor>(EVS.ID);
                }
            }
        }

        readonly IEntityFunctions _entityFunctions;
    }
}

The DynamicEntityDescriptor is instead used to compose behaviors dynamically, when the entity must be built. This may be useful to compose entity views from config files and similar occasions. Although DynamicEntityDescriptor is a struct, it allocates every time it's newed. This can be avoided. EntityDescriptors are ultimately considered static classes and they never change at run time. Therefore is possible to preallocate the DynamicEntityDescriptors if they are setup at initialization time and use the preallocated descriptors at run time.