using System; using System.Collections.Generic; using System.Reflection; using System.Threading; 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, _IInternalEntityComponent { internal static readonly Type ENTITY_COMPONENT_TYPE; internal static readonly bool IS_ENTITY_VIEW_COMPONENT; static readonly string ENTITY_COMPONENT_NAME; static readonly bool IS_UNMANAGED; #if SLOW_SVELTO_SUBMISSION public static readonly bool HAS_EGID; public static readonly bool HAS_REFERENCE; #endif static ComponentBuilder() { ENTITY_COMPONENT_TYPE = typeof(T); IS_ENTITY_VIEW_COMPONENT = typeof(IEntityViewComponent).IsAssignableFrom(ENTITY_COMPONENT_TYPE); #if SLOW_SVELTO_SUBMISSION HAS_EGID = typeof(INeedEGID).IsAssignableFrom(ENTITY_COMPONENT_TYPE); HAS_REFERENCE = typeof(INeedEntityReference).IsAssignableFrom(ENTITY_COMPONENT_TYPE); SetEGIDWithoutBoxing.Warmup(); #endif ENTITY_COMPONENT_NAME = ENTITY_COMPONENT_TYPE.ToString(); IS_UNMANAGED = TypeCache.isUnmanaged; //attention this is important as it serves as warm up for Type #if UNITY_NATIVE if (IS_UNMANAGED) EntityComponentIDMap.Register(new Filler()); #endif ComponentTypeID.Init(); 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 && TypeCache.isUnmanaged == false) throw new Exception( $"Entity Component check failed, unexpected struct type (must be unmanaged) {ENTITY_COMPONENT_TYPE}"); } } public ComponentBuilder() { _initializer = default; } public ComponentBuilder(in T initializer) : this() { _initializer = initializer; } public bool isUnmanaged => IS_UNMANAGED; public ComponentID getComponentID => ComponentTypeID.id; static readonly ThreadLocal _localCache = new ThreadLocal(() => new EntityViewComponentCache()); public void BuildEntityAndAddToList(ITypeSafeDictionary dictionary, EGID egid, IEnumerable implementors) { var castedDic = dictionary as ITypeSafeDictionary; if (IS_ENTITY_VIEW_COMPONENT) { T entityComponent = default; 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, implementors, _localCache.Value); 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.EnsureCapacity(size); } readonly T _initializer; internal class EntityViewComponentCache { internal readonly FasterList>> cachedFields; internal readonly Dictionary cachedTypes; //this is just a local static cache that is cleared after every use #if DEBUG && !PROFILE_SVELTO internal readonly Dictionary> implementorsByType; #else internal readonly Dictionary implementorsByType; #endif internal 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() { } } } }