static int CheckMissingTraits <T>(FunctionalityIsland island, HashSet <T> requiredTraits) where T : TraitDefinition { var compareSet = new HashSet <TraitDefinition>(requiredTraits); var existingTraits = new HashSet <TraitDefinition>(); foreach (var kvp in island.providers) { var traitsProvider = kvp.Value as IProvidesTraits; if (traitsProvider == null) { continue; } var providedTraits = traitsProvider.GetProvidedTraits(); existingTraits.UnionWith(providedTraits); } foreach (var definition in existingTraits) { compareSet.Remove(definition); } return(compareSet.Count); }
/// <summary> /// Inject functionality on all modules using the provided FunctionalityIsland /// </summary> /// <param name="island">The functionality island to use for functionality injection</param> public void InjectFunctionalityInModules(FunctionalityIsland island) { foreach (var module in m_Modules) { island.InjectFunctionalitySingle(module); } }
void IModule.UnloadModule() { m_SlowTaskModule.ClearTasks(); m_SimulationPollTask = null; QueryObjectMapping.Map.Clear(); EditorApplication.update -= Update; SimulationSceneModule.SimulationSceneOpened -= OnSimulationSceneOpened; SimulationSceneModule.SimulationSceneClosing -= OnSimulationSceneClosing; EditorOnlyDelegates.IsSimulatingTemporal = null; RestoreOriginalActiveIsland(); m_SimulationRestartNeeded = false; NextSimModeSelection = SimulationModeSelection.NoModePreference; KeepDataBetweenSimulations = false; m_SimulationPollTask = null; m_OriginalActiveIsland = null; CleanupProviders(); simulating = false; simulatingTemporal = false; functionalityIsland = null; providersRoot = null; m_SimulationContext.Clear(); }
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)); }); }
void OnEnable() { m_Island = (FunctionalityIsland)target; if (ShowAllProviderTypes) { m_ShowAllProviderTypes = true; } }
/// <summary> /// Set the active island /// </summary> /// <param name="island">The island to set as the active island; it must have already been added</param> public void SetActiveIsland(FunctionalityIsland island) { Assert.IsTrue(m_Islands.Contains(island), string.Format("Cannot set active island to {0}. It is not in the list of islands", island)); activeIsland = island; if (activeIslandChanged != null) { activeIslandChanged(island); } }
void CacheOriginalActiveIsland() { // This module does not override the active island in play mode if (Application.isPlaying) { return; } m_OriginalActiveIsland = m_FIModule.activeIsland; }
void IModuleDependency <FunctionalityInjectionModule> .ConnectDependency(FunctionalityInjectionModule dependency) { m_FIModule = dependency; // TODO: Collect all scenes and add islands if modules persist between scene loads #if UNITY_EDITOR var session = Application.isPlaying ? MARSSession.Instance : MarsRuntimeUtils.GetMarsSessionInActiveScene(); #else var session = MARSSession.Instance; #endif // TODO: Don't load modules for non-MARS scenes if (session == null) { return; } FunctionalityIsland island = null; if (session.island) { island = session.island; } #if UNITY_EDITOR if (Application.isPlaying && m_SimulateInPlayMode) { if (m_SimulateDiscovery) { if (m_SimulatedDiscoveryIsland == null) { Debug.LogWarning("There is no simulated discovery island set in the MARSSceneModule to be used for play mode simulation"); return; } island = m_SimulatedDiscoveryIsland; } else { if (m_SimulationIsland == null) { Debug.LogWarning("There is no simulation island set in the MARSSceneModule to be used for play mode simulation"); return; } island = m_SimulationIsland; } } #endif if (island) { dependency.AddIsland(island); } }
/// <summary> /// Set up a Functionality Island and add it to the list of islands /// This must be done before the FunctionalityInjectionModule is loaded /// The recommended pattern is to implement IModuleDependency<FunctionalityInjectionModule> and add the /// island in ConnectDependency. If a hard dependency is not possible, ensure your module has a load order /// which is earlier than FunctionalityInjectionModule and add the island in LoadModule /// </summary> /// <param name="island">The functionality island to add to the list of islands</param> public void AddIsland(FunctionalityIsland island) { // Do not add the default island, as it will be added automatically if (island == m_DefaultIsland) { return; } Assert.IsFalse(m_Loaded, "It is too late to add a functionality island. All modules are loaded"); Assert.IsNotNull(island, "Trying to add an island that is null"); var wasAdded = m_Islands.Add(island); Assert.IsTrue(wasAdded, "Island has already been added"); island.Setup(); }
/// <summary> /// Called before system shuts down /// </summary> void IModule.UnloadModule() { foreach (var island in m_Islands) { if (island) { island.Unload(); } else { Debug.LogError("Encountered a null island during Unload--this should only happen if you recently deleted a Functionality Island asset"); } } activeIsland = null; m_Islands.Clear(); m_Loaded = false; }
public static void GetProvidedTraits(this FunctionalityIsland island, HashSet <TraitDefinition> traits) { if (traits.Count == 0) { return; } foreach (var kvp in island.providers) { var traitsProvider = kvp.Value as IProvidesTraits; if (traitsProvider == null) { continue; } var providedTraits = traitsProvider.GetProvidedTraits(); traits.UnionWith(providedTraits); } }
// We have to set up islands before the other modules get loaded so they can use FI internal void PreLoad() { if (!m_DefaultIsland) { #if UNITY_EDITOR DebugUtils.Log("You must set up a default functionality island. One has been created for you."); m_DefaultIsland = CreateInstance <FunctionalityIsland>(); AssetDatabase.CreateAsset(m_DefaultIsland, k_DefaultIslandPath); EditorUtility.SetDirty(this); if (!Application.isBatchMode) { AssetDatabase.SaveAssets(); } #else Debug.LogError("You must set up a default functionality island."); return; #endif } m_DefaultIsland.Setup(); m_Islands.Add(m_DefaultIsland); activeIsland = m_DefaultIsland; }
public static void DrawProviders(FunctionalityIsland island) { using (new EditorGUI.DisabledScope(true)) { using (new EditorGUI.IndentLevelScope()) { foreach (var row in island.providers) { var provider = row.Value; var providerType = row.Key.GetNameWithGenericArguments(); var unityObject = provider as UnityObject; if (unityObject) { EditorGUILayout.ObjectField(providerType, unityObject, typeof(UnityObject), true); } else { EditorGUILayout.LabelField(providerType, row.Value.GetType().GetNameWithGenericArguments()); } } } } }
void OnActiveIslandChanged(FunctionalityIsland island) { ResetReasoningAPIs(); }
/// <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()); } } } } }