static ComponentType[] GetComponentTypes(Type jobType, Type interfaceType, out int processCount, out ComponentType[] changedFilter) { var genericArgs = interfaceType.GetGenericArguments(); var executeMethodParameters = jobType.GetMethod("Execute").GetParameters(); var componentTypes = new List <ComponentType>(); var changedFilterTypes = new List <ComponentType>(); // void Execute(Entity entity, int index, ref T0 data0, ref T1 data1, ref T2 data2); // First two parameters are optional, depending on the interface name used. var methodParameterOffset = genericArgs.Length != executeMethodParameters.Length ? 2 : 0; for (var i = 0; i < genericArgs.Length; i++) { var isReadonly = executeMethodParameters[i + methodParameterOffset].GetCustomAttribute(typeof(ReadOnlyAttribute)) != null; var type = new ComponentType(genericArgs[i], isReadonly ? ComponentType.AccessMode.ReadOnly : ComponentType.AccessMode.ReadWrite); componentTypes.Add(type); var isChangedFilter = executeMethodParameters[i + methodParameterOffset].GetCustomAttribute(typeof(ChangedFilterAttribute)) != null; if (isChangedFilter) { changedFilterTypes.Add(type); } } var subtractive = jobType.GetCustomAttribute <ExcludeComponentAttribute>(); if (subtractive != null) { foreach (var type in subtractive.ExcludeComponents) { componentTypes.Add(ComponentType.Exclude(type)); } } var requiredTags = jobType.GetCustomAttribute <RequireComponentTagAttribute>(); if (requiredTags != null) { foreach (var type in requiredTags.TagComponents) { componentTypes.Add(ComponentType.ReadOnly(type)); } } processCount = genericArgs.Length; changedFilter = changedFilterTypes.ToArray(); return(componentTypes.ToArray()); }
/// <summary> /// Gets the array of <see cref="ComponentType"/> objects included in this EntityQuery. /// </summary> /// <returns>Array of ComponentTypes</returns> internal ComponentType[] GetQueryTypes() { #if !NET_DOTS var types = new HashSet <ComponentType>(); #else var types = new SlowListSet <ComponentType>(); #endif for (var i = 0; i < m_QueryData->ArchetypeQueryCount; ++i) { for (var j = 0; j < m_QueryData->ArchetypeQuery[i].AnyCount; ++j) { types.Add(TypeManager.GetType(m_QueryData->ArchetypeQuery[i].Any[j])); } for (var j = 0; j < m_QueryData->ArchetypeQuery[i].AllCount; ++j) { types.Add(TypeManager.GetType(m_QueryData->ArchetypeQuery[i].All[j])); } for (var j = 0; j < m_QueryData->ArchetypeQuery[i].NoneCount; ++j) { types.Add(ComponentType.Exclude(TypeManager.GetType(m_QueryData->ArchetypeQuery[i].None[j]))); } } #if !NET_DOTS var array = new ComponentType[types.Count]; var t = 0; foreach (var type in types) { array[t++] = type; } return(array); #else return(types.ToArray()); #endif }
public ComponentGroupArrayStaticCache(Type type, EntityManager entityManager, ComponentSystemBase system) { var fields = type.GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public); var componentFieldOffsetsBuilder = new List <int>(); var componentTypesBuilder = new List <ComponentType>(); var componentDataFieldOffsetsBuilder = new List <int>(); var componentDataTypesBuilder = new List <ComponentType>(); var subtractiveComponentTypesBuilder = new List <ComponentType>(); foreach (var field in fields) { var fieldType = field.FieldType; var offset = UnsafeUtility.GetFieldOffset(field); if (fieldType.IsPointer) { var isReadOnly = field.GetCustomAttributes(typeof(ReadOnlyAttribute), true).Length != 0; var accessMode = isReadOnly ? ComponentType.AccessMode.ReadOnly : ComponentType.AccessMode.ReadWrite; var elementType = fieldType.GetElementType(); #if ENABLE_UNITY_COLLECTIONS_CHECKS if (!typeof(IComponentData).IsAssignableFrom(elementType) && elementType != typeof(Entity)) { throw new ArgumentException( $"{type}.{field.Name} is a pointer type but not a IComponentData. Only IComponentData or Entity may be a pointer type for enumeration."); } #endif componentDataFieldOffsetsBuilder.Add(offset); componentDataTypesBuilder.Add(new ComponentType(elementType, accessMode)); } else if (TypeManager.UnityEngineComponentType?.IsAssignableFrom(fieldType) ?? false) { componentFieldOffsetsBuilder.Add(offset); componentTypesBuilder.Add(fieldType); } else if (fieldType.IsGenericType && fieldType.GetGenericTypeDefinition() == typeof(ExcludeComponent <>)) { subtractiveComponentTypesBuilder.Add(ComponentType.Exclude(fieldType.GetGenericArguments()[0])); } #if ENABLE_UNITY_COLLECTIONS_CHECKS else if (typeof(IComponentData).IsAssignableFrom(fieldType)) { throw new ArgumentException( $"{type}.{field.Name} must be an unsafe pointer to the {fieldType}. Like this: {fieldType}* {field.Name};"); } else { throw new ArgumentException($"{type}.{field.Name} can not be used in a component enumerator"); } #endif } #if ENABLE_UNITY_COLLECTIONS_CHECKS if (componentTypesBuilder.Count + componentDataTypesBuilder.Count > ComponentGroupArrayData.kMaxStream) { throw new ArgumentException( $"{type} has too many component references. A ComponentGroup Array can have up to {ComponentGroupArrayData.kMaxStream}."); } #endif ComponentDataCount = componentDataTypesBuilder.Count; ComponentCount = componentTypesBuilder.Count; componentDataTypesBuilder.AddRange(componentTypesBuilder); componentDataTypesBuilder.AddRange(subtractiveComponentTypesBuilder); ComponentTypes = componentDataTypesBuilder.ToArray(); componentDataFieldOffsetsBuilder.AddRange(componentFieldOffsetsBuilder); ComponentFieldOffsets = componentDataFieldOffsetsBuilder.ToArray(); ComponentGroup = system.GetComponentGroupInternal(ComponentTypes); SafetyManager = entityManager.ComponentJobSafetyManager; CachedType = type; }
private static string CollectInjectedGroup(ComponentSystemBase system, FieldInfo groupField, Type injectedGroupType, out FieldInfo entityArrayField, out FieldInfo indexFromEntityField, InjectionContext injectionContext, out FieldInfo lengthField, out FieldInfo componentGroupIndexField, ISet <ComponentType> componentRequirements, ICollection <InjectionData> componentDataInjections, ICollection <InjectionData> bufferDataInjections, ICollection <InjectionData> sharedComponentInjections) { //@TODO: Improve error messages... var fields = injectedGroupType.GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public); entityArrayField = null; indexFromEntityField = null; lengthField = null; componentGroupIndexField = null; foreach (var field in fields) { var isReadOnly = field.GetCustomAttributes(typeof(ReadOnlyAttribute), true).Length != 0; //@TODO: Prevent using GameObjectEntity, it will never show up. Point to GameObjectArray instead... if (field.FieldType.IsGenericType && field.FieldType.GetGenericTypeDefinition() == typeof(ComponentDataArray <>)) { var injection = new InjectionData(field, field.FieldType.GetGenericArguments()[0], isReadOnly); componentDataInjections.Add(injection); componentRequirements.Add(injection.ComponentType); } else if (field.FieldType.IsGenericType && field.FieldType.GetGenericTypeDefinition() == typeof(ExcludeComponent <>)) { componentRequirements.Add(ComponentType.Exclude(field.FieldType.GetGenericArguments()[0])); } else if (field.FieldType.IsGenericType && field.FieldType.GetGenericTypeDefinition() == typeof(BufferArray <>)) { var injection = new InjectionData(field, field.FieldType.GetGenericArguments()[0], isReadOnly); bufferDataInjections.Add(injection); componentRequirements.Add(injection.ComponentType); } else if (field.FieldType.IsGenericType && field.FieldType.GetGenericTypeDefinition() == typeof(SharedComponentDataArray <>)) { if (!isReadOnly) { return ($"{system.GetType().Name}:{groupField.Name} SharedComponentDataArray<> must always be injected as [ReadOnly]"); } var injection = new InjectionData(field, field.FieldType.GetGenericArguments()[0], true); sharedComponentInjections.Add(injection); componentRequirements.Add(injection.ComponentType); } else if (field.FieldType == typeof(EntityArray)) { // Error on multiple EntityArray if (entityArrayField != null) { return ($"{system.GetType().Name}:{groupField.Name} An [Inject] struct, may only contain a single EntityArray"); } entityArrayField = field; } else if (field.FieldType == typeof(int)) { if (field.Name != "Length" && field.Name != "GroupIndex") { return ($"{system.GetType().Name}:{groupField.Name} Int in an [Inject] struct should be named \"Length\" (group length) or \"GroupIndex\" (index in ComponentGroup[])"); } if (!field.IsInitOnly) { return ($"{system.GetType().Name}:{groupField.Name} {field.Name} must use the \"readonly\" keyword"); } if (field.Name == "Length") { lengthField = field; } if (field.Name == "GroupIndex") { componentGroupIndexField = field; } } else { var hook = InjectionHookSupport.HookFor(field); if (hook == null) { return ($"{system.GetType().Name}:{groupField.Name} [Inject] may only be used on ComponentDataArray<>, ComponentArray<>, TransformAccessArray, EntityArray, {string.Join(",", InjectionHookSupport.Hooks.Select(h => h.FieldTypeOfInterest.Name))} and int Length."); } var error = hook.ValidateField(field, isReadOnly, injectionContext); if (error != null) { return(error); } injectionContext.AddEntry(hook.CreateInjectionInfoFor(field, isReadOnly)); } } if (injectionContext.HasComponentRequirements) { foreach (var requirement in injectionContext.ComponentRequirements) { componentRequirements.Add(requirement); } } return(null); }