using System; using System.Collections.Generic; using System.Reflection; using DBC.ECS; using Svelto.Common; using Svelto.DataStructures; using Svelto.ECS.Hybrid; using Svelto.ECS.Internal; using Svelto.Utilities; namespace Svelto.ECS { struct ComponentBuilderComparer : IEqualityComparer { public bool Equals(IComponentBuilder x, IComponentBuilder y) { return x.GetEntityComponentType() == y.GetEntityComponentType(); } public int GetHashCode(IComponentBuilder obj) { return obj.GetEntityComponentType().GetHashCode(); } } public class ComponentBuilder : IComponentBuilder where T : struct, IEntityComponent { internal static readonly Type ENTITY_COMPONENT_TYPE; public static readonly bool HAS_EGID; internal static readonly bool IS_ENTITY_VIEW_COMPONENT; static readonly T DEFAULT_IT; static readonly string ENTITY_COMPONENT_NAME; static readonly bool IS_UNMANAGED; public static bool HAS_REFERENCE; static ComponentBuilder() { ENTITY_COMPONENT_TYPE = typeof(T); DEFAULT_IT = default; IS_ENTITY_VIEW_COMPONENT = typeof(IEntityViewComponent).IsAssignableFrom(ENTITY_COMPONENT_TYPE); HAS_EGID = typeof(INeedEGID).IsAssignableFrom(ENTITY_COMPONENT_TYPE); HAS_REFERENCE = typeof(INeedEntityReference).IsAssignableFrom(ENTITY_COMPONENT_TYPE); ENTITY_COMPONENT_NAME = ENTITY_COMPONENT_TYPE.ToString(); IS_UNMANAGED = ENTITY_COMPONENT_TYPE.IsUnmanagedEx(); if (IS_UNMANAGED) EntityComponentIDMap.Register(new Filler()); SetEGIDWithoutBoxing.Warmup(); ComponentBuilderUtilities.CheckFields(ENTITY_COMPONENT_TYPE, IS_ENTITY_VIEW_COMPONENT); if (IS_ENTITY_VIEW_COMPONENT) { EntityViewComponentCache.InitCache(); } else { if (ENTITY_COMPONENT_TYPE != ComponentBuilderUtilities.ENTITY_INFO_COMPONENT && ENTITY_COMPONENT_TYPE.IsUnmanagedEx() == false) throw new Exception( $"Entity Component check failed, unexpected struct type (must be unmanaged) {ENTITY_COMPONENT_TYPE}"); } } public ComponentBuilder() { _initializer = DEFAULT_IT; } public ComponentBuilder(in T initializer) : this() { _initializer = initializer; } public bool isUnmanaged => IS_UNMANAGED; public void BuildEntityAndAddToList(ITypeSafeDictionary dictionary, EGID egid, IEnumerable implementors) { var castedDic = dictionary as ITypeSafeDictionary; T entityComponent = default; if (IS_ENTITY_VIEW_COMPONENT) { Check.Require(castedDic.ContainsKey(egid.entityID) == false , $"building an entity with already used entity id! id: '{(ulong) egid}', {ENTITY_COMPONENT_NAME}"); this.SetEntityViewComponentImplementors(ref entityComponent, EntityViewComponentCache.cachedFields , implementors, EntityViewComponentCache.implementorsByType , EntityViewComponentCache.cachedTypes); castedDic.Add(egid.entityID, entityComponent); } else { Check.Require(!castedDic.ContainsKey(egid.entityID) , $"building an entity with already used entity id! id: '{egid.entityID}'"); castedDic.Add(egid.entityID, _initializer); } } void IComponentBuilder.Preallocate(ITypeSafeDictionary dictionary, uint size) { Preallocate(dictionary, size); } public ITypeSafeDictionary CreateDictionary(uint size) { return TypeSafeDictionaryFactory.Create(size); } public Type GetEntityComponentType() { return ENTITY_COMPONENT_TYPE; } public override int GetHashCode() { return _initializer.GetHashCode(); } static void Preallocate(ITypeSafeDictionary dictionary, uint size) { dictionary.ResizeTo(size); } readonly T _initializer; /// /// Note: this static class will hold forever the references of the entities implementors. These references /// are not even cleared when the engines root is destroyed, as they are static references. /// It must be seen as an application-wide cache system. Honestly, I am not sure if this may cause leaking /// issues in some kind of applications. To remember. /// static class EntityViewComponentCache { internal static readonly FasterList>> cachedFields; internal static readonly Dictionary cachedTypes; #if DEBUG && !PROFILE_SVELTO internal static readonly Dictionary> implementorsByType; #else internal static readonly Dictionary implementorsByType; #endif static EntityViewComponentCache() { cachedFields = new FasterList>>(); var type = typeof(T); var fields = type.GetFields(BindingFlags.Public | BindingFlags.Instance); for (var i = fields.Length - 1; i >= 0; --i) { var field = fields[i]; if (field.FieldType.IsInterface == true) { var setter = FastInvoke.MakeSetter(field); //for each interface, cache the setter for this type cachedFields.Add(new KeyValuePair>(field.FieldType, setter)); } } #if DEBUG && !PROFILE_SVELTO if (fields.Length == 0) Console.LogWarning($"No fields found in component {type}. Are you declaring only properties?"); #endif cachedTypes = new Dictionary(); #if DEBUG && !PROFILE_SVELTO implementorsByType = new Dictionary>(); #else implementorsByType = new Dictionary(); #endif } internal static void InitCache() { } } } }