static FunctionalityInjectionModuleEditor() { #if UNITY_2019_2_OR_NEWER foreach (var type in TypeCache.GetTypesDerivedFrom <IFunctionalityProvider>()) { if (type.IsAbstract || type.IsInterface) { continue; } k_AllProviderTypes.Add(type); } foreach (var type in TypeCache.GetTypesDerivedFrom <IFunctionalitySubscriber>()) { if (type.IsAbstract || type.IsInterface) { continue; } k_AllSubscriberTypes.Add(type); } #else typeof(IFunctionalityProvider).GetImplementationsOfInterface(k_AllProviderTypes); typeof(IFunctionalitySubscriber).GetImplementationsOfInterface(k_AllSubscriberTypes); #endif k_AllProviderTypes.Sort((a, b) => { var optionsA = FunctionalityIsland.GetProviderSelectionOptions(a); var optionsB = FunctionalityIsland.GetProviderSelectionOptions(b); return(optionsB.Priority.CompareTo(optionsA.Priority)); }); }
/// <summary> /// Set up functionality providers from the list of default providers /// This allows custom serialized data to be set up on prefabs for providers /// </summary> /// <param name="island">The functionality island on which to set up default providers</param> /// <param name="requiredTraits">The required traits that must be satisfied by providers</param> /// <param name="newProviders">(Optional) A list to which new providers will be added</param> public static void RequireProvidersWithDefaultProviders(this FunctionalityIsland island, HashSet <TraitDefinition> requiredTraits, List <IFunctionalityProvider> newProviders = null) { if (requiredTraits.Count == 0) { return; } Profiler.BeginSample(FunctionalityIsland.SetupDefaultProvidersProfilerLabel); island.CheckSetup(); var functionalityInjectionModuleLogging = ModuleLoaderDebugSettings.instance.functionalityInjectionModuleLogging; if (functionalityInjectionModuleLogging) { var list = new StringBuilder(); if (requiredTraits.Count > 0) { foreach (var trait in requiredTraits) { list.Append(trait); list.Append(", "); } list.Length -= 2; } Debug.LogFormat("Requiring providers with defaults on: {0}", string.Join(", ", list)); } // Copy the collection so that we don't modify the original requiredTraits = new HashSet <TraitDefinition>(requiredTraits); // Clear out traits that are already being provided foreach (var provider in island.uniqueProviders) { var traitsProvider = provider as IProvidesTraits; if (traitsProvider != null) { var providedTraits = traitsProvider.GetProvidedTraits(); foreach (var trait in providedTraits) { requiredTraits.Remove(trait); } } } k_DefaultProviderMap.Clear(); foreach (var row in island.defaultProviders) { var providerTypeName = row.providerTypeName; var prefab = row.defaultProviderPrefab; if (prefab != null) { var providersInPrefab = prefab.GetComponentsInChildren <IFunctionalityProvider>(); foreach (var provider in providersInPrefab) { k_DefaultProviderMap[provider.GetType()] = row; } continue; } var defaultProviderType = row.defaultProviderType; if (defaultProviderType == null) { var defaultProviderTypeName = row.defaultProviderTypeName; Debug.LogWarningFormat("Cannot set up default provider for {0} in {1}. Type {2} cannot be found.", providerTypeName, island.name, defaultProviderTypeName); continue; } k_DefaultProviderMap[defaultProviderType] = row; } // Determine which provider interfaces are needed for the given subscribers k_NewProviderPrefabInstances.Clear(); var currentPlatform = ModuleLoaderCore.GetCurrentPlatform(); while (CheckMissingTraits(island, requiredTraits) > 0) { var providerAdded = false; k_RequiredTraits.Clear(); k_RequiredTraits.UnionWith(requiredTraits); // Copy the collection in order to modify requiredTraits foreach (var trait in k_RequiredTraits) { List <Type> providerTypes; if (!k_TraitProviderMap.TryGetValue(trait, out providerTypes)) { continue; } // Make sure potential provider types can be added k_ProviderTypes.Clear(); foreach (var providerType in providerTypes) { var options = FunctionalityIsland.GetProviderSelectionOptions(providerType); if (options != null) { if (options.DisallowAutoCreation) { if (!k_DefaultProviderMap.TryGetValue(providerType, out var defaultProvider) || defaultProvider.defaultProviderPrefab == null) { continue; } } var excludedPlatforms = options.ExcludedPlatforms; if (excludedPlatforms != null && options.ExcludedPlatforms.Contains(currentPlatform)) { continue; } } if (island.CanAddProviderType(providerType)) { k_ProviderTypes.Add(providerType); } } if (k_ProviderTypes.Count == 0) { continue; } k_ProviderTypes.Sort((a, b) => { var compare = GetProviderScore(b, requiredTraits).CompareTo(GetProviderScore(a, requiredTraits)); if (compare != 0) { return(compare); } compare = k_DefaultProviderMap.ContainsKey(b).CompareTo(k_DefaultProviderMap.ContainsKey(a)); if (compare != 0) { return(compare); } var optionsA = FunctionalityIsland.GetProviderSelectionOptions(a); var optionsB = FunctionalityIsland.GetProviderSelectionOptions(b); return(optionsB.Priority.CompareTo(optionsA.Priority)); }); var firstProvider = k_ProviderTypes[0]; if (k_ProviderTypes.Count > 1) { var secondProvider = k_ProviderTypes[1]; var firstScore = GetProviderScore(firstProvider, requiredTraits); var secondScore = GetProviderScore(secondProvider, requiredTraits); var firstIsDefault = k_DefaultProviderMap.ContainsKey(firstProvider); var secondIsDefault = k_DefaultProviderMap.ContainsKey(secondProvider); var firstPriority = FunctionalityIsland.GetProviderSelectionOptions(firstProvider).Priority; var secondPriority = FunctionalityIsland.GetProviderSelectionOptions(secondProvider).Priority; if (firstScore == secondScore && firstIsDefault == secondIsDefault && firstPriority == secondPriority) { Debug.LogWarning(string.Format(k_EqualScoreMessage, trait, firstProvider, secondProvider, island.name), island); } } if (k_DefaultProviderMap.TryGetValue(firstProvider, out var row)) { var prefab = row.defaultProviderPrefab; var providesRequiredTrait = false; if (prefab != null) { var prefabComponents = prefab.gameObject.GetComponentsInChildren <IProvidesTraits>(); foreach (var prefabComponent in prefabComponents) { var providedTraits = prefabComponent.GetProvidedTraits(); foreach (var providedTrait in providedTraits) { providesRequiredTrait |= requiredTraits.Remove(providedTrait); } } if (!providesRequiredTrait) { continue; } GameObject instance; if (!k_NewProviderPrefabInstances.TryGetValue(prefab, out instance)) { if (functionalityInjectionModuleLogging) { Debug.LogFormat("Functionality Injection Module creating default provider: {0}", prefab); } instance = GameObjectUtils.Instantiate(prefab); k_NewProviderPrefabInstances[prefab] = instance; } var providersInPrefab = instance.GetComponentsInChildren <IFunctionalityProvider>(); foreach (var provider in providersInPrefab) { AddRequirementsAndRemoveProvidedTraits(requiredTraits, provider); island.AddProvider(provider.GetType(), provider); } continue; } var defaultProviderTypeName = row.defaultProviderTypeName; var defaultProviderType = row.defaultProviderType; var providerType = row.providerType; TraitDefinition[] staticProvided; if (!k_ProvidedTraits.TryGetValue(defaultProviderType, out staticProvided)) { continue; } foreach (var providedTrait in staticProvided) { providesRequiredTrait |= requiredTraits.Remove(providedTrait); } if (!providesRequiredTrait) { continue; } var vanillaProvider = FunctionalityIsland.GetOrCreateProviderInstance(defaultProviderType, providerType); if (vanillaProvider == null) { Debug.LogWarningFormat("Cannot instantiate {0} as an IFunctionalityProvider.", defaultProviderTypeName); continue; } if (newProviders != null) { newProviders.Add(vanillaProvider); } providerAdded = true; AddRequirementsAndRemoveProvidedTraits(requiredTraits, vanillaProvider); island.AddProvider(defaultProviderType, vanillaProvider); } else { // Spawn or gain access to the class needed to support the functionality var provider = FunctionalityIsland.GetOrCreateProviderInstance(firstProvider, firstProvider); if (provider == null) { Debug.LogWarningFormat("Cannot instantiate {0} as an IFunctionalityProvider.", firstProvider.Name); continue; } if (newProviders != null) { newProviders.Add(provider); } providerAdded = true; AddRequirementsAndRemoveProvidedTraits(requiredTraits, provider); island.AddProvider(firstProvider, provider); } break; } if (!providerAdded) { break; } } island.InjectFunctionalityInDefaultProviders(k_NewProviderPrefabInstances, newProviders); island.ActivateProviderGameObjects(); Profiler.EndSample(); }
public override void OnInspectorGUI() { using (var check = new EditorGUI.ChangeCheckScope()) { EditorGUILayout.PropertyField(m_DefaultIslandProperty); m_ShowIslands = EditorGUILayout.Foldout(m_ShowIslands, "Current Islands", true); if (m_ShowIslands) { DrawCurrentIslands(); } if (check.changed) { serializedObject.ApplyModifiedProperties(); } } const float providerColumnWidthRatio = 0.5f; var providerWidth = EditorGUIUtility.currentViewWidth * providerColumnWidthRatio; var foldoutStyle = styles.ProviderTypesFoldoutStyle; styles.ProviderTypesFoldoutStyle.fixedWidth = providerWidth; var providerColumnWidth = GUILayout.Width(providerWidth); var priorityColumnWidth = GUILayout.Width(k_PriorityColumnWidth); using (new EditorGUILayout.HorizontalScope()) { m_ShowProviderTypeList = EditorGUILayout.Foldout(m_ShowProviderTypeList, "Provider Types", true, foldoutStyle); if (m_ShowProviderTypeList) { GUILayout.Space(providerWidth - k_PriorityHeaderOffset); GUILayout.Label("Priorities", priorityColumnWidth); GUILayout.Label("Excluded Platforms"); } } if (m_ShowProviderTypeList) { using (new EditorGUI.IndentLevelScope()) { foreach (var type in k_AllProviderTypes) { using (new EditorGUILayout.HorizontalScope()) { var priority = 0; var excludedPlatforms = string.Empty; var priorityAttribute = FunctionalityIsland.GetProviderSelectionOptions(type); if (priorityAttribute != null) { priority = priorityAttribute.Priority; var platforms = priorityAttribute.ExcludedPlatforms; if (platforms != null) { excludedPlatforms = String.Join(", ", platforms); } } MonoScript script; if (k_MonoScripts.TryGetValue(type, out script)) { EditorGUILayout.ObjectField(script, typeof(MonoScript), false, providerColumnWidth); } else { EditorGUILayout.LabelField(type.GetFullNameWithGenericArguments(), providerColumnWidth); } EditorGUILayout.LabelField(priority.ToString(), priorityColumnWidth); EditorGUILayout.LabelField(string.Join(", ", excludedPlatforms)); } } } } m_ShowSubscriberTypeList = EditorGUILayout.Foldout(m_ShowSubscriberTypeList, "Subscriber Types", true); if (m_ShowSubscriberTypeList) { using (new EditorGUI.IndentLevelScope()) { foreach (var type in k_AllSubscriberTypes) { MonoScript script; if (k_MonoScripts.TryGetValue(type, out script)) { EditorGUILayout.ObjectField(script, typeof(MonoScript), false); } else { EditorGUILayout.LabelField(type.GetFullNameWithGenericArguments()); } } } } }